<p align="center">
  <img src="https://raw.githubusercontent.com/jacobaraujo7/remote_pi/main/branding/logo-full.svg" width="160" alt="Remote Pi logo" />
</p>

<h1 align="center">Remote Pi</h1>

> A Pi Extension for controlling the current Pi process from the browser through a Relay.

`/remote-pi` connects the current Pi process to a Relay, supports Owner pairing, and exposes the live timeline plus typed session actions to the Remote Pi PWA.

## Endpoint model

```text
device -> endpoint -> runtime -> session / history generation
```

- **Device**: the computer's Ed25519 identity.
- **Endpoint and runtime**: generated randomly when a Pi process loads the Extension. They remain stable across Extension reloads in that process and are regenerated for the next Pi process.
- **Session / generation**: the active Pi conversation and its current history branch. Remote Pi does not list or resume historical sessions.

The endpoint never derives from the working directory. Pairing QR codes target the current endpoint and runtime, while the resulting Owner authorization is stored at device scope. Later Pi processes on that computer are discovered without pairing again.

## Quick start

Install the Extension once:

```bash
pi install npm:@yefengr/remote-pi
```

Open Pi in the project you want to control. When the session starts, the Extension automatically connects to the configured Relay. Pair the browser device from Pi:

```text
/remote-pi pair
```

Scan the code in the [Remote Pi PWA](https://remote-pi.jacobmoura.work/app), select the endpoint card, and send a prompt. Pairings are local to the computer that creates them:

```text
/remote-pi devices
/remote-pi revoke <shortid>
```

## Commands

| Command | Description |
|---|---|
| `/remote-pi` | Connect the current Pi endpoint after it was stopped |
| `/remote-pi start` / `/remote-pi stop` | Connect or disconnect this endpoint |
| `/remote-pi status` | Show Relay, endpoint, runtime, and Owner state |
| `/remote-pi pair` | Show an endpoint-aware pairing QR |
| `/remote-pi devices` | List locally paired Owners |
| `/remote-pi revoke <shortid>` | Revoke one locally stored Owner |
| `/remote-pi set-relay <url>` | Persist the Relay URL |
| `/remote-pi config` | Show the resolved Relay URL |

The Extension handles remote `session_new` requests in-process through Pi's session API. There is no standalone `remote-pi` CLI, background process, scheduler, or service installation command.

## Relay configuration

The effective Relay URL resolves in this order:

1. `REMOTE_PI_RELAY`
2. `~/.pi/remote/config.json`
3. `https://relay-pi.yefengr.cn`

Set and inspect it from Pi:

```text
/remote-pi set-relay https://relay.example.com
/remote-pi config
```

Only `http://` and `https://` are accepted at the command boundary; WebSocket conversion happens inside the Extension. The Relay forwards opaque payloads and retains endpoint routing state in memory.

## Pairing and security

- `device_id` is the Host Ed25519 public key in canonical Base64 form.
- Owner messages are trusted only through the Relay-injected `source_owner_id`.
- Pairing and revocation update the Relay endpoint ACL with `authorized_owner_ids`.
- Relay loss enters reconnecting state; the Extension does not restart Pi to recover.
- Device private keys, pairing tokens, encrypted payloads, and message bodies are not logged.
- Concurrent Pi processes coordinate device identity initialization through a local lock. If initialization is interrupted, follow the [identity storage and lock recovery rules](../docs/reference/protocol/pairing.md#host); do not delete identity or pairing data to retry.

## Migration from older releases

Older releases could have installed a standalone command and a per-user supervisor. This package does not start, stop, alter, or uninstall those leftovers. After confirming they belong to an older Remote Pi installation, remove them manually:

```bash
# macOS
launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/dev.remotepi.supervisord.plist" 2>/dev/null || launchctl unload "$HOME/Library/LaunchAgents/dev.remotepi.supervisord.plist"
rm -f "$HOME/Library/LaunchAgents/dev.remotepi.supervisord.plist"

# Linux
systemctl --user disable --now remote-pi-supervisord.service
rm -f "$HOME/.config/systemd/user/remote-pi-supervisord.service"
systemctl --user daemon-reload

# Windows PowerShell or Command Prompt
schtasks /End /TN RemotePiSupervisor
schtasks /Delete /TN RemotePiSupervisor /F
```

Do not remove `~/.pi/remote/identity.json` or `peers.json`: they retain the existing device identity and pairings.

## Development

Install dependencies from the repository root with `pnpm install --frozen-lockfile`.
The root workspace owns dependency catalogs, build approvals, and the lockfile. The private workspace package [`@remote-pi/protocol`](../packages/protocol/) provides the shared protocol build artifacts; the Extension uses it as a development dependency, while installed users receive vendored artifacts and need neither the workspace nor a separately published shared package. Its module and distribution boundary is defined in [ARCHITECTURE](../docs/ARCHITECTURE.md#工程与构建边界).

The root `prepare` script and an Extension `pnpm build` build the shared package first. After changing shared sources, run `pnpm --filter @remote-pi/protocol build` from the repository root before an Extension-only `typecheck` or `test`, or use the corresponding root command.

Run these commands from `pi-extension/`, or use the `@yefengr/remote-pi` package filter:

```bash
pnpm typecheck
pnpm test
pnpm build
```

The development toolchain is pinned by the root configuration. The published
extension retains its Node 20+ runtime requirement. TypeScript ESM imports must
use `.js` extensions. `prepack` builds before packaging; `pnpm pack` resolves
catalog and workspace references, and `publish-npm.sh` uploads the inspected tarball.

## License

MIT
