shell-mirror 1.5.111 โ 1.5.113
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.css +72 -19
- package/public/app/dashboard.js +77 -19
package/package.json
CHANGED
package/public/app/dashboard.css
CHANGED
|
@@ -23,11 +23,15 @@
|
|
|
23
23
|
box-sizing: border-box;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
html {
|
|
27
|
+
background: #0a0b0d;
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
body {
|
|
27
31
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
28
32
|
line-height: 1.5;
|
|
29
33
|
color: var(--text-primary);
|
|
30
|
-
background: var(--bg-primary);
|
|
34
|
+
background: var(--bg-primary) !important;
|
|
31
35
|
min-height: 100vh;
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -194,12 +198,7 @@ body {
|
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
.agent-count {
|
|
197
|
-
|
|
198
|
-
color: white;
|
|
199
|
-
padding: 2px 8px;
|
|
200
|
-
border-radius: 10px;
|
|
201
|
-
font-size: 0.75rem;
|
|
202
|
-
font-weight: 600;
|
|
201
|
+
display: none;
|
|
203
202
|
}
|
|
204
203
|
|
|
205
204
|
.refresh-time {
|
|
@@ -250,20 +249,58 @@ body {
|
|
|
250
249
|
color: var(--text-primary);
|
|
251
250
|
}
|
|
252
251
|
|
|
253
|
-
|
|
252
|
+
/* Agent Menu Dropdown */
|
|
253
|
+
.agent-menu {
|
|
254
|
+
position: relative;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.btn-agent-menu {
|
|
254
258
|
background: none;
|
|
255
259
|
border: none;
|
|
260
|
+
color: var(--text-muted);
|
|
261
|
+
font-size: 1.2rem;
|
|
256
262
|
cursor: pointer;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
transition:
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
padding: 4px 8px;
|
|
264
|
+
border-radius: 4px;
|
|
265
|
+
transition: all 0.15s ease;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.btn-agent-menu:hover {
|
|
269
|
+
color: var(--text-primary);
|
|
270
|
+
background: var(--bg-hover);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.agent-menu-dropdown {
|
|
274
|
+
display: none;
|
|
275
|
+
position: absolute;
|
|
276
|
+
right: 0;
|
|
277
|
+
top: calc(100% + 4px);
|
|
278
|
+
background: var(--bg-secondary);
|
|
279
|
+
border: 1px solid var(--border);
|
|
280
|
+
border-radius: 6px;
|
|
281
|
+
min-width: 160px;
|
|
282
|
+
z-index: 100;
|
|
283
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
262
284
|
}
|
|
263
285
|
|
|
264
|
-
.
|
|
265
|
-
|
|
286
|
+
.agent-menu-dropdown.show {
|
|
287
|
+
display: block;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.agent-menu-dropdown button {
|
|
291
|
+
width: 100%;
|
|
292
|
+
padding: 10px 14px;
|
|
293
|
+
background: none;
|
|
294
|
+
border: none;
|
|
266
295
|
color: var(--danger);
|
|
296
|
+
text-align: left;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
font-size: 0.85rem;
|
|
299
|
+
transition: background 0.15s ease;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.agent-menu-dropdown button:hover {
|
|
303
|
+
background: var(--bg-hover);
|
|
267
304
|
}
|
|
268
305
|
|
|
269
306
|
.agent-status {
|
|
@@ -276,7 +313,7 @@ body {
|
|
|
276
313
|
}
|
|
277
314
|
|
|
278
315
|
.agent-status.online {
|
|
279
|
-
color: var(--
|
|
316
|
+
color: var(--text-secondary);
|
|
280
317
|
background: none;
|
|
281
318
|
padding: 0;
|
|
282
319
|
}
|
|
@@ -290,17 +327,33 @@ body {
|
|
|
290
327
|
}
|
|
291
328
|
|
|
292
329
|
.agent-status.recent {
|
|
293
|
-
color: var(--
|
|
330
|
+
color: var(--text-secondary);
|
|
294
331
|
background: none;
|
|
295
332
|
padding: 0;
|
|
296
333
|
}
|
|
297
334
|
|
|
335
|
+
.agent-status.recent::before {
|
|
336
|
+
content: '';
|
|
337
|
+
width: 8px;
|
|
338
|
+
height: 8px;
|
|
339
|
+
background: var(--warning);
|
|
340
|
+
border-radius: 50%;
|
|
341
|
+
}
|
|
342
|
+
|
|
298
343
|
.agent-status.offline {
|
|
299
|
-
color: var(--
|
|
344
|
+
color: var(--text-secondary);
|
|
300
345
|
background: none;
|
|
301
346
|
padding: 0;
|
|
302
347
|
}
|
|
303
348
|
|
|
349
|
+
.agent-status.offline::before {
|
|
350
|
+
content: '';
|
|
351
|
+
width: 8px;
|
|
352
|
+
height: 8px;
|
|
353
|
+
background: var(--danger);
|
|
354
|
+
border-radius: 50%;
|
|
355
|
+
}
|
|
356
|
+
|
|
304
357
|
.agent-session-count {
|
|
305
358
|
color: var(--text-muted);
|
|
306
359
|
font-weight: 400;
|
|
@@ -556,7 +609,7 @@ body {
|
|
|
556
609
|
.command-box code {
|
|
557
610
|
font-family: 'Monaco', 'Menlo', monospace;
|
|
558
611
|
font-size: 0.85rem;
|
|
559
|
-
color: var(--
|
|
612
|
+
color: var(--text-primary);
|
|
560
613
|
flex: 1;
|
|
561
614
|
}
|
|
562
615
|
|
package/public/app/dashboard.js
CHANGED
|
@@ -556,12 +556,6 @@ class ShellMirrorDashboard {
|
|
|
556
556
|
const sessionCount = sessions.length;
|
|
557
557
|
|
|
558
558
|
const isConnectable = agent.status === 'online' || agent.status === 'recent';
|
|
559
|
-
const statusIcon = {
|
|
560
|
-
'online': '๐ข',
|
|
561
|
-
'recent': '๐ก',
|
|
562
|
-
'offline': '๐ด'
|
|
563
|
-
}[agent.status] || 'โ';
|
|
564
|
-
|
|
565
559
|
const statusText = {
|
|
566
560
|
'online': 'Live',
|
|
567
561
|
'recent': 'Recent',
|
|
@@ -592,13 +586,15 @@ class ShellMirrorDashboard {
|
|
|
592
586
|
<div class="agent-info">
|
|
593
587
|
<div class="agent-name-row">
|
|
594
588
|
<span class="agent-name">${agent.machineName || agent.agentId}</span>
|
|
595
|
-
<
|
|
596
|
-
|
|
597
|
-
|
|
589
|
+
<div class="agent-menu">
|
|
590
|
+
<button class="btn-agent-menu" onclick="event.stopPropagation(); dashboard.toggleAgentMenu('${agent.agentId}')">โฎ</button>
|
|
591
|
+
<div class="agent-menu-dropdown" id="agent-menu-${agent.agentId}">
|
|
592
|
+
<button onclick="dashboard.showShutdownConfirm('${agent.agentId}')">Shut down agent</button>
|
|
593
|
+
</div>
|
|
594
|
+
</div>
|
|
598
595
|
</div>
|
|
599
596
|
<div class="agent-status ${agent.status}">
|
|
600
|
-
${
|
|
601
|
-
${sessionCount > 0 ? ` ยท ${sessionCount} session${sessionCount !== 1 ? 's' : ''}` : ''}
|
|
597
|
+
${statusText}${sessionCount > 0 ? ` ยท ${sessionCount} session${sessionCount !== 1 ? 's' : ''}` : ''}
|
|
602
598
|
</div>
|
|
603
599
|
</div>
|
|
604
600
|
</div>
|
|
@@ -1072,13 +1068,66 @@ class ShellMirrorDashboard {
|
|
|
1072
1068
|
return true;
|
|
1073
1069
|
}
|
|
1074
1070
|
|
|
1075
|
-
|
|
1071
|
+
toggleAgentMenu(agentId) {
|
|
1072
|
+
// Close all other menus first
|
|
1073
|
+
document.querySelectorAll('.agent-menu-dropdown.show').forEach(el => {
|
|
1074
|
+
el.classList.remove('show');
|
|
1075
|
+
});
|
|
1076
|
+
const menu = document.getElementById(`agent-menu-${agentId}`);
|
|
1077
|
+
if (menu) {
|
|
1078
|
+
menu.classList.toggle('show');
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
showShutdownConfirm(agentId) {
|
|
1083
|
+
// Close menu
|
|
1084
|
+
document.querySelectorAll('.agent-menu-dropdown.show').forEach(el => {
|
|
1085
|
+
el.classList.remove('show');
|
|
1086
|
+
});
|
|
1087
|
+
this.showShutdownModal(agentId);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
showShutdownModal(agentId) {
|
|
1076
1091
|
const agent = this.agents.find(a => a.agentId === agentId);
|
|
1077
1092
|
const agentName = agent ? (agent.machineName || agentId) : agentId;
|
|
1078
1093
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1094
|
+
const modalOverlay = document.createElement('div');
|
|
1095
|
+
modalOverlay.className = 'modal-overlay';
|
|
1096
|
+
modalOverlay.id = 'shutdown-modal';
|
|
1097
|
+
modalOverlay.onclick = (e) => {
|
|
1098
|
+
if (e.target === modalOverlay) {
|
|
1099
|
+
document.body.removeChild(modalOverlay);
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
modalOverlay.innerHTML = `
|
|
1104
|
+
<div class="modal" onclick="event.stopPropagation()">
|
|
1105
|
+
<div class="modal-header">
|
|
1106
|
+
<h3>Shut down agent</h3>
|
|
1107
|
+
<button class="modal-close" onclick="document.getElementById('shutdown-modal').remove()">ร</button>
|
|
1108
|
+
</div>
|
|
1109
|
+
<div class="modal-body">
|
|
1110
|
+
<p style="margin-bottom: 12px; color: var(--text-secondary);">
|
|
1111
|
+
Are you sure you want to shut down <strong style="color: var(--text-primary);">${agentName}</strong>?
|
|
1112
|
+
</p>
|
|
1113
|
+
<p style="font-size: 0.85rem; color: var(--text-muted);">
|
|
1114
|
+
This will unregister the agent from the dashboard. If the agent is still running, it will re-register on next heartbeat.
|
|
1115
|
+
</p>
|
|
1116
|
+
</div>
|
|
1117
|
+
<div class="modal-footer" style="display: flex; justify-content: flex-end; gap: 10px; padding: 16px; border-top: 1px solid var(--border);">
|
|
1118
|
+
<button onclick="document.getElementById('shutdown-modal').remove()" style="padding: 8px 16px; background: var(--bg-tertiary); color: var(--text-secondary); border: 1px solid var(--border); border-radius: 6px; cursor: pointer;">Cancel</button>
|
|
1119
|
+
<button onclick="dashboard.shutdownAgent('${agentId}')" style="padding: 8px 16px; background: var(--danger); color: white; border: none; border-radius: 6px; cursor: pointer;">Shut down</button>
|
|
1120
|
+
</div>
|
|
1121
|
+
</div>
|
|
1122
|
+
`;
|
|
1123
|
+
|
|
1124
|
+
document.body.appendChild(modalOverlay);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
async shutdownAgent(agentId) {
|
|
1128
|
+
// Close modal
|
|
1129
|
+
const modal = document.getElementById('shutdown-modal');
|
|
1130
|
+
if (modal) modal.remove();
|
|
1082
1131
|
|
|
1083
1132
|
try {
|
|
1084
1133
|
const response = await fetch('/php-backend/api/delete-agent.php', {
|
|
@@ -1090,14 +1139,14 @@ class ShellMirrorDashboard {
|
|
|
1090
1139
|
|
|
1091
1140
|
const data = await response.json();
|
|
1092
1141
|
if (data.success) {
|
|
1093
|
-
console.log('[DASHBOARD] Agent
|
|
1142
|
+
console.log('[DASHBOARD] Agent shut down:', agentId);
|
|
1094
1143
|
await this.refreshDashboardData();
|
|
1095
1144
|
} else {
|
|
1096
|
-
alert('Failed to
|
|
1145
|
+
alert('Failed to shut down agent: ' + (data.message || 'Unknown error'));
|
|
1097
1146
|
}
|
|
1098
1147
|
} catch (error) {
|
|
1099
|
-
console.error('[DASHBOARD]
|
|
1100
|
-
alert('Failed to
|
|
1148
|
+
console.error('[DASHBOARD] Shutdown agent failed:', error);
|
|
1149
|
+
alert('Failed to shut down agent: ' + error.message);
|
|
1101
1150
|
}
|
|
1102
1151
|
}
|
|
1103
1152
|
|
|
@@ -1329,4 +1378,13 @@ window.addEventListener('beforeunload', () => {
|
|
|
1329
1378
|
if (dashboard && dashboard.refreshInterval) {
|
|
1330
1379
|
clearInterval(dashboard.refreshInterval);
|
|
1331
1380
|
}
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
// Close agent menu when clicking outside
|
|
1384
|
+
document.addEventListener('click', (e) => {
|
|
1385
|
+
if (!e.target.closest('.agent-menu')) {
|
|
1386
|
+
document.querySelectorAll('.agent-menu-dropdown.show').forEach(el => {
|
|
1387
|
+
el.classList.remove('show');
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1332
1390
|
});
|