@bd7pil/ocrc 0.12.2 → 0.12.4
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 +137 -289
- package/dist/cli/index.js +1 -1
- package/package.json +1 -1
- package/web/dist/_app/immutable/chunks/{Bg7LUw5F.js → 9FNwjwpR.js} +1 -1
- package/web/dist/_app/immutable/chunks/{Ch0wcSIh.js → CEn8-WG3.js} +1 -1
- package/web/dist/_app/immutable/entry/{app.SlL9cDMT.js → app.D1PVdsZ0.js} +2 -2
- package/web/dist/_app/immutable/entry/start.DBrlF_4z.js +1 -0
- package/web/dist/_app/immutable/nodes/{0.ynrdxIlK.js → 0.D8zDxzVz.js} +1 -1
- package/web/dist/_app/immutable/nodes/{1.hazJVhLI.js → 1.DDBSIioJ.js} +1 -1
- package/web/dist/_app/immutable/nodes/{2.D0Cw_lWq.js → 2.CQexb4qk.js} +1 -1
- package/web/dist/_app/immutable/nodes/{3.CQY5wCg0.js → 3.DPl0KnmZ.js} +1 -1
- package/web/dist/_app/version.json +1 -1
- package/web/dist/index.html +8 -8
- package/web/dist/service-worker.js +1 -1
- package/web/dist/_app/immutable/entry/start.DKWIWM1X.js +0 -1
package/README.md
CHANGED
|
@@ -1,338 +1,186 @@
|
|
|
1
1
|
# ocrc — remote control for opencode
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
> notifications, Chinese i18n — transplanted onto ocrc's relay pipeline.
|
|
17
|
-
> 4. **LAN-first web** — binds `0.0.0.0:4099` by default (token gate mandatory,
|
|
18
|
-
> no cloud relay needed); dual light/dark "paper-ink" theme, auto-switched.
|
|
19
|
-
>
|
|
20
|
-
> Fork identity: npm `@bd7pil/ocrc`, config home `~/.ocrc/`, CLI `ocrc`.
|
|
21
|
-
> Functional behaviour otherwise follows upstream's original design — this fork
|
|
22
|
-
> changes identity, defaults and skins, not the interaction model.
|
|
23
|
-
|
|
24
|
-
# OCRC — opencode-remote-control (upstream README follows)
|
|
25
|
-
|
|
26
|
-
> **Drive your local opencode from your phone or browser.** An [opencode](https://opencode.ai)
|
|
27
|
-
> **plugin** that runs a **Telegram bot + a Web PWA** in-process — fire off a prompt from
|
|
28
|
-
> anywhere and watch the assistant code in real time, even when you're away from your desk.
|
|
29
|
-
> Now also drives any [ACP](https://agentclientprotocol.com) agent (e.g. Kimi) in standalone host mode.
|
|
30
|
-
|
|
31
|
-
[](https://github.com/BD7PIL/ocrc/releases)
|
|
32
|
-
[](LICENSE)
|
|
33
|
-
[](https://github.com/BD7PIL/ocrc/actions)
|
|
34
|
-
[](https://opencode.ai)
|
|
35
|
-
[](./CHANGELOG.md)
|
|
3
|
+
> **Drive your local opencode from your phone or browser** — a Telegram bot
|
|
4
|
+
> plus a scan-to-pair Web PWA, running as an **in-process opencode plugin**.
|
|
5
|
+
> One install; both surfaces stream the same live sessions.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/BD7PIL/ocrc/releases)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://github.com/BD7PIL/ocrc/actions)
|
|
10
|
+
[](CHANGELOG.md)
|
|
11
|
+
|
|
12
|
+
A fork of [agentjoey/opencode-remote-control](https://github.com/agentjoey/opencode-remote-control)
|
|
13
|
+
(MIT); the Telegram interaction model is inspired by
|
|
14
|
+
[@grinev/opencode-telegram-bot](https://github.com/grinev/opencode-telegram-bot).
|
|
15
|
+
Full attribution in [NOTICE](NOTICE). npm `@bd7pil/ocrc` · config `~/.ocrc/` · CLI `ocrc`.
|
|
36
16
|
|
|
37
17
|
<p align="center">
|
|
38
|
-
<img src="docs/assets/ocrc-web.png" width="840" alt="
|
|
18
|
+
<img src="docs/assets/ocrc-web.png" width="840" alt="ocrc — the Web PWA driving a live opencode session (sessions, live chat, task & cost inspector)">
|
|
39
19
|
</p>
|
|
40
20
|
|
|
41
|
-
|
|
42
|
-
**Telegram** and a desktop **PWA** at the same time — switch surfaces mid-task without
|
|
43
|
-
losing context.
|
|
21
|
+
## What it is
|
|
44
22
|
|
|
45
|
-
|
|
23
|
+
- **One process.** The plugin loads inside opencode — no daemon, no extra
|
|
24
|
+
services. Telegram and the Web PWA start with opencode and die with it
|
|
25
|
+
(an optional supervisor is available, see [Lifecycle](#lifecycle)).
|
|
26
|
+
- **Two surfaces, one session.** Prompts in from either side; streaming
|
|
27
|
+
output, tool calls, diffs, todos, costs mirror to both in real time.
|
|
28
|
+
- **Local-first, single-user.** Runs on your machine against your local
|
|
29
|
+
opencode server. One allowlisted Telegram user; the web panel is gated by a
|
|
30
|
+
device token. No cloud, no shared backend.
|
|
31
|
+
- **Honest channel status.** Telegram and Web are implemented and stable.
|
|
32
|
+
Feishu / WeChat have a configuration panel pre-wired but the transports
|
|
33
|
+
themselves are **not implemented yet**.
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
`npm i -g @bd7pil/ocrc` — or build from source below. (The
|
|
49
|
-
`opencode-remote-control` name on npm is an unrelated package — don't `npx` it.)
|
|
35
|
+
## Quick start
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
### Mode A — Plugin (default; controls opencode)
|
|
37
|
+
Requires **opencode 1.17+** (verified on 1.18.32) and **Node 20+**.
|
|
54
38
|
|
|
55
39
|
```bash
|
|
56
|
-
# 1. Install
|
|
40
|
+
# 1. Install
|
|
57
41
|
npm i -g @bd7pil/ocrc
|
|
58
|
-
ocrc install
|
|
42
|
+
ocrc install # interactive: Telegram bot token + your user id
|
|
43
|
+
# (the `opencode-remote-control` name on npm is an
|
|
44
|
+
# unrelated package — don't npx it)
|
|
59
45
|
|
|
60
|
-
# 2.
|
|
61
|
-
|
|
62
|
-
```
|
|
46
|
+
# 2. Start (supervised: crash auto-restart)
|
|
47
|
+
ocrc start --watch /path/to/your/project
|
|
63
48
|
|
|
64
|
-
|
|
49
|
+
# 3. Talk to it
|
|
50
|
+
# Telegram: send "hello" to your bot
|
|
51
|
+
# Web: open http://<host>:4099 and pair (see below)
|
|
52
|
+
```
|
|
65
53
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
cd ocrc
|
|
69
|
-
npm install && npm run build:all
|
|
70
|
-
node dist/cli/install.js # interactive — paste your Telegram bot token + user id
|
|
54
|
+
Upgrades: `npm i -g @bd7pil/ocrc@<version>` + restart. The opencode binary
|
|
55
|
+
itself is yours to manage (`opencode upgrade <version>` — pin the version).
|
|
71
56
|
|
|
72
|
-
|
|
73
|
-
```
|
|
57
|
+
## Pairing a device
|
|
74
58
|
|
|
75
|
-
|
|
76
|
-
|
|
59
|
+
Web access is gated by a device token. Two ways to pair — **Telegram is not
|
|
60
|
+
required**:
|
|
77
61
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
1. **From the host terminal** (works always, no Telegram):
|
|
63
|
+
```bash
|
|
64
|
+
ocrc pair # prints a QR + link — scan or open on the device
|
|
65
|
+
```
|
|
66
|
+
2. **From Telegram** (once the bot is running): send `/pair`, open the link.
|
|
81
67
|
|
|
82
|
-
|
|
68
|
+
Links carry a *pending* token — valid **5 minutes, single use**, and issuing
|
|
69
|
+
a new one invalidates the old (refresh = new code). The device exchanges it
|
|
70
|
+
for the real access token on first open; the token never appears in a URL
|
|
71
|
+
again. Already-paired sessions can onboard further devices from the web
|
|
72
|
+
panel's QR (机器人面板 → 配对新设备).
|
|
83
73
|
|
|
84
|
-
|
|
85
|
-
[ACP](https://agentclientprotocol.com) agent like Kimi) with an in-UI switcher —
|
|
86
|
-
no opencode plugin needed. Requires the ACP agent to be installed and logged in
|
|
87
|
-
(e.g. `kimi login`).
|
|
74
|
+
## Lifecycle
|
|
88
75
|
|
|
89
76
|
```bash
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
ocrc start <dir> # start (detached, no supervision)
|
|
78
|
+
ocrc start --watch <dir> # start under the supervisor (recommended)
|
|
79
|
+
ocrc status # server / watcher / web panel at a glance
|
|
80
|
+
ocrc stop # graceful stop (watcher first, then the instance)
|
|
81
|
+
ocrc restart [dir] # restart, keeping the current mode
|
|
82
|
+
ocrc restore # re-launch the last instance under --watch
|
|
96
83
|
```
|
|
97
84
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
## How we're different
|
|
105
|
-
|
|
106
|
-
- **Runs as an opencode plugin, in-process.** One install, no extra process, no
|
|
107
|
-
daemon to babysit — it starts and stops with `opencode` itself.
|
|
108
|
-
- **Telegram + Web from a single codebase.** The same sessions stream live to
|
|
109
|
-
Telegram and a desktop PWA simultaneously; switch surfaces mid-task without
|
|
110
|
-
losing context.
|
|
111
|
-
- **SDK-native.** Built on `@opencode-ai/sdk` and the opencode plugin event
|
|
112
|
-
hook — it speaks opencode's own protocol rather than scraping a UI, so agent /
|
|
113
|
-
model overrides, approvals, diffs, and cost/token metadata all come through
|
|
114
|
-
first-class.
|
|
115
|
-
- **Transport-agnostic core.** A channel-neutral `CardBus` carries structured
|
|
116
|
-
cards; each transport renders them independently. Adding a new channel
|
|
117
|
-
(Discord, Slack, …) doesn't touch the relay core.
|
|
118
|
-
- **Local-first & single-user.** It runs on your machine against your local
|
|
119
|
-
opencode server, stores state in a local file, and answers to one allowlisted
|
|
120
|
-
user. No cloud, no shared backend.
|
|
121
|
-
|
|
122
|
-
## Architecture
|
|
85
|
+
`--watch` is a foreground supervisor with octg-proven semantics: adopts an
|
|
86
|
+
already-running instance, restarts the child after a crash (default 5 s,
|
|
87
|
+
`OCRC_WATCH_DELAY`), and treats SIGKILL / segfaults as crashes — an OOM kill
|
|
88
|
+
self-heals. SIGTERM or the stop file means "stop for real". Boot-time
|
|
89
|
+
recovery is opt-in:
|
|
123
90
|
|
|
124
91
|
```
|
|
125
|
-
|
|
126
|
-
│ opencode (single process) │
|
|
127
|
-
│ │
|
|
128
|
-
│ ┌──────────────────┐ ┌───────────────────────────┐ │
|
|
129
|
-
│ │ AI engine :4096 │ │ plugin: remote-control │ │
|
|
130
|
-
│ │ │ │ ├─ Telegraf (Telegram) │ │
|
|
131
|
-
│ │ event hook ────┼──┼─►├─ Hono + WS (Web PWA) │ │
|
|
132
|
-
│ │ │ │ └─ relay + CardBus │ │
|
|
133
|
-
│ └──────────────────┘ └──────────┬────────────────┘ │
|
|
134
|
-
│ ▼ │
|
|
135
|
-
│ Telegram / Web (PWA) │
|
|
136
|
-
└──────────────────────────────────────────────────────┘
|
|
92
|
+
@reboot sleep 60 && ocrc restore >> ~/.ocrc/prod.log 2>&1
|
|
137
93
|
```
|
|
138
94
|
|
|
139
|
-
|
|
140
|
-
submits prompts via the SDK, consumes streaming events, and renders structured
|
|
141
|
-
cards to whichever transports are enabled. A TUI, if you run one, is just
|
|
142
|
-
another client of the same opencode server.
|
|
143
|
-
|
|
144
|
-
Full deep-dive: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
|
|
145
|
-
|
|
146
|
-
## Quick Start (Telegram)
|
|
147
|
-
|
|
148
|
-
1. **Create a bot** with [@BotFather](https://t.me/BotFather), get a token.
|
|
149
|
-
2. **Find your user ID** — message [@userinfobot](https://t.me/userinfobot).
|
|
150
|
-
3. **Build and install the plugin** (opencode 1.17+):
|
|
151
|
-
```bash
|
|
152
|
-
npm install && npm run build
|
|
153
|
-
node dist/cli/install.js
|
|
154
|
-
```
|
|
155
|
-
The installer writes a plugin bridge to `~/.config/opencode/plugins/`
|
|
156
|
-
(opencode 1.17 loads local plugins from there — directory paths in
|
|
157
|
-
`opencode.json` no longer work), ensures that dir's `package.json` has
|
|
158
|
-
`"type": "module"`, and saves `TELEGRAM_BOT_TOKEN` / `ALLOWED_USER_IDS` /
|
|
159
|
-
`WEB_ENABLED` / `WEB_PORT` to the repo's `.env`.
|
|
160
|
-
4. **Run opencode** from any directory — the plugin loads globally:
|
|
161
|
-
```bash
|
|
162
|
-
opencode
|
|
163
|
-
```
|
|
164
|
-
The plugin auto-starts. For an always-on remote-control hub, run it from a
|
|
165
|
-
small/empty directory (e.g. `~/ocrc-hub`) so opencode's file watcher stays
|
|
166
|
-
fast. You can run several opencode instances — they elect one **PRIMARY**
|
|
167
|
-
(atomic lock at `~/.ocrc/primary.lock`) to own the web (`:4099`) and
|
|
168
|
-
Telegram singletons; the rest stand down PASSIVE. The web/bot can switch
|
|
169
|
-
between the workspaces of the running instances.
|
|
170
|
-
5. **Send "hello"** in Telegram → the assistant responds.
|
|
171
|
-
|
|
172
|
-
## Web UI (PWA)
|
|
95
|
+
## Telegram
|
|
173
96
|
|
|
174
|
-
|
|
175
|
-
|
|
97
|
+
~34 commands, grouped: sessions (`/sessions /session /new /rename /workspaces
|
|
98
|
+
/projects /cleanup`), running work (`/skills /ls /open /worktree /diff /todo
|
|
99
|
+
/context /subs`), controls (`/agent /model /mode /task /tasks /tasklist
|
|
100
|
+
/taskdel /mcps /commands /messages /detach`), ops (`/start /status /version
|
|
101
|
+
/pair /channels /help`), plus `/abort` and plain text relay. Send any text to
|
|
102
|
+
drive the agent; approvals and interactive questions arrive as buttons.
|
|
176
103
|
|
|
177
|
-
|
|
178
|
-
own server occupies `7081`).
|
|
179
|
-
2. Build the web app: `cd web && npm run build`.
|
|
180
|
-
3. The plugin serves the PWA at `http://127.0.0.1:17081`.
|
|
104
|
+
## Web panel
|
|
181
105
|
|
|
182
|
-
|
|
106
|
+
PWA (installable), token-gated, with a live inspector per session: todos,
|
|
107
|
+
MCP servers, schedules, usage/cost, context, working-dir diff, skills, file
|
|
108
|
+
browser, worktrees — plus a floating plan HUD for subagent jumps. The bot
|
|
109
|
+
channels panel configures reply granularity, workspace scope, and shows the
|
|
110
|
+
pairing QR.
|
|
183
111
|
|
|
184
|
-
|
|
185
|
-
`ocrc pair` (or Telegram `/pair`): it prints a URL + QR with the token in the
|
|
186
|
-
fragment (`https://<host>/#token=…`). Open it once — the app stores the token
|
|
187
|
-
and attaches it to every request thereafter. The token is persisted at
|
|
188
|
-
`~/.ocrc/token`, so it survives restarts and re-installs.
|
|
112
|
+
## Remote access
|
|
189
113
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Prefer Cloudflare Access instead? Set `WEB_AUTH=cf-access` with
|
|
194
|
-
`WEB_CF_ACCESS_TEAM` / `WEB_CF_ACCESS_AUD` — see [`docs/OPS.md`](docs/OPS.md).
|
|
195
|
-
|
|
196
|
-
### Remote access without a domain
|
|
197
|
-
|
|
198
|
-
A PWA install needs a **secure context** (HTTPS, or `http://localhost`). To
|
|
199
|
-
reach the hub from another machine without owning a domain:
|
|
114
|
+
The web binds `0.0.0.0:4099` by default (token-gated). For a PWA install you
|
|
115
|
+
need a secure context:
|
|
200
116
|
|
|
201
117
|
| Method | Command | Notes |
|
|
202
118
|
|---|---|---|
|
|
203
|
-
| **
|
|
204
|
-
| **
|
|
205
|
-
| **
|
|
206
|
-
|
|
207
|
-
Set `WEB_PUBLIC_URL` to the resulting HTTPS URL so `/pair` emits the right
|
|
208
|
-
links. (If you already run a `cloudflared` tunnel to `:4099`, `/pair`
|
|
209
|
-
auto-detects its hostname from `~/.cloudflared`.) Plain `http://<LAN-IP>` is
|
|
210
|
-
**not** a secure context — Chrome won't install it as an app.
|
|
211
|
-
|
|
212
|
-
### Install as an app
|
|
213
|
-
|
|
214
|
-
In Chrome: the omnibox install icon, or **⋮ → Cast, save, and share → Install
|
|
215
|
-
page as app…**. The installed PWA reuses the browser's stored token, so it stays
|
|
216
|
-
signed in.
|
|
217
|
-
|
|
218
|
-
## Commands
|
|
219
|
-
|
|
220
|
-
| Command | Description |
|
|
221
|
-
|---|---|
|
|
222
|
-
| `/start` | Handshake + health check |
|
|
223
|
-
| `/status` | Server health, session count, pinned session |
|
|
224
|
-
| `/sessions` | List all sessions with pin buttons |
|
|
225
|
-
| `/session <id>` | Pin a specific session |
|
|
226
|
-
| `/workspaces` | List known workspaces (directories) |
|
|
227
|
-
| `/new` | Start a new session in the active workspace |
|
|
228
|
-
| `/rename <title>` | Rename the pinned session |
|
|
229
|
-
| `/files` | Files touched in the last session |
|
|
230
|
-
| `/diff` | Pending git diff for the session |
|
|
231
|
-
| `/todo` | Session todo list |
|
|
232
|
-
| `/context` | Tokens + cost + model for the session |
|
|
233
|
-
| `/agent` | Set next agent (sticky until cleared) |
|
|
234
|
-
| `/model` | Set next model (sticky until cleared) |
|
|
235
|
-
| `/current` | Show pinned session |
|
|
236
|
-
| `/abort` | Stop the current generation |
|
|
237
|
-
| `/pair` | Pair a device (URL + QR with token) for the Web PWA |
|
|
238
|
-
| `/version` | Plugin version + uptime |
|
|
239
|
-
| `/help` | Show this list |
|
|
240
|
-
|
|
241
|
-
Send any text to relay it into opencode.
|
|
242
|
-
|
|
243
|
-
## Push notifications
|
|
244
|
-
|
|
245
|
-
The plugin watches opencode sessions and proactively pushes summaries:
|
|
246
|
-
|
|
247
|
-
| Trigger | When | Content |
|
|
248
|
-
|---|---|---|
|
|
249
|
-
| Session finished | >60s run completes | Duration + assistant text summary (first 300 chars) |
|
|
250
|
-
| Test failure | Bash output contains FAIL/FAILED | Last 200 chars of output |
|
|
119
|
+
| **LAN, plain HTTP** | open `http://<lan-ip>:4099` | Works in-browser; PWA install needs HTTPS |
|
|
120
|
+
| **Tailscale** | `tailscale serve 4099` | Stable `https://<host>.ts.net`, device auth |
|
|
121
|
+
| **cloudflared** | `cloudflared tunnel --url http://localhost:4099` | Free, URL rotates; set `OCRC_WEB_PUBLIC_URL` |
|
|
251
122
|
|
|
252
|
-
|
|
253
|
-
|
|
123
|
+
## Security model
|
|
124
|
+
|
|
125
|
+
- One allowlisted Telegram user; web devices hold a token generated at first
|
|
126
|
+
start (persisted `0600` at `~/.ocrc/token`), verified with constant-time
|
|
127
|
+
compare on HTTP and WS.
|
|
128
|
+
- Pairing QR links carry a pending token (5 min, single use) — never the
|
|
129
|
+
permanent credential.
|
|
130
|
+
- If the opencode server itself runs with `OPENCODE_SERVER_PASSWORD`, ocrc
|
|
131
|
+
authenticates its server calls with HTTP Basic (same env, no extra config).
|
|
132
|
+
- No cloud. Everything stays on the machine except Telegram API traffic.
|
|
254
133
|
|
|
255
|
-
##
|
|
134
|
+
## Configuration
|
|
256
135
|
|
|
257
|
-
|
|
258
|
-
relaying output to every connected channel in real time.
|
|
136
|
+
Settings live in `~/.ocrc/config.env` (`0600`; `KEY=VALUE`). Highlights:
|
|
259
137
|
|
|
260
|
-
|
|
|
138
|
+
| Key | Default | Notes |
|
|
261
139
|
|---|---|---|
|
|
262
|
-
|
|
|
263
|
-
|
|
|
140
|
+
| `TELEGRAM_BOT_TOKEN` | — | required for the Telegram surface |
|
|
141
|
+
| `ALLOWED_USER_IDS` | — | comma-separated Telegram user ids |
|
|
142
|
+
| `OCRC_WEB_ENABLED` | `true` | web panel on/off |
|
|
143
|
+
| `OCRC_WEB_PORT` | `4099` | web panel port |
|
|
144
|
+
| `OCRC_WEB_HOST` | `0.0.0.0` | bind address |
|
|
145
|
+
| `OCRC_SERVER_PORT` | `4096` | opencode server port (lifecycle commands) |
|
|
146
|
+
| `OCRC_SERVER_BIN` | `~/.local/bin/opencode` | binary used by `ocrc start` |
|
|
147
|
+
| `OCRC_WATCH_DELAY` | `5` | supervisor restart delay (s) |
|
|
148
|
+
| `OPENCODE_SERVER_PASSWORD` | — | enables HTTP Basic for server calls |
|
|
149
|
+
| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
|
|
150
|
+
|
|
151
|
+
Legacy `WEB_*` names are honored as fallbacks. Full list:
|
|
152
|
+
[`docs/OPS.md`](docs/OPS.md).
|
|
264
153
|
|
|
265
|
-
|
|
266
|
-
[`docs/transports/CONTRIBUTING-NEW-TRANSPORT.md`](docs/transports/CONTRIBUTING-NEW-TRANSPORT.md).
|
|
154
|
+
## Architecture
|
|
267
155
|
|
|
268
|
-
|
|
156
|
+
```
|
|
157
|
+
┌────────────────────────────────────────────────────┐
|
|
158
|
+
│ opencode (single process) │
|
|
159
|
+
│ │
|
|
160
|
+
│ ┌─────────────────┐ ┌──────────────────────────┐ │
|
|
161
|
+
│ │ AI engine :4096 │ │ plugin: ocrc │ │
|
|
162
|
+
│ │ │ │ ├─ grammY (Telegram) │ │
|
|
163
|
+
│ │ event hook ────┼──┼─►├─ Hono + WS (Web PWA) │ │
|
|
164
|
+
│ │ │ │ └─ relay + CardBus │ │
|
|
165
|
+
│ └─────────────────┘ └─────────┬────────────────┘ │
|
|
166
|
+
│ ▼ │
|
|
167
|
+
│ Telegram / Web (PWA) │
|
|
168
|
+
└────────────────────────────────────────────────────┘
|
|
169
|
+
```
|
|
269
170
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
stores state in a local JSON file (`data/state.json`).
|
|
274
|
-
- **No secrets in repo.** `.env` is gitignored; `.env.example` documents every
|
|
275
|
-
variable.
|
|
276
|
-
- **Web auth is pluggable.** Default is an app **token** (auto-generated,
|
|
277
|
-
persisted `0600` at `~/.ocrc/token`), verified on HTTP and WS with a
|
|
278
|
-
constant-time compare; `WEB_AUTH=cf-access` switches to Cloudflare Access. The
|
|
279
|
-
dev bypass only trusts a real loopback peer (never the bind address) and is
|
|
280
|
-
**off by default**.
|
|
281
|
-
|
|
282
|
-
## Environment variables
|
|
283
|
-
|
|
284
|
-
| Variable | Default | Description |
|
|
285
|
-
|---|---|---|
|
|
286
|
-
| `TELEGRAM_BOT_TOKEN` | — (required) | Telegram bot token from @BotFather |
|
|
287
|
-
| `ALLOWED_USER_IDS` | — (required) | Comma-separated allowed Telegram user IDs |
|
|
288
|
-
| `OPENCODE_BASE_URL` | — | opencode server URL. Unused in plugin mode (the SDK client is injected); legacy/sidecar only |
|
|
289
|
-
| `CHAT_TIMEOUT_MS` | `600000` | Per-message timeout (ms) |
|
|
290
|
-
| `TUI_VISIBLE` | `true` | Navigate the TUI to the target session; `false` = pure direct API |
|
|
291
|
-
| `STATE_PATH` | `./data/state.json` | Persistent state file |
|
|
292
|
-
| `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` |
|
|
293
|
-
| `TG_CHUNK_SOFT_LIMIT` | `3500` | Telegram message pagination soft limit |
|
|
294
|
-
| **Web** |||
|
|
295
|
-
| `WEB_ENABLED` | `false` | Enable the Web transport |
|
|
296
|
-
| `WEB_HOST` | `127.0.0.1` | Web bind address (keep loopback; front with a tunnel) |
|
|
297
|
-
| `WEB_PORT` | `17081` | Web port (opencode 1.17's own server occupies `7081`) |
|
|
298
|
-
| `WEB_AUTH` | `token` | Auth strategy: `token` (app token) or `cf-access` |
|
|
299
|
-
| `WEB_TOKEN` | auto | App token; auto-generated and persisted `0600` at `~/.ocrc/token` if unset |
|
|
300
|
-
| `WEB_PUBLIC_URL` | — | Public URL for pairing/QR; falls back to LAN, then loopback |
|
|
301
|
-
| `WEB_STATIC_ROOT` | `<repo>/web/dist` | Built PWA path (resolved from the plugin dir, cwd-independent) |
|
|
302
|
-
| `WEB_SESSION_CACHE_SIZE` | `100` | Per-session card ring-buffer size |
|
|
303
|
-
| `WEB_CF_ACCESS_TEAM` | — | Cloudflare Access team name (when `WEB_AUTH=cf-access`) |
|
|
304
|
-
| `WEB_CF_ACCESS_AUD` | — | Cloudflare Access app AUD tag (when `WEB_AUTH=cf-access`) |
|
|
305
|
-
| `WEB_CF_ACCESS_DEV_BYPASS` | `false` | Bypass auth **only for a loopback socket peer** |
|
|
306
|
-
|
|
307
|
-
## opencode 1.17+ notes
|
|
308
|
-
|
|
309
|
-
opencode 1.17 changed plugin loading; this project accounts for all of it:
|
|
310
|
-
|
|
311
|
-
- **Local plugins load from `~/.config/opencode/plugins/`**, not from directory
|
|
312
|
-
paths in `opencode.json`. The installer writes a bridge file there that
|
|
313
|
-
re-invokes the built `dist/` (the source of truth — rebuild + restart to
|
|
314
|
-
update). 1.17 also only calls functions *defined* in the loaded module, so the
|
|
315
|
-
bridge wraps the plugin in a local function rather than re-exporting it.
|
|
316
|
-
- **Plugins run in a worker thread.** An unhandled rejection would otherwise
|
|
317
|
-
crash the worker (`Worker has been terminated`), taking down the web server.
|
|
318
|
-
The plugin installs absorbing guards so it survives.
|
|
319
|
-
- **Web runs on `17081`** because opencode's own server occupies `7081`. Point
|
|
320
|
-
your tunnel ingress at `17081`.
|
|
321
|
-
- **PRIMARY election.** Web (`:4099`) and the Telegram bot are global
|
|
322
|
-
singletons. Multiple opencode instances elect one PRIMARY (atomic lock at
|
|
323
|
-
`~/.ocrc/primary.lock`) to own them; the others stand down PASSIVE.
|
|
324
|
-
Run the hub from a small/empty directory so the file watcher stays fast.
|
|
325
|
-
|
|
326
|
-
## Testing
|
|
171
|
+
Deep dive: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
|
|
172
|
+
|
|
173
|
+
## Development
|
|
327
174
|
|
|
328
175
|
```bash
|
|
329
|
-
npm
|
|
330
|
-
|
|
331
|
-
cd web && npm
|
|
332
|
-
|
|
333
|
-
cd web && npm run build # build PWA
|
|
176
|
+
npm install && npm run build:all # plugin + web
|
|
177
|
+
npm test # backend (vitest)
|
|
178
|
+
cd web && npm test # web (vitest)
|
|
179
|
+
bash scripts/spike-restart.sh # isolated dev instance (never touches prod)
|
|
334
180
|
```
|
|
335
181
|
|
|
336
|
-
##
|
|
182
|
+
## Credits & license
|
|
337
183
|
|
|
338
|
-
MIT — see [LICENSE](LICENSE)
|
|
184
|
+
MIT — see [LICENSE](LICENSE) and [NOTICE](NOTICE): forked from
|
|
185
|
+
[agentjoey/opencode-remote-control](https://github.com/agentjoey/opencode-remote-control);
|
|
186
|
+
Telegram UX model from [@grinev/opencode-telegram-bot](https://github.com/grinev/opencode-telegram-bot).
|
package/dist/cli/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bd7pil/ocrc",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.4",
|
|
4
4
|
"description": "OpenCode plugin — remote-control your local opencode from Telegram and a mobile web console (channels, schedules, skills, files, worktrees).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/plugin/entry.js",
|