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 +110 -198
- package/dist/ui/assets/{grammar-map-zNF3jzH0.js → grammar-map-BUAa5ICq.js} +1 -1
- package/dist/ui/assets/index-BA5r5TxA.css +1 -0
- package/dist/ui/assets/index-BXDFZiai.js +48 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/sw.js +48 -6
- package/package.json +1 -1
- package/dist/ui/assets/index-Cg14LRiC.css +0 -1
- package/dist/ui/assets/index-CxdjMFJ5.js +0 -45
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
|
-
**
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
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
|
-
##
|
|
32
|
+
## Requirements
|
|
54
33
|
|
|
55
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
explanation rather than failing confusingly later.
|
|
66
|
-
|
|
67
|
-
## Design in one picture
|
|
68
|
-
|
|
69
|
-

|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npm audit signatures
|
|
136
|
-
```
|
|
62
|
+
### Verify what you installed
|
|
137
63
|
|
|
138
|
-
|
|
139
|
-
workflow's OIDC identity rather than a stored token, that this tarball came from
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
**
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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
|
-
|
|
79
|
+
## Why it exists
|
|
154
80
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
170
|
-
|
|
90
|
+

|
|
171
93
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
orchestrator and still needs no configuration:
|
|
103
|
+
## Install it as an app
|
|
182
104
|
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
|
108
|
+
| Browser | How |
|
|
189
109
|
|---|---|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
|
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
searchable by content, with a clear refusal when a session's directory is gone;
|
|
209
|
-
credentials with the host's own credentials denied, and commits
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
251
|
-
| [docs/
|
|
252
|
-
| [docs/
|
|
253
|
-
| [docs/
|
|
254
|
-
| [docs/
|
|
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. **
|
|
262
|
-
|
|
263
|
-
|
|
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
|
|