viewable-one 0.0.0-stage → 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/CHANGELOG.md +1460 -0
- package/LICENSE +21 -0
- package/README.md +755 -2
- package/SPAWN.md +2105 -0
- package/glytch.mjs +517 -0
- package/lib/acp.mjs +978 -0
- package/lib/admission.mjs +105 -0
- package/lib/agent-selection.mjs +123 -0
- package/lib/agents.mjs +913 -0
- package/lib/atomic.mjs +96 -0
- package/lib/auth.mjs +1387 -0
- package/lib/authenticatedHttp.mjs +68 -0
- package/lib/boardside.mjs +225 -0
- package/lib/child.mjs +1252 -0
- package/lib/cli-output.mjs +176 -0
- package/lib/cloudflared-release.json +25 -0
- package/lib/cloudflared.mjs +81 -0
- package/lib/codex-rpc.mjs +92 -0
- package/lib/codex.mjs +747 -0
- package/lib/codexApps.mjs +332 -0
- package/lib/codexPlanWindow.mjs +84 -0
- package/lib/codexToolInputs.mjs +139 -0
- package/lib/codingChecks.mjs +55 -0
- package/lib/compose.mjs +76 -0
- package/lib/computerApps.mjs +110 -0
- package/lib/conversationRules.mjs +45 -0
- package/lib/detect.mjs +605 -0
- package/lib/devserver.mjs +659 -0
- package/lib/digest.mjs +290 -0
- package/lib/doctor.mjs +464 -0
- package/lib/exitClass.mjs +64 -0
- package/lib/glytchFolder.mjs +105 -0
- package/lib/hosted.mjs +112 -0
- package/lib/imageAttachments.mjs +31 -0
- package/lib/init-connect.mjs +418 -0
- package/lib/kill.mjs +104 -0
- package/lib/logindaemon.mjs +976 -0
- package/lib/logs.mjs +120 -0
- package/lib/mcpjson.mjs +242 -0
- package/lib/memberPreview.mjs +165 -0
- package/lib/multiwatch.mjs +428 -0
- package/lib/pagePictures.mjs +396 -0
- package/lib/pageRoutes.mjs +67 -0
- package/lib/paths.mjs +9 -0
- package/lib/permissionAsk.mjs +418 -0
- package/lib/picker.mjs +300 -0
- package/lib/platform.mjs +60 -0
- package/lib/previewGateway.mjs +129 -0
- package/lib/previewTunnel.mjs +85 -0
- package/lib/procs.mjs +428 -0
- package/lib/project-mcp.mjs +288 -0
- package/lib/projectPaths.mjs +31 -0
- package/lib/refusal.mjs +70 -0
- package/lib/registry.mjs +204 -0
- package/lib/repomap.mjs +322 -0
- package/lib/restartPass.mjs +151 -0
- package/lib/retired-address.mjs +78 -0
- package/lib/rowsAnswers.mjs +389 -0
- package/lib/rowsEvents.mjs +86 -0
- package/lib/run-engine.mjs +16 -0
- package/lib/runner-health.mjs +57 -0
- package/lib/runner.mjs +1569 -0
- package/lib/runnerAdmission.mjs +35 -0
- package/lib/runnerApi.mjs +312 -0
- package/lib/runnerCompletion.mjs +406 -0
- package/lib/runnerLease.mjs +37 -0
- package/lib/runnerMaintenance.mjs +27 -0
- package/lib/runnerPace.mjs +283 -0
- package/lib/runnerPerformance.mjs +26 -0
- package/lib/runnerRealtime.mjs +340 -0
- package/lib/runnerRegister.mjs +96 -0
- package/lib/runnerSettings.mjs +40 -0
- package/lib/runnerSettlement.mjs +138 -0
- package/lib/runnerWorkspace.mjs +162 -0
- package/lib/serverSweep.mjs +90 -0
- package/lib/sharedPreview.mjs +292 -0
- package/lib/signIn.mjs +47 -0
- package/lib/spawn.mjs +330 -0
- package/lib/sweep.mjs +161 -0
- package/lib/sync.mjs +484 -0
- package/lib/uninstall-project.mjs +588 -0
- package/lib/uninstall.mjs +457 -0
- package/lib/update.mjs +302 -0
- package/lib/usageLimit.mjs +108 -0
- package/lib/windows-command.mjs +85 -0
- package/lib/worktree.mjs +1138 -0
- package/lib/writePolicy.mjs +45 -0
- package/package.json +58 -4
- package/spawn-runner.mjs +167 -0
package/README.md
CHANGED
|
@@ -1,3 +1,756 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Viewable
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Viewable connects coding agents to the Viewable board over a hosted
|
|
4
|
+
MCP endpoint (`https://app.viewable.one/mcp`), the same one claude.ai,
|
|
5
|
+
Claude Code, Codex and opencode all reach over OAuth. Agents can read the
|
|
6
|
+
board, propose changes, ask you questions on the canvas, and report their
|
|
7
|
+
work. A background runner can also start Claude Code, Codex and opencode
|
|
8
|
+
sessions requested from the board.
|
|
9
|
+
|
|
10
|
+
The package is one command, `viewable`: the background runner, plus `init`,
|
|
11
|
+
`doctor`, `login` and `uninstall` to connect a project, sign this machine in
|
|
12
|
+
and take it all away again. It starts no server of its own. The board tools
|
|
13
|
+
are served from the hosted endpoint, and a run is a row your runner claims,
|
|
14
|
+
so both work the same whether or not the app is open.
|
|
15
|
+
|
|
16
|
+
Linking a folder is for the project files - the file tree, documents,
|
|
17
|
+
briefs on disk. `.glytch/` inside the project holds only the saved agent
|
|
18
|
+
selection, image staging and a repo-map cache: this machine's own scratch,
|
|
19
|
+
never the board.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
You need Node.js 22 or newer and Chrome or Edge for the board's folder link.
|
|
24
|
+
|
|
25
|
+
1. Install the coding agents you want to use - Claude Code, Codex, opencode.
|
|
26
|
+
2. `npm install -g viewable-one@latest` (Windows PowerShell: `npm.cmd i -g viewable-one@latest`).
|
|
27
|
+
Never use sudo. If npm reports a permissions error, use a user-owned prefix.
|
|
28
|
+
Persist its bin directory in your shell startup configuration or Windows user
|
|
29
|
+
PATH. Open a new normal terminal and check `viewable -v` (`viewable.cmd -v`
|
|
30
|
+
in PowerShell) before continuing. A temporary PATH export is not enough.
|
|
31
|
+
3. `viewable init` inside the project folder. Choose one or more agents with
|
|
32
|
+
the arrow keys (`j`/`k` also work); Enter or Space checks the row under the
|
|
33
|
+
cursor, `a` checks or unchecks all, and Enter on the "Continue" row below
|
|
34
|
+
the agents finishes. Without a terminal - a script, CI, a pipe - there is
|
|
35
|
+
no picker to drive: pass `--agents claude,codex` (names, comma separated)
|
|
36
|
+
or `--yes` for every agent this machine already has installed.
|
|
37
|
+
4. Open the board and run an idea on your coding agent. With one project
|
|
38
|
+
connected it is picked for you; with more, pick one in Settings >
|
|
39
|
+
Connection. Linking the folder in the app is optional: it only shows the
|
|
40
|
+
project's files on the board.
|
|
41
|
+
|
|
42
|
+
`viewable init` signs this machine in and then connects each selected agent, so
|
|
43
|
+
a browser opens once for the machine and once per agent rather than leaving
|
|
44
|
+
you a list of commands to run - Claude Code needs 2.1.186 or newer for its
|
|
45
|
+
part, and below that floor `init` prints the `/mcp` step instead.
|
|
46
|
+
|
|
47
|
+
Then restart your coding agent's MCP connection to load the board tools - in
|
|
48
|
+
Codex, the project must also be trusted - and ask it to read the board.
|
|
49
|
+
`viewable doctor` names anything that still needs attention.
|
|
50
|
+
|
|
51
|
+
Repeat `viewable init` in each project you want to connect. Installing the npm
|
|
52
|
+
package and signing this machine in are one-time steps; agent choices and
|
|
53
|
+
board connections are per project.
|
|
54
|
+
|
|
55
|
+
### Windows
|
|
56
|
+
|
|
57
|
+
PowerShell blocks npm's shim by default: `viewable : File ...\viewable.ps1
|
|
58
|
+
cannot be loaded because running scripts is disabled on this system`. Either
|
|
59
|
+
run the command with `.cmd` once (`viewable.cmd init`), or allow your own
|
|
60
|
+
scripts once with `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` and
|
|
61
|
+
use plain `viewable init` from then on. Check `node -v` is 22 or newer first,
|
|
62
|
+
and run `viewable init` from inside the project folder itself, not a parent
|
|
63
|
+
folder or a shortcut to it.
|
|
64
|
+
|
|
65
|
+
## Uninstall
|
|
66
|
+
|
|
67
|
+
`viewable uninstall` is the one command: it disconnects every project this
|
|
68
|
+
machine has registered (agent sign-outs, config entries, `.glytch/agents.json`,
|
|
69
|
+
each folder's registry entry), stops the login daemon and removes its file,
|
|
70
|
+
revokes this machine's board sign-in and forgets the credential, and deletes
|
|
71
|
+
`~/.viewable` entirely. It confirms once - the plan is printed and it asks you
|
|
72
|
+
to type `yes` - before touching anything.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
viewable uninstall
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- `--project` disconnects only the current folder; nothing machine-wide runs.
|
|
79
|
+
- `--keep-login` leaves this machine signed in to the board.
|
|
80
|
+
- `--dry-run` reports what each step would do and changes nothing.
|
|
81
|
+
- `--yes` skips the confirmation prompt (required without a terminal).
|
|
82
|
+
|
|
83
|
+
Two things it cannot do for you, printed at the end: uninstall the npm
|
|
84
|
+
package itself (`npm uninstall -g viewable-one`, or `npm unlink -g viewable-one` from
|
|
85
|
+
`bridge/` in a checkout), and unlink the folder in the app's Project panel.
|
|
86
|
+
|
|
87
|
+
`forget`, `stop` and `logout` remain as the three partial verbs `uninstall`
|
|
88
|
+
runs together - use one of them directly when you want only its own piece.
|
|
89
|
+
|
|
90
|
+
## Choose coding agents
|
|
91
|
+
|
|
92
|
+
| Selection | Project configuration | Board MCP tools | Sessions launched from the board |
|
|
93
|
+
| --- | --- | --- | --- |
|
|
94
|
+
| `claude` | `.mcp.json` | Yes, once you sign in (`/mcp` in a session) | Yes |
|
|
95
|
+
| `codex` | `.codex/config.toml` | Yes, once you sign in (`codex mcp login viewable`) | Yes |
|
|
96
|
+
| `opencode` | `opencode.jsonc` if present, otherwise `opencode.json` | Yes, once you sign in (`opencode mcp auth viewable`) | Yes |
|
|
97
|
+
|
|
98
|
+
The board is one hosted connection now (`https://app.viewable.one/mcp`), not a
|
|
99
|
+
binary this package spawns: `viewable init` writes the URL into each selected
|
|
100
|
+
agent's configuration, and the agent's own OAuth flow is the one-time step
|
|
101
|
+
that finishes connecting it - `viewable doctor` names the same step again if
|
|
102
|
+
you skip it.
|
|
103
|
+
|
|
104
|
+
`viewable init` defaults to detected agents on the first run and the saved
|
|
105
|
+
selection on later runs. You can select an agent before installing it; the
|
|
106
|
+
configuration will be ready, and `doctor` will report the missing executable.
|
|
107
|
+
|
|
108
|
+
For scripts, pass the selection explicitly:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
viewable init --agents claude,codex
|
|
112
|
+
viewable init --agents claude,opencode,codex
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`viewable init --yes` accepts saved choices, or all detected agents when there
|
|
116
|
+
are no saved choices. It fails if neither provides an agent. Without a
|
|
117
|
+
terminal, a first run requires `--agents` or `--yes`; subsequent runs can reuse
|
|
118
|
+
the saved selection without a prompt.
|
|
119
|
+
|
|
120
|
+
Choosing an agent here connects its MCP tools. Which agent runs a prompt is
|
|
121
|
+
chosen separately on the board.
|
|
122
|
+
|
|
123
|
+
## What setup changes
|
|
124
|
+
|
|
125
|
+
`viewable init`:
|
|
126
|
+
|
|
127
|
+
- Saves the selection in `.glytch/agents.json` in the project.
|
|
128
|
+
- Registers the project in `~/.glytch/folders.json`.
|
|
129
|
+
- Adds the board's hosted URL (`https://app.viewable.one/mcp`) to each
|
|
130
|
+
selected agent's project configuration.
|
|
131
|
+
- Signs this machine in when it is not already, the same flow as
|
|
132
|
+
`viewable login`. `--no-login` skips it.
|
|
133
|
+
- Runs each selected and installed agent's own sign-in. `--no-agent-login`
|
|
134
|
+
skips all of them.
|
|
135
|
+
- Installs or repairs one login daemon that watches registered projects.
|
|
136
|
+
- Reports which coding agents are installed and Claude Code's login status.
|
|
137
|
+
|
|
138
|
+
Existing board entries are preserved, including custom commands and disabled
|
|
139
|
+
entries. Other servers and settings are kept; Codex and opencode comments
|
|
140
|
+
are kept too. A file that cannot be safely extended is reported without being
|
|
141
|
+
overwritten. Codex inline tables such as `mcp_servers = {...}` may need to be
|
|
142
|
+
converted to TOML sections before adding a new board entry.
|
|
143
|
+
|
|
144
|
+
One exception to "preserved": an entry written by an older version of this
|
|
145
|
+
package is recognised as its own and upgraded in place on the next `glytch
|
|
146
|
+
init` or `viewable doctor`. That covers both shapes it could be in - an entry
|
|
147
|
+
that spawns a local command, which is rewritten to the hosted URL because no
|
|
148
|
+
such server runs any more, and an entry keyed `board` already sitting at
|
|
149
|
+
the hosted URL, which is renamed to the key this version writes. Any other
|
|
150
|
+
custom entry, including one that merely looks similar, is left untouched.
|
|
151
|
+
|
|
152
|
+
The MCP server key is `viewable`. A `board` entry pointing anywhere but the
|
|
153
|
+
hosted URL is left exactly alone.
|
|
154
|
+
|
|
155
|
+
The board is one hosted connection, so there is no more `PM_BOARD_ROOT` or
|
|
156
|
+
working directory to write into a Codex or opencode board entry, and no
|
|
157
|
+
per-machine path to update if you copy a config or move the project - the URL
|
|
158
|
+
is the same everywhere.
|
|
159
|
+
|
|
160
|
+
What each agent still needs once, per project, is its own sign-in, and `init`
|
|
161
|
+
now runs it for you rather than printing it. Each agent keeps its own token
|
|
162
|
+
under its own account; there is no shared credential written into any config
|
|
163
|
+
file. Claude Code's shell verb needs 2.1.186 or newer - below that, `init`
|
|
164
|
+
prints the `/mcp` step to run inside a session. An agent that already holds a
|
|
165
|
+
token is skipped, except Codex, which does not report whether it holds one,
|
|
166
|
+
so its sign-in runs every time and short-circuits itself. With no terminal,
|
|
167
|
+
or with `--no-agent-login`, `init` prints each command instead of running it,
|
|
168
|
+
and `viewable doctor` names the same step again if you skip it.
|
|
169
|
+
|
|
170
|
+
`viewable doctor` and background repair maintain the saved selection. Changing
|
|
171
|
+
it does not uninstall an existing connection; it stops maintaining deselected
|
|
172
|
+
agents. Older installs retain their legacy Claude/opencode repair behavior
|
|
173
|
+
until `viewable init` saves choices. An invalid selection file stops repair
|
|
174
|
+
rather than silently choosing other agents.
|
|
175
|
+
|
|
176
|
+
### Background runner
|
|
177
|
+
|
|
178
|
+
| Platform | Login daemon |
|
|
179
|
+
| --- | --- |
|
|
180
|
+
| macOS | LaunchAgent at `~/Library/LaunchAgents/ai.glytch.runner.plist` |
|
|
181
|
+
| Windows | Per-user onlogon task `ai.glytch.runner`, registered from `~/.glytch/ai.glytch.runner.xml`, running `~/.glytch/ai.glytch.runner.cmd` |
|
|
182
|
+
| WSL / Linux | User service at `~/.config/systemd/user/ai.glytch.runner.service` |
|
|
183
|
+
|
|
184
|
+
The daemon carries `PM_SPAWN_RUNNER=on` and your shell's PATH. It runs approved
|
|
185
|
+
board requests using your coding agent subscription; installing the bridge
|
|
186
|
+
itself does not make a model call. `viewable stop` stops the daemon. On Linux or
|
|
187
|
+
WSL without a user systemd service, setup prints the manual command:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
PM_SPAWN_RUNNER=on viewable runner watch
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
On Windows the task is registered from an XML definition rather than from
|
|
194
|
+
`schtasks /create /sc onlogon /tr`: the command line names no principal, so
|
|
195
|
+
Task Scheduler registers the logon trigger for the Users group, and writing
|
|
196
|
+
an all-users task into the root of the library needs an administrator. The
|
|
197
|
+
XML names your own account on the trigger and on the principal
|
|
198
|
+
(`InteractiveToken`, `LeastPrivilege`), which is a registration you may make
|
|
199
|
+
yourself. If Task Scheduler still refuses it - a policy, or a task of that
|
|
200
|
+
name owned by another account - `viewable doctor` says so and names the two
|
|
201
|
+
ways out instead of sending you back to `viewable init`: run `viewable init` once
|
|
202
|
+
from a terminal opened with **Run as administrator**, or skip the daemon and
|
|
203
|
+
run the runner by hand:
|
|
204
|
+
|
|
205
|
+
```powershell
|
|
206
|
+
$env:PM_SPAWN_RUNNER="on"; viewable runner watch
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
or, in `cmd.exe`:
|
|
210
|
+
|
|
211
|
+
```bat
|
|
212
|
+
set PM_SPAWN_RUNNER=on && viewable runner watch
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### MCP-only setup by hand
|
|
216
|
+
|
|
217
|
+
For board tools without the background runner, install the package and add
|
|
218
|
+
one of these project configurations directly, then sign in. There is no path
|
|
219
|
+
or working directory to fill in: the board is one hosted URL, the same on
|
|
220
|
+
every machine.
|
|
221
|
+
|
|
222
|
+
Claude Code, in `.mcp.json`:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{ "mcpServers": { "viewable": { "type": "http", "url": "https://app.viewable.one/mcp" } } }
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Then, in a session on this project, run `/mcp` and authenticate the board
|
|
229
|
+
connection.
|
|
230
|
+
|
|
231
|
+
Codex, in `.codex/config.toml`:
|
|
232
|
+
|
|
233
|
+
```toml
|
|
234
|
+
[mcp_servers.viewable]
|
|
235
|
+
url = "https://app.viewable.one/mcp"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Then sign in: `codex mcp login viewable`.
|
|
239
|
+
|
|
240
|
+
opencode, in `opencode.json` or `opencode.jsonc`:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{ "mcp": { "viewable": { "type": "remote", "url": "https://app.viewable.one/mcp", "enabled": true } } }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Then sign in: `opencode mcp auth viewable`.
|
|
247
|
+
|
|
248
|
+
Configuration references: [Codex MCP](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)
|
|
249
|
+
and [opencode MCP](https://opencode.ai/docs/mcp-servers/).
|
|
250
|
+
|
|
251
|
+
## The board tools
|
|
252
|
+
|
|
253
|
+
Fifteen tools reach the board from a session, over the hosted endpoint. They
|
|
254
|
+
work with no app open at all; what to authenticate with when no browser is
|
|
255
|
+
signed in is in [Reading the board with no app
|
|
256
|
+
open](#reading-the-board-with-no-app-open).
|
|
257
|
+
|
|
258
|
+
| tool | what it does |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| `get_board(board_id?, tag?, group?, kind?, since_days?, match?, limit?, view?, geometry?, detail?)` | The board as a readable digest: ids, one line per idea, groups, edges, question state, open flags, ages. Slim by default. The filters narrow it, and the reply always says what it left out. `view:"full"` returns everything, `geometry:true` adds positions and sizes. |
|
|
261
|
+
| `get_node(id)` | One node in full from the graph mirror, with its incoming and outgoing edges. Images attached to the idea come back as image blocks to look at, each labelled with the node and field it came from. An image the mirror does not hold is named as unavailable rather than quietly left out. |
|
|
262
|
+
| `get_library()` | The component library a DRAWN idea is written against, generated from the same registry the app renders with: every tag, its attributes, which are required, a working example, and the rules a drawing must keep. Read it once before passing `snippet` on `addIdea` or `addTextNote`. About 2k tokens, so once per session, not once per drawing. |
|
|
263
|
+
| `propose(ops[], label?)` | Sends board change requests and returns its op id. Every create, edit and delete lands as a proposal you accept or reject. |
|
|
264
|
+
| `ask_user(question, why, options?, from_id?)` | Adds a question to an idea's conversation and returns at once; the answer comes back through `check_answer` or `collect_session`. `why` is required: it is shown under the question and is the context that travels with it. With no `from_id` the question goes on the idea the run belongs to. |
|
|
265
|
+
| `check_answer(question_id, wait_s?)` | Where an earlier question stands: answered with the decision, still open, dismissed, or not yet ingested. With `wait_s` (up to 50) it waits that long for the answer and returns as soon as it lands; an agent that cannot go on without the answer keeps calling it until it is answered or dismissed. |
|
|
266
|
+
| `report(did[], decide?, flags?, title?)` | The end of session report, as ideas rather than a paragraph. See below. |
|
|
267
|
+
| `collect_session(since?, key?)` | What you have said about this session's run: answered questions with the decision, questions still open, and notes on any idea the run produced. Pass `since` for only what is new. |
|
|
268
|
+
| `set_brief_status(id, status, result?)` | Moves a brief through draft, sent or done. Done needs a one line result, which lands as an idea. This applies without a proposal, the way a comment does. |
|
|
269
|
+
| `list_sessions()` | Every run row on this account, in one of three states: open to messages, running one-shot, or finished. Only the first can be talked to. |
|
|
270
|
+
| `mark(kind, ids[], why, evidence?)` | Points at one or more ideas that deserve attention, with a reason and a receipt. It changes nothing on the board: the mark draws as a badge and lands in your inbox, where you act on it or dismiss it. `why` and the evidence are required - a mark is read far from the turn that made it. A pair you already dismissed is not raised again until one of the ideas changes. |
|
|
271
|
+
| `get_log(since?, limit?)` | The calling agent's own log: one line per turn it took, what it read and called, what it produced, what it cost, and what you did with the result afterwards - accepted, rejected, dismissed, edited or deleted, with your reason. Its own rows only, never another agent's. |
|
|
272
|
+
| `send_message(to_session_id, message, from)` | Delivers a message into a running persistent session, which its runner hands over as a user turn within a couple of seconds (the row equivalent of a reply file, `bridge/lib/rowsAnswers.mjs`). Refused for a finished session, a one-shot session and an unknown id, each with the reason. |
|
|
273
|
+
| `open_session(title, brief?)` | Accepted and ignored. Session frames are gone, so this opens nothing; it stays so a session that still calls it is told plainly instead of getting an unknown tool error. |
|
|
274
|
+
| `close_session()` | Accepted and ignored, for the same reason. Calling `report` is the whole ending. |
|
|
275
|
+
|
|
276
|
+
`list_sessions` and `send_message` are a PULL, not a push. Nothing holds a
|
|
277
|
+
session's stdin open over the network, so a session is OPEN TO MESSAGES only
|
|
278
|
+
while it keeps calling tools, STALE once it has gone quiet without closing,
|
|
279
|
+
and FINISHED once it has. `send_message` refuses a STALE or FINISHED target,
|
|
280
|
+
and a message to an OPEN one is delivered the next time that session calls
|
|
281
|
+
`collect_session`.
|
|
282
|
+
|
|
283
|
+
There is deliberately no way to answer a question or run an idea from these
|
|
284
|
+
tools. Questions are yours to answer. A run uses your own Claude Code or Codex plan.
|
|
285
|
+
|
|
286
|
+
Every rule these tools state, they also check. A bad call is refused before
|
|
287
|
+
any file is written and every violation comes back in one message, so nothing
|
|
288
|
+
lands half applied on your board: `report`'s counts, lengths and evidence
|
|
289
|
+
rule, `propose`'s op count, call names and `$name` references, `ask_user`'s
|
|
290
|
+
options and required `why`, `set_brief_status`'s status and result.
|
|
291
|
+
|
|
292
|
+
### Reading the board with no app open
|
|
293
|
+
|
|
294
|
+
Every board read goes over the hosted endpoint. There is no mode to turn on
|
|
295
|
+
and no local board server to start. What decides whether a read has anything
|
|
296
|
+
to authenticate with:
|
|
297
|
+
|
|
298
|
+
1. **`viewable login`** - opens a browser, you sign in and approve, and the
|
|
299
|
+
credential lands in `~/.glytch/auth.json` (mode 0600; `GLYTCH_HOME` moves
|
|
300
|
+
it). This is the OAuth flow claude.ai's own connector uses
|
|
301
|
+
(`supabase/functions/board-oauth`), driven from a terminal instead of a
|
|
302
|
+
phone. `viewable logout` forgets it.
|
|
303
|
+
2. **`GLYTCH_BOARD_TOKEN`**, for a script that would rather not open a
|
|
304
|
+
browser: a personal access token minted in Viewable (**Settings,
|
|
305
|
+
Connection, Access tokens** - it starts with `pmb_`, shown once). It wins
|
|
306
|
+
outright over a `viewable login` credential and needs no file.
|
|
307
|
+
|
|
308
|
+
That bearer is what every board tool call uses, including every call made by
|
|
309
|
+
a session the runner started - its own bearer is resolved the same way, once
|
|
310
|
+
per run (see `bridge/SPAWN.md`).
|
|
311
|
+
|
|
312
|
+
If a read says the token was refused or expired, run `viewable login` again, or
|
|
313
|
+
mint a fresh personal access token and set it again. `viewable doctor`'s board
|
|
314
|
+
sign-in row names the account the credential actually reaches, which is worth
|
|
315
|
+
checking when work lands somewhere you are not looking.
|
|
316
|
+
|
|
317
|
+
### How `ask_user` waits, and `check_answer` collects
|
|
318
|
+
|
|
319
|
+
`ask_user` does not wait. It writes the question note and returns a
|
|
320
|
+
`question_id` (the note's id) at once, because nothing on a stateless HTTP endpoint can hold
|
|
321
|
+
a connection open while you think. `check_answer(question_id)` is the way
|
|
322
|
+
back to that one question's decision, dismissed or open state, and it works
|
|
323
|
+
across restarts because it re-reads the row rather than anything held in
|
|
324
|
+
memory.
|
|
325
|
+
|
|
326
|
+
`collect_session(since?)` is the wider door: everything THIS run's own key
|
|
327
|
+
has asked that now has an answer (every one, every call - there is no time
|
|
328
|
+
filter on an answered question), every message another session sent this
|
|
329
|
+
one (drained, `delivered_at`-stamped, on the way out), and notes written
|
|
330
|
+
on any idea the run produced (these ARE filtered by `since`). Pass the
|
|
331
|
+
`next_since` a previous call returned to see only what is new.
|
|
332
|
+
|
|
333
|
+
**A runner-spawned child gets this pushed, not polled by hand.** While the
|
|
334
|
+
child is still running, the runner itself calls `collect_session` under the
|
|
335
|
+
child's own run key every `PM_ROWS_POLL_MS` (default 5s) and, for anything
|
|
336
|
+
new, writes a message onto the child's stdin the same door a human's typed
|
|
337
|
+
reply uses - an answered question once (the runner's own delivered-set is
|
|
338
|
+
what stops the "every answered question, every call" shape above from
|
|
339
|
+
saying the same ruling to the child twice), a message as `[message from
|
|
340
|
+
<from>] <text>` (`bridge/lib/rowsAnswers.mjs`). After the child exits, ONE
|
|
341
|
+
more `collect_session` call catches anything that arrived in the gap between
|
|
342
|
+
the last live poll and the exit - a persistent session's late answer or
|
|
343
|
+
message becomes a resume request through the same mechanism a reply typed
|
|
344
|
+
into an already-finished session does; a one-shot session has nothing to
|
|
345
|
+
resume into, so it is logged and dropped. See `bridge/SPAWN.md`, "Answers and
|
|
346
|
+
comments coming back", for the full mechanism and its one known gap.
|
|
347
|
+
|
|
348
|
+
### How `report` lands
|
|
349
|
+
|
|
350
|
+
`report` takes three lists, and each lands differently:
|
|
351
|
+
|
|
352
|
+
- **`did[]`** goes to the board's own Viewable Agent, which reads the board and
|
|
353
|
+
places it: an item about an idea that already exists becomes an edit or a
|
|
354
|
+
tag on it, and the rest land as ideas beside their relatives, all as
|
|
355
|
+
proposals you rule on. The result names each idea and where it went, so
|
|
356
|
+
write each line in words that name the ideas it touches.
|
|
357
|
+
- **`decide[]`** lands each fork as a live question note with its own
|
|
358
|
+
durable `question_id`, and each item needs its own `why`, for the same
|
|
359
|
+
reason `ask_user` does.
|
|
360
|
+
- **`flags[]`** lands each one as a note on the idea it is about.
|
|
361
|
+
Every flag must carry evidence the board cannot produce about itself: a
|
|
362
|
+
commit sha, a `file:line`, a test count, or a shipped on date. A flag
|
|
363
|
+
without one is refused.
|
|
364
|
+
|
|
365
|
+
While the app is closed, the report still lands: `did[]` arrives as machine
|
|
366
|
+
ideas inside one titled frame, drawn rather than dropped as plain text,
|
|
367
|
+
pale until you touch them and with nothing to accept. Nothing waits in a
|
|
368
|
+
queue and nothing is lost.
|
|
369
|
+
|
|
370
|
+
`summary` is a deprecated single blob form, kept for a hand rolled MCP client
|
|
371
|
+
that cannot produce `did`.
|
|
372
|
+
|
|
373
|
+
## Running sessions from the board
|
|
374
|
+
|
|
375
|
+
The spawn runner is the outside actor that turns "run a session on this" into
|
|
376
|
+
a real process, because a browser cannot spawn one. The full contract, every
|
|
377
|
+
row it reads and writes and every bound it respects, is in
|
|
378
|
+
[SPAWN.md](./SPAWN.md).
|
|
379
|
+
|
|
380
|
+
A run is a row in Postgres, reached through `supabase/functions/board-runner/`
|
|
381
|
+
(`bridge/lib/runnerApi.mjs`), an ordinary session and a chat turn alike.
|
|
382
|
+
|
|
383
|
+
The shape of a run:
|
|
384
|
+
|
|
385
|
+
1. An idea (or a DM, for a chat turn) files a run row, unapproved.
|
|
386
|
+
It never runs until it is approved.
|
|
387
|
+
2. Approval is the click in the app, or an agent acting under its own caps.
|
|
388
|
+
3. A watcher polls `claim`, which atomically hands back the oldest approved,
|
|
389
|
+
unclaimed, pending row on one of this machine's registered projects -
|
|
390
|
+
claiming a row IS the run's start, with nothing left to wait on. It then
|
|
391
|
+
gives the session its own git worktree and starts the engine. A planning
|
|
392
|
+
draft is the exception: it researches read-only in the project checkout,
|
|
393
|
+
so no worktree is made for it. A chat turn is the other exception: it
|
|
394
|
+
runs in the DM's own long-lived chat worktree and mints no tree per
|
|
395
|
+
message.
|
|
396
|
+
4. The run's card on the board shows it working, driven by the row's own
|
|
397
|
+
`status` and by `run_events` streaming its output live. At the end the
|
|
398
|
+
runner writes ONE `update` carrying the verdict, the cost and the
|
|
399
|
+
sidecar together; `result` is write-once.
|
|
400
|
+
5. The session reports back through `report` before it finishes (a chat
|
|
401
|
+
turn's answer belongs in the room instead - it proposes nothing to the
|
|
402
|
+
board at all).
|
|
403
|
+
|
|
404
|
+
The runner is off by default and has no default on path: nothing spawns
|
|
405
|
+
without `PM_SPAWN_RUNNER=on`, which the login daemon carries in its own file
|
|
406
|
+
so it is set once at install rather than once per session. The MCP server does
|
|
407
|
+
not import the runner, so a session cannot start one however it is prompted.
|
|
408
|
+
|
|
409
|
+
### The engines
|
|
410
|
+
|
|
411
|
+
| engine | what it is |
|
|
412
|
+
| --- | --- |
|
|
413
|
+
| `claude` | Claude Code, and the default. Fenced by a named tool allowlist. |
|
|
414
|
+
| `opencode` | opencode, run with `--auto`, which approves anything not explicitly denied. Select opencode during `viewable init` to register its project board connection. |
|
|
415
|
+
| `codex` | Codex, through its own app-server protocol and your signed-in Codex account. Sandboxed to workspace writes, and it never approves an escalation. Like every engine, its receipts show turns and time, never a cost. See [CODEX.md](CODEX.md). |
|
|
416
|
+
| `acp-claude` | The same Claude Code reached through the Agent Client Protocol adapter instead of the CLI. Same allowlist, answered at the permission door rather than passed as a flag. The cost on its receipt is an estimate, because ACP reports tokens and no dollars, and it is the least exercised of these. |
|
|
417
|
+
|
|
418
|
+
An unknown engine name falls back to the default rather than failing, and the
|
|
419
|
+
receipt says which one ran, so a typo runs something rather than nothing.
|
|
420
|
+
|
|
421
|
+
"Fenced" is narrower than it sounds: the allowlist includes `Bash(node:*)` and
|
|
422
|
+
`Bash(npx:*)`, so a Claude session can run arbitrary code too, and both
|
|
423
|
+
engines can write to any path the process can reach. What bounds the damage is
|
|
424
|
+
the git worktree and the wall clock. opencode also
|
|
425
|
+
namespaces the board tools as `glytch_get_board`, `glytch_report`,
|
|
426
|
+
`glytch_ask_user`, so
|
|
427
|
+
telling an opencode session to call `report` names something that does not
|
|
428
|
+
exist.
|
|
429
|
+
|
|
430
|
+
### Planning drafts
|
|
431
|
+
|
|
432
|
+
A run can ask for a plan instead of an implementation. The session researches
|
|
433
|
+
read-only, writes nothing, and returns the plan as its final answer: findings
|
|
434
|
+
in their own sections, then the work under an `## Implementation plan`
|
|
435
|
+
heading with one `### Step N: ...` heading per item. The app reads that shape
|
|
436
|
+
to lay the plan out for you to edit, approve or reject. An approved plan is
|
|
437
|
+
handed to a second session as the exact instructions to follow.
|
|
438
|
+
|
|
439
|
+
### Reference images
|
|
440
|
+
|
|
441
|
+
An idea can carry images: screenshots, mockups, anything on it or on an idea
|
|
442
|
+
wired into it. Every attached image is delivered or the run is refused before an
|
|
443
|
+
agent starts and before anything is billed, and the refusal names which
|
|
444
|
+
attachment and why. Nothing is silently dropped.
|
|
445
|
+
|
|
446
|
+
- PNG, JPEG, GIF and WebP are supported. Another format, or damaged data,
|
|
447
|
+
refuses the run.
|
|
448
|
+
- Claude Code streaming, Codex and ACP take the images as native image
|
|
449
|
+
content. An engine that has no image channel gets the files written beside
|
|
450
|
+
the run instead, with their paths listed in the prompt.
|
|
451
|
+
- An ACP adapter that does not advertise image support refuses the turn
|
|
452
|
+
rather than sending the text without the pictures.
|
|
453
|
+
- Sessions are told to inspect each reference and state what they see before
|
|
454
|
+
planning or editing, and to compare finished visual work against it.
|
|
455
|
+
|
|
456
|
+
Delivery is checked all the way to the engine. Whether the model then reads
|
|
457
|
+
the picture correctly is the model's own business, so treat a session's
|
|
458
|
+
description of a reference as a claim to verify, not a guarantee.
|
|
459
|
+
|
|
460
|
+
### What it costs
|
|
461
|
+
|
|
462
|
+
Installing the package and exchanging board files make no model calls.
|
|
463
|
+
Your coding agent can still bill for its own work, and the board agent may
|
|
464
|
+
use a model to place reports.
|
|
465
|
+
|
|
466
|
+
Turning on the runner is different: from that point the board can start coding
|
|
467
|
+
agents on your machine, and they bill your subscription. The bounds are real
|
|
468
|
+
and they stay on. Nothing spawns without `PM_SPAWN_RUNNER=on`, nothing starts
|
|
469
|
+
without an approval, each implementing session gets its own git worktree (a
|
|
470
|
+
read-only planning draft stays in the project checkout), a failed session is
|
|
471
|
+
not retried, and editing an idea does not re-run it. Rerun is its own press.
|
|
472
|
+
|
|
473
|
+
One wall bounds a single run: the wall clock, 90 minutes by default and 2
|
|
474
|
+
hours at most. Past it the process is killed. There is no spend or token
|
|
475
|
+
limit on a run; the free AI that comes with a new account is the only limit,
|
|
476
|
+
and it applies to Viewable's free AI, not to your own subscription.
|
|
477
|
+
|
|
478
|
+
What you do not get is a confirmation box per run, deliberately: a dialog you
|
|
479
|
+
click through fifty times is not a safety feature. The cost of a careless run
|
|
480
|
+
is real money, and the run happens the moment you ask for it.
|
|
481
|
+
|
|
482
|
+
## Sessions in a git worktree
|
|
483
|
+
|
|
484
|
+
Dispatched sessions run in their own git worktree by default (read-only
|
|
485
|
+
planning drafts being the exception), and a session's board entry is the
|
|
486
|
+
hosted URL - `{"type":"http","url":"https://app.viewable.one/mcp"}`, or
|
|
487
|
+
`{"type":"remote", ...}` on opencode. There is no local process and no folder
|
|
488
|
+
to point it at, so a session reaches the board over the network the same way
|
|
489
|
+
whichever worktree it is standing in.
|
|
490
|
+
|
|
491
|
+
`PM_BOARD_ROOT` is how you point the runner, or a `bridge/lib` script, at a
|
|
492
|
+
project folder that is not its working directory's: set it in the runner's own
|
|
493
|
+
environment (the login daemon's file, or `PM_SPAWN_RUNNER=on
|
|
494
|
+
PM_BOARD_ROOT=... viewable runner watch` by hand).
|
|
495
|
+
|
|
496
|
+
A session reports by calling the `report` tool. There is no fallback behind
|
|
497
|
+
it: a session that never calls it has told the board nothing.
|
|
498
|
+
|
|
499
|
+
## Commands
|
|
500
|
+
|
|
501
|
+
| command | what it does |
|
|
502
|
+
| --- | --- |
|
|
503
|
+
| `viewable init` | Choose coding agents and connect this project. Safe to run twice. |
|
|
504
|
+
| `viewable init --agents claude,codex` | Configure the named agents without a prompt. |
|
|
505
|
+
| `viewable init --yes` | Use saved choices or detected agents. |
|
|
506
|
+
| `viewable login` | Sign in to the hosted board over OAuth. See ["Reading the board with no app open"](#reading-the-board-with-no-app-open). |
|
|
507
|
+
| `viewable logout` | Forget the signed-in credential (`~/.glytch/auth.json`). |
|
|
508
|
+
| `viewable doctor` | Grouped Project, Runner, Coding agents and System checks. Labels are `OK`, `WARN` or `FAIL`; exit 0 when no check fails. |
|
|
509
|
+
| `viewable forget` | Stop watching this folder. Nothing inside it is deleted: `.mcp.json`, `.glytch/` and every session it holds stay. |
|
|
510
|
+
| `viewable stop` | Stop the login daemon. The file it runs from stays on disk, so `viewable init` brings it back without a reinstall. |
|
|
511
|
+
| `viewable uninstall` | Every registered project, the login daemon, this machine's board sign-in, and `~/.viewable` - one command. Confirms once; `--project`, `--keep-login`, `--dry-run` and `--yes` narrow or skip pieces of it. |
|
|
512
|
+
| `viewable runner ...` | The spawn runner. `watch` with no `PM_BOARD_ROOT` watches every registered folder; with it set, it watches that one. |
|
|
513
|
+
| `glytch mcp` | Retired. Prints one line pointing at `viewable init` and `viewable login`, and exits 1. There is no local board MCP server to start. |
|
|
514
|
+
| `viewable --version` | The installed version. |
|
|
515
|
+
|
|
516
|
+
`pm-spawn-runner` is installed as an alias of `viewable runner`, so anything
|
|
517
|
+
already wired to that name keeps working. The second bin alias every version
|
|
518
|
+
up to 0.6.0 installed is gone from 0.7.0, with the local MCP server it
|
|
519
|
+
started.
|
|
520
|
+
|
|
521
|
+
The runner's own subcommands act on `PM_BOARD_ROOT` or the folder you are
|
|
522
|
+
standing in: `watch` polls and runs what is approved, `once` does one pass
|
|
523
|
+
instead of a loop, and `merge`/`delete-worktree`, each taking a worktree
|
|
524
|
+
path, finish with a session's branch by hand.
|
|
525
|
+
|
|
526
|
+
`request`, `approve`, `status`, `show` and `release` are retired. They filed,
|
|
527
|
+
approved, listed, inspected and released a run as a FILE; a run is a row now,
|
|
528
|
+
and this CLI holds no Supabase JWT to reach the app's own
|
|
529
|
+
`run_request`/`run_approve` RPCs directly. Each prints a one-line pointer at
|
|
530
|
+
`supabase/functions/board-runner/` and exits 1. File and approve a run from
|
|
531
|
+
the board instead.
|
|
532
|
+
|
|
533
|
+
Unchanged one-shot worktrees are removed after a run. Worktrees containing
|
|
534
|
+
commits or uncommitted changes are kept for review. If Git cannot verify the
|
|
535
|
+
changes or comparison base, cleanup keeps the worktree and reports why.
|
|
536
|
+
Persistent prompt sessions and agent chat worktrees stay available for resume;
|
|
537
|
+
a question-only turn does not end that conversation or delete its workspace.
|
|
538
|
+
|
|
539
|
+
Run `viewable update` (`viewable.cmd update` in PowerShell) to install the latest
|
|
540
|
+
published package and restart the computer's connection. It verifies the CLI
|
|
541
|
+
from a new normal terminal before touching the service (with Volta, through
|
|
542
|
+
`volta which viewable`), switches an old npx
|
|
543
|
+
service to the persistent installation, and restarts even when the script path
|
|
544
|
+
and version stay the same. It waits for a successful hosted heartbeat from a
|
|
545
|
+
new process reporting that version before claiming success. Folders, sign-in
|
|
546
|
+
and agent configuration are kept; it does not run init or an agent session.
|
|
547
|
+
|
|
548
|
+
What changed in each version is on the
|
|
549
|
+
[release notes](https://glytch.ai/release-notes) page. To check an update
|
|
550
|
+
worked, open Settings in the app: each connected computer shows the version it
|
|
551
|
+
runs.
|
|
552
|
+
|
|
553
|
+
Older versions (including 0.11.0) have no update command. If `viewable` is missing
|
|
554
|
+
or update is unrecognized, first install with `npm i -g viewable@latest` (Windows:
|
|
555
|
+
`npm.cmd i -g viewable-one@latest`), make the bin directory persistent as above, and
|
|
556
|
+
verify `viewable -v` in a new terminal. Then use `viewable update` once a published
|
|
557
|
+
version supports it. This repository change does not publish that version.
|
|
558
|
+
|
|
559
|
+
The daily version checker stays notice-only. Installation, PATH, restart or
|
|
560
|
+
connection failures report partial completion and a next step; retry update
|
|
561
|
+
after fixing them. Without a working user service (including older WSL without
|
|
562
|
+
systemd), update does not report a verified connection. No command here
|
|
563
|
+
publishes npm or redeploys the app or Supabase functions.
|
|
564
|
+
|
|
565
|
+
`init` also verifies the persistent CLI, compares the loaded process's version
|
|
566
|
+
proof and replaces an older or unverified loaded process even at the same path.
|
|
567
|
+
A process that has attested the installed version is left running, including
|
|
568
|
+
while busy or waiting for its next heartbeat. It waits for its folder's fresh
|
|
569
|
+
successful heartbeat before printing that it is watching. Setup copied from
|
|
570
|
+
the app carries a non-secret attempt id; only a matching verified CLI version
|
|
571
|
+
and that attempt's heartbeat completes the setup screen. Local `cli.json` and
|
|
572
|
+
`health.json` under `~/.viewable` hold version/process proof without credentials.
|
|
573
|
+
|
|
574
|
+
## Where things live
|
|
575
|
+
|
|
576
|
+
- `~/.glytch/folders.json`, the registry of folders the daemon watches, and
|
|
577
|
+
`~/.glytch/logs/runner.log`, rotated at 5 MB with three kept. `GLYTCH_HOME`
|
|
578
|
+
moves both, and the daemon file with them.
|
|
579
|
+
- `.glytch/agents.json`, the project's saved coding-agent selection.
|
|
580
|
+
- The selected agents' project configs, from the setup table above.
|
|
581
|
+
- The login daemon's own file for your platform, from the table above.
|
|
582
|
+
`GLYTCH_NO_DAEMON_INSTALL=1` writes it and registers nothing.
|
|
583
|
+
- `.glytch/images/<id>/` in the linked folder, where an image on its way to a
|
|
584
|
+
run is staged, and the repo-map cache beside it. Both are this machine's own
|
|
585
|
+
scratch. Nothing in this package writes a board file: every board write and
|
|
586
|
+
every run is a row, reached over the hosted endpoint.
|
|
587
|
+
|
|
588
|
+
A second project needs no restart: `viewable init` in another folder adds it to
|
|
589
|
+
the registry, and the daemon is watching that file, so it starts watching the
|
|
590
|
+
new folder within seconds without touching sessions already running elsewhere.
|
|
591
|
+
`viewable forget` is the other half. Nothing needs `launchctl`, `schtasks` or
|
|
592
|
+
`systemctl` by hand.
|
|
593
|
+
|
|
594
|
+
## Troubleshooting
|
|
595
|
+
|
|
596
|
+
`viewable doctor` is the first thing to run. It groups checks under Project,
|
|
597
|
+
Runner, Coding agents and System, and exits 0 only when no check fails. The
|
|
598
|
+
`OK`, `WARN` and `FAIL` labels remain visible without color; interactive
|
|
599
|
+
terminals color them, while piped output is plain. Set `NO_COLOR` to disable
|
|
600
|
+
styling (`NO_COLOR` wins over `FORCE_COLOR`). Run it inside the linked project
|
|
601
|
+
folder, for example `cd ~/Projects/my-project`; from your home folder doctor
|
|
602
|
+
prints a warning because the registry, mirror and heartbeat may belong to a
|
|
603
|
+
different project.
|
|
604
|
+
|
|
605
|
+
The daemon checks are separate: `daemon loaded` means the operating system is
|
|
606
|
+
holding the login service, and `daemon beating` means the board has heard from
|
|
607
|
+
it about this folder recently - which is also how you know the folder is
|
|
608
|
+
linked. Immediately after `viewable init`, a loaded daemon may need a few seconds
|
|
609
|
+
before its first beat; rerun doctor after that grace period. The rows cover the
|
|
610
|
+
platform, folder and agent configuration, daemon PATH, daemon state and
|
|
611
|
+
version, the heartbeat, the board sign-in and which account it reaches, coding
|
|
612
|
+
agents, Claude Code login, git and the npm version.
|
|
613
|
+
|
|
614
|
+
Common failures, and what the row says:
|
|
615
|
+
|
|
616
|
+
| row | what it means, and what to do |
|
|
617
|
+
| --- | --- |
|
|
618
|
+
| `daemon beating never` | The daemon is loaded but has not checked this folder yet, or this folder is not registered. Wait a few seconds and rerun doctor; if it persists, run `viewable init` from this project folder. |
|
|
619
|
+
| `daemon beating stopped` | This folder has not been reported recently; a stale beat alone does not prove the daemon is stopped. If it persists, restart with `viewable stop` then `viewable init --yes`, and inspect the runner log. |
|
|
620
|
+
| `board sign-in signed in as <someone else>` | The credential works, but it reaches a different account from the board you are watching. `viewable logout` then `viewable login` as the right one. |
|
|
621
|
+
| `daemon loaded not loaded` | The OS does not have `ai.glytch.runner`. Run `viewable init` from this project folder. |
|
|
622
|
+
| `daemon loaded no systemd here` | An older WSL with no `systemctl --user`. Run `PM_SPAWN_RUNNER=on viewable runner watch` instead. |
|
|
623
|
+
| `coding agents none` | "no coding agent on PATH - install Claude Code, opencode or Codex". |
|
|
624
|
+
| `Claude Code login logged out` | "run `claude` once and log in, or sessions fail at spawn". |
|
|
625
|
+
| `git missing` (a warning) | "no git - sessions run unisolated in the linked folder, with no worktree of their own". On Windows the row adds that Claude Code's Bash tool needs Git for Windows. |
|
|
626
|
+
| `.mcp.json unreadable` | The file exists and is not JSON glytch can extend. It is your work, so nothing was written: add the `viewable` server by hand. |
|
|
627
|
+
| `version unknown` | npm did not answer. Being offline is not a broken install. |
|
|
628
|
+
|
|
629
|
+
One failure doctor cannot see: an opencode session whose board tools do
|
|
630
|
+
nothing. Run `viewable init` in the folder and include opencode in the
|
|
631
|
+
selection. opencode namespaces the tools as `glytch_get_board`,
|
|
632
|
+
`glytch_report`, `glytch_ask_user` and so on, so a session told to call
|
|
633
|
+
`report` or `get_board` by their plain names finds nothing there.
|
|
634
|
+
|
|
635
|
+
## For maintainers
|
|
636
|
+
|
|
637
|
+
`npm ci` then `npm test` inside `bridge/` runs the suite on Node's built in
|
|
638
|
+
runner: the CLI's commands, the doctor rows, the wall clock, and the spawn
|
|
639
|
+
runner end to end. It also covers the hosted board tools in
|
|
640
|
+
`supabase/functions/board-mcp/tools.mjs`, which live in the app's repository
|
|
641
|
+
but are exercised from here. The spawn runner's tests never touch a real
|
|
642
|
+
account, because `PM_CLAUDE_BIN` points at a fake binary that exits, hangs or
|
|
643
|
+
floods on demand. CI runs everything on `ubuntu-latest` and `windows-latest`,
|
|
644
|
+
and both block.
|
|
645
|
+
|
|
646
|
+
Versions follow semver over the commands, their flags, and what the runner
|
|
647
|
+
promises a session, not the internals: renaming a command, dropping a flag or
|
|
648
|
+
changing that promise is breaking, and rewriting how one of them does its job
|
|
649
|
+
is not. The board tools are served from the hosted endpoint and ship on their
|
|
650
|
+
own schedule, not with this package. [CHANGELOG.md](./CHANGELOG.md) is the
|
|
651
|
+
record, written by hand from the commit bodies.
|
|
652
|
+
|
|
653
|
+
One workflow publishes, **release glytch**, and it has two ways in.
|
|
654
|
+
|
|
655
|
+
The usual one is the Actions tab, Run workflow, and a `patch`, `minor` or
|
|
656
|
+
`major` bump. It installs dependencies, runs tests, updates
|
|
657
|
+
`bridge/package.json` and `bridge/package-lock.json`, rotates the changelog's
|
|
658
|
+
`## [Unreleased]` section into a dated version, commits, tags
|
|
659
|
+
`glytch-v<version>`, pushes and publishes, so pull afterwards. The bump is
|
|
660
|
+
refused when nothing sits under `## [Unreleased]`: a version with no changelog
|
|
661
|
+
entry is a release nobody can read. `scripts/release-glytch.mjs` with a bump
|
|
662
|
+
prepares that same commit locally.
|
|
663
|
+
|
|
664
|
+
The other is a tag, for a version already written into `package.json` and
|
|
665
|
+
already dated in the changelog - there is nothing left to bump, so pushing the
|
|
666
|
+
tag is the whole release:
|
|
667
|
+
|
|
668
|
+
```bash
|
|
669
|
+
git tag -a glytch-v0.8.0 -m "glytch v0.8.0"
|
|
670
|
+
git push origin glytch-v0.8.0
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
The prefix changed on 2026-09-14 when the release path took the package's
|
|
674
|
+
name. Tags cut before then carry the old prefix; the workflow no longer
|
|
675
|
+
answers to them, and they stay in the history as they were cut.
|
|
676
|
+
|
|
677
|
+
It checks the tag against `package.json` before it publishes, because a tag
|
|
678
|
+
pushed by hand can disagree with the file and a publish is the wrong place to
|
|
679
|
+
find that out.
|
|
680
|
+
|
|
681
|
+
Neither path needs an npm token. Publishing is over OIDC trusted publishing,
|
|
682
|
+
switched on once by hand on npmjs.com under the package's Publishing access.
|
|
683
|
+
|
|
684
|
+
Nothing publishes on a bridge commit. This package is what a stranger
|
|
685
|
+
installs, the runner spends that stranger's subscription, and a publish cannot
|
|
686
|
+
be taken back.
|
|
687
|
+
|
|
688
|
+
### Developing against a local app
|
|
689
|
+
|
|
690
|
+
`GLYTCH_APP_URL=http://localhost:<port> viewable login` works once the app's dev server (`npm run dev`, or `npx vite --port <port>`
|
|
691
|
+
from the repo root) is up - `vite.config.ts` generates a dev-server proxy
|
|
692
|
+
from `routes.json`'s own rewrites (`vite/routeProxy.ts`), so the discovery
|
|
693
|
+
document, `/oauth/*` and `/mcp` reach the real, deployed Supabase functions
|
|
694
|
+
instead of falling through to the SPA's `index.html`. The discovery
|
|
695
|
+
document itself still names the production issuer
|
|
696
|
+
(`https://app.viewable.one`) no matter what port the app is running on -
|
|
697
|
+
`board-oauth`'s issuer is a deploy-time config value, not derived from the
|
|
698
|
+
request - so this doesn't stand up a local OAuth server; it's for testing
|
|
699
|
+
the local app's UI against the real, deployed board while signed in.
|
|
700
|
+
|
|
701
|
+
### Automatic preview tunnel setup
|
|
702
|
+
|
|
703
|
+
Installing the package does not download Cloudflare's `cloudflared`; there is no
|
|
704
|
+
install script. The runner downloads it the first time a shared preview starts,
|
|
705
|
+
so that first preview needs the network. If the download fails, the preview does
|
|
706
|
+
not start and the next one tries again; nothing else in the runner needs it.
|
|
707
|
+
No Homebrew, administrator access, or system service is needed.
|
|
708
|
+
|
|
709
|
+
Automatic downloads support macOS x64/ARM64, Linux x64/ARM64, and Windows x64.
|
|
710
|
+
Windows ARM64 has no native binary in this upstream release; unsupported systems
|
|
711
|
+
can supply a compatible executable through `PM_CLOUDFLARED_BIN`. WSL uses Linux.
|
|
712
|
+
|
|
713
|
+
Official release `2026.8.3` assets and SHA-256 digests are pinned in
|
|
714
|
+
`lib/cloudflared-release.json`, from the Cloudflare GitHub release API. Downloads
|
|
715
|
+
are bounded and verified before use, then cached under the user's home directory
|
|
716
|
+
at `.glytch/bin/cloudflared/<version>/<platform>-<arch>/`. Cached content is
|
|
717
|
+
verified and a changed executable is repaired from the verified asset. The
|
|
718
|
+
`PM_CLOUDFLARED_BIN` override bypasses managed setup. Updating the pinned release
|
|
719
|
+
requires a package update. Downloading it does not open a tunnel; startup still
|
|
720
|
+
requires the board-member authorization configuration.
|
|
721
|
+
|
|
722
|
+
Shared previews also require the deployed `preview-grant` authorization
|
|
723
|
+
function and runner configuration: set its public verifier JWK in
|
|
724
|
+
`PREVIEW_VERIFY_JWK` and the
|
|
725
|
+
exact app origins in `PM_PREVIEW_APP_ORIGINS` (comma-separated HTTPS origins;
|
|
726
|
+
localhost loopback is allowed for local development). The tunnel binary alone
|
|
727
|
+
does not provide preview access, and missing configuration fails closed.
|
|
728
|
+
|
|
729
|
+
### Reading setup and health checks
|
|
730
|
+
|
|
731
|
+
`viewable init` and `viewable doctor` group output into Project, Runner, Coding agents,
|
|
732
|
+
and System sections. OK, WARN and FAIL labels remain visible without color;
|
|
733
|
+
interactive terminals add green, yellow and red. Piped output is plain by default.
|
|
734
|
+
`NO_COLOR` disables styling, including when `FORCE_COLOR` is set. Failure summaries
|
|
735
|
+
count the checks that need attention and include a separate Next steps section.
|
|
736
|
+
|
|
737
|
+
Run project commands from the folder linked to your board. A prominent warning
|
|
738
|
+
appears when the current directory is your home folder. A loaded daemon with no
|
|
739
|
+
heartbeat may still be starting; doctor distinguishes that from an unloaded daemon.
|
|
740
|
+
|
|
741
|
+
## License
|
|
742
|
+
|
|
743
|
+
MIT. See [LICENSE](./LICENSE).
|
|
744
|
+
|
|
745
|
+
## Moving from Glytch
|
|
746
|
+
|
|
747
|
+
If you already installed `glytch` globally, run
|
|
748
|
+
`npm install -g viewable-one@latest --force`, then use `viewable` (or the
|
|
749
|
+
compatible `glytch` command). The flag lets npm replace the old command aliases.
|
|
750
|
+
For a clean install, use `npm install -g viewable-one@latest`.
|
|
751
|
+
The last `glytch` package forwards to `viewable-one`. Saved settings, tokens,
|
|
752
|
+
projects and runs stay in `~/.glytch` and each project’s `.glytch` folder;
|
|
753
|
+
existing service and board connection names stay valid. No setup is lost.
|
|
754
|
+
Every `VIEWABLE_*` environment variable takes precedence over its matching
|
|
755
|
+
`GLYTCH_*` variable, which remains supported. New connections use
|
|
756
|
+
`https://app.viewable.one/mcp`; existing saved addresses keep working.
|