@av-pi-studio/server 0.0.10 → 0.0.12

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.
Files changed (53) hide show
  1. package/README.md +61 -17
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agent/agent-service.d.ts +3 -0
  4. package/dist/agent/agent-service.d.ts.map +1 -1
  5. package/dist/agent/agent-service.js +13 -1
  6. package/dist/agent/agent-service.js.map +1 -1
  7. package/dist/agent/provider-registry.d.ts.map +1 -1
  8. package/dist/agent/provider-registry.js +2 -0
  9. package/dist/agent/provider-registry.js.map +1 -1
  10. package/dist/agent/providers/pi/agent.d.ts +2 -0
  11. package/dist/agent/providers/pi/agent.d.ts.map +1 -1
  12. package/dist/agent/providers/pi/agent.js +3 -0
  13. package/dist/agent/providers/pi/agent.js.map +1 -1
  14. package/dist/agent/providers/pi/rpc-transport.d.ts +2 -0
  15. package/dist/agent/providers/pi/rpc-transport.d.ts.map +1 -1
  16. package/dist/agent/providers/pi/rpc-transport.js +10 -2
  17. package/dist/agent/providers/pi/rpc-transport.js.map +1 -1
  18. package/dist/config/daemon-config.d.ts.map +1 -1
  19. package/dist/config/daemon-config.js +5 -0
  20. package/dist/config/daemon-config.js.map +1 -1
  21. package/dist/daemon/bootstrap.d.ts +8 -0
  22. package/dist/daemon/bootstrap.d.ts.map +1 -1
  23. package/dist/daemon/bootstrap.js +55 -13
  24. package/dist/daemon/bootstrap.js.map +1 -1
  25. package/dist/daemon/dev-bootstrap.d.ts +4 -0
  26. package/dist/daemon/dev-bootstrap.d.ts.map +1 -1
  27. package/dist/daemon/dev-bootstrap.js +8 -3
  28. package/dist/daemon/dev-bootstrap.js.map +1 -1
  29. package/dist/daemon/dev-main.js +3 -6
  30. package/dist/daemon/dev-main.js.map +1 -1
  31. package/dist/daemon/main.js +8 -7
  32. package/dist/daemon/main.js.map +1 -1
  33. package/dist/daemon/relay-transport.d.ts +26 -5
  34. package/dist/daemon/relay-transport.d.ts.map +1 -1
  35. package/dist/daemon/relay-transport.js +21 -9
  36. package/dist/daemon/relay-transport.js.map +1 -1
  37. package/dist/logging/logger.d.ts +10 -4
  38. package/dist/logging/logger.d.ts.map +1 -1
  39. package/dist/logging/logger.js +23 -18
  40. package/dist/logging/logger.js.map +1 -1
  41. package/dist/terminal/terminal-manager.d.ts +4 -0
  42. package/dist/terminal/terminal-manager.d.ts.map +1 -1
  43. package/dist/terminal/terminal-manager.js +20 -8
  44. package/dist/terminal/terminal-manager.js.map +1 -1
  45. package/dist/ws/router.d.ts +8 -0
  46. package/dist/ws/router.d.ts.map +1 -1
  47. package/dist/ws/router.js +19 -0
  48. package/dist/ws/router.js.map +1 -1
  49. package/dist/ws/ws-server.d.ts +3 -0
  50. package/dist/ws/ws-server.d.ts.map +1 -1
  51. package/dist/ws/ws-server.js +15 -2
  52. package/dist/ws/ws-server.js.map +1 -1
  53. package/package.json +4 -4
package/README.md CHANGED
@@ -125,16 +125,34 @@ curl http://127.0.0.1:6767/api/health
125
125
 
126
126
  ### Dev daemon
127
127
 
128
- `src/daemon/dev-main.ts` is a development entry that wires the full feature surface and binds
129
- `0.0.0.0` with developer-friendly defaults. From the root:
128
+ `src/daemon/dev-main.ts` is a development entry that wires only a **minimal handler subset**
129
+ (`dev-bootstrap.ts`: agent list/archive/delete, workspaces/projects listing, providers, file
130
+ read/diff, schedule listing — no terminals, git ops, worktrees, chat, loops, or relay), the mock
131
+ provider, and in-memory state, and binds `0.0.0.0` with developer-friendly defaults. From the root:
130
132
 
131
133
  ```bash
132
134
  npm run dev:daemon
133
135
  ```
134
136
 
