mstro-app 0.1.47

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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/bin/commands/config.js +145 -0
  4. package/bin/commands/login.js +313 -0
  5. package/bin/commands/logout.js +75 -0
  6. package/bin/commands/status.js +197 -0
  7. package/bin/commands/whoami.js +161 -0
  8. package/bin/configure-claude.js +298 -0
  9. package/bin/mstro.js +581 -0
  10. package/bin/postinstall.js +45 -0
  11. package/bin/release.sh +110 -0
  12. package/dist/server/cli/headless/claude-invoker.d.ts +17 -0
  13. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -0
  14. package/dist/server/cli/headless/claude-invoker.js +311 -0
  15. package/dist/server/cli/headless/claude-invoker.js.map +1 -0
  16. package/dist/server/cli/headless/index.d.ts +13 -0
  17. package/dist/server/cli/headless/index.d.ts.map +1 -0
  18. package/dist/server/cli/headless/index.js +10 -0
  19. package/dist/server/cli/headless/index.js.map +1 -0
  20. package/dist/server/cli/headless/mcp-config.d.ts +11 -0
  21. package/dist/server/cli/headless/mcp-config.d.ts.map +1 -0
  22. package/dist/server/cli/headless/mcp-config.js +76 -0
  23. package/dist/server/cli/headless/mcp-config.js.map +1 -0
  24. package/dist/server/cli/headless/output-utils.d.ts +33 -0
  25. package/dist/server/cli/headless/output-utils.d.ts.map +1 -0
  26. package/dist/server/cli/headless/output-utils.js +101 -0
  27. package/dist/server/cli/headless/output-utils.js.map +1 -0
  28. package/dist/server/cli/headless/prompt-utils.d.ts +21 -0
  29. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -0
  30. package/dist/server/cli/headless/prompt-utils.js +84 -0
  31. package/dist/server/cli/headless/prompt-utils.js.map +1 -0
  32. package/dist/server/cli/headless/runner.d.ts +24 -0
  33. package/dist/server/cli/headless/runner.d.ts.map +1 -0
  34. package/dist/server/cli/headless/runner.js +99 -0
  35. package/dist/server/cli/headless/runner.js.map +1 -0
  36. package/dist/server/cli/headless/types.d.ts +106 -0
  37. package/dist/server/cli/headless/types.d.ts.map +1 -0
  38. package/dist/server/cli/headless/types.js +4 -0
  39. package/dist/server/cli/headless/types.js.map +1 -0
  40. package/dist/server/cli/improvisation-session-manager.d.ts +155 -0
  41. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -0
  42. package/dist/server/cli/improvisation-session-manager.js +415 -0
  43. package/dist/server/cli/improvisation-session-manager.js.map +1 -0
  44. package/dist/server/index.d.ts +2 -0
  45. package/dist/server/index.d.ts.map +1 -0
  46. package/dist/server/index.js +386 -0
  47. package/dist/server/index.js.map +1 -0
  48. package/dist/server/mcp/bouncer-cli.d.ts +3 -0
  49. package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
  50. package/dist/server/mcp/bouncer-cli.js +99 -0
  51. package/dist/server/mcp/bouncer-cli.js.map +1 -0
  52. package/dist/server/mcp/bouncer-integration.d.ts +36 -0
  53. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -0
  54. package/dist/server/mcp/bouncer-integration.js +301 -0
  55. package/dist/server/mcp/bouncer-integration.js.map +1 -0
  56. package/dist/server/mcp/security-audit.d.ts +52 -0
  57. package/dist/server/mcp/security-audit.d.ts.map +1 -0
  58. package/dist/server/mcp/security-audit.js +118 -0
  59. package/dist/server/mcp/security-audit.js.map +1 -0
  60. package/dist/server/mcp/security-patterns.d.ts +73 -0
  61. package/dist/server/mcp/security-patterns.d.ts.map +1 -0
  62. package/dist/server/mcp/security-patterns.js +247 -0
  63. package/dist/server/mcp/security-patterns.js.map +1 -0
  64. package/dist/server/mcp/server.d.ts +3 -0
  65. package/dist/server/mcp/server.d.ts.map +1 -0
  66. package/dist/server/mcp/server.js +146 -0
  67. package/dist/server/mcp/server.js.map +1 -0
  68. package/dist/server/routes/files.d.ts +9 -0
  69. package/dist/server/routes/files.d.ts.map +1 -0
  70. package/dist/server/routes/files.js +24 -0
  71. package/dist/server/routes/files.js.map +1 -0
  72. package/dist/server/routes/improvise.d.ts +3 -0
  73. package/dist/server/routes/improvise.d.ts.map +1 -0
  74. package/dist/server/routes/improvise.js +72 -0
  75. package/dist/server/routes/improvise.js.map +1 -0
  76. package/dist/server/routes/index.d.ts +10 -0
  77. package/dist/server/routes/index.d.ts.map +1 -0
  78. package/dist/server/routes/index.js +12 -0
  79. package/dist/server/routes/index.js.map +1 -0
  80. package/dist/server/routes/instances.d.ts +10 -0
  81. package/dist/server/routes/instances.d.ts.map +1 -0
  82. package/dist/server/routes/instances.js +47 -0
  83. package/dist/server/routes/instances.js.map +1 -0
  84. package/dist/server/routes/notifications.d.ts +3 -0
  85. package/dist/server/routes/notifications.d.ts.map +1 -0
  86. package/dist/server/routes/notifications.js +136 -0
  87. package/dist/server/routes/notifications.js.map +1 -0
  88. package/dist/server/services/analytics.d.ts +56 -0
  89. package/dist/server/services/analytics.d.ts.map +1 -0
  90. package/dist/server/services/analytics.js +240 -0
  91. package/dist/server/services/analytics.js.map +1 -0
  92. package/dist/server/services/auth.d.ts +26 -0
  93. package/dist/server/services/auth.d.ts.map +1 -0
  94. package/dist/server/services/auth.js +71 -0
  95. package/dist/server/services/auth.js.map +1 -0
  96. package/dist/server/services/client-id.d.ts +10 -0
  97. package/dist/server/services/client-id.d.ts.map +1 -0
  98. package/dist/server/services/client-id.js +61 -0
  99. package/dist/server/services/client-id.js.map +1 -0
  100. package/dist/server/services/credentials.d.ts +39 -0
  101. package/dist/server/services/credentials.d.ts.map +1 -0
  102. package/dist/server/services/credentials.js +110 -0
  103. package/dist/server/services/credentials.js.map +1 -0
  104. package/dist/server/services/files.d.ts +119 -0
  105. package/dist/server/services/files.d.ts.map +1 -0
  106. package/dist/server/services/files.js +560 -0
  107. package/dist/server/services/files.js.map +1 -0
  108. package/dist/server/services/instances.d.ts +52 -0
  109. package/dist/server/services/instances.d.ts.map +1 -0
  110. package/dist/server/services/instances.js +241 -0
  111. package/dist/server/services/instances.js.map +1 -0
  112. package/dist/server/services/pathUtils.d.ts +47 -0
  113. package/dist/server/services/pathUtils.d.ts.map +1 -0
  114. package/dist/server/services/pathUtils.js +124 -0
  115. package/dist/server/services/pathUtils.js.map +1 -0
  116. package/dist/server/services/platform.d.ts +72 -0
  117. package/dist/server/services/platform.d.ts.map +1 -0
  118. package/dist/server/services/platform.js +368 -0
  119. package/dist/server/services/platform.js.map +1 -0
  120. package/dist/server/services/sentry.d.ts +5 -0
  121. package/dist/server/services/sentry.d.ts.map +1 -0
  122. package/dist/server/services/sentry.js +71 -0
  123. package/dist/server/services/sentry.js.map +1 -0
  124. package/dist/server/services/terminal/pty-manager.d.ts +149 -0
  125. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -0
  126. package/dist/server/services/terminal/pty-manager.js +377 -0
  127. package/dist/server/services/terminal/pty-manager.js.map +1 -0
  128. package/dist/server/services/terminal/tmux-manager.d.ts +82 -0
  129. package/dist/server/services/terminal/tmux-manager.d.ts.map +1 -0
  130. package/dist/server/services/terminal/tmux-manager.js +352 -0
  131. package/dist/server/services/terminal/tmux-manager.js.map +1 -0
  132. package/dist/server/services/websocket/autocomplete.d.ts +50 -0
  133. package/dist/server/services/websocket/autocomplete.d.ts.map +1 -0
  134. package/dist/server/services/websocket/autocomplete.js +361 -0
  135. package/dist/server/services/websocket/autocomplete.js.map +1 -0
  136. package/dist/server/services/websocket/file-utils.d.ts +44 -0
  137. package/dist/server/services/websocket/file-utils.d.ts.map +1 -0
  138. package/dist/server/services/websocket/file-utils.js +272 -0
  139. package/dist/server/services/websocket/file-utils.js.map +1 -0
  140. package/dist/server/services/websocket/handler.d.ts +246 -0
  141. package/dist/server/services/websocket/handler.d.ts.map +1 -0
  142. package/dist/server/services/websocket/handler.js +1771 -0
  143. package/dist/server/services/websocket/handler.js.map +1 -0
  144. package/dist/server/services/websocket/index.d.ts +11 -0
  145. package/dist/server/services/websocket/index.d.ts.map +1 -0
  146. package/dist/server/services/websocket/index.js +14 -0
  147. package/dist/server/services/websocket/index.js.map +1 -0
  148. package/dist/server/services/websocket/types.d.ts +214 -0
  149. package/dist/server/services/websocket/types.d.ts.map +1 -0
  150. package/dist/server/services/websocket/types.js +4 -0
  151. package/dist/server/services/websocket/types.js.map +1 -0
  152. package/dist/server/utils/agent-manager.d.ts +69 -0
  153. package/dist/server/utils/agent-manager.d.ts.map +1 -0
  154. package/dist/server/utils/agent-manager.js +269 -0
  155. package/dist/server/utils/agent-manager.js.map +1 -0
  156. package/dist/server/utils/paths.d.ts +25 -0
  157. package/dist/server/utils/paths.d.ts.map +1 -0
  158. package/dist/server/utils/paths.js +38 -0
  159. package/dist/server/utils/paths.js.map +1 -0
  160. package/dist/server/utils/port-manager.d.ts +10 -0
  161. package/dist/server/utils/port-manager.d.ts.map +1 -0
  162. package/dist/server/utils/port-manager.js +60 -0
  163. package/dist/server/utils/port-manager.js.map +1 -0
  164. package/dist/server/utils/port.d.ts +26 -0
  165. package/dist/server/utils/port.d.ts.map +1 -0
  166. package/dist/server/utils/port.js +83 -0
  167. package/dist/server/utils/port.js.map +1 -0
  168. package/hooks/bouncer.sh +138 -0
  169. package/package.json +74 -0
  170. package/server/README.md +191 -0
  171. package/server/cli/headless/claude-invoker.ts +415 -0
  172. package/server/cli/headless/index.ts +39 -0
  173. package/server/cli/headless/mcp-config.ts +87 -0
  174. package/server/cli/headless/output-utils.ts +109 -0
  175. package/server/cli/headless/prompt-utils.ts +108 -0
  176. package/server/cli/headless/runner.ts +133 -0
  177. package/server/cli/headless/types.ts +118 -0
  178. package/server/cli/improvisation-session-manager.ts +531 -0
  179. package/server/index.ts +456 -0
  180. package/server/mcp/README.md +122 -0
  181. package/server/mcp/bouncer-cli.ts +127 -0
  182. package/server/mcp/bouncer-integration.ts +430 -0
  183. package/server/mcp/security-audit.ts +180 -0
  184. package/server/mcp/security-patterns.ts +290 -0
  185. package/server/mcp/server.ts +174 -0
  186. package/server/routes/files.ts +29 -0
  187. package/server/routes/improvise.ts +82 -0
  188. package/server/routes/index.ts +13 -0
  189. package/server/routes/instances.ts +54 -0
  190. package/server/routes/notifications.ts +158 -0
  191. package/server/services/analytics.ts +277 -0
  192. package/server/services/auth.ts +80 -0
  193. package/server/services/client-id.ts +68 -0
  194. package/server/services/credentials.ts +134 -0
  195. package/server/services/files.ts +710 -0
  196. package/server/services/instances.ts +275 -0
  197. package/server/services/pathUtils.ts +158 -0
  198. package/server/services/platform.test.ts +1314 -0
  199. package/server/services/platform.ts +435 -0
  200. package/server/services/sentry.ts +81 -0
  201. package/server/services/terminal/pty-manager.ts +464 -0
  202. package/server/services/terminal/tmux-manager.ts +426 -0
  203. package/server/services/websocket/autocomplete.ts +438 -0
  204. package/server/services/websocket/file-utils.ts +305 -0
  205. package/server/services/websocket/handler.test.ts +20 -0
  206. package/server/services/websocket/handler.ts +2047 -0
  207. package/server/services/websocket/index.ts +40 -0
  208. package/server/services/websocket/types.ts +339 -0
  209. package/server/tsconfig.json +19 -0
  210. package/server/utils/agent-manager.ts +323 -0
  211. package/server/utils/paths.ts +45 -0
  212. package/server/utils/port-manager.ts +70 -0
  213. package/server/utils/port.ts +102 -0
