@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 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