shell-mirror 1.5.83 → 1.5.85

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.
@@ -931,6 +931,31 @@ function setupDataChannel(clientId) {
931
931
  message: `Session ${message.sessionId} not found`
932
932
  }));
933
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
+ }
934
959
  }
935
960
  } catch (err) {
936
961
  logToFile(`[AGENT] Error parsing data channel message: ${err.message}`);
@@ -1038,6 +1063,7 @@ function startLocalServer() {
1038
1063
  localWs.send(JSON.stringify({
1039
1064
  type: 'session_created',
1040
1065
  sessionId,
1066
+ sessionName: `Session ${sessionId.slice(0, 8)}`,
1041
1067
  cols: message.cols || 120,
1042
1068
  rows: message.rows || 30
1043
1069
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shell-mirror",
3
- "version": "1.5.83",
3
+ "version": "1.5.85",
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": {
@@ -109,6 +109,26 @@
109
109
  gap: 8px;
110
110
  }
111
111
 
112
+ /* Connection Status Indicator */
113
+ .connection-status {
114
+ width: 12px;
115
+ height: 12px;
116
+ border-radius: 50%;
117
+ background: #ff4444;
118
+ margin-right: 12px;
119
+ flex-shrink: 0;
120
+ }
121
+
122
+ .connection-status.connecting {
123
+ background: #ffaa44;
124
+ animation: pulse 1.5s ease-in-out infinite;
125
+ }
126
+
127
+ .connection-status.connected {
128
+ background: #44ff44;
129
+ box-shadow: 0 0 6px rgba(68, 255, 68, 0.5);
130
+ }
131
+
112
132
  /* Session Tab Bar */
113
133
  .session-tab-bar {
114
134
  display: flex;
@@ -158,27 +178,6 @@
158
178
  background: #1a1a1a;
159
179
  }
160
180
 
161
- .session-tab-status {
162
- width: 8px;
163
- height: 8px;
164
- border-radius: 50%;
165
- flex-shrink: 0;
166
- }
167
-
168
- .session-tab-status.connected {
169
- background: #44ff44;
170
- box-shadow: 0 0 6px rgba(68, 255, 68, 0.5);
171
- }
172
-
173
- .session-tab-status.connecting {
174
- background: #ffaa44;
175
- animation: pulse 1.5s ease-in-out infinite;
176
- }
177
-
178
- .session-tab-status.disconnected {
179
- background: #ff4444;
180
- }
181
-
182
181
  .session-tab-name {
183
182
  overflow: hidden;
184
183
  text-overflow: ellipsis;
@@ -237,23 +236,6 @@
237
236
  }
238
237
  }
239
238
 
240
- /* Dashboard button with status dot */
241
- .dashboard-btn {
242
- position: relative;
243
- }
244
-
245
- .dashboard-btn::before {
246
- content: '';
247
- position: absolute;
248
- top: 4px;
249
- right: 4px;
250
- width: 8px;
251
- height: 8px;
252
- border-radius: 50%;
253
- background: #44ff44;
254
- box-shadow: 0 0 8px rgba(68, 255, 68, 0.5);
255
- }
256
-
257
239
  #terminal {
258
240
  padding: 8px; /* Mac Terminal.app padding */
259
241
  background-color: #000000;
@@ -355,6 +337,9 @@
355
337
  </div>
356
338
  <div id="terminal-container">
357
339
  <div class="session-header" id="session-header" style="display: none;">
340
+ <!-- Connection Status Indicator -->
341
+ <div class="connection-status" id="connection-status"></div>
342
+
358
343
  <!-- Session Tab Bar -->
359
344
  <div class="session-tab-bar" id="session-tab-bar">
360
345
  <!-- Tabs will be rendered here by JavaScript -->
@@ -153,10 +153,16 @@ const chunkAssembler = {
153
153
 
154
154
  // Connection status management
155
155
  function updateConnectionStatus(status) {
156
- // Update tab status indicators only (removed global connection-status element)
157
- if (currentSession) {
158
- renderTabs();
156
+ const statusElement = document.getElementById('connection-status');
157
+ if (!statusElement) return;
158
+
159
+ statusElement.className = 'connection-status';
160
+ if (status === 'connecting') {
161
+ statusElement.classList.add('connecting');
162
+ } else if (status === 'connected') {
163
+ statusElement.classList.add('connected');
159
164
  }
165
+ // else: disconnected (default red)
160
166
  }
161
167
 
162
168
  // Cleanup timer for chunk assembler
@@ -382,8 +388,20 @@ function setupDirectConnection(directWs) {
382
388
 
383
389
  case 'session_created':
384
390
  console.log('[CLIENT] ✅ Direct session created:', data.sessionId);
385
- currentSession = { id: data.sessionId };
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
+
386
401
  updateSessionDisplay();
402
+
403
+ // Save to localStorage
404
+ saveSessionToLocalStorage(AGENT_ID, currentSession);
387
405
  break;
388
406
 
389
407
  case 'output':
@@ -958,14 +976,12 @@ function renderTabs() {
958
976
  tabsHTML = sessionsToRender.map(session => {
959
977
  const isActive = currentSession && session.id === currentSession.id;
960
978
  const displayName = session.name || 'Terminal Session';
961
- const status = getSessionStatus(session.id); // Per-session status
962
979
 
963
980
  return `
964
981
  <button class="session-tab ${isActive ? 'active' : ''}"
965
982
  onclick="switchToSession('${session.id}')"
966
983
  ${isActive ? 'disabled' : ''}
967
984
  title="${displayName}">
968
- <span class="session-tab-status ${status}"></span>
969
985
  <span class="session-tab-name">${displayName}</span>
970
986
  </button>
971
987
  `;
@@ -979,18 +995,27 @@ function renderTabs() {
979
995
  console.log('[CLIENT] ✅ Tabs rendered:', sessionsToRender.length, 'tabs');
980
996
  }
981
997
 
982
- function getSessionStatus(sessionId) {
983
- // Current/active session shows actual connection status
984
- if (currentSession && sessionId === currentSession.id) {
985
- if (dataChannel && dataChannel.readyState === 'open') {
986
- return 'connected';
987
- } else if (dataChannel && dataChannel.readyState === 'connecting') {
988
- return 'connecting';
989
- }
990
- return 'disconnected';
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
1005
+ if (dataChannel && dataChannel.readyState === 'open') {
1006
+ return 'connected';
1007
+ }
1008
+
1009
+ // Check connecting states
1010
+ if (ws && ws.readyState === WebSocket.CONNECTING) {
1011
+ return 'connecting';
991
1012
  }
992
1013
 
993
- // Inactive sessions show as disconnected (not currently connected)
1014
+ if (dataChannel && dataChannel.readyState === 'connecting') {
1015
+ return 'connecting';
1016
+ }
1017
+
1018
+ // Not connected
994
1019
  return 'disconnected';
995
1020
  }
996
1021
 
@@ -1021,16 +1046,60 @@ function switchToSession(sessionId) {
1021
1046
  }
1022
1047
 
1023
1048
  function createNewSession() {
1024
- // Navigate to terminal with new session request
1025
- const url = new URL(window.location.href);
1026
- url.searchParams.delete('session'); // Remove session param to create new one
1027
- window.location.href = url.toString();
1049
+ console.log('[CLIENT] 🆕 Creating new session...');
1050
+
1051
+ if (ws && ws.readyState === WebSocket.OPEN) {
1052
+ // Direct WebSocket connection - use underscore
1053
+ ws.send(JSON.stringify({
1054
+ type: 'create_session',
1055
+ cols: term.cols,
1056
+ rows: term.rows
1057
+ }));
1058
+ } else if (dataChannel && dataChannel.readyState === 'open') {
1059
+ // WebRTC data channel connection - use hyphen
1060
+ dataChannel.send(JSON.stringify({
1061
+ type: 'session-create',
1062
+ cols: term.cols,
1063
+ rows: term.rows
1064
+ }));
1065
+ } else {
1066
+ console.error('[CLIENT] ❌ Cannot create session - no active connection');
1067
+ term.write('\r\n\x1b[31m❌ Cannot create session - not connected\x1b[0m\r\n');
1068
+ }
1028
1069
  }
1029
1070
 
1030
1071
 
1031
1072
  // Handle session-related data channel messages
1032
1073
  function handleSessionMessage(message) {
1033
1074
  switch (message.type) {
1075
+ case 'session-created':
1076
+ console.log('[CLIENT] ✅ New session created:', message.sessionId);
1077
+
1078
+ // Update current session
1079
+ currentSession = {
1080
+ id: message.sessionId,
1081
+ name: message.sessionName || 'Terminal Session'
1082
+ };
1083
+
1084
+ // Update available sessions list
1085
+ if (message.availableSessions) {
1086
+ availableSessions = message.availableSessions;
1087
+ } else {
1088
+ // Add to local list if not provided
1089
+ availableSessions.push(currentSession);
1090
+ }
1091
+
1092
+ // Clear terminal for new session
1093
+ term.clear();
1094
+ term.write(`\r\n\x1b[36m✨ New session created: ${currentSession.name}\x1b[0m\r\n\r\n`);
1095
+
1096
+ // Update UI
1097
+ updateSessionDisplay();
1098
+
1099
+ // Save to localStorage
1100
+ saveSessionToLocalStorage(AGENT_ID, currentSession);
1101
+ break;
1102
+
1034
1103
  case 'session-switched':
1035
1104
  currentSession = {
1036
1105
  id: message.sessionId,
@@ -1039,7 +1108,7 @@ function handleSessionMessage(message) {
1039
1108
  updateSessionDisplay();
1040
1109
  term.clear(); // Clear terminal for new session
1041
1110
  console.log('[CLIENT] ✅ Switched to session:', currentSession);
1042
-
1111
+
1043
1112
  // Save updated session info
1044
1113
  saveSessionToLocalStorage(AGENT_ID, currentSession);
1045
1114
  break;