shell-mirror 1.5.56 → 1.5.57

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shell-mirror",
3
- "version": "1.5.56",
3
+ "version": "1.5.57",
4
4
  "description": "Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -833,4 +833,23 @@ body {
833
833
  opacity: 0;
834
834
  transform: translateX(100%);
835
835
  }
836
+ }
837
+
838
+ /* API Error Display */
839
+ .api-error {
840
+ text-align: center;
841
+ padding: 20px;
842
+ background: #fff5f5;
843
+ border: 1px solid #fed7d7;
844
+ border-radius: 8px;
845
+ color: #c53030;
846
+ }
847
+
848
+ .api-error p {
849
+ margin-bottom: 15px;
850
+ font-weight: 500;
851
+ }
852
+
853
+ .api-error button {
854
+ margin-top: 10px;
836
855
  }
@@ -102,8 +102,24 @@ class ShellMirrorDashboard {
102
102
  }
103
103
 
104
104
  setupWebSocket() {
105
+ // Detect production environment
106
+ const isProduction = window.location.hostname === 'shellmirror.app' ||
107
+ window.location.hostname === 'www.shellmirror.app' ||
108
+ window.location.hostname === 'www.igori.eu' ||
109
+ window.location.hostname === 'igori.eu';
110
+
105
111
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
106
- const wsUrl = `${protocol}//${window.location.host}/?role=dashboard`;
112
+ let wsUrl;
113
+
114
+ if (isProduction) {
115
+ // For production, try the Heroku WebSocket app URL
116
+ // This may need to be adjusted based on actual deployment architecture
117
+ wsUrl = `wss://shell-mirror-30aa5479ceaf.herokuapp.com/?role=dashboard`;
118
+ console.log('[DASHBOARD] 🌐 Production environment detected, using Heroku WebSocket URL');
119
+ } else {
120
+ // For development, use same host
121
+ wsUrl = `${protocol}//${window.location.host}/?role=dashboard`;
122
+ }
107
123
 
108
124
  console.log('[DASHBOARD] 🔌 Connecting to WebSocket:', wsUrl);
109
125
 
@@ -111,9 +127,20 @@ class ShellMirrorDashboard {
111
127
  this.websocket = new WebSocket(wsUrl);
112
128
 
113
129
  this.websocket.onopen = () => {
114
- console.log('[DASHBOARD] ✅ WebSocket connected');
130
+ console.log('[DASHBOARD] ✅ WebSocket connected to:', wsUrl);
115
131
  this.reconnectAttempts = 0;
116
132
  this.updateConnectionStatus('connected');
133
+
134
+ // Send authentication info if available
135
+ const user = this.user;
136
+ if (user) {
137
+ console.log('[DASHBOARD] 🔐 Sending authentication to WebSocket');
138
+ this.websocket.send(JSON.stringify({
139
+ type: 'authenticate',
140
+ userId: user.id || user.email,
141
+ email: user.email
142
+ }));
143
+ }
117
144
  };
118
145
 
119
146
  this.websocket.onmessage = (event) => {
@@ -121,13 +148,38 @@ class ShellMirrorDashboard {
121
148
  };
122
149
 
123
150
  this.websocket.onclose = (event) => {
124
- console.log('[DASHBOARD] 🔌 WebSocket closed:', event.code, event.reason);
151
+ const closeReasons = {
152
+ 1000: 'Normal Closure',
153
+ 1001: 'Going Away',
154
+ 1002: 'Protocol Error',
155
+ 1003: 'Unsupported Data',
156
+ 1004: 'Reserved',
157
+ 1005: 'No Status Rcvd',
158
+ 1006: 'Abnormal Closure',
159
+ 1007: 'Invalid frame payload data',
160
+ 1008: 'Policy Violation',
161
+ 1009: 'Message too big',
162
+ 1010: 'Mandatory Extension',
163
+ 1011: 'Internal Server Error',
164
+ 1015: 'TLS Handshake'
165
+ };
166
+
167
+ const reason = closeReasons[event.code] || 'Unknown';
168
+ console.log(`[DASHBOARD] 🔌 WebSocket closed: ${event.code} (${reason})`, event.reason);
169
+
170
+ if (event.code === 1008) {
171
+ console.error('[DASHBOARD] ❌ Authentication required - WebSocket rejected connection');
172
+ } else if (event.code === 1006) {
173
+ console.error('[DASHBOARD] ❌ Abnormal closure - WebSocket endpoint may not exist');
174
+ }
175
+
125
176
  this.updateConnectionStatus('disconnected');
126
177
  this.attemptReconnect();
127
178
  };
128
179
 
129
180
  this.websocket.onerror = (error) => {
130
181
  console.error('[DASHBOARD] ❌ WebSocket error:', error);
182
+ console.log('[DASHBOARD] 🔍 WebSocket URL attempted:', wsUrl);
131
183
  this.updateConnectionStatus('error');
132
184
  };
133
185
 
@@ -222,9 +274,26 @@ class ShellMirrorDashboard {
222
274
  this.setupWebSocket();
223
275
  }, delay);
224
276
  } else {
225
- console.log('[DASHBOARD] ❌ Max reconnection attempts reached or not authenticated');
226
- this.updateConnectionStatus('failed');
277
+ console.log('[DASHBOARD] ❌ Max reconnection attempts reached, switching to HTTP-only mode');
278
+ this.updateConnectionStatus('offline');
279
+ this.enableHttpOnlyMode();
280
+ }
281
+ }
282
+
283
+ enableHttpOnlyMode() {
284
+ console.log('[DASHBOARD] 📡 Enabling HTTP-only mode (no real-time updates)');
285
+ this.websocket = null;
286
+
287
+ // Update UI to show HTTP-only mode
288
+ const connectionStatus = document.getElementById('connection-status');
289
+ if (connectionStatus) {
290
+ connectionStatus.textContent = '📡 HTTP Only';
291
+ connectionStatus.className = 'connection-status offline';
292
+ connectionStatus.title = 'Real-time updates unavailable - using polling';
227
293
  }
294
+
295
+ // Continue with HTTP polling only
296
+ console.log('[DASHBOARD] ✅ Dashboard running in HTTP-only mode');
228
297
  }
