shell-mirror 1.5.34 → 1.5.36

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.34",
3
+ "version": "1.5.36",
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": {
@@ -58,8 +58,8 @@ class ShellMirrorDashboard {
58
58
  const agentsResponse = await fetch('/php-backend/api/agents-list.php');
59
59
  const agentsData = await agentsResponse.json();
60
60
 
61
- if (agentsData.success && agentsData.data) {
62
- this.agents = agentsData.data;
61
+ if (agentsData.success && agentsData.data && agentsData.data.agents) {
62
+ this.agents = agentsData.data.agents;
63
63
  }
64
64
 
65
65
  // TODO: Load session history when API is available
@@ -91,33 +91,20 @@ class ShellMirrorDashboard {
91
91
  <button class="btn-primary small" onclick="handleLogin()">Sign In</button>
92
92
  `;
93
93
 
94
- // Show blurred dashboard skeleton
94
+ // Show actual dashboard content but blurred
95
95
  document.getElementById('dashboard-main').innerHTML = `
96
96
  <div class="dashboard-skeleton">
97
97
  <div class="dashboard-grid">
98
98
  <div class="dashboard-card blurred">
99
- <h2>🖥️ Active Agents</h2>
100
- <div class="skeleton-content">
101
- <div class="skeleton-agent"></div>
102
- <div class="skeleton-agent"></div>
103
- </div>
99
+ ${this.renderActiveAgentsPreview()}
104
100
  </div>
105
101
 
106
102
  <div class="dashboard-card blurred">
107
- <h2>⚡ Quick Actions</h2>
108
- <div class="skeleton-content">
109
- <div class="skeleton-action"></div>
110
- <div class="skeleton-action"></div>
111
- <div class="skeleton-action"></div>
112
- </div>
103
+ ${this.renderQuickActions()}
113
104
  </div>
114
105
 
115
106
  <div class="dashboard-card blurred full-width">
116
- <h2>📊 Recent Sessions</h2>
117
- <div class="skeleton-content">
118
- <div class="skeleton-session"></div>
119
- <div class="skeleton-session"></div>
120
- </div>
107
+ ${this.renderRecentSessionsPreview()}
121
108
  </div>
122
109
  </div>
123
110
  </div>
@@ -160,11 +147,11 @@ class ShellMirrorDashboard {
160
147
  const agentsHtml = this.agents.map(agent => `
161
148
  <div class="agent-item">
162
149
  <div class="agent-info">
163
- <div class="agent-name">${agent.hostname || agent.id}</div>
164
- <div class="agent-status ${agent.status}">${agent.status}</div>
150
+ <div class="agent-name">${agent.machineName || agent.agentId}</div>
151
+ <div class="agent-status ${agent.onlineStatus}">${agent.onlineStatus}</div>
165
152
  <div class="agent-last-seen">Last seen: ${this.formatLastSeen(agent.lastSeen)}</div>
166
153
  </div>
167
- <button class="btn-connect" onclick="dashboard.connectToAgent('${agent.id}')">
154
+ <button class="btn-connect" onclick="dashboard.connectToAgent('${agent.agentId}')">
168
155
  Connect
169
156
  </button>
170
157
  </div>
@@ -231,6 +218,75 @@ class ShellMirrorDashboard {
231
218
  `;
232
219
  }
233
220
 
221
+ renderActiveAgentsPreview() {
222
+ // Show sample agent data for preview
223
+ const sampleAgents = [
224
+ { machineName: 'MacBook Pro', onlineStatus: 'online', lastSeen: Date.now() / 1000 - 60 },
225
+ { machineName: 'Mac Studio', onlineStatus: 'offline', lastSeen: Date.now() / 1000 - 3600 }
226
+ ];
227
+
228
+ const agentsHtml = sampleAgents.map(agent => `
229
+ <div class="agent-item">
230
+ <div class="agent-info">
231
+ <div class="agent-name">${agent.machineName}</div>
232
+ <div class="agent-status ${agent.onlineStatus}">${agent.onlineStatus}</div>
233
+ <div class="agent-last-seen">Last seen: ${this.formatLastSeen(agent.lastSeen)}</div>
234
+ </div>
235
+ <button class="btn-connect" disabled>
236
+ Connect
237
+ </button>
238
+ </div>
239
+ `).join('');
240
+
241
+ return `
242
+ <div class="card-header">
243
+ <h2>🖥️ Active Agents</h2>
244
+ <span class="agent-count">${sampleAgents.length} agents</span>
245
+ </div>
246
+ <div class="card-content">
247
+ ${agentsHtml}
248
+ </div>
249
+ `;
250
+ }
251
+
252
+ renderRecentSessionsPreview() {
253
+ // Show sample session data for preview
254
+ const sampleSessions = [
255
+ {
256
+ agentId: 'MacBook-Pro-xyz',
257
+ startTime: new Date(Date.now() - 86400000), // 1 day ago
258
+ duration: '45 minutes',
259
+ status: 'completed'
260
+ },
261
+ {
262
+ agentId: 'Mac-Studio-abc',
263
+ startTime: new Date(Date.now() - 3600000), // 1 hour ago
264
+ duration: '2 hours',
265
+ status: 'completed'
266
+ }
267
+ ];
268
+
269
+ const sessionsHtml = sampleSessions.map(session => `
270
+ <div class="session-item">
271
+ <div class="session-info">
272
+ <div class="session-agent">${session.agentId}</div>
273
+ <div class="session-time">${this.formatDate(session.startTime)}</div>
274
+ </div>
275
+ <div class="session-details">
276
+ <span class="session-duration">${session.duration}</span>
277
+ <span class="session-status ${session.status}">${session.status}</span>
278
+ </div>
279
+ </div>
280
+ `).join('');
281
+
282
+ return `
283
+ <h2>📊 Recent Sessions</h2>
284
+ <div class="card-content">
285
+ ${sessionsHtml}
286
+ </div>
287
+ `;
288
+ }
289
+
234
290
  renderErrorState() {
235
291
  document.getElementById('dashboard-main').innerHTML = `
236
292
  <div class="error-state">
@@ -66,17 +66,7 @@
66
66
 
67
67
  <div id="connect-container">
68
68
  <h2>Terminal Mirror</h2>
69
- <div id="agent-discovery">
70
- <p>Discovering available Mac agents...</p>
71
- <div id="agent-list"></div>
72
- </div>
73
- <div id="manual-connect" style="display: none; margin-top: 20px;">
74
- <p>Or manually enter Agent ID:</p>
75
- <input type="text" id="agent-id-input" placeholder="e.g., agent-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">
76
- <br>
77
- <button id="connect-btn">Connect</button>
78
- </div>
79
- <button id="show-manual" style="margin-top: 10px;">Manual Connect</button>
69
+ <p>Connecting to terminal...</p>
80
70
  </div>
81
71
  <div id="terminal-container">
82
72
  <div id="terminal"></div>
@@ -41,12 +41,6 @@ term.loadAddon(fitAddon);
41
41
 
42
42
  const connectContainer = document.getElementById('connect-container');
43
43
  const terminalContainer = document.getElementById('terminal-container');
44
- const agentIdInput = document.getElementById('agent-id-input');
45
- const connectBtn = document.getElementById('connect-btn');
46
- const showManualBtn = document.getElementById('show-manual');
47
- const manualConnect = document.getElementById('manual-connect');
48
- const agentDiscovery = document.getElementById('agent-discovery');
49
- const agentList = document.getElementById('agent-list');
50
44
 
51
45
  let ws;
52
46
  let peerConnection;
@@ -56,10 +50,24 @@ let AGENT_ID;
56
50
  let CLIENT_ID;
57
51
  let SELECTED_AGENT; // Store full agent data including WebSocket URL
58
52
 
59
- // Auto-discover agents on page load
53
+ // Check for agent parameter and connect directly
60
54
  window.addEventListener('load', () => {
61
- discoverAgents();
62
55
  loadVersionInfo();
56
+
57
+ // Get agent ID from URL parameter
58
+ const urlParams = new URLSearchParams(window.location.search);
59
+ const agentId = urlParams.get('agent');
60
+
61
+ if (agentId) {
62
+ AGENT_ID = agentId;
63
+ SELECTED_AGENT = { id: agentId, agentId: agentId };
64
+ console.log('[CLIENT] 🔗 Connecting directly to agent:', agentId);
65
+ startConnection();
66
+ } else {
67
+ // No agent specified, redirect to dashboard
68
+ console.log('[CLIENT] ❌ No agent specified, redirecting to dashboard');
69
+ window.location.href = '/app/dashboard.html';
70
+ }
63
71
  });
64
72
 
65
73
  // Load version info for footer
@@ -86,34 +94,7 @@ async function loadVersionInfo() {
86
94
  }
87
95
  }
88
96
 
89
- showManualBtn.onclick = () => {
90
- agentDiscovery.style.display = 'none';
91
- manualConnect.style.display = 'block';
92
- showManualBtn.style.display = 'none';
93
- };
94
-
95
- connectBtn.onclick = () => {
96
- AGENT_ID = agentIdInput.value.trim();
97
- if (!AGENT_ID) {
98
- alert('Please enter a valid Agent ID.');
99
- return;
100
- }
101
- startConnection();
102
- };
103
97
 
104
- function connectToAgent(agent) {
105
- if (typeof agent === 'string') {
106
- // Fallback for manual connection - agent is just the ID
107
- AGENT_ID = agent;
108
- SELECTED_AGENT = { id: agent, websocketUrl: null };
109
- } else {
110
- // Full agent object from discovery
111
- AGENT_ID = agent.id;
112
- SELECTED_AGENT = agent;
113
- }
114
- console.log('[CLIENT] 🔗 Connecting to agent:', SELECTED_AGENT);
115
- startConnection();
116
- }
117
98
 
118
99
  function startConnection() {
119
100
  connectContainer.style.display = 'none';
@@ -126,103 +107,6 @@ function startConnection() {
126
107
  initialize();
127
108
  }
128
109
 
129
- async function discoverAgents() {
130
- console.log('[DISCOVERY] 🔍 Starting agent discovery via PHP backend...');
131
- agentList.innerHTML = '<p style="color: #ccc;">Searching for Mac agents...</p>';
132
-
133
- try {
134
- // Use PHP backend for agent discovery instead of WebSocket
135
- const response = await fetch('/php-backend/api/list-agents.php', {
136
- method: 'GET',
137
- credentials: 'include', // Include session cookies for authentication
138
- headers: {
139
- 'Accept': 'application/json',
140
- 'Content-Type': 'application/json'
141
- }
142
- });
143
-
144
- if (!response.ok) {
145
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
146
- }
147
-
148
- const data = await response.json();
149
- console.log('[DISCOVERY] 📨 PHP Backend Response:', data);
150
-
151
- if (data.success && data.data && data.data.agents) {
152
- displayAvailableAgents(data.data.agents);
153
- } else {
154
- console.log('[DISCOVERY] ⚠️ No agents found in response');
155
- agentList.innerHTML = '<p style="color: #ff9800;">⚠️ No Mac agents found.<br><small>Make sure your Mac agent is running with: <code>shell-mirror</code></small></p>';
156
- showManualBtn.style.display = 'block';
157
- }
158
-
159
- } catch (error) {
160
- console.error('[DISCOVERY] ❌ PHP Backend error:', error);
161
-
162
- if (error.message.includes('401')) {
163
- agentList.innerHTML = '<p style="color: #f44336;">Authentication required. <a href="/php-backend/api/auth-login.php" style="color: #4CAF50;">Please log in</a></p>';
164
- } else {
165
- agentList.innerHTML = '<p style="color: #f44336;">Discovery failed. Check server connection.</p>';
166
- }
167
-
168
- showManualBtn.style.display = 'block';
169
- }
170
- }
171
-
172
- function displayAvailableAgents(agents) {
173
- console.log('[DISCOVERY] 🖥️ Displaying agents:', agents);
174
- agentList.innerHTML = '';
175
-
176
- if (agents.length === 0) {
177
- agentList.innerHTML = '<p style="color: #ff9800;">❌ No Mac agents currently running.</p>';
178
- showManualBtn.style.display = 'block';
179
- return;
180
- }
181
-
182
- console.log(`[DISCOVERY] ✅ Found ${agents.length} agent(s)`);
183
-
184
- agents.forEach(agent => {
185
- const agentDiv = document.createElement('div');
186
- agentDiv.style.cssText = 'margin: 10px 0; padding: 15px; background: #333; border-radius: 8px; cursor: pointer; border: 2px solid #555; transition: all 0.3s ease;';
187
- agentDiv.innerHTML = `
188
- <div style="display: flex; align-items: center; gap: 10px;">
189
- <div style="width: 12px; height: 12px; background: #4CAF50; border-radius: 50%; animation: pulse 2s infinite;"></div>
190
- <div>
191
- <strong style="color: #fff;">${agent.id}</strong><br>
192
- <small style="color: #aaa;">🖱️ Click to connect to Mac terminal</small>
193
- </div>
194
- </div>
195
- `;
196
-
197
- agentDiv.onmouseover = () => {
198
- agentDiv.style.borderColor = '#4CAF50';
199
- agentDiv.style.background = '#444';
200
- };
201
- agentDiv.onmouseout = () => {
202
- agentDiv.style.borderColor = '#555';
203
- agentDiv.style.background = '#333';
204
- };
205
- agentDiv.onclick = () => {
206
- console.log(`[DISCOVERY] 🖱️ User clicked on agent: ${agent.id}`, agent);
207
- connectToAgent(agent);
208
- };
209
-
210
- agentList.appendChild(agentDiv);
211
- });
212
-
213
- showManualBtn.style.display = 'block';
214
-
215
- // Add CSS animation for pulse effect
216
- const style = document.createElement('style');
217
- style.textContent = `
218
- @keyframes pulse {
219
- 0% { opacity: 1; }
220
- 50% { opacity: 0.5; }
221
- 100% { opacity: 1; }
222
- }
223
- `;
224
- document.head.appendChild(style);
225
- }
226
110
 
227
111
  async function initialize() {
228
112
  console.log('[CLIENT] 🚀 Initializing WebRTC connection to agent:', AGENT_ID);