- tsconfig.json simulates pi's ESM TypeScript runtime
- Resolves peer deps from pi's global node_modules
- 'type: module' added to package.json (correct — pi loads as ESM)
- Fixes found by tsc:
- buffer/writer.ts: correct Dirent import from node:fs
- messages.ts: toolCall id/name may be undefined, default to empty string
- Remaining warnings: pi event API names (session_switch etc.) not in types;
these are guarded with try/catch at runtime — acceptable
- npm run typecheck: tsc --noEmit
- scripts/smoke/helpers.mjs — spawn-pi (via python3 pty.spawn), wait-for-port,
fetch, WebSocket helpers; createSmokeHome/removeSmokeHome for isolated HOME
- scripts/smoke/smoke.mjs — 6 node:test assertions:
GET /manifest.json → 200 + JSON shape
GET /icon.svg → 200 + <svg body
GET / (with token) → 302→200 + HTML marker
GET / (no token) → 403
WS /ws (with token) → 101 upgrade
pi process alive check
- scripts/smoke/README.md — usage, design notes, extension guide
- package.json: add 'smoke' script
- docs/SYNC.md: add scripts/smoke/** ownership row + History entry
All 6 tests pass in ~1.4 s locally.
Key finding: pi requires a PTY to enter interactive mode and fire
session_start. Spawning without a TTY causes immediate exit. Workaround:
python3 pty.spawn() — allocates a PTY with no additional deps.
Use the 'qrcode' npm package instead of shelling out to the 'qrencode'
binary. Load via createRequire for ESM/CJS interop. Use margin: 2 to
avoid the utf8 renderer's invalid array length bug with odd margins.