135
- > The production `bootstrap.ts` and dev `dev-bootstrap.ts` both register the full RPC surface;
136
- > `bootstrap.ts` is production-grade (real provider, disk persistence, config loading) while
137
- > `dev-bootstrap.ts` is for local testing and **must never be imported by `bootstrap.ts`**.
137
+ > `bootstrap.ts` (production) registers the **full** RPC surface — agents, terminals, git/worktrees/
138
+ > GitHub, files, service proxy, schedules/chat/loops, rewind, optional outbound relay — with the
139
+ > real provider and disk persistence. `dev-bootstrap.ts` stays intentionally minimal for fast local
140
+ > iteration and **must never grow to duplicate that surface**; `bootstrap.ts` must never import
141
+ > `dev-bootstrap.ts` (the reverse — `dev-bootstrap.ts` importing one shared helper,
142
+ > `wrapSessionEnvelope`, from `bootstrap.ts` — is fine and is what happens today).
143
+
144
+ ### Docker
145
+
146
+ A production daemon image (multi-stage, compiles the native `node-pty` addon, ships `git` + the
147
+ bundled `pi` runtime) lives at `docker/daemon.Dockerfile`, with a compose file that also runs the
148
+ relay. From the repo root:
149
+
150
+ ```bash
151
+ cd docker && docker compose up --build # daemon :6767 + relay :7000
152
+ ```
153
+
154
+ `$PI_STUDIO_HOME` is `/data` (mount a volume); bind-mount your projects at `/workspace`. See
155
+ `docker/README.md` for the full env/volume/auth/security matrix.
138
156
 
139
157
  ---
140
158
 
@@ -156,6 +174,7 @@ All optional.
156
174
  | `PI_STUDIO_PASSWORD` | _(unset)_ | Require this password for connections (bcrypt-checked) |
157
175
  | `PI_STUDIO_HOSTNAMES` | `localhost,*.localhost` | Allowed `Host` header values (comma-separated, or `true` to allow all) |
158
176
  | `PI_STUDIO_SERVER_ID` | _(persisted/generated)_ | Stable server identity |
177
+ | `PI_STUDIO_RELAY_ENABLED` | `false` | Opt into the outbound relay dial (env equivalent of `daemon.relay.enabled`; `1`/`true`/`yes`/`on`) |
159
178
  | `PI_STUDIO_RELAY_ENDPOINT` | _(unset)_ | Relay server to dial outbound to when `daemon.relay.enabled` (`host:port`) |
160
179
  | `PI_STUDIO_RELAY_USE_TLS` | `false` | Use `wss://` for the outbound relay dial (`1`/`true`/`yes`/`on`) |
161
180
  | `PI_STUDIO_RELAY_PUBLIC_ENDPOINT` | _(unset)_ | Client-facing relay address, if different from the daemon's own dial target |
@@ -163,6 +182,7 @@ All optional.
163
182
  | `PI_STUDIO_SERVICE_PROXY_LISTEN` | _(unset)_ | Service-proxy listen address |
164
183
  | `PI_STUDIO_SERVICE_PROXY_PUBLIC_BASE_URL` | _(unset)_ | Public base URL advertised for proxied services |
165
184
  | `PI_STUDIO_SERVICE_PROXY_ENABLED` | _(unset)_ | Enable the service proxy (`1`/`true`/`yes`/`on`) |
185
+ | `PI_STUDIO_APP_BASE_URL` | `https://app.pi-studio.sh` | Pairing link origin (`daemon pair`) — set to your own web-client URL for self-hosted/local pairing |
166
186
 
167
187
  > Literal IP addresses always pass the Host allowlist, so binding `0.0.0.0` and connecting via the
168
188
  > server's IP needs no extra config. To reach the daemon by **hostname**, add it to
