@junyoung-kim/reins 0.1.0
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 +229 -0
- package/dist/cli.mjs +8598 -0
- package/dist/cli.mjs.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# reins — keep your AI coding agent on a leash, from your phone
|
|
2
|
+
|
|
3
|
+
> Read in other languages: [한국어](README_KR.md)
|
|
4
|
+
|
|
5
|
+
You started Claude Code (or Codex / Gemini CLI) on your computer, then walked away.
|
|
6
|
+
**reins** lets you watch that session, type into it, and approve what it does —
|
|
7
|
+
from your phone. It hosts your computer's terminal and links it to the phone app
|
|
8
|
+
through a cloud relay (or your LAN). Scan a QR once and you're paired.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx @junyoung-kim/reins
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
That's it. A pairing QR appears in your terminal — scan it with the phone app and
|
|
15
|
+
your shell is now mirrored to your pocket.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What you get
|
|
20
|
+
|
|
21
|
+
- **Mirror your terminal to your phone** — see the live session, type, and approve
|
|
22
|
+
the agent's actions remotely.
|
|
23
|
+
- **One command to host** — `npx @junyoung-kim/reins`, no GUI, no config files required.
|
|
24
|
+
- **Pair by QR** — scan once. The QR is an HTTPS link, so a phone without the app
|
|
25
|
+
installed lands on the install page instead of a dead end.
|
|
26
|
+
- **Keep it running after you log out** — register reins as a background service so
|
|
27
|
+
the connection survives an SSH disconnect or a reboot ([details](#keep-it-running-after-you-log-out)).
|
|
28
|
+
- **Manage machines and settings from the terminal** — add/edit SSH or local
|
|
29
|
+
machines, change the relay, all without leaving the TUI.
|
|
30
|
+
- **A full desktop GUI too**, if you prefer windows over a terminal ([below](#prefer-a-gui-the-desktop-app)).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Install & run
|
|
35
|
+
|
|
36
|
+
You need **Node.js ≥ 18**. Then, in the **real terminal** of the computer you want
|
|
37
|
+
to host from:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx @junyoung-kim/reins # run once, no install
|
|
41
|
+
# or
|
|
42
|
+
npm i -g @junyoung-kim/reins && reins # install globally, then just `reins`
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A pairing QR shows up. The pairing URL is also printed as plain text right below it,
|
|
46
|
+
so you can pair even when a camera can't reach the screen (remote SSH, a tiny
|
|
47
|
+
terminal window) — just open the URL on your phone.
|
|
48
|
+
|
|
49
|
+
**Keys**
|
|
50
|
+
|
|
51
|
+
| Key | Action |
|
|
52
|
+
|-----|--------|
|
|
53
|
+
| `q` | quit |
|
|
54
|
+
| `r` | reconnect to relay |
|
|
55
|
+
| `n` | new QR |
|
|
56
|
+
| `c` | show / hide the QR (URL text is always visible) |
|
|
57
|
+
| `m` | machines (list / add / edit / delete) |
|
|
58
|
+
| `o` | options (relay URL, port, timeouts) |
|
|
59
|
+
| `i` | info (read-only diagnostics) |
|
|
60
|
+
| `a` | toggle auto-start (run as a background service) |
|
|
61
|
+
| `v` | verbose log (`↑ / ↓` to scroll) |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Pairing — how it connects
|
|
66
|
+
|
|
67
|
+
1. reins starts a small local server and connects to the cloud relay as a host.
|
|
68
|
+
2. It shows a **QR** — routed through the cloud once the relay is up, or a **LAN
|
|
69
|
+
fallback** QR before that. Either way it's an HTTPS pairing link
|
|
70
|
+
(`https://arv.juny-api.kr/pair?u=…`), not a raw socket address.
|
|
71
|
+
3. Scan it with the phone app. Your machine list and terminals appear on the phone,
|
|
72
|
+
and you can type and approve from there.
|
|
73
|
+
|
|
74
|
+
The default relay is `wss://juny-api.kr/relay`. You can point reins at your own relay
|
|
75
|
+
(see [Configuration](#configuration)).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Keep it running after you log out
|
|
80
|
+
|
|
81
|
+
If you start reins over SSH and then close that SSH session, reins gets killed along
|
|
82
|
+
with it — the host disappears and your phone can't reconnect. On **Linux (systemd)**,
|
|
83
|
+
reins can register **itself** as a background service that survives logout and reboot.
|
|
84
|
+
|
|
85
|
+
The easy way — press **`a`** in the TUI to turn auto-start on. (It enables boot start,
|
|
86
|
+
then hands off to the background service when you press `q` to quit, so you never run
|
|
87
|
+
two copies at once.)
|
|
88
|
+
|
|
89
|
+
Or from the command line:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm i -g @junyoung-kim/reins # a one-off `npx` cache path can't be a stable service
|
|
93
|
+
reins service install # register + start in the background
|
|
94
|
+
reins service status # is it installed / active / enabled?
|
|
95
|
+
reins service uninstall # stop and remove
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A couple of things to know:
|
|
99
|
+
|
|
100
|
+
- **Survive a full logout, not just reboot** — run this once (needs root):
|
|
101
|
+
`sudo loginctl enable-linger <your-user>`. `reins service install` reminds you if
|
|
102
|
+
it's needed.
|
|
103
|
+
- **Put your relay secret where the service can find it** — the service reads it from
|
|
104
|
+
`~/.ai_remote_vibe_agent/.env` (see [Configuration](#configuration)). `reins service
|
|
105
|
+
install` warns you if it's missing.
|
|
106
|
+
- **Run `reins` again while the service is up** and you won't start a second copy —
|
|
107
|
+
reins opens a small **manager screen** instead, showing service status, recent logs,
|
|
108
|
+
and the pairing QR, with `r` restart · `x` stop · `u` disable · `q` quit.
|
|
109
|
+
|
|
110
|
+
> macOS and Windows don't have this yet — `reins service …` will tell you it's
|
|
111
|
+
> Linux-only for now and exit cleanly, without breaking anything.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Managing machines & options
|
|
116
|
+
|
|
117
|
+
reins is more than a viewer — you can manage everything from the terminal:
|
|
118
|
+
|
|
119
|
+
- **Machines (`m`)** — list, add (SSH or local; key or password), edit, delete. reins
|
|
120
|
+
shares the same machine registry as the desktop app, so changes show up on both
|
|
121
|
+
sides, and live-update when the phone edits the list.
|
|
122
|
+
- **Options (`o`)** — change the relay URL (comma-separate for a fallback), the local
|
|
123
|
+
port, and timeouts. A relay change reconnects right away; port / timeout changes
|
|
124
|
+
apply on the next start.
|
|
125
|
+
- **Info (`i`)** — a read-only screen with the pairing URL, relay status, local server,
|
|
126
|
+
agent identity, and system info. Your relay secret is only ever shown as set or
|
|
127
|
+
missing — never the value.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
reins works out of the box. To connect through the cloud relay (rather than LAN only),
|
|
134
|
+
give it a relay secret. To use a different relay, override the URL.
|
|
135
|
+
|
|
136
|
+
| Variable | What it does |
|
|
137
|
+
|----------|--------------|
|
|
138
|
+
| `MAIN_VITE_RELAY_AGENT_SECRET` (or `VIBE_RELAY_AGENT_SECRET`) | Relay auth secret. Without it, only the LAN-fallback QR works. |
|
|
139
|
+
| `MAIN_VITE_RELAY_URL` (or `VIBE_RELAY_URL`) | Use a different relay (comma-separate for fallbacks). Default: `wss://juny-api.kr/relay`. |
|
|
140
|
+
|
|
141
|
+
Set them as shell environment variables, or put them in a **`.env` file**:
|
|
142
|
+
|
|
143
|
+
```ini
|
|
144
|
+
# ./.env (next to where you run reins), or ~/.ai_remote_vibe_agent/.env
|
|
145
|
+
MAIN_VITE_RELAY_AGENT_SECRET=your-secret-here
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
reins looks for the nearest `.env` walking up from the current directory, then falls
|
|
149
|
+
back to `~/.ai_remote_vibe_agent/.env`. Your shell env always wins, and only the relay
|
|
150
|
+
keys above are read from `.env` (nothing else is injected).
|
|
151
|
+
|
|
152
|
+
> **Running as a service?** The service starts from `/`, so it can't find a `.env` next
|
|
153
|
+
> to you — it reads **only** `~/.ai_remote_vibe_agent/.env`. Put your secret there.
|
|
154
|
+
>
|
|
155
|
+
> **Trust note:** because `.env` is discovered from parent directories, anyone who can
|
|
156
|
+
> place a `.env` above where you run reins can change where your secret is sent. Don't
|
|
157
|
+
> run `npx @junyoung-kim/reins` inside a directory tree you don't trust. The boot log prints the actual
|
|
158
|
+
> `relay target` so you can confirm.
|
|
159
|
+
|
|
160
|
+
Settings (machines, install id, FCM tokens) are stored at
|
|
161
|
+
`~/.ai_remote_vibe_agent/`. The secret is never written into the package or the service
|
|
162
|
+
unit — it stays in your environment.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Prefer a GUI? The desktop app
|
|
167
|
+
|
|
168
|
+
The same host also ships as a full desktop app (`@arva/desktop`, Electron + React) with
|
|
169
|
+
a terminal view, machine list, a cockpit, and settings. Headless reins and the desktop
|
|
170
|
+
app share the **same engine and the same protocol** — pick whichever fits the machine.
|
|
171
|
+
|
|
172
|
+
The phone side is a separate app (`claude_code_mobile`, Flutter) that talks to either
|
|
173
|
+
host over the same WebSocket relay / SSH.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## For developers
|
|
178
|
+
|
|
179
|
+
This repo is a pnpm monorepo (`packages/*`, `apps/*`):
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
ai_remote_vibe_agent/
|
|
183
|
+
├── apps/
|
|
184
|
+
│ ├── desktop/ @arva/desktop — Electron desktop host (GUI)
|
|
185
|
+
│ └── headless/ reins — Ink headless TUI host → has its own README
|
|
186
|
+
├── packages/
|
|
187
|
+
│ ├── core/ @arva/core — createAgentCore: the shared SSH/PTY/WS/relay engine
|
|
188
|
+
│ └── shared/ @arva/shared — protocol types · relay defaults (kept in sync with mobile)
|
|
189
|
+
└── docs/requests/ work requests / history (start from INDEX.md)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`@arva/core` has no Electron dependency — it takes its config/identity via DI, so the
|
|
193
|
+
desktop (electron-store) and headless (JSON file) hosts inject their own backends. The
|
|
194
|
+
types in `@arva/shared` must stay **1:1 with the mobile app** (`[mobile-impact]` commits).
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
pnpm install # whole workspace + desktop electron-rebuild
|
|
198
|
+
pnpm dev # desktop dev (watch)
|
|
199
|
+
pnpm typecheck # tsc --noEmit across packages
|
|
200
|
+
pnpm test # vitest across packages (headless TUI tested with ink-testing-library)
|
|
201
|
+
pnpm lint # biome check
|
|
202
|
+
pnpm --filter @junyoung-kim/reins build # headless → dist/cli.mjs (tsup)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
> The workspace's `node-pty` is `electron-rebuild`-ed for the desktop, so running
|
|
206
|
+
> `pnpm --filter @junyoung-kim/reins dev` (plain Node) **inside the workspace** can crash with an ABI
|
|
207
|
+
> mismatch. Use the clean-install smoke procedure in
|
|
208
|
+
> [`apps/headless/README.md`](apps/headless/README.md) to test the published form.
|
|
209
|
+
|
|
210
|
+
**Releasing**
|
|
211
|
+
|
|
212
|
+
- **Desktop**: `electron-builder`, via GitLab CI (`.gitlab-ci.yml`, with the GitHub
|
|
213
|
+
`release.yml` kept as fallback). Auto-update uses a self-hosted (`arv`) generic feed.
|
|
214
|
+
- **Headless (reins)**: `pnpm --filter @junyoung-kim/reins publish`. `prepack` builds `dist` and the
|
|
215
|
+
tarball ships **only `dist`** — no source, secrets, or native modules. Full steps in
|
|
216
|
+
[`apps/headless/README.md`](apps/headless/README.md).
|
|
217
|
+
|
|
218
|
+
Contribution rules (UI tokens, TSDoc, file-size limits, i18n, cross-platform) live in
|
|
219
|
+
[`CLAUDE.md`](CLAUDE.md) and [`.claude/rules/`](.claude/rules/).
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Reference
|
|
224
|
+
|
|
225
|
+
- [`README_KR.md`](README_KR.md) — 한국어 버전
|
|
226
|
+
- [`apps/headless/README.md`](apps/headless/README.md) — reins run / env / release details
|
|
227
|
+
- [`CLAUDE.md`](CLAUDE.md) · [`.claude/rules/`](.claude/rules/) — project guidelines & coding rules
|
|
228
|
+
- [`docs/requests/INDEX.md`](docs/requests/INDEX.md) — work request / history index
|
|
229
|
+
- Mobile side: `claude_code_mobile` (Flutter) · Base template: https://github.com/daltonmenezes/electron-app
|