@firstpick/pi-package-remote-webui 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/LICENSE +21 -0
- package/README.md +77 -0
- package/docs/PLAN.md +367 -0
- package/index.ts +284 -0
- package/lib/remote-core.mjs +350 -0
- package/package.json +55 -0
- package/tests/remote-args.test.mjs +112 -0
- package/tests/remote-webui-control.test.mjs +128 -0
- package/tests/run-all.mjs +22 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Firstpick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @firstpick/pi-package-remote-webui
|
|
2
|
+
|
|
3
|
+
Mobile connection helper for [Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
|
|
4
|
+
|
|
5
|
+
This package adds a `/remote` slash command that reuses the existing `@firstpick/pi-package-webui` server/UI, opens it to a trusted local network, and shows a QR code in Pi so a phone can connect quickly.
|
|
6
|
+
|
|
7
|
+
> **Security:** Pi Web UI can control the Web UI/Pi session. Remote PIN authentication is off by default; enable it in Web UI **Controls → Network → Remote PIN auth** if you want a 4-digit PIN for non-local clients. Use `/remote` only on trusted local networks and close LAN access when done.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pi install npm:@firstpick/pi-package-remote-webui
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Restart Pi after installation so the `/remote` command is loaded. The QR renderer (`qrcode-terminal`) is a runtime dependency of this Pi package and is installed with the package. For local checkout development, run `npm install` in this package directory instead of installing `qrcode-terminal` globally.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
/remote
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Default behavior:
|
|
24
|
+
|
|
25
|
+
1. Reuse a running Pi Web UI on `127.0.0.1:31415`, or start one for the current working directory.
|
|
26
|
+
2. Open the Web UI listener to the local network through the existing Web UI `/api/network/open` endpoint.
|
|
27
|
+
3. Show a terminal QR code, the LAN URL, and the current Remote PIN auth state.
|
|
28
|
+
4. Scan the QR code from your phone and use the normal Pi Web UI. If Remote PIN auth is enabled, enter the displayed 4-digit PIN on the phone.
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
/remote
|
|
34
|
+
/remote status
|
|
35
|
+
/remote refresh
|
|
36
|
+
/remote close
|
|
37
|
+
/remote --port 31500
|
|
38
|
+
/remote --name mobile
|
|
39
|
+
/remote --yes
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Command | Behavior |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `/remote` | Start/reuse Web UI, confirm, open LAN access, and show QR plus Remote PIN auth state. |
|
|
45
|
+
| `/remote status` | Show Web UI online/network state, LAN URLs, and auth state. |
|
|
46
|
+
| `/remote refresh` | Re-read current LAN URL/auth state and redraw the QR widget. |
|
|
47
|
+
| `/remote close` | Close Web UI LAN exposure and clear the QR widget. |
|
|
48
|
+
| `/remote --port 31500` | Use another Web UI port. |
|
|
49
|
+
| `/remote --name mobile` | Name the initial Web UI tab when this package starts the server. |
|
|
50
|
+
| `/remote --yes` | Skip the LAN exposure confirmation. |
|
|
51
|
+
|
|
52
|
+
## Remote PIN auth
|
|
53
|
+
|
|
54
|
+
`/remote` does not enable Remote PIN auth by itself. Auth is intentionally controlled by the Web UI server:
|
|
55
|
+
|
|
56
|
+
- In the local Web UI, open **Controls → Network → Remote PIN auth**.
|
|
57
|
+
- Enabling it generates a random 4-digit PIN.
|
|
58
|
+
- Non-local browser clients must enter that PIN before reaching Web UI.
|
|
59
|
+
- Localhost clients can always use the UI and toggle the setting.
|
|
60
|
+
|
|
61
|
+
The `/remote` QR widget shows `Remote PIN auth: off` or `Remote PIN auth: on · PIN 1234` when the Web UI server reports it. You can also start Web UI with auth already enabled by using `pi-webui --remote-auth` or `/webui-start --remote-auth` from `@firstpick/pi-package-webui`.
|
|
62
|
+
|
|
63
|
+
## Caveat
|
|
64
|
+
|
|
65
|
+
This package does not mirror the exact live TUI conversation into the phone. It connects mobile to the existing Pi Web UI package, which starts/uses Pi RPC tabs with the same Pi installation, working directory, settings, packages, and session storage.
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cd pi-package-remote-webui
|
|
71
|
+
npm test
|
|
72
|
+
npm run check
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Network safety
|
|
76
|
+
|
|
77
|
+
`/remote` intentionally uses `pi-package-webui`'s direct LAN mode instead of a reverse proxy, preserving Web UI's current localhost-vs-remote trust boundaries. Remote PIN auth remains an explicit Web UI Controls toggle and is a trusted-LAN convenience gate, not hardened multi-user authentication. Use `/remote close` when you are done.
|
package/docs/PLAN.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
## Plan: `pi-package-remote-webui`
|
|
2
|
+
|
|
3
|
+
## Implementation Progress
|
|
4
|
+
|
|
5
|
+
- [x] Plan captured in this file.
|
|
6
|
+
- [x] Implement package manifest, extension command, and reusable core helpers.
|
|
7
|
+
- [x] Add tests for argument parsing, status formatting, QR/widget output, and mocked WebUI control.
|
|
8
|
+
- [x] Run package checks and record results.
|
|
9
|
+
|
|
10
|
+
### Verification Log
|
|
11
|
+
|
|
12
|
+
- `cd pi-package-remote-webui && npm test` — passed 14/14 tests.
|
|
13
|
+
- `cd pi-package-remote-webui && npm run check` — passed syntax check for `lib/remote-core.mjs` and 14/14 tests.
|
|
14
|
+
- `cd pi-package-remote-webui && npm pack --dry-run` — package dry-run succeeded and includes 9 files.
|
|
15
|
+
|
|
16
|
+
### Recommendation
|
|
17
|
+
|
|
18
|
+
Create `pi-package-remote-webui` as a **thin Pi extension package** that reuses the existing `@firstpick/pi-package-webui` server/UI instead of building a second mobile UI.
|
|
19
|
+
|
|
20
|
+
The package should add one main command:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
/remote
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That command should:
|
|
27
|
+
|
|
28
|
+
1. Start or reuse the Pi Web UI server.
|
|
29
|
+
2. Open it to the local network using the existing WebUI network endpoint.
|
|
30
|
+
3. Pick a LAN URL.
|
|
31
|
+
4. Render a QR code in Pi.
|
|
32
|
+
5. Let the user scan the QR code on mobile and use the existing Pi Web UI.
|
|
33
|
+
|
|
34
|
+
This should be a UX wrapper around the current WebUI package, not a fork of it.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Important caveat
|
|
39
|
+
|
|
40
|
+
Current `pi-package-webui` does **not** mirror the live terminal/TUI session. It starts Pi in RPC mode for WebUI tabs.
|
|
41
|
+
|
|
42
|
+
So the first version of `/remote` should mean:
|
|
43
|
+
|
|
44
|
+
> “Open a mobile WebUI connected to this Pi installation, current working directory, settings, packages, and session storage.”
|
|
45
|
+
|
|
46
|
+
It should **not** promise:
|
|
47
|
+
|
|
48
|
+
> “The phone controls the exact same live terminal conversation.”
|
|
49
|
+
|
|
50
|
+
That would require deeper Pi/WebUI core changes or a dedicated bridge.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Package structure
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
pi-package-remote-webui/
|
|
58
|
+
package.json
|
|
59
|
+
index.ts
|
|
60
|
+
README.md
|
|
61
|
+
LICENSE
|
|
62
|
+
tests/
|
|
63
|
+
remote-args.test.mjs
|
|
64
|
+
remote-webui-control.test.mjs
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Suggested package name:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"name": "@firstpick/pi-package-remote-webui",
|
|
72
|
+
"keywords": ["pi-package", "pi", "webui", "remote", "mobile", "qr"],
|
|
73
|
+
"pi": {
|
|
74
|
+
"extensions": ["./index.ts"]
|
|
75
|
+
},
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"@firstpick/pi-package-webui": "^0.3.8",
|
|
78
|
+
"qrcode-terminal": "^0.12.0"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## `/remote` command design
|
|
89
|
+
|
|
90
|
+
### Default
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
/remote
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Behavior:
|
|
97
|
+
|
|
98
|
+
1. Probe `http://127.0.0.1:31415/api/health`.
|
|
99
|
+
2. If no WebUI server is running, start `pi-webui` from `@firstpick/pi-package-webui`.
|
|
100
|
+
3. Ensure it is open to LAN via:
|
|
101
|
+
|
|
102
|
+
```http
|
|
103
|
+
POST /api/network/open
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
4. Poll:
|
|
107
|
+
|
|
108
|
+
```http
|
|
109
|
+
GET /api/network
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
5. Use the first available LAN URL, e.g.
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
http://192.168.1.42:31415/
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
6. Render QR code plus text:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
Pi Remote WebUI
|
|
122
|
+
|
|
123
|
+
Scan with your phone:
|
|
124
|
+
|
|
125
|
+
<QR CODE>
|
|
126
|
+
|
|
127
|
+
http://192.168.1.42:31415/
|
|
128
|
+
|
|
129
|
+
Remote PIN auth: off
|
|
130
|
+
|
|
131
|
+
Trusted LAN only. Remote PIN auth is off; anyone with this URL can control Pi/WebUI.
|
|
132
|
+
Close with: /remote close
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Use `ctx.ui.setWidget()` so the QR remains visible after the command finishes.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Command options
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
/remote
|
|
143
|
+
/remote status
|
|
144
|
+
/remote close
|
|
145
|
+
/remote refresh
|
|
146
|
+
/remote --port 31500
|
|
147
|
+
/remote --name mobile
|
|
148
|
+
/remote --yes
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Recommended meanings
|
|
152
|
+
|
|
153
|
+
| Command | Behavior |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `/remote` | Start/reuse WebUI, open to LAN, show QR |
|
|
156
|
+
| `/remote status` | Show WebUI status, LAN URLs, whether network is open |
|
|
157
|
+
| `/remote close` | Call `/api/network/close`, clear QR widget/status |
|
|
158
|
+
| `/remote refresh` | Re-read LAN URLs and redraw QR |
|
|
159
|
+
| `/remote --port 31500` | Use another WebUI port |
|
|
160
|
+
| `/remote --name mobile` | Name the initial WebUI tab |
|
|
161
|
+
| `/remote --yes` | Skip confirmation warning |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Security model
|
|
166
|
+
|
|
167
|
+
Use the existing WebUI direct LAN mode for v1.
|
|
168
|
+
|
|
169
|
+
Do **not** put a reverse proxy in front of WebUI for v1, because WebUI currently uses the real client address to distinguish localhost clients from remote LAN clients. A proxy would make mobile requests look like localhost unless carefully redesigned, weakening WebUI’s current trust boundaries.
|
|
170
|
+
|
|
171
|
+
### Required v1 behavior
|
|
172
|
+
|
|
173
|
+
Before opening the WebUI to LAN, show a confirmation that remote browsers can control the Web UI/Pi session and that Remote PIN auth is off by default unless enabled in Web UI Controls.
|
|
174
|
+
|
|
175
|
+
Default should be “No” unless `/remote --yes` is provided.
|
|
176
|
+
|
|
177
|
+
### Remote PIN auth
|
|
178
|
+
|
|
179
|
+
Remote PIN auth is implemented in `pi-package-webui`, not in `/remote`, so the localhost-vs-remote trust semantics remain server-owned:
|
|
180
|
+
|
|
181
|
+
- The Web UI side-panel **Controls → Network → Remote PIN auth** toggle is off by default.
|
|
182
|
+
- Enabling it generates a random 4-digit PIN.
|
|
183
|
+
- Non-local clients are challenged before accessing Web UI routes and APIs.
|
|
184
|
+
- Localhost clients can toggle the setting and see the PIN.
|
|
185
|
+
- `/remote` reads the reported auth state from `/api/network` and includes it in the QR widget.
|
|
186
|
+
|
|
187
|
+
Startup auth is also available through:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
pi-webui --remote-auth
|
|
191
|
+
/webui-start --remote-auth
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Implementation flow
|
|
197
|
+
|
|
198
|
+
### 1. Resolve WebUI binary
|
|
199
|
+
|
|
200
|
+
In `index.ts`, locate the bundled WebUI CLI:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
@firstpick/pi-package-webui/bin/pi-webui.mjs
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Use `createRequire(import.meta.url).resolve(...)` or a safe package-root fallback.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### 2. Probe existing server
|
|
211
|
+
|
|
212
|
+
Check:
|
|
213
|
+
|
|
214
|
+
```http
|
|
215
|
+
GET http://127.0.0.1:<port>/api/health
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Accept only responses with:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"ok": true,
|
|
223
|
+
"webuiVersion": "..."
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### 3. Start WebUI if needed
|
|
230
|
+
|
|
231
|
+
Spawn:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
node path/to/pi-webui.mjs --host 127.0.0.1 --port <port> --cwd <ctx.cwd>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Do not open the desktop browser.
|
|
238
|
+
|
|
239
|
+
Wait until `/api/health` succeeds.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### 4. Open LAN access
|
|
244
|
+
|
|
245
|
+
Call from localhost:
|
|
246
|
+
|
|
247
|
+
```http
|
|
248
|
+
POST http://127.0.0.1:<port>/api/network/open
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Then poll:
|
|
252
|
+
|
|
253
|
+
```http
|
|
254
|
+
GET http://127.0.0.1:<port>/api/network
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
until:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"open": true,
|
|
262
|
+
"networkUrls": ["http://192.168.x.x:31415/"]
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
If no LAN address is found, show a helpful error.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### 5. Render QR
|
|
271
|
+
|
|
272
|
+
Use `qrcode-terminal` or a tiny local QR helper.
|
|
273
|
+
|
|
274
|
+
Display with:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
ctx.ui.setWidget("pi-remote-webui", lines, { placement: "aboveEditor" });
|
|
278
|
+
ctx.ui.setStatus("pi-remote-webui", "remote webui open");
|
|
279
|
+
ctx.ui.notify("Pi Remote WebUI ready", "info");
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
### 6. Close remote mode
|
|
285
|
+
|
|
286
|
+
`/remote close` should:
|
|
287
|
+
|
|
288
|
+
1. Call:
|
|
289
|
+
|
|
290
|
+
```http
|
|
291
|
+
POST http://127.0.0.1:<port>/api/network/close
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
2. Clear:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
ctx.ui.setWidget("pi-remote-webui", undefined);
|
|
298
|
+
ctx.ui.setStatus("pi-remote-webui", undefined);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Do not kill the WebUI server unless an explicit future option like `/remote stop` is added.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Testing plan
|
|
306
|
+
|
|
307
|
+
### Unit tests
|
|
308
|
+
|
|
309
|
+
Test:
|
|
310
|
+
|
|
311
|
+
- Argument parsing.
|
|
312
|
+
- Port validation.
|
|
313
|
+
- URL selection from multiple LAN URLs.
|
|
314
|
+
- QR text generation.
|
|
315
|
+
- Status formatting.
|
|
316
|
+
- Confirmation-required behavior.
|
|
317
|
+
|
|
318
|
+
### Mock WebUI tests
|
|
319
|
+
|
|
320
|
+
Use a local mock HTTP server for:
|
|
321
|
+
|
|
322
|
+
- `/api/health`
|
|
323
|
+
- `/api/network`
|
|
324
|
+
- `/api/network/open`
|
|
325
|
+
- `/api/network/close`
|
|
326
|
+
|
|
327
|
+
Verify `/remote` calls endpoints in the correct order.
|
|
328
|
+
|
|
329
|
+
### Manual acceptance test
|
|
330
|
+
|
|
331
|
+
1. Install package locally:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
pi install ./pi-package-remote-webui
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
2. Restart Pi.
|
|
338
|
+
3. Run:
|
|
339
|
+
|
|
340
|
+
```text
|
|
341
|
+
/remote
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
4. Confirm warning.
|
|
345
|
+
5. Scan QR on phone.
|
|
346
|
+
6. Verify Pi WebUI loads.
|
|
347
|
+
7. Send a prompt from mobile.
|
|
348
|
+
8. Run:
|
|
349
|
+
|
|
350
|
+
```text
|
|
351
|
+
/remote close
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
9. Verify mobile disconnects or can no longer reach the WebUI.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Acceptance criteria
|
|
359
|
+
|
|
360
|
+
- `/remote` starts from a normal Pi TUI session.
|
|
361
|
+
- QR code is visible and scannable from terminal.
|
|
362
|
+
- Mobile browser opens existing Pi WebUI.
|
|
363
|
+
- No separate mobile UI is built.
|
|
364
|
+
- `/remote close` closes LAN exposure.
|
|
365
|
+
- User is warned that Remote PIN auth is off by default unless enabled in Web UI Controls.
|
|
366
|
+
- QR/widget output shows Remote PIN auth state and PIN when enabled.
|
|
367
|
+
- Existing WebUI remote trust boundaries remain intact.
|