pi-central 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,272 +10,184 @@
10
10
  <img alt="license" src="https://img.shields.io/npm/l/pi-central" />
11
11
  </p>
12
12
 
13
- **Pi Central** (/paɪ ˈsɛntrəl/) is a local dashboard for running, watching, and switching between
14
- multiple [Pi](https://pi.dev) coding-agent sessions that live in **different working directories**,
15
- while keeping every session inside its own **Agent Safehouse** sandbox.
13
+ **A local dashboard for running several [Pi](https://pi.dev) coding-agent sessions at once — each in its
14
+ own working directory, each inside its own sandbox.** /paɪ ˈsɛntrəl/
16
15
 
17
- The central station is the design brief: independent lines arrive, are dispatched, and are kept apart
18
- from one another. Pi Central does the same for agent sessions.
16
+ `pi` gives you one process, one session, one directory. Pi Central is the fleet you were missing:
19
17
 
20
- **Status: works end to end, verified, and published.** The version is deliberately not written down
21
- here: `pi-central --version` reports the version of the package it was actually shipped in, and a
22
- number restated in prose is a number that drifts. See [docs/RELEASING.md](docs/RELEASING.md).
18
+ - **See them all.** One rail with every session's state, cost and context burn — instead of three
19
+ terminals and three scrollbacks.
20
+ - **Answer them from one place.** The approval dock handles `confirm`, `select`, `input` and `editor`
21
+ for whichever session is blocked.
22
+ - **Keep them apart.** Each session gets its own Safehouse sandbox for its own directory. They cannot
23
+ read each other's files.
23
24
 
24
- ## Install it as an app
25
-
26
- Pi Central is a web app that installs to your dock. Start it once (see [Quick start](#quick-start)),
27
- open <http://127.0.0.1:8787>, and then install it from the browser's own menu:
28
-
29
- | Browser | How |
30
- |---|---|
31
- | **Safari** | *File → Add to Dock* |
32
- | **Chrome / Edge** | the install icon at the right end of the address bar, or *⋮ → Install page as app* |
33
- | **Firefox** | **not supported** — Mozilla has no PWA install on desktop. The service worker still works; use another browser for the dock icon. |
34
-
35
- You get the π mark in the dock and a window with no tab bar or address field.
36
-
37
- **It is not an offline app, deliberately.** Every screen is fed by the socket, so if the orchestrator is
38
- not running the installed app says `disconnected` and nothing else. A window that claimed to work
39
- offline would open onto a blank dashboard.
40
-
41
- ## The problem
42
-
43
- Pi has no fleet concept. One `pi` process owns one session and one working directory:
44
-
45
- - Sessions are JSONL files grouped by working directory under `~/.pi/agent/sessions/`.
46
- - RPC mode (`pi --mode rpc`) is a long-lived child process speaking JSONL over stdio.
47
- - There is **no daemon, no attach protocol, no socket registry, and no port**. Each RPC process
48
- privately owns its own stdin/stdout.
25
+ **Who this is for.** People who already run `pi` and want more than one session at a time.
26
+ Contributors: jump to [Working on this project](#working-on-this-project).
49
27
 
50
- So working on three repositories means three terminals, three scrollbacks, and no single place to
51
- see what is running, what is waiting for approval, or how much context each session has burned.
28
+ **Status: works end to end, verified, and published.** The version is deliberately not written here —
29
+ `pi-central --version` reports the version of the package it shipped in. See
30
+ [docs/RELEASING.md](docs/RELEASING.md).
52
31
 
53
- ## The non-obvious constraint
32
+ ## Requirements
54
33
 
55
- macOS Seatbelt sandboxing does **not nest**. A process already inside `sandbox-exec` cannot apply a
56
- second profile — `sandbox_init` returns `EPERM`. Safehouse's own profile documents this as not
57
- fixable with additional allow rules.
34
+ Check these first — Pi Central will not run without them, and it is better to know now.
58
35
 
59
- This single fact rules out the obvious design (a Pi extension that hosts other sessions in-process)
60
- and dictates the architecture: **the orchestrator must live outside any sandbox, and each session
61
- must be its own `safehouse` invocation.** See [docs/CONSTRAINTS.md](docs/CONSTRAINTS.md).
62
-
63
- It also means Pi Central must run from a **plain terminal** — not from an editor task, a sandboxed
64
- shell, or an agent harness. It checks this before binding a port and refuses to start with an
65
- explanation rather than failing confusingly later.
66
-
67
- ## Design in one picture
68
-
69
- ![The Pi Central dashboard. On the left, a session rail lists two sessions with their state and
70
- cost. In the middle, a transcript shows the reader's own prompt in a tinted block labelled YOU,
71
- followed by the agent's reasoning, two tool runs, and a live "responding" indicator. Below the
72
- transcript, a tool trace lists the last three commands with their durations. The right-hand panel is
73
- collapsed.](docs/images/dashboard.png)
74
-
75
- *A session mid-run. The rail carries the fleet, the transcript carries the conversation, and the tool
76
- trace carries what the agent actually ran. The right-hand panel — session facts, or a file browser —
77
- is **collapsed by default**; `Show panel` in the top bar opens it.*
78
-
79
- The layers behind that window:
80
-
81
- ```
82
- ┌──────────────────────────────┐
83
- │ Pi Central UI (localhost) │ session rail · transcript · approvals · tool trace
84
- │ React + Vite, TypeScript │ Markdown rendered inert (ADR-0006)
85
- └──────────────┬───────────────┘
86
- │ WebSocket over loopback (typed envelopes)
87
- ┌──────────────▼───────────────┐
88
- │ Orchestrator (unsandboxed) │ session registry · process supervision
89
- │ TypeScript on Node │ · one sandboxed pi process per session
90
- └──────────────┬───────────────┘
91
- │ spawn: safehouse --workdir <cwd> -- pi --mode rpc
92
- ┌─────────┼─────────┐
93
- ▼ ▼ ▼
94
- ┌──────┐ ┌──────┐ ┌──────┐
95
- │ pi │ │ pi │ │ pi │ one sandbox per working directory
96
- │ /a │ │ /b │ │ /c │ isolation preserved per session
97
- └──────┘ └──────┘ └──────┘
98
- ```
99
-
100
- Each session gets a correctly composed Safehouse profile for its own workdir. The orchestrator
101
- never runs untrusted content, so being outside the sandbox is safe by construction.
36
+ | | Why | Check |
37
+ |---|---|---|
38
+ | **macOS** | Safehouse (the sandbox) has no Linux backend yet. | `uname` |
39
+ | **[Pi](https://pi.dev)** | Pi Central supervises `pi --mode rpc`; it is not a fork of it. | `pi --version` |
40
+ | **[Safehouse](https://github.com/earendil-works/safehouse)** | Composes the per-session sandbox profile. | `safehouse --help` |
41
+ | **Node ≥ 22** | The orchestrator runs TypeScript directly. | `node --version` |
102
42
 
103
43
  ## Quick start
104
44
 
105
- **One process.** The orchestrator serves the built UI and the WebSocket from the same origin, so
106
- there is no dev server to start and no port to coordinate. It must run from a plain terminal (see
107
- the constraint above), and it prints the URL it is serving on — <http://127.0.0.1:8787> unless you
108
- passed `--port`.
109
-
110
- ### From npm — what a user does
111
-
112
45
  ```bash
113
46
  npm install --global pi-central # inert: this package runs no install scripts
114
47
  npm audit signatures # verify BEFORE the first run
115
48
  pi-central
116
49
  ```
117
50
 
118
- **The order is the point, and it is only sound because of the comment on line one.** `npm install`
119
- executes no code from this package — it declares no `preinstall`, `install`, `postinstall` or `prepare`
120
- script, and a test asserts that stays true. So there is a window between "the bytes are on disk" and
121
- "anything has run" in which you can check what you got.
51
+ It prints the URL it is serving — <http://127.0.0.1:8787> unless you passed `--port`. One process serves
52
+ both the UI and the socket, so there is no dev server to start.
122
53
 
123
- `npx pi-central` has no such window: it fetches and executes in one step. It is convenient and it is the
124
- least verifiable way to run this. If you use it, **pin an exact version** (`npx pi-central@<version>`, where
125
- `<version>` is what `pi-central --version` reports for the copy you mean) so you at least know which artifact
126
- you asked for. The number is not written here on purpose: a README that restates a version goes stale the
127
- moment the next release ships, and `scripts/check-version.mjs` refuses to let one.
54
+ > **Run it from a plain terminal.** Not from an editor task, a sandboxed shell, or an agent harness.
55
+ > macOS Seatbelt does not nest: `safehouse` applies its policy with `sandbox-exec`, which cannot run
56
+ > inside an existing sandbox. Pi Central checks this before binding a port and refuses with an
57
+ > explanation rather than failing confusingly later. ([C1](docs/CONSTRAINTS.md))
128
58
 
129
59
  Once it is running, [install it as an app](#install-it-as-an-app) and you will not need the terminal
130
60
  again until the next reboot.
131
61
 
132
- #### Verify what you installed
133
-
134
- ```bash
135
- npm audit signatures
136
- ```
62
+ ### Verify what you installed
137
63
 
138
- Every release carries a **provenance attestation** — a signed statement, minted by the release
139
- workflow's OIDC identity rather than a stored token, that this tarball came from this commit via this
140
- workflow. That command checks it, along with the registry signature.
64
+ `npm audit signatures` checks the **provenance attestation** every release carries — a signed statement,
65
+ minted by the release workflow's OIDC identity rather than a stored token, that this tarball came from
66
+ this commit via this workflow.
141
67
 
142
- **Be clear about what this does and does not do.** It is a check you have to run; npm does not refuse
143
- an unattested tarball, and neither does the app. And it answers *"did the publisher produce this?"* —
144
- **not** *"is the publisher trustworthy?"*. Pi Central runs **outside** the sandbox (see the constraint
145
- above) with your privileges, deliberately, because it is the component that spawns the sandboxed
146
- sessions. A malicious release of it would not need an exploit; it would have exactly what you have.
147
- The attestation is what lets you tell a tampered tarball from a genuine one. It cannot make a genuine
148
- tarball safe.
68
+ What it does **not** do: it is a check you have to run (npm does not refuse an unattested tarball), and
69
+ it answers *"did the publisher produce this?"* — not *"is the publisher trustworthy?"*. Pi Central runs
70
+ **outside** the sandbox with your privileges by design, because it is the component that spawns sandboxed
71
+ sessions. The attestation tells a tampered tarball from a genuine one; it cannot make a genuine tarball
72
+ safe.
149
73
 
150
- The package is **one bundled ESM file plus the built UI**, with a single runtime dependency (`ws`),
151
- and `pi-central --version` reports the version of the package it was actually shipped in.
74
+ `npx pi-central` has no window to check in: it fetches and executes in one step. If you use it, pin an
75
+ exact version (`npx pi-central@<version>`, where `<version>` is what `pi-central --version` reports).
76
+ The number is not written here on purpose — a README that restates a version goes stale, and
77
+ `scripts/check-version.mjs` refuses to let one.
152
78
 
153
- ### From a checkout — what a contributor does
79
+ ## Why it exists
154
80
 
155
- ```bash
156
- npm install
157
- npm run ui:build # once, and after any UI change
158
- npm run server # the orchestrator, which also serves the UI
159
- ```
81
+ `pi` has no fleet concept. One process owns one session and one directory: sessions are JSONL files under
82
+ `~/.pi/agent/sessions/`, RPC mode is a long-lived child speaking JSONL over stdio, and there is **no
83
+ daemon, no attach protocol, no socket registry, no port**.
160
84
 
161
- To build the publishable artifact from a checkout:
85
+ So three repositories means three terminals, three scrollbacks, and no single place to see what is
86
+ running, what is waiting for approval, or how much context each session has burned.
162
87
 
163
- ```bash
164
- npm run build:package # bundles the orchestrator, copies the built UI
165
- npm pack --dry-run # show exactly what would ship
166
- npm run serve:package # run the packaged binary from ./bin
167
- ```
88
+ ## How it works
168
89
 
169
- It ships as JavaScript — TypeScript cannot be loaded from `node_modules` — as one bundled ESM file
170
- plus the built UI. See [ADR-0011](docs/decisions/0011-distribution-and-update.md).
90
+ ![Pi Central's architecture: a browser UI talks over a loopback WebSocket to an orchestrator, which
91
+ runs outside any sandbox and spawns one pi process per session, each inside its own Safehouse
92
+ sandbox drawn as a separate dashed frame.](assets/architecture.svg)
171
93
 
172
- Serving the UI from the orchestrator is not only convenience. A Vite dev server watches
173
- `packages/ui/src`, so an agent editing a component reloads the interface you are using out from
174
- under you, mid-edit, into a syntax-error overlay. A built UI removes the watcher. It also makes
175
- the `connect-src 'self'` in the CSP truthful by construction: the socket really is same-origin with
176
- the document. See [docs/DOGFOODING.md](docs/DOGFOODING.md).
94
+ The two boxes on top are ordinary and unremarkable. **The three frames below are the design**: the
95
+ orchestrator sits *outside* every sandbox, and each `pi` sits *inside* its own, with no frame shared
96
+ between them.
177
97
 
178
- ### Working on the UI
98
+ That shape is forced by one fact — **Seatbelt does not nest** ([C1](docs/CONSTRAINTS.md)). A process
99
+ already inside `sandbox-exec` cannot apply a second profile, so an architecture that hosted sessions
100
+ in-process would have no sandbox at all. The orchestrator must live outside and spawn one `safehouse`
101
+ invocation per session. Details in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
179
102
 
180
- For hot reload while editing components, run the dev server instead — it proxies `/ws` to the
181
- orchestrator and still needs no configuration:
103
+ ## Install it as an app
182
104
 
183
- ```bash
184
- npm run server -- --no-ui # terminal 1 — socket only
185
- npm run ui:dev # terminal 2 — Vite, on http://localhost:5173
186
- ```
105
+ Pi Central is a web app that installs to your dock. Start it once (see [Quick start](#quick-start)),
106
+ open <http://127.0.0.1:8787>, then install it from the browser's own menu:
187
107
 
188
- | Key | Action |
108
+ | Browser | How |
189
109
  |---|---|
190
- | `⌘N` | New session in an app-owned scratch workspace — no directory to choose |
191
- | `⌘⇧O` | Open in a directory (the secondary path) |
192
- | `Enter` | Send, or queue while a run is in progress |
193
- | `Esc` | Answer an open dialog → close a popover → stop the run. Never clears your draft |
110
+ | **Safari** | *File → Add to Dock* |
111
+ | **Chrome / Edge** | the install icon at the right end of the address bar, or *⋮ → Install page as app* |
112
+ | **Firefox** | **not supported** — Mozilla has no PWA install on desktop. The service worker still works; use another browser for the dock icon. |
194
113
 
195
- **Settings** (top of the window) shows which build is running, which protocol it speaks, and where
196
- Pi Central keeps sessions, workspaces and the agent root — facts that were previously only in the
197
- terminal's startup output. It has no preference controls yet, deliberately: update checking
198
- ([#55](https://github.com/sven1103-agent/pi-central/issues/55)) will add the first one, and it will be
199
- opt-in.
114
+ You get the π mark in the dock and a window with no tab bar or address field.
115
+
116
+ **It is not an offline app, deliberately.** Every screen is fed by the socket, so if the orchestrator is
117
+ not running the installed app says `disconnected` and nothing else. A window that claimed to work
118
+ offline would open onto a blank dashboard.
200
119
 
201
120
  ## What works, and what does not
202
121
 
203
122
  Stated plainly, because a dashboard that overstates itself is worse than one that does less.
204
123
 
205
- **Works:** multiple sandboxed sessions in different directories with verified isolation; live
206
- transcripts reconstructed from Pi's delta-only stream; the approval dock (`confirm` / `select` /
207
- `input` / `editor`) answerable for any session; a bounded tool trace with full traversal; history
208
- searchable by content, with a clear refusal when a session's directory is gone; agent-scoped
209
- credentials with the host's own credentials denied, and commits signed as the agent when the store is
210
- configured to sign.
124
+ **Works:** multiple sandboxed sessions in different directories with verified isolation; live transcripts
125
+ reconstructed from Pi's delta-only stream; the approval dock answerable for any session; a bounded tool
126
+ trace with full traversal; a read-only file tree and image viewer confined to the session's directory;
127
+ history searchable by content, with a clear refusal when a session's directory is gone; a session roster
128
+ that survives a restart; agent-scoped credentials with the host's own credentials denied, and commits
129
+ signed as the agent when the store is configured to sign.
211
130
 
212
- **Not yet:** no file navigation. It is an issue rather than a wish — see below.
131
+ **Not yet:** no file editing from the browser, no update checking, no export. Each is a tracked issue
132
+ rather than a wish.
213
133
 
214
134
  ## Working on this project
215
135
 
216
- Work is organised as **increments** (GitHub milestones) containing **slices** (issues). A slice is
217
- sized to land in one pull request and to be verifiable on its own.
218
-
219
- - [docs/INCREMENTS.md](docs/INCREMENTS.md) — what each increment delivers and why it is ordered
220
- that way
221
- - Issues — the slices, with acceptance criteria
222
- - [docs/ROADMAP.md](docs/ROADMAP.md) — the long view, including what is deliberately out of scope
136
+ Work is organised as **increments** (GitHub milestones) containing **slices** (issues). A slice is sized
137
+ to land in one pull request and to be verifiable on its own. See
138
+ [docs/INCREMENTS.md](docs/INCREMENTS.md) and [docs/ROADMAP.md](docs/ROADMAP.md).
223
139
 
224
140
  ```bash
141
+ npm install && npm run ui:build && npm run server # from a checkout
225
142
  npm test # unit + component tests, all packages
226
143
  npm run typecheck # two configs: Node libs and DOM libs cannot share one
227
144
  npm run check:docs # every reference in every Markdown file resolves
228
145
  npm run check:version # one version, declared once
229
- npm run ui:build # production build of the UI
230
146
  ```
231
147
 
232
- The `check:docs` step exists because this repository is docs-driven, and a reference to a deleted
233
- file still *looks* authoritative. It has caught real rot more than once. `check:version` exists for
234
- the same reason one level up: the version was previously claimed in three places — `package.json`,
235
- `README.md`, and a `CHANGELOG.md` heading — and three claims are three chances to disagree.
148
+ Serving the built UI from the orchestrator is not only convenience: a Vite dev server watches
149
+ `packages/ui/src`, so an agent editing a component would reload the interface you are using out from
150
+ under you. For hot reload, run the dev server deliberately — see
151
+ [docs/DOGFOODING.md](docs/DOGFOODING.md).
152
+
153
+ ### Keyboard
236
154
 
237
- Releases are described in [docs/RELEASING.md](docs/RELEASING.md).
155
+ | Key | Action |
156
+ |---|---|
157
+ | `⌘N` | New session in an app-owned scratch workspace — no directory to choose |
158
+ | `⌘⇧O` | Open in a directory (the secondary path) |
159
+ | `Enter` | Send, or queue while a run is in progress |
160
+ | `Esc` | Answer an open dialog → close a popover → stop the run. Never clears your draft |
161
+
162
+ **Settings** shows which build is running, which protocol it speaks, where Pi Central keeps sessions,
163
+ workspaces and the agent root, and how to verify the install.
238
164
 
239
165
  ## Documentation
240
166
 
241
167
  | Document | What it covers |
242
168
  |---|---|
243
- | [docs/USER-STORIES.md](docs/USER-STORIES.md) | What users need, and the constraints those needs create |
244
169
  | [docs/CONSTRAINTS.md](docs/CONSTRAINTS.md) | The Seatbelt nesting limit and its consequences (C1–C15c) |
245
170
  | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Components, trust boundaries, lifecycle |
246
- | [docs/STACK.md](docs/STACK.md) | TypeScript/Node orchestrator, React UI, rendering pipeline |
247
- | [docs/PROTOCOL.md](docs/PROTOCOL.md) | The Pi RPC surface Pi Central depends on |
248
- | [docs/SANDBOX.md](docs/SANDBOX.md) | How safehouse is invoked per session, and what the composed policy denies |
171
+ | [docs/SANDBOX.md](docs/SANDBOX.md) | How Safehouse is invoked per session, and what the policy denies |
249
172
  | [docs/AGENT-SETUP.md](docs/AGENT-SETUP.md) | Creating the agent identity a session runs as: SSH key, gitconfig, signing, `gh` |
250
- | [docs/INCREMENTS.md](docs/INCREMENTS.md) | Increments, slices, and how work is sequenced |
251
- | [docs/DOGFOODING.md](docs/DOGFOODING.md) | Using Pi Central to develop Pi Central, and what breaks if you run it from the checkout you edit |
252
- | [docs/RELEASING.md](docs/RELEASING.md) | Cutting a release: the version source, the tag, provenance, and how to roll back |
253
- | [docs/ROADMAP.md](docs/ROADMAP.md) | Phases, verification status, and out-of-scope items |
254
- | [docs/OPEN-QUESTIONS.md](docs/OPEN-QUESTIONS.md) | Unresolved decisions |
173
+ | [docs/PROTOCOL.md](docs/PROTOCOL.md) | The Pi RPC surface Pi Central depends on |
174
+ | [docs/STACK.md](docs/STACK.md) | TypeScript/Node orchestrator, React UI, rendering pipeline |
175
+ | [docs/USER-STORIES.md](docs/USER-STORIES.md) | What users need, and the constraints those needs create |
176
+ | [docs/PRINCIPLES.md](docs/PRINCIPLES.md) | The nine rules, in full |
177
+ | [docs/DOGFOODING.md](docs/DOGFOODING.md) | Using Pi Central to develop Pi Central, and what breaks if you do |
178
+ | [docs/RELEASING.md](docs/RELEASING.md) | Cutting a release: the version source, the tag, provenance, rollback |
179
+ | [docs/INCREMENTS.md](docs/INCREMENTS.md) · [docs/ROADMAP.md](docs/ROADMAP.md) | How work is sequenced, and the long view |
255
180
  | [docs/decisions/](docs/decisions/) | Architecture decision records |
256
181
  | [packages/ui/DESIGN.md](packages/ui/DESIGN.md) | The UI design system, as implemented |
257
182
 
258
183
  ## Principles
259
184
 
185
+ Three that explain most decisions. The full nine are in [docs/PRINCIPLES.md](docs/PRINCIPLES.md).
186
+
260
187
  1. **Isolation is the feature.** Never trade per-session sandboxing for convenience.
261
- 2. **The orchestrator is the trust boundary.** It stays local, holds no untrusted content, and
262
- never executes model output.
263
- 3. **Pi is the source of truth.** Do not re-implement session semantics; consume RPC and the
264
- persisted session tree.
265
- 4. **Additive, not a replacement.** Pi Central supervises `pi`; it does not fork it.
266
- 5. **Local first.** No telemetry, no network egress beyond providers Pi already calls.
267
- 6. **Untrusted content is inert.** Transcripts and file contents are strings to display, never
268
- markup to execute — see [ADR-0006](docs/decisions/0006-untrusted-markdown-rendering.md).
269
- 7. **Confine the agents, not the user.** Safehouse constrains model-generated actions in a workdir.
270
- The orchestrator is a user-level app acting as the user, on the user's files — see
271
- [ADR-0007](docs/decisions/0007-filesystem-access-model.md).
272
- 8. **Never state something false.** A displayed value that is wrong is a defect, not a cosmetic
273
- issue: this project has shipped misleading counts, a mislabelled session id, and a permission
274
- error reported as a missing directory, and treats each as a bug.
275
- 9. **A peer, not a configurator.** Pi Central does not write into a user's Pi environment — no
276
- skills, no extensions, no settings. It reads what exists and writes only its own state. A
277
- defense does not require a new artifact in the environment it defends, and any setup a session
278
- needs is a documented action the user takes (see [docs/AGENT-SETUP.md](docs/AGENT-SETUP.md)).
188
+ 2. **Never state something false.** A displayed value that is wrong is a defect, not a cosmetic issue.
189
+ 3. **A peer, not a configurator.** Pi Central writes nothing into your Pi environment — no skills, no
190
+ extensions, no settings.
279
191
 
280
192
  ## License
281
193