@@ -0,0 +1,368 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+ /**
4
+ * Platform Connection Service
5
+ *
6
+ * Handles WebSocket connection to the Mstro platform.
7
+ * Requires token-based authentication from `mstro login`.
8
+ *
9
+ * Flow:
10
+ * 1. Client reads token from ~/.mstro/credentials.json
11
+ * 2. Client connects to platform WebSocket with auth token
12
+ * 3. Platform validates token and auto-pairs to user's account
13
+ * 4. Client becomes an "orchestra" visible in user's web dashboard
14
+ */
15
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
16
+ import { arch, homedir, hostname, type } from 'node:os';
17
+ import { basename, join } from 'node:path';
18
+ import { getClientId } from './client-id.js';
19
+ import { captureException } from './sentry.js';
20
+ import { isTmuxAvailable } from './terminal/tmux-manager.js';
21
+ const MSTRO_DIR = join(homedir(), '.mstro');
22
+ const CREDENTIALS_FILE = join(MSTRO_DIR, 'credentials.json');
23
+ // Refresh token every 30 days
24
+ const TOKEN_REFRESH_INTERVAL_MS = 30 * 24 * 60 * 60 * 1000;
25
+ /**
26
+ * Get stored credentials from ~/.mstro/credentials.json
27
+ */
28
+ function getCredentials() {
29
+ if (!existsSync(CREDENTIALS_FILE)) {
30
+ return null;
31
+ }
32
+ try {
33
+ const content = readFileSync(CREDENTIALS_FILE, 'utf-8');
34
+ const creds = JSON.parse(content);
35
+ if (creds.token && creds.userId && creds.email) {
36
+ return creds;
37
+ }
38
+ return null;
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ /**
45
+ * Update stored credentials (for token refresh)
46
+ */
47
+ function updateCredentials(updates) {
48
+ const creds = getCredentials();
49
+ if (!creds)
50
+ return;
51
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify({ ...creds, ...updates }, null, 2), {
52
+ mode: 0o600
53
+ });
54
+ }
55
+ /**
56
+ * Check if token should be refreshed
57
+ */
58
+ function shouldRefreshToken(creds) {
59
+ if (!creds.lastRefreshedAt) {
60
+ return true; // Never refreshed
61
+ }
62
+ const lastRefreshed = new Date(creds.lastRefreshedAt).getTime();
63
+ const now = Date.now();
64
+ return now - lastRefreshed > TOKEN_REFRESH_INTERVAL_MS;
65
+ }
66
+ /**
67
+ * Get machine identification string
68
+ * Format: "hostname @ node-vX.X.X platform (arch)"
69
+ * Example: "Jessica @ node-v22.21.1 linux (arm64)"
70
+ */
71
+ export function getMachineIdentifier() {
72
+ const machineHostname = hostname();
73
+ const nodeVersion = process.version;
74
+ const osType = type().toLowerCase();
75
+ const cpuArch = arch();
76
+ return `${machineHostname} @ node-${nodeVersion} ${osType} (${cpuArch})`;
77
+ }
78
+ // Get WebSocket class - use global if available (Bun, Node 21+), otherwise import from undici (Node 18-20)
79
+ let WebSocketImpl;
80
+ if (typeof WebSocket !== 'undefined') {
81
+ WebSocketImpl = WebSocket;
82
+ }
83
+ else {
84
+ // Node 18-20: use undici's WebSocket (bundled with Node.js but not typed)
85
+ // @ts-expect-error undici is bundled with Node.js but lacks type declarations
86
+ const { WebSocket: UndiciWS } = await import('undici');
87
+ WebSocketImpl = UndiciWS;
88
+ }
89
+ const DEFAULT_PLATFORM_URL = process.env.PLATFORM_URL || 'https://api.mstro.app';
90
+ /**
91
+ * Platform WebSocket connection with token-based authentication
92
+ */
93
+ export class PlatformConnection {
94
+ ws = null;
95
+ reconnectTimeout = null;
96
+ reconnectAttempts = 0;
97
+ maxReconnectAttempts = 10;
98
+ isIntentionallyClosed = false;
99
+ workingDirectory;
100
+ platformUrl;
101
+ callbacks;
102
+ connectionId = null;
103
+ isConnected = false;
104
+ tokenRefreshInterval = null;
105
+ heartbeatInterval = null;
106
+ constructor(workingDirectory, callbacks = {}, platformUrl) {
107
+ this.workingDirectory = workingDirectory;
108
+ this.platformUrl = platformUrl || DEFAULT_PLATFORM_URL;
109
+ this.callbacks = callbacks;
110
+ }
111
+ /**
112
+ * Refresh the device token if needed
113
+ */
114
+ async maybeRefreshToken() {
115
+ const creds = getCredentials();
116
+ if (!creds || !shouldRefreshToken(creds)) {
117
+ return;
118
+ }
119
+ try {
120
+ const response = await fetch(`${this.platformUrl}/api/auth/device/refresh`, {
121
+ method: 'POST',
122
+ headers: {
123
+ 'Authorization': `Bearer ${creds.token}`,
124
+ 'Content-Type': 'application/json'
125
+ }
126
+ });
127
+ if (response.ok) {
128
+ const data = await response.json();
129
+ updateCredentials({
130
+ token: data.accessToken,
131
+ lastRefreshedAt: new Date().toISOString()
132
+ });
133
+ }
134
+ else {
135
+ console.warn('[Platform] Token refresh failed, will retry later');
136
+ }
137
+ }
138
+ catch (err) {
139
+ console.warn('[Platform] Token refresh error:', err);
140
+ }
141
+ }
142
+ /**
143
+ * Start periodic token refresh check
144
+ */
145
+ startTokenRefreshCheck() {
146
+ // Check every 24 hours
147
+ this.tokenRefreshInterval = setInterval(() => {
148
+ this.maybeRefreshToken();
149
+ }, 24 * 60 * 60 * 1000);
150
+ }
151
+ /**
152
+ * Start heartbeat to keep connection alive and refresh server-side TTL
153
+ */
154
+ startHeartbeat() {
155
+ // Send ping every 2 minutes (server TTL is 5 minutes)
156
+ this.heartbeatInterval = setInterval(() => {
157
+ if (this.ws && this.isConnected) {
158
+ try {
159
+ this.ws.send(JSON.stringify({ type: 'ping' }));
160
+ }
161
+ catch {
162
+ // Ignore send errors - will reconnect if disconnected
163
+ }
164
+ }
165
+ }, 2 * 60 * 1000);
166
+ }
167
+ /**
168
+ * Stop heartbeat
169
+ */
170
+ stopHeartbeat() {
171
+ if (this.heartbeatInterval) {
172
+ clearInterval(this.heartbeatInterval);
173
+ this.heartbeatInterval = null;
174
+ }
175
+ }
176
+ /**
177
+ * Stop periodic token refresh check
178
+ */
179
+ stopTokenRefreshCheck() {
180
+ if (this.tokenRefreshInterval) {
181
+ clearInterval(this.tokenRefreshInterval);
182
+ this.tokenRefreshInterval = null;
183
+ }
184
+ }
185
+ /**
186
+ * Connect to platform WebSocket
187
+ */
188
+ connect() {
189
+ this.isIntentionallyClosed = false;
190
+ const name = basename(this.workingDirectory);
191
+ const machineHostname = hostname();
192
+ const clientId = getClientId();
193
+ const machineId = getMachineIdentifier();
194
+ const nodeVersion = process.version;
195
+ const osType = type().toLowerCase();
196
+ const cpuArch = arch();
197
+ // Get auth token from credentials
198
+ const credentials = getCredentials();
199
+ const authToken = credentials?.token;
200
+ if (!authToken) {
201
+ console.error('\n❌ Not logged in. Run `mstro login` first.\n');
202
+ this.callbacks.onError?.('Not logged in - run `mstro login` first');
203
+ return;
204
+ }
205
+ // Check for tmux availability (for persistent terminals)
206
+ const hasTmux = isTmuxAvailable();
207
+ // Build URL params WITHOUT the auth token — token is sent post-connection
208
+ // to avoid leaking it in proxy logs, browser history, and server access logs
209
+ const params = new URLSearchParams({
210
+ name,
211
+ workingDirectory: this.workingDirectory,
212
+ machineHostname,
213
+ clientId,
214
+ machineId,
215
+ nodeVersion,
216
+ osType,
217
+ cpuArch,
218
+ capabilities: JSON.stringify({ tmux: hasTmux })
219
+ });
220
+ const wsUrl = `${this.platformUrl.replace(/^http/, 'ws')}/ws/client?${params}`;
221
+ try {
222
+ this.ws = new WebSocketImpl(wsUrl);
223
+ }
224
+ catch (err) {
225
+ console.error('Failed to create WebSocket connection:', err);
226
+ captureException(err instanceof Error ? err : new Error(String(err)), { context: 'platform.connect' });
227
+ this.callbacks.onError?.('Failed to connect to platform');
228
+ this.scheduleReconnect();
229
+ return;
230
+ }
231
+ // Connection timeout - if not connected within 10 seconds, show helpful error
232
+ const connectionTimeout = setTimeout(() => {
233
+ const state = this.ws?.readyState;
234
+ if (this.ws && (state === 0 || state === undefined)) { // CONNECTING or unknown
235
+ console.error('\n❌ Connection timeout. The platform may have rejected your credentials.');
236
+ console.error(' Run `mstro login --force` to re-authenticate.\n');
237
+ this.ws.close();
238
+ this.callbacks.onError?.('Connection timeout - run `mstro login --force`');
239
+ }
240
+ }, 10000);
241
+ this.ws.onopen = () => {
242
+ clearTimeout(connectionTimeout);
243
+ console.log(`🌐 Connected to platform`);
244
+ // Send auth token as first message instead of URL param
245
+ this.ws.send(JSON.stringify({ type: 'auth', token: authToken }));
246
+ // Check if token needs refresh on connect
247
+ this.maybeRefreshToken();
248
+ // Start periodic refresh checks
249
+ this.startTokenRefreshCheck();
250
+ this.reconnectAttempts = 0;
251
+ };
252
+ this.ws.onmessage = (event) => {
253
+ try {
254
+ const message = JSON.parse(event.data.toString());
255
+ this.handleMessage(message);
256
+ }
257
+ catch (err) {
258
+ console.error('Failed to parse platform message:', err);
259
+ }
260
+ };
261
+ // Track if we ever successfully connected (received 'paired' message)
262
+ let everConnected = false;
263
+ const originalOnConnected = this.callbacks.onConnected;
264
+ this.callbacks.onConnected = (connectionId) => {
265
+ everConnected = true;
266
+ originalOnConnected?.(connectionId);
267
+ };
268
+ this.ws.onclose = (event) => {
269
+ // Stop heartbeat on any close
270
+ this.stopHeartbeat();
271
+ this.isConnected = false;
272
+ if (!this.isIntentionallyClosed) {
273
+ // Check if we were rejected due to auth (code 4001 or 1006 before ever connecting)
274
+ const isAuthFailure = event.code === 4001 ||
275
+ event.reason?.includes('Unauthorized') ||
276
+ (event.code === 1006 && !everConnected);
277
+ if (isAuthFailure) {
278
+ console.error('\n❌ Authentication failed. Your device token may be invalid or expired.');
279
+ console.error(' Run `mstro login --force` to re-authenticate.\n');
280
+ this.callbacks.onError?.('Authentication failed - run `mstro login --force`');
281
+ return;
282
+ }
283
+ console.log('Disconnected from platform, reconnecting...');
284
+ this.callbacks.onDisconnected?.();
285
+ this.scheduleReconnect();
286
+ }
287
+ };
288
+ this.ws.onerror = () => {
289
+ // onclose will be called after this
290
+ };
291
+ }
292
+ handleMessage(message) {
293
+ switch (message.type) {
294
+ case 'paired':
295
+ this.isConnected = true;
296
+ this.connectionId = message.connectionId;
297
+ console.log(`⚡ Connected to mstro.app!`);
298
+ // Start heartbeat to keep server-side TTL refreshed
299
+ this.startHeartbeat();
300
+ this.callbacks.onConnected?.(message.connectionId);
301
+ break;
302
+ case 'web_connected':
303
+ console.log('🔗 Web client connected');
304
+ this.callbacks.onWebConnected?.();
305
+ break;
306
+ case 'web_disconnected':
307
+ console.log('🔗 Web client disconnected');
308
+ this.callbacks.onWebDisconnected?.();
309
+ break;
310
+ case 'pong':
311
+ // Heartbeat response, ignore
312
+ break;
313
+ default:
314
+ // Relay message from web to wsHandler
315
+ // These are messages like 'execute', 'initTab', 'autocomplete', etc.
316
+ this.callbacks.onRelayedMessage?.(message);
317
+ break;
318
+ }
319
+ }
320
+ scheduleReconnect() {
321
+ if (this.reconnectTimeout)
322
+ return;
323
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
324
+ console.log('Max reconnection attempts reached. Restart "mstro" to try again.');
325
+ return;
326
+ }
327
+ this.reconnectAttempts++;
328
+ const delay = Math.min(1000 * 2 ** (this.reconnectAttempts - 1), 30000);
329
+ this.reconnectTimeout = setTimeout(() => {
330
+ this.reconnectTimeout = null;
331
+ this.connect();
332
+ }, delay);
333
+ }
334
+ /**
335
+ * Send message to platform (will be relayed to web if connected)
336
+ */
337
+ send(message) {
338
+ if (this.ws && this.ws.readyState === WebSocketImpl.OPEN) {
339
+ this.ws.send(JSON.stringify(message));
340
+ }
341
+ }
342
+ /**
343
+ * Check if connected to platform
344
+ */
345
+ isConnectedToPlatform() {
346
+ return this.isConnected && this.ws?.readyState === WebSocketImpl.OPEN;
347
+ }
348
+ /**
349
+ * Disconnect from platform
350
+ */
351
+ disconnect() {
352
+ this.isIntentionallyClosed = true;
353
+ // Stop heartbeat and token refresh checks
354
+ this.stopHeartbeat();
355
+ this.stopTokenRefreshCheck();
356
+ if (this.reconnectTimeout) {
357
+ clearTimeout(this.reconnectTimeout);
358
+ this.reconnectTimeout = null;
359
+ }
360
+ if (this.ws) {
361
+ this.ws.close();
362
+ this.ws = null;
363
+ }
364
+ this.isConnected = false;
365
+ this.connectionId = null;
366
+ }
367
+ }
368
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../../server/services/platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function initSentry(): void;
2
+ export declare function captureException(error: unknown, context?: Record<string, any>): void;
3
+ export declare function captureMessage(message: string, level?: 'info' | 'warning' | 'error'): void;
4
+ export declare function flushSentry(timeout?: number): Promise<void>;
5
+ //# sourceMappingURL=sentry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../../server/services/sentry.ts"],"names":[],"mappings":"AA8CA,wBAAgB,UAAU,IAAI,IAAI,CAmBjC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAGpF;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,OAAgB,GAAG,IAAI,CAGlG;AAED,wBAAsB,WAAW,CAAC,OAAO,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/D"}
@@ -0,0 +1,71 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import * as Sentry from '@sentry/node';
7
+ // Hardcoded DSN for production - this is safe to expose (can only send, not read)
8
+ // Override with SENTRY_DSN env var for development/testing
9
+ const SENTRY_DSN = process.env.SENTRY_DSN || 'https://2a8d2493e3ee5a7beec30f4518a5e24c@o4510824844820480.ingest.us.sentry.io/4510824923594752';
10
+ const CONFIG_FILE = join(homedir(), '.mstro', 'config.json');
11
+ let initialized = false;
12
+ /**
13
+ * Check if telemetry/error reporting is enabled
14
+ * Respects the same config as analytics (unified telemetry setting)
15
+ */
16
+ function isTelemetryEnabled() {
17
+ // Check environment variable first
18
+ const envValue = process.env.MSTRO_TELEMETRY;
19
+ if (envValue === '0' || envValue === 'false') {
20
+ return false;
21
+ }
22
+ // Check config file
23
+ if (existsSync(CONFIG_FILE)) {
24
+ try {
25
+ const config = JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
26
+ if (config.telemetry === false) {
27
+ return false;
28
+ }
29
+ }
30
+ catch {
31
+ // Ignore parse errors
32
+ }
33
+ }
34
+ return true;
35
+ }
36
+ export function initSentry() {
37
+ if (initialized)
38
+ return;
39
+ if (!isTelemetryEnabled())
40
+ return;
41
+ initialized = true;
42
+ Sentry.init({
43
+ dsn: SENTRY_DSN,
44
+ environment: process.env.NODE_ENV || 'development',
45
+ release: `mstro-cli@${process.env.npm_package_version || '0.0.0'}`,
46
+ tracesSampleRate: 0.1,
47
+ beforeSend(event) {
48
+ // Strip PII from error events
49
+ if (event.user) {
50
+ delete event.user.ip_address;
51
+ }
52
+ return event;
53
+ },
54
+ });
55
+ }
56
+ export function captureException(error, context) {
57
+ if (!initialized)
58
+ return;
59
+ Sentry.captureException(error, context ? { extra: context } : undefined);
60
+ }
61
+ export function captureMessage(message, level = 'info') {
62
+ if (!initialized)
63
+ return;
64
+ Sentry.captureMessage(message, level);
65
+ }
66
+ export async function flushSentry(timeout = 2000) {
67
+ if (!initialized)
68
+ return;
69
+ await Sentry.flush(timeout);
70
+ }
71
+ //# sourceMappingURL=sentry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../../server/services/sentry.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,KAAK,MAAM,MAAM,cAAc,CAAA;AAEtC,kFAAkF;AAClF,2DAA2D;AAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,iGAAiG,CAAA;AAE9I,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAA;AAE5D,IAAI,WAAW,GAAG,KAAK,CAAA;AAMvB;;;GAGG;AACH,SAAS,kBAAkB;IACzB,mCAAmC;IACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IAC5C,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC7C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,oBAAoB;IACpB,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAgB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAA;YAC1E,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;gBAC/B,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,WAAW;QAAE,OAAM;IACvB,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAM;IAEjC,WAAW,GAAG,IAAI,CAAA;IAElB,MAAM,CAAC,IAAI,CAAC;QACV,GAAG,EAAE,UAAU;QACf,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;QAClD,OAAO,EAAE,aAAa,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,EAAE;QAClE,gBAAgB,EAAE,GAAG;QACrB,UAAU,CAAC,KAAK;YACd,8BAA8B;YAC9B,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBACf,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAA;YAC9B,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,OAA6B;IAC5E,IAAI,CAAC,WAAW;QAAE,OAAM;IACxB,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AAC1E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,QAAsC,MAAM;IAC1F,IAAI,CAAC,WAAW;QAAE,OAAM;IACxB,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAO,GAAG,IAAI;IAC9C,IAAI,CAAC,WAAW;QAAE,OAAM;IACxB,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AAC7B,CAAC"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * PTY Manager - Manages pseudo-terminal sessions for shell access
3
+ *
4
+ * Provides terminal emulation for running shell commands on the local machine.
5
+ * Each terminal session is managed independently with its own PTY process.
6
+ *
7
+ * Supports session persistence:
8
+ * - Sessions survive WebSocket disconnections
9
+ * - Scrollback buffer is maintained for replay on reconnect
10
+ * - Sessions can be reattached without losing running processes
11
+ *
12
+ * Also supports tmux-backed persistence for sessions that survive server restarts.
13
+ *
14
+ * NOTE: node-pty is an optional dependency requiring native compilation.
15
+ * Terminal features gracefully degrade when node-pty is not available.
16
+ */
17
+ import { EventEmitter } from 'node:events';
18
+ import { type TmuxSession } from './tmux-manager.js';
19
+ /**
20
+ * Check if node-pty is available
21
+ */
22
+ export declare function isPtyAvailable(): boolean;
23
+ /**
24
+ * Get installation instructions for node-pty based on platform
25
+ */
26
+ export declare function getPtyInstallInstructions(): string;
27
+ type IPty = import('node-pty').IPty;
28
+ export interface PTYSession {
29
+ id: string;
30
+ pty: IPty;
31
+ shell: string;
32
+ cwd: string;
33
+ scrollback: string[];
34
+ createdAt: number;
35
+ lastActivityAt: number;
36
+ cols: number;
37
+ rows: number;
38
+ }
39
+ export declare class PTYManager extends EventEmitter {
40
+ private terminals;
41
+ constructor();
42
+ /**
43
+ * Check if a terminal session exists and is still running
44
+ */
45
+ exists(terminalId: string): boolean;
46
+ /**
47
+ * Get session info for reconnection
48
+ * Returns null if session doesn't exist
49
+ */
50
+ getSessionInfo(terminalId: string): {
51
+ shell: string;
52
+ cwd: string;
53
+ cols: number;
54
+ rows: number;
55
+ } | null;
56
+ /**
57
+ * Get scrollback buffer for replay on reconnect
58
+ * Returns the stored output history
59
+ */
60
+ getScrollback(terminalId: string): string[];
61
+ /**
62
+ * Add data to scrollback buffer
63
+ * Maintains a rolling buffer of recent terminal output
64
+ */
65
+ private addToScrollback;
66
+ /**
67
+ * Check if PTY functionality is available
68
+ */
69
+ isPtyAvailable(): boolean;
70
+ /**
71
+ * Get installation instructions if PTY is not available
72
+ */
73
+ getPtyInstallInstructions(): string;
74
+ /**
75
+ * Create a new terminal session
76
+ */
77
+ create(terminalId: string, workingDir: string, cols?: number, rows?: number, requestedShell?: string): {
78
+ shell: string;
79
+ cwd: string;
80
+ isReconnect: boolean;
81
+ };
82
+ /**
83
+ * Write input data to terminal
84
+ */
85
+ write(terminalId: string, data: string): boolean;
86
+ /**
87
+ * Resize terminal
88
+ */
89
+ resize(terminalId: string, cols: number, rows: number): boolean;
90
+ /**
91
+ * Close terminal session
92
+ */
93
+ close(terminalId: string): boolean;
94
+ /**
95
+ * Get terminal session info
96
+ */
97
+ getSession(terminalId: string): PTYSession | undefined;
98
+ /**
99
+ * Check if terminal exists
100
+ */
101
+ has(terminalId: string): boolean;
102
+ /**
103
+ * Get all active terminal IDs
104
+ */
105
+ getActiveTerminals(): string[];
106
+ /**
107
+ * Close all terminals
108
+ */
109
+ closeAll(): void;
110
+ /**
111
+ * Check if tmux persistence is available
112
+ */
113
+ isTmuxAvailable(): boolean;
114
+ /**
115
+ * Get list of persistent tmux sessions that can be restored
116
+ * These are sessions that survived a server restart
117
+ */
118
+ getPersistentSessions(): TmuxSession[];
119
+ /**
120
+ * Create a persistent (tmux-backed) terminal session
121
+ * These sessions survive server restarts
122
+ */
123
+ createPersistent(terminalId: string, workingDir: string, cols?: number, rows?: number, requestedShell?: string): {
124
+ shell: string;
125
+ cwd: string;
126
+ isReconnect: boolean;
127
+ persistent: true;
128
+ };
129
+ /**
130
+ * Attach to a persistent (tmux) session
131
+ * Returns handlers for write, resize, and detach
132
+ */
133
+ attachPersistent(terminalId: string, onOutput: (data: string) => void, onExit: (code: number) => void): {
134
+ write: (data: string) => void;
135
+ resize: (cols: number, rows: number) => void;
136
+ detach: () => void;
137
+ } | null;
138
+ /**
139
+ * Get scrollback from a persistent (tmux) session
140
+ */
141
+ getPersistentScrollback(terminalId: string): string[];
142
+ /**
143
+ * Close a persistent (tmux) session
144
+ */
145
+ closePersistent(terminalId: string): boolean;
146
+ }
147
+ export declare function getPTYManager(): PTYManager;
148
+ export {};
149
+ //# sourceMappingURL=pty-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-manager.d.ts","sourceRoot":"","sources":["../../../../server/services/terminal/pty-manager.ts"],"names":[],"mappings":"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"}