claude-code-remote-pilot 0.4.4 → 0.4.5
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/CHANGELOG.md +9 -0
- package/bin/claude-pilot.js +4 -2
- package/lib/WebServer.js +30 -3
- package/lib/ui.html +412 -421
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.5 — 2026-05-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Terminal redesign**: input is now embedded inside the terminal box (dark background, monospace font, `❯` prompt) — feels like a real terminal. After sending, focus returns to the input automatically.
|
|
7
|
+
- **Full-height terminal**: terminal fills the viewport height (`calc(100vh - 210px)`) with the output scrolling above the pinned input row.
|
|
8
|
+
- **Password auth**: `web [port] [host] [password]` — if a password is given, the browser shows a login screen. Token is stored in localStorage and sent as `Authorization: Bearer` on all requests (query param for SSE). `POST /api/login` is the only unauthenticated endpoint.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
3
12
|
## 0.4.4 — 2026-05-06
|
|
4
13
|
|
|
5
14
|
### Added
|
package/bin/claude-pilot.js
CHANGED
|
@@ -331,7 +331,7 @@ const HELP = `
|
|
|
331
331
|
spawn <path> [name] Start Claude at path (name defaults to dir name)
|
|
332
332
|
list Show all sessions
|
|
333
333
|
watch Live session monitor (q to exit)
|
|
334
|
-
web [port] [host]
|
|
334
|
+
web [port] [host] [password] Start web dashboard (default: 3742 127.0.0.1)
|
|
335
335
|
attach <name> Open tmux session in this terminal
|
|
336
336
|
kill <name> Stop a session
|
|
337
337
|
resume [message] Show or set the message sent after a limit resets
|
|
@@ -445,16 +445,18 @@ ${HELP}`);
|
|
|
445
445
|
case 'web': {
|
|
446
446
|
const port = parseInt(args[0]) || 3742;
|
|
447
447
|
const host = args[1] || '127.0.0.1';
|
|
448
|
+
const password = args[2] || null;
|
|
448
449
|
let webServer = manager._webServer;
|
|
449
450
|
if (webServer) {
|
|
450
451
|
console.log(` Web dashboard already running at http://${webServer.host}:${webServer.port}`);
|
|
451
452
|
break;
|
|
452
453
|
}
|
|
453
|
-
webServer = new WebServer(manager, port, host);
|
|
454
|
+
webServer = new WebServer(manager, port, host, password);
|
|
454
455
|
manager._webServer = webServer;
|
|
455
456
|
webServer.start();
|
|
456
457
|
const url = `http://${host}:${port}`;
|
|
457
458
|
console.log(` ✓ Web dashboard started at ${url}`);
|
|
459
|
+
if (password) console.log(` Password protected. Enter password in the browser.`);
|
|
458
460
|
const opener = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
459
461
|
spawn(opener, [url], { stdio: 'ignore', detached: true }).unref();
|
|
460
462
|
break;
|
package/lib/WebServer.js
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
5
6
|
const { spawnSync } = require('child_process');
|
|
6
7
|
const config = require('./config');
|
|
7
8
|
|
|
8
9
|
const STRIP_ANSI = /\x1b\[[0-9;]*[mGKHFABCDJsuhl]|\x1b[()][AB012]/g;
|
|
9
10
|
|
|
10
11
|
class WebServer {
|
|
11
|
-
constructor(manager, port = 3742, host = '127.0.0.1') {
|
|
12
|
+
constructor(manager, port = 3742, host = '127.0.0.1', password = null) {
|
|
12
13
|
this.manager = manager;
|
|
13
14
|
this.port = port;
|
|
14
15
|
this.host = host;
|
|
16
|
+
this.password = password || null;
|
|
17
|
+
this._token = password ? crypto.randomBytes(20).toString('hex') : null;
|
|
15
18
|
this.startedAt = new Date();
|
|
16
19
|
this.server = null;
|
|
17
20
|
this._clients = new Set();
|
|
@@ -47,7 +50,30 @@ class WebServer {
|
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
// Returns true if authorized (or no password set). Sends 401 and returns false otherwise.
|
|
54
|
+
_checkAuth(req, res, url) {
|
|
55
|
+
if (!this.password) return true;
|
|
56
|
+
const authHeader = req.headers['authorization'];
|
|
57
|
+
if (authHeader === `Bearer ${this._token}`) return true;
|
|
58
|
+
if (url && url.searchParams.get('token') === this._token) return true;
|
|
59
|
+
this._json(res, 401, { error: 'Unauthorized' });
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_handleApi(req, res, pathname, url) {
|
|
64
|
+
// POST /api/login — no auth required
|
|
65
|
+
if (req.method === 'POST' && pathname === '/api/login') {
|
|
66
|
+
return this._readBody(req, (err, body) => {
|
|
67
|
+
if (err) return this._json(res, 400, { error: err.message });
|
|
68
|
+
if (!this.password) return this._json(res, 200, { token: null });
|
|
69
|
+
if (body.password !== this.password) return this._json(res, 401, { error: 'Wrong password' });
|
|
70
|
+
return this._json(res, 200, { token: this._token });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// All other API routes require auth
|
|
75
|
+
if (!this._checkAuth(req, res, url)) return;
|
|
76
|
+
|
|
51
77
|
// GET /api/sessions
|
|
52
78
|
if (req.method === 'GET' && pathname === '/api/sessions') {
|
|
53
79
|
return this._json(res, 200, this._buildAllSessions());
|
|
@@ -141,6 +167,7 @@ class WebServer {
|
|
|
141
167
|
}
|
|
142
168
|
|
|
143
169
|
if (pathname === '/events') {
|
|
170
|
+
if (!this._checkAuth(req, res, url)) return;
|
|
144
171
|
res.writeHead(200, {
|
|
145
172
|
'Content-Type': 'text/event-stream',
|
|
146
173
|
'Cache-Control': 'no-cache',
|
|
@@ -153,7 +180,7 @@ class WebServer {
|
|
|
153
180
|
}
|
|
154
181
|
|
|
155
182
|
if (pathname.startsWith('/api/')) {
|
|
156
|
-
return this._handleApi(req, res, pathname);
|
|
183
|
+
return this._handleApi(req, res, pathname, url);
|
|
157
184
|
}
|
|
158
185
|
|
|
159
186
|
res.writeHead(404);
|