@@ -210,11 +230,24 @@ extend the `pi` provider via `"extends": "pi"` (a custom provider must also set
210
230
 
211
231
  **Relay (opt-in, off by default):** with `daemon.relay.enabled: true`, the daemon dials outbound
212
232
  to the `endpoint` (a self-hosted `@av-pi-studio/relay` server or Cloudflare Workers deployment)
213
- after the WS server is up, so remote clients can reach it without an inbound port. See
233
+ after the WS server is up, so remote clients can reach it without an inbound port. It registers
234
+ under a **deterministic** rendezvous session id derived from its own persistent public key
235
+ (`deriveRelaySessionId`, `@av-pi-studio/relay`) — the same id on every (re)connect, so a pairing
236
+ link printed once (`pi-studio daemon pair`) keeps working across relay drops/restarts. See
214
237
  `@av-pi-studio/relay`'s README for running a relay (`pi-studio-relay` bin / `pi-studio relay
215
238
  start`). Direct WebSocket connections are completely unaffected either way — the relay only adds
216
239
  an additional connection path.
217
240
 
241
+ The same pairing link can be opened by any number of clients over the daemon's lifetime — each
242
+ one gets its own fresh E2EE handshake and its own daemon-side `Session` (see `relay-transport.ts`'s
243
+ `onHandshake` → `bootstrap.ts`'s `resetRelaySession()`); connecting a second client after a first
244
+ one disconnected is a normal, supported reconnection, not a re-pairing.
245
+
246
+ Terminal I/O and file-transfer chunks work over a relay connection too — the daemon and client
247
+ encrypt binary application data as the relay channel's `e2ee_bin` frame (a base64-wrapped JSON
248
+ text frame, not a raw binary WebSocket frame; see `@av-pi-studio/relay`'s README § Wire protocol),
249
+ so no daemon or web-client feature is direct-connection-only.
250
+
218
251
  ---
219
252
 
220
253
  ## The wire API
@@ -301,17 +334,19 @@ All state lives under `$PI_STUDIO_HOME/`. Every write goes through `AtomicStore`
301
334
  (write-to-temp-then-rename) for crash safety.
302
335
 
303
336
  ```
304
- config.json Daemon config (password hash, provider overrides, service proxy, …)
305
- server-id Stable server identity
337
+ config.json Daemon config (password hash, provider overrides, relay, service proxy, …)
338
+ pi-studio.pid PID lock (prevents a second daemon owning this home)
339
+ server-id Stable server identity (plain UUID via randomUUID())
340
+ daemon-keypair.json Persistent Curve25519 keypair (pairing / outbound relay E2EE)
306
341
  logs/ Rotating NDJSON log files (pino)
307
342
  agents/
308
343
  <sanitized-cwd>/
309
344
  <agentId>.json Agent record (status, config, timeline seq, labels, …)
310
345
  chat/rooms.json Chat rooms + messages
311
- loops/<loopId>.json Loop records
346
+ loops/loops.json ALL loop records (single queued-write file, NOT one file per loop)
312
347
  schedules/<scheduleId>.json Schedule records
313
- projects.json Project registry
314
- workspaces.json Workspace registry
348
+ projects/projects.json Project registry
349
+ projects/workspaces.json Workspace registry
315
350
  ```
316
351
 
317
352
  All entity schemas use `.passthrough()` and optional fields — unknown/future fields from a newer
@@ -385,16 +420,24 @@ For a deeper subsystem reference, see [`AGENTS.md`](AGENTS.md) in this package a
385
420
 
386
421
  ## Logging
387
422
 
388
- `createLogger(name, opts)` returns a `pino` logger that writes pretty output to stdout in
389
- development and rotating NDJSON to `$PI_STUDIO_HOME/logs/` in production. The level comes from the
390
- `log.level` config key (default `info`) or `LOG_LEVEL`.
423
+ The daemon logs its full operational lifecycle through one `pino` logger created in the bootstrap
424
+ (`src/logging/logger.ts`): startup (home, config, serverId), agent recovery, WS client
425
+ connect/disconnect (with close code + duration), upgrade/auth rejections, every RPC (at `debug`,
426
+ with duration; failures at `warn`), agent create/turn lifecycle (prompt *sizes*, never contents),
427
+ terminal open/kill/exit, `pi` provider process spawn/exit, and relay dial events.
428
+
429
+ Output goes to **stdout always** — pretty on a TTY, NDJSON otherwise (so `docker logs` /
430
+ journald / PM2 work out of the box) — **plus** a rotating NDJSON file under
431
+ `$PI_STUDIO_HOME/logs/` in production (both destinations, never either/or). Level comes from
432
+ `PI_STUDIO_LOG_LEVEL` (`trace`|`debug`|`info`|`warn`|`error`|`fatal`|`silent`, default `info`);
433
+ `debug` adds per-RPC request lines, `trace` is the most verbose.
391
434
 
392
435
  ---
393
436
 
394
437
  ## Development
395
438
 
396
439
  ```bash
397
- npm test -- --project packages/server # run this package's Vitest suite
440
+ npx vitest run packages/server # run this package's Vitest suite
398
441
  npm run typecheck # tsc -b across all packages
399
442
  npm run lint # oxlint
400
443
  npm run fmt:check # oxfmt --check
@@ -416,8 +459,9 @@ real wall-clock timers in tests — await real completion signals instead.
416
459
  4. **All entity + wire schemas use `.passthrough()` and optional fields** — newer data must load on
417
460
  older daemons.
418
461
  5. **The wire protocol is append-only.** Never remove or narrow a field, never change a discriminant.
419
- 6. **`dev-bootstrap.ts` must not be imported by `bootstrap.ts`.**
462
+ 6. **`bootstrap.ts` must never import `dev-bootstrap.ts`.** (The reverse — `dev-bootstrap.ts`
463
+ importing the shared `wrapSessionEnvelope` helper from `bootstrap.ts` — is fine and is what
464
+ happens today.)
420
465
  7. **Binary frame codecs are cross-platform** (`Uint8Array`, no Node `Buffer`).
421
466
  8. **`~` in `cwd` is expanded server-side** before it reaches a provider.
422
- ```
423
467