shell-mirror 1.5.88 → 1.5.90
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 +139 -3
- package/public/app/dashboard.js +40 -57
package/package.json
CHANGED
package/public/app/dashboard.css
CHANGED
|
@@ -349,12 +349,18 @@ body {
|
|
|
349
349
|
/* Agent Items */
|
|
350
350
|
.agent-item {
|
|
351
351
|
display: flex;
|
|
352
|
-
|
|
353
|
-
align-items: center;
|
|
352
|
+
flex-direction: column;
|
|
354
353
|
padding: 16px 0;
|
|
355
354
|
border-bottom: 1px solid #f0f0f0;
|
|
356
355
|
}
|
|
357
356
|
|
|
357
|
+
.agent-header {
|
|
358
|
+
display: flex;
|
|
359
|
+
justify-content: space-between;
|
|
360
|
+
align-items: center;
|
|
361
|
+
width: 100%;
|
|
362
|
+
}
|
|
363
|
+
|
|
358
364
|
.agent-item:last-child {
|
|
359
365
|
border-bottom: none;
|
|
360
366
|
}
|
|
@@ -406,6 +412,115 @@ body {
|
|
|
406
412
|
font-weight: 500;
|
|
407
413
|
}
|
|
408
414
|
|
|
415
|
+
/* Inline Session List Styles */
|
|
416
|
+
.agent-sessions-inline {
|
|
417
|
+
margin-top: 12px;
|
|
418
|
+
padding-top: 12px;
|
|
419
|
+
border-top: 1px solid #eee;
|
|
420
|
+
width: 100%;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.sessions-label {
|
|
424
|
+
font-size: 0.8rem;
|
|
425
|
+
color: #666;
|
|
426
|
+
font-weight: 500;
|
|
427
|
+
margin-bottom: 8px;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.sessions-list {
|
|
431
|
+
margin-bottom: 10px;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.inline-session-item {
|
|
435
|
+
display: flex;
|
|
436
|
+
align-items: center;
|
|
437
|
+
gap: 10px;
|
|
438
|
+
padding: 10px 12px;
|
|
439
|
+
margin-bottom: 6px;
|
|
440
|
+
background: #f8f9fa;
|
|
441
|
+
border-radius: 8px;
|
|
442
|
+
border: 1px solid #eee;
|
|
443
|
+
transition: all 0.2s ease;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.inline-session-item:hover {
|
|
447
|
+
background: #fff;
|
|
448
|
+
border-color: #4285f4;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.session-status-dot {
|
|
452
|
+
width: 8px;
|
|
453
|
+
height: 8px;
|
|
454
|
+
border-radius: 50%;
|
|
455
|
+
flex-shrink: 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.session-status-dot.active {
|
|
459
|
+
background: #34a853;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.session-status-dot.crashed {
|
|
463
|
+
background: #ea4335;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.session-name {
|
|
467
|
+
font-weight: 500;
|
|
468
|
+
color: #333;
|
|
469
|
+
flex: 1;
|
|
470
|
+
font-size: 0.9rem;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.session-activity {
|
|
474
|
+
font-size: 0.75rem;
|
|
475
|
+
color: #888;
|
|
476
|
+
margin-right: 8px;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.btn-session-connect {
|
|
480
|
+
background: #4285f4;
|
|
481
|
+
color: white;
|
|
482
|
+
border: none;
|
|
483
|
+
border-radius: 6px;
|
|
484
|
+
padding: 6px 14px;
|
|
485
|
+
font-size: 0.8rem;
|
|
486
|
+
font-weight: 500;
|
|
487
|
+
cursor: pointer;
|
|
488
|
+
transition: all 0.2s ease;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.btn-session-connect:hover {
|
|
492
|
+
background: #3367d6;
|
|
493
|
+
transform: translateY(-1px);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.btn-new-session {
|
|
497
|
+
background: #fff;
|
|
498
|
+
color: #4285f4;
|
|
499
|
+
border: 1px dashed #4285f4;
|
|
500
|
+
border-radius: 8px;
|
|
501
|
+
padding: 10px 16px;
|
|
502
|
+
font-size: 0.85rem;
|
|
503
|
+
font-weight: 500;
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
width: 100%;
|
|
506
|
+
transition: all 0.2s ease;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.btn-new-session:hover {
|
|
510
|
+
background: #f0f7ff;
|
|
511
|
+
border-style: solid;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.agent-offline-message {
|
|
515
|
+
margin-top: 12px;
|
|
516
|
+
padding: 10px;
|
|
517
|
+
background: #f5f5f5;
|
|
518
|
+
border-radius: 6px;
|
|
519
|
+
color: #999;
|
|
520
|
+
font-size: 0.85rem;
|
|
521
|
+
text-align: center;
|
|
522
|
+
}
|
|
523
|
+
|
|
409
524
|
.agent-actions {
|
|
410
525
|
display: flex;
|
|
411
526
|
gap: 8px;
|
|
@@ -751,12 +866,33 @@ body {
|
|
|
751
866
|
align-items: flex-start;
|
|
752
867
|
gap: 12px;
|
|
753
868
|
}
|
|
754
|
-
|
|
869
|
+
|
|
755
870
|
.session-details {
|
|
756
871
|
align-items: flex-start;
|
|
757
872
|
flex-direction: row;
|
|
758
873
|
gap: 12px;
|
|
759
874
|
}
|
|
875
|
+
|
|
876
|
+
/* Mobile inline session styles */
|
|
877
|
+
.inline-session-item {
|
|
878
|
+
flex-wrap: wrap;
|
|
879
|
+
gap: 8px;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.session-name {
|
|
883
|
+
flex-basis: calc(100% - 30px);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.session-activity {
|
|
887
|
+
order: 3;
|
|
888
|
+
flex-basis: 50%;
|
|
889
|
+
margin-right: 0;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.btn-session-connect {
|
|
893
|
+
order: 4;
|
|
894
|
+
padding: 8px 16px;
|
|
895
|
+
}
|
|
760
896
|
}
|
|
761
897
|
|
|
762
898
|
/* Modal Styles */
|
package/public/app/dashboard.js
CHANGED
|
@@ -420,9 +420,10 @@ class ShellMirrorDashboard {
|
|
|
420
420
|
|
|
421
421
|
if (agentsData.success && agentsData.data && agentsData.data.agents) {
|
|
422
422
|
this.agents = agentsData.data.agents;
|
|
423
|
-
|
|
424
|
-
//
|
|
425
|
-
|
|
423
|
+
|
|
424
|
+
// Don't load stale sessions from localStorage - only show live sessions from agents
|
|
425
|
+
// Sessions will be populated via WebSocket updates from connected agents
|
|
426
|
+
localStorage.removeItem('shell-mirror-sessions'); // Clear any stale data
|
|
426
427
|
} else {
|
|
427
428
|
this.agents = [];
|
|
428
429
|
}
|
|
@@ -566,26 +567,48 @@ class ShellMirrorDashboard {
|
|
|
566
567
|
? this.formatPreciseLastSeen(agent.timeSinceLastSeen)
|
|
567
568
|
: this.formatLastSeen(agent.lastSeen);
|
|
568
569
|
|
|
570
|
+
// Build inline session list
|
|
571
|
+
const sessionsHtml = sessions.map(session => {
|
|
572
|
+
const sessionStatus = session.status === 'active' ? 'active' : 'crashed';
|
|
573
|
+
const activityText = this.formatLastActivity(session.lastActivity);
|
|
574
|
+
return `
|
|
575
|
+
<div class="inline-session-item">
|
|
576
|
+
<span class="session-status-dot ${sessionStatus}"></span>
|
|
577
|
+
<span class="session-name">${session.name}</span>
|
|
578
|
+
<span class="session-activity">${activityText}</span>
|
|
579
|
+
<button class="btn-session-connect" onclick="dashboard.connectToSession('${agent.agentId}', '${session.id}')">
|
|
580
|
+
Connect
|
|
581
|
+
</button>
|
|
582
|
+
</div>
|
|
583
|
+
`;
|
|
584
|
+
}).join('');
|
|
585
|
+
|
|
569
586
|
return `
|
|
570
587
|
<div class="agent-item ${!isConnectable ? 'agent-offline' : ''}">
|
|
571
|
-
<div class="agent-
|
|
572
|
-
<div class="agent-
|
|
573
|
-
|
|
574
|
-
|
|
588
|
+
<div class="agent-header">
|
|
589
|
+
<div class="agent-info">
|
|
590
|
+
<div class="agent-name">${agent.machineName || agent.agentId}</div>
|
|
591
|
+
<div class="agent-status ${agent.status}">
|
|
592
|
+
${statusIcon} ${statusText}
|
|
593
|
+
</div>
|
|
594
|
+
<div class="agent-last-seen">Last seen: ${lastSeenText}</div>
|
|
575
595
|
</div>
|
|
576
|
-
<div class="agent-last-seen">Last seen: ${lastSeenText}</div>
|
|
577
|
-
${sessionCount > 0 ? `<div class="agent-sessions">${sessionCount} active session${sessionCount !== 1 ? 's' : ''}</div>` : ''}
|
|
578
596
|
</div>
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
597
|
+
${isConnectable ? `
|
|
598
|
+
<div class="agent-sessions-inline">
|
|
599
|
+
${sessionCount > 0 ? `
|
|
600
|
+
<div class="sessions-label">Sessions (${sessionCount})</div>
|
|
601
|
+
<div class="sessions-list">
|
|
602
|
+
${sessionsHtml}
|
|
603
|
+
</div>
|
|
604
|
+
` : ''}
|
|
605
|
+
<button class="btn-new-session" onclick="dashboard.createNewSession('${agent.agentId}')">
|
|
606
|
+
+ New Session
|
|
584
607
|
</button>
|
|
585
|
-
${sessionCount > 0 && isConnectable ? `<button class="btn-sessions" onclick="dashboard.showAgentSessions('${agent.agentId}')">
|
|
586
|
-
All Sessions
|
|
587
|
-
</button>` : ''}
|
|
588
608
|
</div>
|
|
609
|
+
` : `
|
|
610
|
+
<div class="agent-offline-message">Agent offline</div>
|
|
611
|
+
`}
|
|
589
612
|
</div>
|
|
590
613
|
`;
|
|
591
614
|
}).join('');
|
|
@@ -1052,46 +1075,6 @@ class ShellMirrorDashboard {
|
|
|
1052
1075
|
}
|
|
1053
1076
|
}
|
|
1054
1077
|
|
|
1055
|
-
showAgentSessions(agentId) {
|
|
1056
|
-
const sessions = this.agentSessions[agentId] || [];
|
|
1057
|
-
const agent = this.agents.find(a => a.agentId === agentId);
|
|
1058
|
-
const agentName = agent ? (agent.machineName || agent.agentId) : agentId;
|
|
1059
|
-
|
|
1060
|
-
if (sessions.length === 0) {
|
|
1061
|
-
alert(`No active sessions found for ${agentName}`);
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
// Create session list modal
|
|
1066
|
-
const sessionsList = sessions.map(session => `
|
|
1067
|
-
<div class="session-list-item" onclick="dashboard.connectToSession('${agentId}', '${session.id}')">
|
|
1068
|
-
<div class="session-list-info">
|
|
1069
|
-
<div class="session-list-name">${session.name}</div>
|
|
1070
|
-
<div class="session-list-details">
|
|
1071
|
-
<span class="session-list-id">${session.id.substring(0, 8)}...</span>
|
|
1072
|
-
<span class="session-list-activity">Last activity: ${this.formatLastActivity(session.lastActivity)}</span>
|
|
1073
|
-
</div>
|
|
1074
|
-
</div>
|
|
1075
|
-
<div class="session-list-status ${session.status}">${session.status}</div>
|
|
1076
|
-
</div>
|
|
1077
|
-
`).join('');
|
|
1078
|
-
|
|
1079
|
-
// Show modal with sessions
|
|
1080
|
-
this.showModal(`Sessions on ${agentName}`, `
|
|
1081
|
-
<div class="sessions-modal-content">
|
|
1082
|
-
<p>Active terminal sessions (${sessions.length}):</p>
|
|
1083
|
-
<div class="session-list">
|
|
1084
|
-
${sessionsList}
|
|
1085
|
-
</div>
|
|
1086
|
-
<div class="sessions-modal-actions">
|
|
1087
|
-
<button class="btn-primary" onclick="dashboard.createNewSession('${agentId}')">
|
|
1088
|
-
+ Create New Session
|
|
1089
|
-
</button>
|
|
1090
|
-
</div>
|
|
1091
|
-
</div>
|
|
1092
|
-
`);
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
1078
|
showModal(title, content) {
|
|
1096
1079
|
// Create modal overlay
|
|
1097
1080
|
const modalOverlay = document.createElement('div');
|