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 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
@@ -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 DEFAULT_IDLE_SESSION_TTL_MS = 60_000;
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
- process.env.WATCH_REPORT_DEPENDENCIES != null;
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(process.env.HOME || '/');
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
- : resolve(process.env.HOME || '/');
409
+ : HOME_DIR;
414
410
  const sessionFile = typeof msg.sessionFile === 'string' && msg.sessionFile.length > 0
415
411
  ? basename(msg.sessionFile)
416
412
  : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^24"