Go to file
jay 3e813eb90a sync: release T-1.0 claim, add history entry (@worker-t1.0) 2026-05-15 10:58:07 +02:00
.husky fix install 2026-04-30 23:19:49 +08:00
assets docs: improve README with screenshot, security details, and fixes 2026-03-20 21:15:19 +08:00
docs sync: release T-1.0 claim, add history entry (@worker-t1.0) 2026-05-15 10:58:07 +02:00
extensions/remote-control refactor(T-1.0): carve server.ts into server/ sub-modules 2026-05-15 10:57:52 +02:00
.gitignore chore: initial commit 2026-03-19 10:41:11 +08:00
LICENSE chore: initial commit 2026-03-19 10:41:11 +08:00
Makefile add Makefile for debug the extension only 2026-05-10 21:33:08 +08:00
README.md docs: update README for iOS app direction + sidecar architecture 2026-05-15 04:50:22 +02:00
biome.json chore(remote-control): add Biome and fix all lint warnings 2026-04-21 14:09:42 +08:00
package-lock.json npm audit fix 2026-05-08 18:19:14 +08:00
package.json chore: migrate @mariozechner pi packages to @earendil-works namespace 2026-05-08 18:18:04 +08:00

README.md

pi-remote-control

A pi extension that exposes running sessions over WebSocket — used today as a browser-based remote control, and the foundation for a native iOS app currently in development.

Disclaimer

Personal use and research only. Provided as-is, no liability for damage, misuse, or operational consequences. See Security notes.


Current state: browser client

The extension ships a working HTML/WebSocket client that mirrors a pi session in any browser — including iPhone Safari.

Install

pi install git:git.vpsj.de/jay/pi-remote-control

Usage

Run /remote-control inside pi to open the menu:

  • Turn on / Turn off — start or stop the server
  • Configure URL — set the base URL for your tunnel or proxy
  • Status — show the QR code and current session URL

To start the server automatically:

pi --remote-control

The server binds to 127.0.0.1 and is reached through a local tunnel (e.g. Surge Ponte, Tailscale). Open the QR URL in any browser.

pi remote control on iPhone


In development: native iOS app

A native iOS app is being built on top of this extension's WebSocket infrastructure. Design goals:

  • Byte-exact mirror of the terminal session — what you see over SSH is what you see on the phone, rendered via SwiftTerm.
  • Session persistence — sessions run for days, the app reconnects instantly after backgrounding (< 1s, via sequence-cursor delta replay).
  • Multi-session — spawn, name, and switch between pi sessions from the phone as easily as browser tabs.
  • Pi-aware augmentation — modifier bar tuned for pi (Ctrl, Esc, Tab, arrows, Shift+Enter), slash-command palette, status bar showing what pi is currently doing, push notifications when pi is waiting for input.
  • QR pairing — scan once, self-signed TLS + token pinned automatically.

Architecture (in progress)

pi (Ink TUI) ◄──► tmux session ◄──► SSH (Mac terminal)
                       │
                  tmux -C (control mode)
                       │
               pi-remote sidecar (this extension, extended)
                       │ wss:// (binary ANSI + JSON side-channel)
                       ▼
               iOS app (SwiftUI + SwiftTerm)

Streaming uses tmux control mode (tmux -C) rather than pipe-pane — verified reliable across alternate-screen transitions in a PoC spike.

Implementation docs

All planning and coordination lives in docs/:

File Purpose
docs/NEXT-STEPS.md Current state + what to do next
docs/PHASE-1-sidecar.md Sidecar production-ready
docs/PHASE-2-ios-mvp.md iOS app MVP
docs/PHASE-3-ios-augmentation.md iOS polish features
docs/SYNC.md Multi-agent coordination
docs/reference/SPEC-ios-app.md Full feature spec (v3)

Security notes

  • The server only listens on localhost. Remote access depends on your tunnel.
  • No multi-user authentication. The connection URL is a per-session secret.
  • iOS app (in development) uses bearer tokens stored in the iOS Keychain, self-signed TLS with fingerprint pinning via QR pairing, and optional Face-ID gate — no CA or public PKI required.
  • If using a reverse proxy, terminate TLS there and do not expose the dynamic backend port directly.