pi-web 0.12.4 → 0.12.6
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 +51 -0
- package/build/server/server.js +6 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ npx -y pi-web@latest
|
|
|
12
12
|
|
|
13
13
|
Then open [http://127.0.0.1:8192](http://127.0.0.1:8192) in your browser.
|
|
14
14
|
|
|
15
|
+
For remote access, use the **recommended** Tailscale HTTPS setup below.
|
|
16
|
+
|
|
15
17
|
## Options
|
|
16
18
|
|
|
17
19
|
```
|
|
@@ -27,6 +29,55 @@ To run against Oh My Pi, start with:
|
|
|
27
29
|
npx -y pi-web@latest --agent omp
|
|
28
30
|
```
|
|
29
31
|
|
|
32
|
+
## Recommended: secure access with Tailscale (HTTPS, no app password)
|
|
33
|
+
|
|
34
|
+
`pi-web` does not include built-in authentication. **Recommended setup:** keep it bound to `127.0.0.1` (IPv4 loopback) and expose it only through your private Tailnet.
|
|
35
|
+
|
|
36
|
+
1. Start `pi-web` locally (default host is already `127.0.0.1`):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx -y pi-web@latest --host 127.0.0.1 --port 8192
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. In another terminal, expose it over Tailnet HTTPS using one of these bindings:
|
|
43
|
+
|
|
44
|
+
Without specifying an HTTPS port (default HTTPS binding):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
tailscale serve --bg 8192
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- Open in browser: `https://<your-device>.<your-tailnet>.ts.net/`
|
|
51
|
+
|
|
52
|
+
With an explicit HTTPS port binding (example: expose on Tailnet `8192`):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
tailscale serve --bg --https=8192 http://127.0.0.1:8192
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- Open in browser: `https://<your-device>.<your-tailnet>.ts.net:8192/`
|
|
59
|
+
|
|
60
|
+
- Prefer the explicit form when you want a non-default Tailnet HTTPS port, or when you want to avoid `localhost`/`::1` resolution ambiguity.
|
|
61
|
+
|
|
62
|
+
3. Check the HTTPS URL and serve status:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
tailscale serve status
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
4. To stop exposing the service:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
tailscale serve reset
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Notes:
|
|
75
|
+
|
|
76
|
+
- This is accessible only to devices/users authorised in your Tailnet (and ACLs), so no separate `pi-web` password is required.
|
|
77
|
+
- Use `127.0.0.1` explicitly rather than `localhost` for `--host` and local proxy targets; on some systems `localhost` resolves to `::1` (IPv6), which can break loopback forwarding expectations.
|
|
78
|
+
- Avoid binding `pi-web` to `0.0.0.0` when using this setup.
|
|
79
|
+
- Do **not** use `tailscale funnel` unless you explicitly want public internet exposure.
|
|
80
|
+
|
|
30
81
|
## Features
|
|
31
82
|
|
|
32
83
|
- Browse and switch between pi sessions grouped by working directory
|
package/build/server/server.js
CHANGED
|
@@ -3,6 +3,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { createReadStream, existsSync, readFileSync, statSync, unlinkSync } from 'node:fs';
|
|
4
4
|
import { readdir } from 'node:fs/promises';
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve } from 'node:path';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
7
8
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
8
9
|
import { RpcSession } from './rpc.js';
|
|
@@ -48,21 +49,16 @@ const AGENT = parseAgent(getArg('agent'));
|
|
|
48
49
|
const PORT = parseInt(getArg('port') || '8192', 10);
|
|
49
50
|
const HOST = getArg('host') || '127.0.0.1';
|
|
50
51
|
const AGENT_CMD = getAgentCommand(AGENT);
|
|
51
|
-
const
|
|
52
|
-
const idleSessionTtlMsEnv = parseInt(process.env.PI_WEB_IDLE_SESSION_TTL_MS || '', 10);
|
|
53
|
-
const IDLE_SESSION_TTL_MS = Number.isFinite(idleSessionTtlMsEnv) && idleSessionTtlMsEnv >= 0
|
|
54
|
-
? idleSessionTtlMsEnv
|
|
55
|
-
: DEFAULT_IDLE_SESSION_TTL_MS;
|
|
52
|
+
const IDLE_SESSION_TTL_MS = 60_000;
|
|
56
53
|
const isWatchMode = process.argv.includes('--watch') ||
|
|
57
|
-
process.execArgv.some((arg) => arg === '--watch' || arg.startsWith('--watch-'))
|
|
58
|
-
|
|
59
|
-
const isDev = process.env.NODE_ENV === 'development' || isWatchMode;
|
|
54
|
+
process.execArgv.some((arg) => arg === '--watch' || arg.startsWith('--watch-'));
|
|
55
|
+
const isDev = isWatchMode;
|
|
60
56
|
const distDirCandidates = [join(__dirname, 'dist'), join(__dirname, '..', '..', 'dist')];
|
|
61
57
|
const distDir = distDirCandidates.find((candidate) => existsSync(join(candidate, 'index.html'))) ??
|
|
62
58
|
distDirCandidates[0];
|
|
63
59
|
const htmlPath = join(distDir, 'index.html');
|
|
64
60
|
const htmlCache = isDev || !existsSync(htmlPath) ? null : readFileSync(htmlPath, 'utf-8');
|
|
65
|
-
const HOME_DIR = resolve(
|
|
61
|
+
const HOME_DIR = resolve(homedir() || '/');
|
|
66
62
|
function isWithinRoot(path, root) {
|
|
67
63
|
const rel = relative(root, path);
|
|
68
64
|
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
@@ -410,7 +406,7 @@ wss.on('connection', (ws) => {
|
|
|
410
406
|
if (msg.type === 'start_session') {
|
|
411
407
|
const cwd = typeof msg.cwd === 'string' && msg.cwd.trim().length > 0
|
|
412
408
|
? resolve(msg.cwd)
|
|
413
|
-
:
|
|
409
|
+
: HOME_DIR;
|
|
414
410
|
const sessionFile = typeof msg.sessionFile === 'string' && msg.sessionFile.length > 0
|
|
415
411
|
? basename(msg.sessionFile)
|
|
416
412
|
: null;
|