shell-mirror 1.5.100 → 1.5.106
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/mac-agent/agent.js +14 -0
- package/package.json +1 -1
- package/public/app/dashboard.css +32 -0
- package/public/app/dashboard.js +22 -8
- package/public/app/terminal.html +2 -5
- package/public/app/terminal.js +16 -14
package/mac-agent/agent.js
CHANGED
|
@@ -976,6 +976,20 @@ function setupDataChannel(clientId) {
|
|
|
976
976
|
}));
|
|
977
977
|
logToFile(`[AGENT] ❌ Failed to create session for client ${clientId}`);
|
|
978
978
|
}
|
|
979
|
+
} else if (message.type === 'close_session') {
|
|
980
|
+
// Handle session closure request from client
|
|
981
|
+
logToFile(`[AGENT] Client ${clientId} closing session ${message.sessionId}`);
|
|
982
|
+
|
|
983
|
+
sessionManager.terminateSession(message.sessionId);
|
|
984
|
+
|
|
985
|
+
// Send confirmation with updated session list
|
|
986
|
+
dataChannel.send(JSON.stringify({
|
|
987
|
+
type: 'session-closed',
|
|
988
|
+
sessionId: message.sessionId,
|
|
989
|
+
availableSessions: sessionManager.getAllSessions()
|
|
990
|
+
}));
|
|
991
|
+
|
|
992
|
+
logToFile(`[AGENT] ✅ Session closed: ${message.sessionId}`);
|
|
979
993
|
}
|
|
980
994
|
} catch (err) {
|
|
981
995
|
logToFile(`[AGENT] Error parsing data channel message: ${err.message}`);
|
package/package.json
CHANGED
package/public/app/dashboard.css
CHANGED
|
@@ -268,6 +268,38 @@ body {
|
|
|
268
268
|
margin-left: 8px;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
/* Animated Loading Dots */
|
|
272
|
+
.loading-dots {
|
|
273
|
+
display: inline-flex;
|
|
274
|
+
gap: 2px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.loading-dots span {
|
|
278
|
+
animation: loadingDot 1.4s infinite;
|
|
279
|
+
opacity: 0.2;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.loading-dots span:nth-child(1) {
|
|
283
|
+
animation-delay: 0s;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.loading-dots span:nth-child(2) {
|
|
287
|
+
animation-delay: 0.2s;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.loading-dots span:nth-child(3) {
|
|
291
|
+
animation-delay: 0.4s;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@keyframes loadingDot {
|
|
295
|
+
0%, 80%, 100% {
|
|
296
|
+
opacity: 0.2;
|
|
297
|
+
}
|
|
298
|
+
40% {
|
|
299
|
+
opacity: 1;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
271
303
|
.connection-status {
|
|
272
304
|
font-size: 0.8rem;
|
|
273
305
|
font-weight: 500;
|
package/public/app/dashboard.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
// Dashboard functionality for Shell Mirror
|
|
2
|
+
|
|
3
|
+
// Session color palette (matches terminal.js)
|
|
4
|
+
const SESSION_COLORS = [
|
|
5
|
+
{ border: '#2196f3', text: '#1565c0' }, // Blue
|
|
6
|
+
{ border: '#4caf50', text: '#2e7d32' }, // Green
|
|
7
|
+
{ border: '#ff9800', text: '#e65100' }, // Orange
|
|
8
|
+
{ border: '#9c27b0', text: '#6a1b9a' }, // Purple
|
|
9
|
+
{ border: '#00bcd4', text: '#00838f' }, // Teal
|
|
10
|
+
{ border: '#e91e63', text: '#ad1457' }, // Pink
|
|
11
|
+
];
|
|
12
|
+
|
|
2
13
|
class ShellMirrorDashboard {
|
|
3
14
|
constructor() {
|
|
4
15
|
this.isAuthenticated = false;
|
|
@@ -575,16 +586,17 @@ class ShellMirrorDashboard {
|
|
|
575
586
|
? this.formatPreciseLastSeen(agent.timeSinceLastSeen)
|
|
576
587
|
: this.formatLastSeen(agent.lastSeen);
|
|
577
588
|
|
|
578
|
-
// Build inline session list
|
|
579
|
-
const sessionsHtml = sessions.map(session => {
|
|
589
|
+
// Build inline session list with colors
|
|
590
|
+
const sessionsHtml = sessions.map((session, index) => {
|
|
580
591
|
const sessionStatus = session.status === 'active' ? 'active' : 'crashed';
|
|
581
592
|
const activityText = this.formatLastActivity(session.lastActivity);
|
|
593
|
+
const color = SESSION_COLORS[index % SESSION_COLORS.length];
|
|
582
594
|
return `
|
|
583
|
-
<div class="inline-session-item">
|
|
584
|
-
<span class="session-status-dot ${
|
|
585
|
-
<span class="session-name">${session.name}</span>
|
|
595
|
+
<div class="inline-session-item" style="border-left: 3px solid ${color.border};">
|
|
596
|
+
<span class="session-status-dot" style="background-color: ${color.border};"></span>
|
|
597
|
+
<span class="session-name" style="color: ${color.text};">${session.name}</span>
|
|
586
598
|
<span class="session-activity">${activityText}</span>
|
|
587
|
-
<button class="btn-session-connect" onclick="dashboard.connectToSession('${agent.agentId}', '${session.id}')">
|
|
599
|
+
<button class="btn-session-connect" onclick="dashboard.connectToSession('${agent.agentId}', '${session.id}')" style="background-color: ${color.border};">
|
|
588
600
|
Connect
|
|
589
601
|
</button>
|
|
590
602
|
</div>
|
|
@@ -632,7 +644,9 @@ class ShellMirrorDashboard {
|
|
|
632
644
|
const showCleanup = offlineAgents.length > 0;
|
|
633
645
|
|
|
634
646
|
// Format last refresh time
|
|
635
|
-
const refreshTime = this.lastRefresh
|
|
647
|
+
const refreshTime = this.lastRefresh
|
|
648
|
+
? new Date(this.lastRefresh).toLocaleTimeString()
|
|
649
|
+
: '<span class="loading-dots"><span>.</span><span>.</span><span>.</span></span>';
|
|
636
650
|
|
|
637
651
|
return `
|
|
638
652
|
<div class="dashboard-card">
|
|
@@ -640,7 +654,7 @@ class ShellMirrorDashboard {
|
|
|
640
654
|
<div class="card-title-section">
|
|
641
655
|
<h2>Active Agents</h2>
|
|
642
656
|
<span class="agent-count">${agentCount}</span>
|
|
643
|
-
<span class="refresh-time"
|
|
657
|
+
<span class="refresh-time">${this.lastRefresh ? 'Updated ' : ''}${refreshTime}</span>
|
|
644
658
|
</div>
|
|
645
659
|
<div class="agent-actions-header">
|
|
646
660
|
<button id="refresh-btn" class="btn-text-action" onclick="dashboard.manualRefresh()" title="Refresh agents">
|
package/public/app/terminal.html
CHANGED
|
@@ -172,11 +172,8 @@
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
.session-tab.active {
|
|
175
|
-
|
|
175
|
+
/* Colors set by inline styles from JavaScript */
|
|
176
176
|
font-weight: 600;
|
|
177
|
-
border-bottom-color: #667eea;
|
|
178
|
-
background: #333;
|
|
179
|
-
box-shadow: inset 0 -3px 0 #667eea;
|
|
180
177
|
}
|
|
181
178
|
|
|
182
179
|
.session-tab-btn {
|
|
@@ -579,6 +576,6 @@
|
|
|
579
576
|
}
|
|
580
577
|
</script>
|
|
581
578
|
|
|
582
|
-
<script src="/app/terminal.js?v=1.5.
|
|
579
|
+
<script src="/app/terminal.js?v=1.5.90"></script>
|
|
583
580
|
</body>
|
|
584
581
|
</html>
|
package/public/app/terminal.js
CHANGED
|
@@ -407,9 +407,10 @@ function setupDirectConnection(directWs) {
|
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
-
// Clear terminal and show success message
|
|
410
|
+
// Clear terminal and show success message with session color
|
|
411
411
|
term.clear();
|
|
412
|
-
|
|
412
|
+
const sessionColor = getSessionColor(currentSession.id);
|
|
413
|
+
term.write(`\r\n\x1b[38;2;${sessionColor.ansi}m✨ New session created: ${currentSession.name}\x1b[0m\r\n\r\n`);
|
|
413
414
|
|
|
414
415
|
// Update URL with session ID so refresh reconnects to same session
|
|
415
416
|
updateUrlWithSession(data.sessionId);
|
|
@@ -967,12 +968,12 @@ function updateSessionDisplay() {
|
|
|
967
968
|
|
|
968
969
|
// Session tab color palette (fixed colors by creation order)
|
|
969
970
|
const SESSION_TAB_COLORS = [
|
|
970
|
-
{ bg: '#e3f2fd', border: '#2196f3', text: '#1565c0' }, // Blue
|
|
971
|
-
{ bg: '#e8f5e9', border: '#4caf50', text: '#2e7d32' },
|
|
972
|
-
{ bg: '#fff3e0', border: '#ff9800', text: '#e65100' },
|
|
973
|
-
{ bg: '#f3e5f5', border: '#9c27b0', text: '#6a1b9a' }, // Purple
|
|
974
|
-
{ bg: '#e0f7fa', border: '#00bcd4', text: '#00838f' },
|
|
975
|
-
{ bg: '#fce4ec', border: '#e91e63', text: '#ad1457' },
|
|
971
|
+
{ bg: '#e3f2fd', border: '#2196f3', text: '#1565c0', muted: '#5a9fd4', ansi: '33;150;243' }, // Blue
|
|
972
|
+
{ bg: '#e8f5e9', border: '#4caf50', text: '#2e7d32', muted: '#6fbf73', ansi: '76;175;80' }, // Green
|
|
973
|
+
{ bg: '#fff3e0', border: '#ff9800', text: '#e65100', muted: '#ffb74d', ansi: '255;152;0' }, // Orange
|
|
974
|
+
{ bg: '#f3e5f5', border: '#9c27b0', text: '#6a1b9a', muted: '#ba68c8', ansi: '156;39;176' }, // Purple
|
|
975
|
+
{ bg: '#e0f7fa', border: '#00bcd4', text: '#00838f', muted: '#4dd0e1', ansi: '0;188;212' }, // Teal
|
|
976
|
+
{ bg: '#fce4ec', border: '#e91e63', text: '#ad1457', muted: '#f06292', ansi: '233;30;99' }, // Pink
|
|
976
977
|
];
|
|
977
978
|
|
|
978
979
|
// Track color assignments by session ID (persists across renders)
|
|
@@ -1020,13 +1021,13 @@ function renderTabs() {
|
|
|
1020
1021
|
const displayName = session.name || 'Terminal Session';
|
|
1021
1022
|
const color = getSessionColor(session.id);
|
|
1022
1023
|
|
|
1023
|
-
// Active tabs get full color, inactive tabs get muted version
|
|
1024
|
+
// Active tabs get full color, inactive tabs get muted version of their color
|
|
1024
1025
|
const tabStyle = isActive
|
|
1025
1026
|
? `background: ${color.bg}; border-color: ${color.border}; border-bottom: 3px solid ${color.border};`
|
|
1026
|
-
: `background:
|
|
1027
|
+
: `background: rgba(255,255,255,0.05); border-color: transparent; border-bottom: 3px solid ${color.muted}40;`;
|
|
1027
1028
|
const textStyle = isActive
|
|
1028
1029
|
? `color: ${color.text}; font-weight: 600;`
|
|
1029
|
-
: `color:
|
|
1030
|
+
: `color: ${color.muted};`;
|
|
1030
1031
|
|
|
1031
1032
|
return `
|
|
1032
1033
|
<div class="session-tab ${isActive ? 'active' : ''}" style="${tabStyle}" title="${displayName}" data-color-index="${sessionColorMap[session.id]}">
|
|
@@ -1035,7 +1036,7 @@ function renderTabs() {
|
|
|
1035
1036
|
style="${textStyle}">
|
|
1036
1037
|
<span class="session-tab-name">${displayName}</span>
|
|
1037
1038
|
</button>
|
|
1038
|
-
<button class="session-tab-close" onclick="closeSession('${session.id}', event)" title="Close session" style="color: ${isActive ? color.text :
|
|
1039
|
+
<button class="session-tab-close" onclick="closeSession('${session.id}', event)" title="Close session" style="color: ${isActive ? color.text : color.muted}">×</button>
|
|
1039
1040
|
</div>
|
|
1040
1041
|
`;
|
|
1041
1042
|
}).join('');
|
|
@@ -1211,9 +1212,10 @@ function handleSessionMessage(message) {
|
|
|
1211
1212
|
availableSessions.push(currentSession);
|
|
1212
1213
|
}
|
|
1213
1214
|
|
|
1214
|
-
// Clear terminal for new session
|
|
1215
|
+
// Clear terminal for new session with session color
|
|
1215
1216
|
term.clear();
|
|
1216
|
-
|
|
1217
|
+
const sessionColor = getSessionColor(currentSession.id);
|
|
1218
|
+
term.write(`\r\n\x1b[38;2;${sessionColor.ansi}m✨ New session created: ${currentSession.name}\x1b[0m\r\n\r\n`);
|
|
1217
1219
|
|
|
1218
1220
|
// Update URL with session ID so refresh reconnects to same session
|
|
1219
1221
|
updateUrlWithSession(message.sessionId);
|