shell-mirror 1.5.107 → 1.5.108

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.
@@ -980,19 +980,41 @@ function setupDataChannel(clientId) {
980
980
  // Handle session closure request from client
981
981
  logToFile(`[AGENT] Client ${clientId} closing session ${message.sessionId}`);
982
982
 
983
- sessionManager.terminateSession(message.sessionId);
983
+ // Get remaining sessions BEFORE termination
984
+ const closingSessionId = message.sessionId;
985
+ sessionManager.terminateSession(closingSessionId);
986
+
987
+ const remainingSessions = sessionManager.getAllSessions();
984
988
 
985
989
  // Send confirmation with updated session list
986
990
  dataChannel.send(JSON.stringify({
987
991
  type: 'session-closed',
988
- sessionId: message.sessionId,
989
- availableSessions: sessionManager.getAllSessions()
992
+ sessionId: closingSessionId,
993
+ availableSessions: remainingSessions
990
994
  }));
991
995
 
996
+ // If client requested auto-switch to next session, do it atomically
997
+ if (message.switchToSessionId && remainingSessions.find(s => s.id === message.switchToSessionId)) {
998
+ logToFile(`[AGENT] Auto-switching client to session ${message.switchToSessionId}`);
999
+ if (sessionManager.connectClientToSession(clientId, message.switchToSessionId)) {
1000
+ const newSession = sessionManager.getSession(message.switchToSessionId);
1001
+ dataChannel.send(JSON.stringify({
1002
+ type: 'session-switched',
1003
+ sessionId: message.switchToSessionId,
1004
+ sessionName: newSession.name
1005
+ }));
1006
+ // Send buffered output
1007
+ const bufferedOutput = newSession.buffer.getAll();
1008
+ if (bufferedOutput) {
1009
+ sendLargeMessage(dataChannel, { type: 'output', data: bufferedOutput }, '[AGENT]');
1010
+ }
1011
+ }
1012
+ }
1013
+
992
1014
  // Send immediate heartbeat to update dashboard
993
1015
  sendHeartbeat();
994
1016
 
995
- logToFile(`[AGENT] ✅ Session closed: ${message.sessionId}`);
1017
+ logToFile(`[AGENT] ✅ Session closed: ${closingSessionId}`);
996
1018
  }
997
1019
  } catch (err) {
998
1020
  logToFile(`[AGENT] Error parsing data channel message: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shell-mirror",
3
- "version": "1.5.107",
3
+ "version": "1.5.108",
4
4
  "description": "Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -576,6 +576,6 @@
576
576
  }
577
577
  </script>
578
578
 
579
- <script src="/app/terminal.js?v=1.5.91"></script>
579
+ <script src="/app/terminal.js?v=1.5.92"></script>
580
580
  </body>
581
581
  </html>
@@ -1080,10 +1080,16 @@ function closeSession(sessionId, event) {
1080
1080
  function doCloseSession(sessionId) {
1081
1081
  console.log('[CLIENT] 🗑️ Closing session:', sessionId);
1082
1082
 
1083
- // Send close request to agent
1083
+ // Remove from available sessions
1084
+ const remainingSessions = availableSessions.filter(s => s.id !== sessionId);
1085
+ const isClosingCurrentSession = currentSession && currentSession.id === sessionId;
1086
+ const nextSession = isClosingCurrentSession && remainingSessions.length > 0 ? remainingSessions[0] : null;
1087
+
1088
+ // Send close request to agent (with auto-switch if closing active session)
1084
1089
  const closeMessage = {
1085
1090
  type: 'close_session',
1086
- sessionId: sessionId
1091
+ sessionId: sessionId,
1092
+ switchToSessionId: nextSession ? nextSession.id : null // Atomic close + switch
1087
1093
  };
1088
1094
 
1089
1095
  if (dataChannel && dataChannel.readyState === 'open') {
@@ -1092,25 +1098,25 @@ function doCloseSession(sessionId) {
1092
1098
  ws.send(JSON.stringify(closeMessage));
1093
1099
  }
1094
1100
 
1095
- // Remove from available sessions
1096
- availableSessions = availableSessions.filter(s => s.id !== sessionId);
1101
+ // Update local state
1102
+ availableSessions = remainingSessions;
1097
1103
 
1098
- // If closing current session, switch to another or show message
1099
- if (currentSession && currentSession.id === sessionId) {
1100
- if (availableSessions.length > 0) {
1104
+ // If closing current session, update UI immediately
1105
+ if (isClosingCurrentSession) {
1106
+ // Clear terminal IMMEDIATELY to prevent garbage from closed session
1107
+ term.clear();
1108
+
1109
+ if (nextSession) {
1101
1110
  // Update currentSession IMMEDIATELY so renderTabs shows correct active tab
1102
- const nextSession = availableSessions[0];
1103
1111
  currentSession = {
1104
1112
  id: nextSession.id,
1105
1113
  name: nextSession.name || 'Terminal Session'
1106
1114
  };
1107
- // Tell agent to switch (will send session-switched confirmation)
1108
- switchToSession(nextSession.id);
1109
1115
  // Update URL
1110
1116
  updateUrlWithSession(nextSession.id);
1117
+ // Note: Agent will send session-switched with buffered output
1111
1118
  } else {
1112
1119
  currentSession = null;
1113
- term.clear();
1114
1120
  term.write('\r\n\x1b[33mSession closed. Click + to create a new session.\x1b[0m\r\n');
1115
1121
  }
1116
1122
  }
@@ -1258,14 +1264,20 @@ function handleSessionMessage(message) {
1258
1264
  term.focus();
1259
1265
  break;
1260
1266
  case 'session-ended':
1261
- term.write(`\r\n\x1b[31m❌ Session ended: ${message.reason}\x1b[0m\r\n`);
1262
- if (message.code) {
1263
- term.write(`Exit code: ${message.code}\r\n`);
1267
+ // Only show if this is for the current session (ignore closed session messages)
1268
+ if (!message.sessionId || (currentSession && message.sessionId === currentSession.id)) {
1269
+ term.write(`\r\n\x1b[31m❌ Session ended: ${message.reason}\x1b[0m\r\n`);
1270
+ if (message.code) {
1271
+ term.write(`Exit code: ${message.code}\r\n`);
1272
+ }
1264
1273
  }
1265
1274
  break;
1266
1275
  case 'session-terminated':
1267
- term.write(`\r\n\x1b[31m❌ Session terminated\x1b[0m\r\n`);
1268
- term.write('🔄 Click Dashboard to start a new session\r\n');
1276
+ // Only show if this is for the current session (ignore closed session messages)
1277
+ if (!message.sessionId || (currentSession && message.sessionId === currentSession.id)) {
1278
+ term.write(`\r\n\x1b[31m❌ Session terminated\x1b[0m\r\n`);
1279
+ term.write('🔄 Click Dashboard to start a new session\r\n');
1280
+ }
1269
1281
  break;
1270
1282
  case 'session-closed':
1271
1283
  console.log('[CLIENT] ✅ Session closed confirmed:', message.sessionId);