claude-code-remote-pilot 0.5.8 → 0.5.10

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.
@@ -154,7 +154,7 @@ function buildAllSessions(manager) {
154
154
  return [...active, ...offline];
155
155
  }
156
156
 
157
- function renderWatchTable(allSessions, selectedIdx) {
157
+ function renderWatchTable(allSessions, selectedIdx, webServer = null) {
158
158
  const NW = 18, SW = 14, UW = 7, TW = 16;
159
159
  const bar = ' ' + '─'.repeat(NW + SW + UW + TW + 10);
160
160
  const header = ` ${'#'.padEnd(3)}${'SESSION'.padEnd(NW)} ${'STATUS'.padEnd(SW)} ${'UP'.padEnd(UW)} ${'USAGE / RESET'.padEnd(TW)}`;
@@ -181,7 +181,16 @@ function renderWatchTable(allSessions, selectedIdx) {
181
181
  footer = ` [1-${Math.min(allSessions.length, 9)}]: select session w: web ui q: exit watch`;
182
182
  }
183
183
 
184
- return ['\n', ' Claude Code Remote Pilot', bar, header, bar, ...rows, bar, footer, ''].join('\n');
184
+ const lines = ['\n', ' Claude Code Remote Pilot'];
185
+ if (webServer) {
186
+ if (webServer._tunnelUrl) {
187
+ lines.push(` ${C.blue}Tunnel${C.reset}: ${webServer._tunnelUrl} ${C.dim}local: http://127.0.0.1:${webServer.port}${C.reset}`);
188
+ } else {
189
+ lines.push(` ${C.dim}Web UI: http://${webServer.host}:${webServer.port}${C.reset}`);
190
+ }
191
+ }
192
+ lines.push(bar, header, bar, ...rows, bar, footer, '');
193
+ return lines.join('\n');
185
194
  }
186
195
 
187
196
  function startWatch(manager, rl) {
@@ -192,7 +201,7 @@ function startWatch(manager, rl) {
192
201
  function draw() {
193
202
  allSessions = buildAllSessions(manager);
194
203
  process.stdout.write('\x1B[2J\x1B[0f');
195
- process.stdout.write(renderWatchTable(allSessions, selectedIdx));
204
+ process.stdout.write(renderWatchTable(allSessions, selectedIdx, manager._webServer));
196
205
  }
197
206
 
198
207
  function startTimer() {
@@ -214,7 +223,7 @@ function startWatch(manager, rl) {
214
223
 
215
224
  function redraw() {
216
225
  process.stdout.write('\x1B[2J\x1B[0f');
217
- process.stdout.write(renderWatchTable(allSessions, selectedIdx));
226
+ process.stdout.write(renderWatchTable(allSessions, selectedIdx, manager._webServer));
218
227
  }
219
228
 
220
229
  function onKeypress(str, key) {
@@ -459,6 +468,7 @@ ${HELP}`);
459
468
  console.log(' Starting cloudflared tunnel...');
460
469
  webServer.startTunnel().then(publicUrl => {
461
470
  console.log(` ✓ Tunnel ready: ${publicUrl}`);
471
+ console.log(' Note: first visit may show a Cloudflare warning — click "Proceed" to open the dashboard.');
462
472
  if (!webPassword) console.log(' ⚠ Reminder: no password set. Restart with a password for security.');
463
473
  if (telegram.token && telegram.chatId) {
464
474
  notifier.send(telegram.token, telegram.chatId,
@@ -538,6 +548,7 @@ ${HELP}`);
538
548
  console.log(' Starting cloudflared tunnel...');
539
549
  webServer.startTunnel().then(publicUrl => {
540
550
  console.log(` ✓ Tunnel ready: ${publicUrl}`);
551
+ console.log(' Note: first visit may show a Cloudflare warning — click "Proceed" to open the dashboard.');
541
552
  if (telegram.token && telegram.chatId) {
542
553
  notifier.send(telegram.token, telegram.chatId,
543
554
  `Claude Remote Pilot tunnel ready: ${publicUrl}${webServer.password ? ' (password protected)' : ' ⚠ no password set'}`);
@@ -564,6 +575,7 @@ ${HELP}`);
564
575
  console.log(' Starting cloudflared tunnel...');
565
576
  webServer.startTunnel().then(publicUrl => {
566
577
  console.log(` ✓ Tunnel ready: ${publicUrl}`);
578
+ console.log(' Note: first visit may show a Cloudflare warning — click "Proceed" to open the dashboard.');
567
579
  if (!password) console.log(' ⚠ Reminder: add a password with: web <port> <host> <password> --tunnel');
568
580
  if (telegram.token && telegram.chatId) {
569
581
  notifier.send(telegram.token, telegram.chatId,
package/lib/WebServer.js CHANGED
@@ -18,6 +18,7 @@ class WebServer {
18
18
  this.server = null;
19
19
  this._clients = new Set();
20
20
  this._broadcastInterval = null;
21
+ this._heartbeatInterval = null;
21
22
  this._tunnelProcess = null;
22
23
  this._tunnelUrl = null;
23
24
  }
@@ -201,6 +202,7 @@ class WebServer {
201
202
  'Content-Type': 'text/event-stream',
202
203
  'Cache-Control': 'no-cache',
203
204
  'Connection': 'keep-alive',
205
+ 'X-Accel-Buffering': 'no', // prevent Cloudflare/nginx from buffering the stream
204
206
  });
205
207
  this._clients.add(res);
206
208
  res.write(`data: ${JSON.stringify(this._buildAllSessions())}\n\n`);
@@ -217,6 +219,12 @@ class WebServer {
217
219
  });
218
220
 
219
221
  this._broadcastInterval = setInterval(() => this._broadcast(), 3000);
222
+ // Keep SSE connections alive through proxies (Cloudflare timeout is ~100s)
223
+ this._heartbeatInterval = setInterval(() => {
224
+ for (const res of this._clients) {
225
+ try { res.write(': heartbeat\n\n'); } catch { this._clients.delete(res); }
226
+ }
227
+ }, 20000);
220
228
  this.server.listen(this.port, this.host);
221
229
  return this.port;
222
230
  }
@@ -259,6 +267,7 @@ class WebServer {
259
267
  stop() {
260
268
  this.stopTunnel();
261
269
  clearInterval(this._broadcastInterval);
270
+ clearInterval(this._heartbeatInterval);
262
271
  for (const res of this._clients) { try { res.end(); } catch {} }
263
272
  this._clients.clear();
264
273
  if (this.server) this.server.close();
package/lib/ui.html CHANGED
@@ -1094,6 +1094,21 @@ function App() {
1094
1094
  return () => { if (esRef.current) esRef.current.close(); };
1095
1095
  }, []);
1096
1096
 
1097
+ // Fallback polling — loads sessions immediately and keeps them fresh when
1098
+ // SSE is blocked (e.g. through a Cloudflare tunnel that buffers streams).
1099
+ useEffect(() => {
1100
+ let mounted = true;
1101
+ const poll = () => {
1102
+ apiFetch('/api/sessions', { cache: 'no-store' })
1103
+ .then(r => r.json())
1104
+ .then(data => { if (mounted) setSessions(data); })
1105
+ .catch(() => {});
1106
+ };
1107
+ poll();
1108
+ const t = setInterval(poll, 5000);
1109
+ return () => { mounted = false; clearInterval(t); };
1110
+ }, []);
1111
+
1097
1112
  const handleLogin = useCallback(() => {
1098
1113
  setNeedsLogin(false);
1099
1114
  apiFetch('/api/status').then(r => r.json()).then(setServerStatus).catch(() => {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-remote-pilot",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
4
  "description": "Interactive Claude Code supervisor — spawn and monitor multiple Claude sessions from a single terminal.",
5
5
  "type": "commonjs",
6
6
  "repository": {