229
298
 
230
299
  updateConnectionStatus(status) {
@@ -301,15 +370,27 @@ class ShellMirrorDashboard {
301
370
 
302
371
  async loadDashboardData() {
303
372
  try {
304
- // Load active agents
305
- const agentsResponse = await fetch('/php-backend/api/agents-list.php');
373
+ // Load active agents with detailed debugging
374
+ console.log('[DASHBOARD] 📡 Fetching agents from API...');
375
+ const agentsResponse = await fetch('/php-backend/api/agents-list.php', {
376
+ credentials: 'include' // Include authentication cookies
377
+ });
378
+
379
+ console.log('[DASHBOARD] 🔍 API Response Status:', agentsResponse.status);
380
+ console.log('[DASHBOARD] 🔍 API Response Headers:', Object.fromEntries(agentsResponse.headers.entries()));
381
+
306
382
  const agentsData = await agentsResponse.json();
383
+ console.log('[DASHBOARD] 🔍 API Response Data:', agentsData);
307
384
 
308
385
  if (agentsData.success && agentsData.data && agentsData.data.agents) {
309
386
  this.agents = agentsData.data.agents;
387
+ console.log('[DASHBOARD] ✅ Loaded agents:', this.agents.length);
310
388
 
311
389
  // Load session data from localStorage (persisted from terminal connections)
312
390
  this.loadSessionsFromStorage();
391
+ } else {
392
+ console.warn('[DASHBOARD] ⚠️ No agents found in API response:', agentsData);
393
+ this.agents = [];
313
394
  }
314
395
 
315
396
  // TODO: Load session history when API is available
@@ -331,7 +412,25 @@ class ShellMirrorDashboard {
331
412
  ];
332
413
 
333
414
  } catch (error) {
334
- console.error('Failed to load dashboard data:', error);
415
+ console.error('[DASHBOARD] ❌ Failed to load dashboard data:', error);
416
+ this.agents = [];
417
+
418
+ // Show error in UI
419
+ const agentsCard = document.querySelector('.dashboard-card');
420
+ if (agentsCard) {
421
+ agentsCard.innerHTML = `
422
+ <div class="card-header">
423
+ <h2>🖥️ Active Agents</h2>
424
+ <span class="agent-count">Error</span>
425
+ </div>
426
+ <div class="card-content">
427
+ <div class="api-error">
428
+ <p>⚠️ Failed to load agents: ${error.message}</p>
429
+ <button onclick="dashboard.manualRefresh()" class="btn-primary">Retry</button>
430
+ </div>
431
+ </div>
432
+ `;
433
+ }
335
434
  }
336
435
  }
337
436