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 +1 -1
- package/public/app/dashboard.js +78 -22
- package/public/app/terminal.html +1 -11
- package/public/app/terminal.js +16 -132
package/package.json
CHANGED
package/public/app/dashboard.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
164
|
-
<div class="agent-status ${agent.
|
|
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.
|
|
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">
|
package/public/app/terminal.html
CHANGED
|
@@ -66,17 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
<div id="connect-container">
|
|
68
68
|
<h2>Terminal Mirror</h2>
|
|
69
|
-
<
|
|
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>
|
package/public/app/terminal.js
CHANGED
|
@@ -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
|
-
//
|
|
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);
|