shell-mirror 1.5.82 → 1.5.84
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 +41 -0
- package/package.json +1 -1
- package/public/app/terminal.html +0 -63
- package/public/app/terminal.js +83 -30
package/mac-agent/agent.js
CHANGED
|
@@ -910,12 +910,52 @@ function setupDataChannel(clientId) {
|
|
|
910
910
|
sessionId: message.sessionId,
|
|
911
911
|
sessionName: newSession.name
|
|
912
912
|
}));
|
|
913
|
+
|
|
914
|
+
// Send buffered output for this session
|
|
915
|
+
const bufferedOutput = newSession.buffer.getAll();
|
|
916
|
+
if (bufferedOutput) {
|
|
917
|
+
logToFile(`[AGENT] 📤 Sending ${bufferedOutput.length} chars of buffered output for session switch`);
|
|
918
|
+
const success = sendLargeMessage(dataChannel, {
|
|
919
|
+
type: 'output',
|
|
920
|
+
data: bufferedOutput
|
|
921
|
+
}, '[AGENT]');
|
|
922
|
+
if (!success) {
|
|
923
|
+
logToFile('[AGENT] ❌ Failed to send buffered output');
|
|
924
|
+
}
|
|
925
|
+
} else {
|
|
926
|
+
logToFile('[AGENT] ℹ️ No buffered output for switched session');
|
|
927
|
+
}
|
|
913
928
|
} else {
|
|
914
929
|
dataChannel.send(JSON.stringify({
|
|
915
930
|
type: 'error',
|
|
916
931
|
message: `Session ${message.sessionId} not found`
|
|
917
932
|
}));
|
|
918
933
|
}
|
|
934
|
+
} else if (message.type === 'session-create') {
|
|
935
|
+
// Handle new session creation via data channel
|
|
936
|
+
logToFile(`[AGENT] Client ${clientId} creating new session`);
|
|
937
|
+
|
|
938
|
+
const newSessionId = sessionManager.createSession(null, clientId);
|
|
939
|
+
|
|
940
|
+
if (newSessionId) {
|
|
941
|
+
const newSession = sessionManager.getSession(newSessionId);
|
|
942
|
+
|
|
943
|
+
// Send confirmation with updated session list
|
|
944
|
+
dataChannel.send(JSON.stringify({
|
|
945
|
+
type: 'session-created',
|
|
946
|
+
sessionId: newSessionId,
|
|
947
|
+
sessionName: newSession.name,
|
|
948
|
+
availableSessions: sessionManager.getAllSessions()
|
|
949
|
+
}));
|
|
950
|
+
|
|
951
|
+
logToFile(`[AGENT] ✅ New session created: ${newSessionId}`);
|
|
952
|
+
} else {
|
|
953
|
+
dataChannel.send(JSON.stringify({
|
|
954
|
+
type: 'error',
|
|
955
|
+
message: 'Failed to create session - maximum sessions reached'
|
|
956
|
+
}));
|
|
957
|
+
logToFile(`[AGENT] ❌ Failed to create session for client ${clientId}`);
|
|
958
|
+
}
|
|
919
959
|
}
|
|
920
960
|
} catch (err) {
|
|
921
961
|
logToFile(`[AGENT] Error parsing data channel message: ${err.message}`);
|
|
@@ -1023,6 +1063,7 @@ function startLocalServer() {
|
|
|
1023
1063
|
localWs.send(JSON.stringify({
|
|
1024
1064
|
type: 'session_created',
|
|
1025
1065
|
sessionId,
|
|
1066
|
+
sessionName: `Session ${sessionId.slice(0, 8)}`,
|
|
1026
1067
|
cols: message.cols || 120,
|
|
1027
1068
|
rows: message.rows || 30
|
|
1028
1069
|
}));
|
package/package.json
CHANGED
package/public/app/terminal.html
CHANGED
|
@@ -178,27 +178,6 @@
|
|
|
178
178
|
background: #1a1a1a;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
.session-tab-status {
|
|
182
|
-
width: 8px;
|
|
183
|
-
height: 8px;
|
|
184
|
-
border-radius: 50%;
|
|
185
|
-
flex-shrink: 0;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.session-tab-status.connected {
|
|
189
|
-
background: #44ff44;
|
|
190
|
-
box-shadow: 0 0 6px rgba(68, 255, 68, 0.5);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.session-tab-status.connecting {
|
|
194
|
-
background: #ffaa44;
|
|
195
|
-
animation: pulse 1.5s ease-in-out infinite;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.session-tab-status.disconnected {
|
|
199
|
-
background: #ff4444;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
181
|
.session-tab-name {
|
|
203
182
|
overflow: hidden;
|
|
204
183
|
text-overflow: ellipsis;
|
|
@@ -257,23 +236,6 @@
|
|
|
257
236
|
}
|
|
258
237
|
}
|
|
259
238
|
|
|
260
|
-
/* Dashboard button with status dot */
|
|
261
|
-
.dashboard-btn {
|
|
262
|
-
position: relative;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.dashboard-btn::before {
|
|
266
|
-
content: '';
|
|
267
|
-
position: absolute;
|
|
268
|
-
top: 4px;
|
|
269
|
-
right: 4px;
|
|
270
|
-
width: 8px;
|
|
271
|
-
height: 8px;
|
|
272
|
-
border-radius: 50%;
|
|
273
|
-
background: #44ff44;
|
|
274
|
-
box-shadow: 0 0 8px rgba(68, 255, 68, 0.5);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
239
|
#terminal {
|
|
278
240
|
padding: 8px; /* Mac Terminal.app padding */
|
|
279
241
|
background-color: #000000;
|
|
@@ -284,31 +246,6 @@
|
|
|
284
246
|
#connect-container { padding: 2em; text-align: center; }
|
|
285
247
|
#agent-id-input { font-size: 1.2em; padding: 8px; width: 400px; margin-bottom: 1em; }
|
|
286
248
|
#connect-btn { font-size: 1.2em; padding: 10px 20px; }
|
|
287
|
-
|
|
288
|
-
/* Connection Status Indicator */
|
|
289
|
-
.connection-status {
|
|
290
|
-
width: 8px;
|
|
291
|
-
height: 8px;
|
|
292
|
-
border-radius: 50%;
|
|
293
|
-
background: #ff4444;
|
|
294
|
-
margin-right: 4px;
|
|
295
|
-
transition: all 0.3s ease;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
.connection-status.connected {
|
|
299
|
-
background: #44ff44;
|
|
300
|
-
box-shadow: 0 0 8px rgba(68, 255, 68, 0.5);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.connection-status.connecting {
|
|
304
|
-
background: #ffaa44;
|
|
305
|
-
animation: pulse 1.5s ease-in-out infinite alternate;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
@keyframes pulse {
|
|
309
|
-
from { opacity: 1; }
|
|
310
|
-
to { opacity: 0.4; }
|
|
311
|
-
}
|
|
312
249
|
|
|
313
250
|
/* Help Modal - Tab Navigation */
|
|
314
251
|
.help-tabs {
|
package/public/app/terminal.js
CHANGED
|
@@ -157,23 +157,12 @@ function updateConnectionStatus(status) {
|
|
|
157
157
|
if (!statusElement) return;
|
|
158
158
|
|
|
159
159
|
statusElement.className = 'connection-status';
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
case 'connected':
|
|
165
|
-
statusElement.classList.add('connected');
|
|
166
|
-
break;
|
|
167
|
-
case 'disconnected':
|
|
168
|
-
default:
|
|
169
|
-
// Default red styling already applied
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Update tab status indicators
|
|
174
|
-
if (currentSession) {
|
|
175
|
-
renderTabs();
|
|
160
|
+
if (status === 'connecting') {
|
|
161
|
+
statusElement.classList.add('connecting');
|
|
162
|
+
} else if (status === 'connected') {
|
|
163
|
+
statusElement.classList.add('connected');
|
|
176
164
|
}
|
|
165
|
+
// else: disconnected (default red)
|
|
177
166
|
}
|
|
178
167
|
|
|
179
168
|
// Cleanup timer for chunk assembler
|
|
@@ -399,8 +388,20 @@ function setupDirectConnection(directWs) {
|
|
|
399
388
|
|
|
400
389
|
case 'session_created':
|
|
401
390
|
console.log('[CLIENT] ✅ Direct session created:', data.sessionId);
|
|
402
|
-
currentSession = {
|
|
391
|
+
currentSession = {
|
|
392
|
+
id: data.sessionId,
|
|
393
|
+
name: data.sessionName || 'Terminal Session'
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// Update available sessions
|
|
397
|
+
if (data.availableSessions) {
|
|
398
|
+
availableSessions = data.availableSessions;
|
|
399
|
+
}
|
|
400
|
+
|
|
403
401
|
updateSessionDisplay();
|
|
402
|
+
|
|
403
|
+
// Save to localStorage
|
|
404
|
+
saveSessionToLocalStorage(AGENT_ID, currentSession);
|
|
404
405
|
break;
|
|
405
406
|
|
|
406
407
|
case 'output':
|
|
@@ -951,9 +952,6 @@ function renderTabs() {
|
|
|
951
952
|
return;
|
|
952
953
|
}
|
|
953
954
|
|
|
954
|
-
// Get connection status
|
|
955
|
-
const connectionStatus = getConnectionStatus();
|
|
956
|
-
|
|
957
955
|
// Ensure we have sessions to display
|
|
958
956
|
let sessionsToRender = [];
|
|
959
957
|
|
|
@@ -968,7 +966,6 @@ function renderTabs() {
|
|
|
968
966
|
console.log('[CLIENT] 🎨 Rendering tabs:', {
|
|
969
967
|
sessionCount: sessionsToRender.length,
|
|
970
968
|
currentSession: currentSession?.id,
|
|
971
|
-
connectionStatus,
|
|
972
969
|
source: availableSessions?.length > 0 ? 'agent' : 'fallback'
|
|
973
970
|
});
|
|
974
971
|
|
|
@@ -985,7 +982,6 @@ function renderTabs() {
|
|
|
985
982
|
onclick="switchToSession('${session.id}')"
|
|
986
983
|
${isActive ? 'disabled' : ''}
|
|
987
984
|
title="${displayName}">
|
|
988
|
-
<span class="session-tab-status ${connectionStatus}"></span>
|
|
989
985
|
<span class="session-tab-name">${displayName}</span>
|
|
990
986
|
</button>
|
|
991
987
|
`;
|
|
@@ -1000,13 +996,27 @@ function renderTabs() {
|
|
|
1000
996
|
}
|
|
1001
997
|
|
|
1002
998
|
function getConnectionStatus() {
|
|
999
|
+
// Check direct WebSocket connection
|
|
1000
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
1001
|
+
return 'connected';
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// Check WebRTC data channel connection
|
|
1003
1005
|
if (dataChannel && dataChannel.readyState === 'open') {
|
|
1004
1006
|
return 'connected';
|
|
1005
|
-
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// Check connecting states
|
|
1010
|
+
if (ws && ws.readyState === WebSocket.CONNECTING) {
|
|
1006
1011
|
return 'connecting';
|
|
1007
|
-
} else {
|
|
1008
|
-
return 'disconnected';
|
|
1009
1012
|
}
|
|
1013
|
+
|
|
1014
|
+
if (dataChannel && dataChannel.readyState === 'connecting') {
|
|
1015
|
+
return 'connecting';
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Not connected
|
|
1019
|
+
return 'disconnected';
|
|
1010
1020
|
}
|
|
1011
1021
|
|
|
1012
1022
|
function formatLastActivity(timestamp) {
|
|
@@ -1036,16 +1046,59 @@ function switchToSession(sessionId) {
|
|
|
1036
1046
|
}
|
|
1037
1047
|
|
|
1038
1048
|
function createNewSession() {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1049
|
+
console.log('[CLIENT] 🆕 Creating new session...');
|
|
1050
|
+
|
|
1051
|
+
// Send create session message via active connection
|
|
1052
|
+
const message = {
|
|
1053
|
+
type: 'session-create',
|
|
1054
|
+
cols: term.cols,
|
|
1055
|
+
rows: term.rows
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
1059
|
+
// Direct WebSocket connection
|
|
1060
|
+
ws.send(JSON.stringify(message));
|
|
1061
|
+
} else if (dataChannel && dataChannel.readyState === 'open') {
|
|
1062
|
+
// WebRTC data channel connection
|
|
1063
|
+
dataChannel.send(JSON.stringify(message));
|
|
1064
|
+
} else {
|
|
1065
|
+
console.error('[CLIENT] ❌ Cannot create session - no active connection');
|
|
1066
|
+
term.write('\r\n\x1b[31m❌ Cannot create session - not connected\x1b[0m\r\n');
|
|
1067
|
+
}
|
|
1043
1068
|
}
|
|
1044
1069
|
|
|
1045
1070
|
|
|
1046
1071
|
// Handle session-related data channel messages
|
|
1047
1072
|
function handleSessionMessage(message) {
|
|
1048
1073
|
switch (message.type) {
|
|
1074
|
+
case 'session-created':
|
|
1075
|
+
console.log('[CLIENT] ✅ New session created:', message.sessionId);
|
|
1076
|
+
|
|
1077
|
+
// Update current session
|
|
1078
|
+
currentSession = {
|
|
1079
|
+
id: message.sessionId,
|
|
1080
|
+
name: message.sessionName || 'Terminal Session'
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
// Update available sessions list
|
|
1084
|
+
if (message.availableSessions) {
|
|
1085
|
+
availableSessions = message.availableSessions;
|
|
1086
|
+
} else {
|
|
1087
|
+
// Add to local list if not provided
|
|
1088
|
+
availableSessions.push(currentSession);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Clear terminal for new session
|
|
1092
|
+
term.clear();
|
|
1093
|
+
term.write(`\r\n\x1b[36m✨ New session created: ${currentSession.name}\x1b[0m\r\n\r\n`);
|
|
1094
|
+
|
|
1095
|
+
// Update UI
|
|
1096
|
+
updateSessionDisplay();
|
|
1097
|
+
|
|
1098
|
+
// Save to localStorage
|
|
1099
|
+
saveSessionToLocalStorage(AGENT_ID, currentSession);
|
|
1100
|
+
break;
|
|
1101
|
+
|
|
1049
1102
|
case 'session-switched':
|
|
1050
1103
|
currentSession = {
|
|
1051
1104
|
id: message.sessionId,
|
|
@@ -1054,7 +1107,7 @@ function handleSessionMessage(message) {
|
|
|
1054
1107
|
updateSessionDisplay();
|
|
1055
1108
|
term.clear(); // Clear terminal for new session
|
|
1056
1109
|
console.log('[CLIENT] ✅ Switched to session:', currentSession);
|
|
1057
|
-
|
|
1110
|
+
|
|
1058
1111
|
// Save updated session info
|
|
1059
1112
|
saveSessionToLocalStorage(AGENT_ID, currentSession);
|
|
1060
1113
|
break;
|