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.
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/bin/commands/config.js +145 -0
- package/bin/commands/login.js +313 -0
- package/bin/commands/logout.js +75 -0
- package/bin/commands/status.js +197 -0
- package/bin/commands/whoami.js +161 -0
- package/bin/configure-claude.js +298 -0
- package/bin/mstro.js +581 -0
- package/bin/postinstall.js +45 -0
- package/bin/release.sh +110 -0
- package/dist/server/cli/headless/claude-invoker.d.ts +17 -0
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker.js +311 -0
- package/dist/server/cli/headless/claude-invoker.js.map +1 -0
- package/dist/server/cli/headless/index.d.ts +13 -0
- package/dist/server/cli/headless/index.d.ts.map +1 -0
- package/dist/server/cli/headless/index.js +10 -0
- package/dist/server/cli/headless/index.js.map +1 -0
- package/dist/server/cli/headless/mcp-config.d.ts +11 -0
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -0
- package/dist/server/cli/headless/mcp-config.js +76 -0
- package/dist/server/cli/headless/mcp-config.js.map +1 -0
- package/dist/server/cli/headless/output-utils.d.ts +33 -0
- package/dist/server/cli/headless/output-utils.d.ts.map +1 -0
- package/dist/server/cli/headless/output-utils.js +101 -0
- package/dist/server/cli/headless/output-utils.js.map +1 -0
- package/dist/server/cli/headless/prompt-utils.d.ts +21 -0
- package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -0
- package/dist/server/cli/headless/prompt-utils.js +84 -0
- package/dist/server/cli/headless/prompt-utils.js.map +1 -0
- package/dist/server/cli/headless/runner.d.ts +24 -0
- package/dist/server/cli/headless/runner.d.ts.map +1 -0
- package/dist/server/cli/headless/runner.js +99 -0
- package/dist/server/cli/headless/runner.js.map +1 -0
- package/dist/server/cli/headless/types.d.ts +106 -0
- package/dist/server/cli/headless/types.d.ts.map +1 -0
- package/dist/server/cli/headless/types.js +4 -0
- package/dist/server/cli/headless/types.js.map +1 -0
- package/dist/server/cli/improvisation-session-manager.d.ts +155 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -0
- package/dist/server/cli/improvisation-session-manager.js +415 -0
- package/dist/server/cli/improvisation-session-manager.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +386 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/bouncer-cli.d.ts +3 -0
- package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-cli.js +99 -0
- package/dist/server/mcp/bouncer-cli.js.map +1 -0
- package/dist/server/mcp/bouncer-integration.d.ts +36 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-integration.js +301 -0
- package/dist/server/mcp/bouncer-integration.js.map +1 -0
- package/dist/server/mcp/security-audit.d.ts +52 -0
- package/dist/server/mcp/security-audit.d.ts.map +1 -0
- package/dist/server/mcp/security-audit.js +118 -0
- package/dist/server/mcp/security-audit.js.map +1 -0
- package/dist/server/mcp/security-patterns.d.ts +73 -0
- package/dist/server/mcp/security-patterns.d.ts.map +1 -0
- package/dist/server/mcp/security-patterns.js +247 -0
- package/dist/server/mcp/security-patterns.js.map +1 -0
- package/dist/server/mcp/server.d.ts +3 -0
- package/dist/server/mcp/server.d.ts.map +1 -0
- package/dist/server/mcp/server.js +146 -0
- package/dist/server/mcp/server.js.map +1 -0
- package/dist/server/routes/files.d.ts +9 -0
- package/dist/server/routes/files.d.ts.map +1 -0
- package/dist/server/routes/files.js +24 -0
- package/dist/server/routes/files.js.map +1 -0
- package/dist/server/routes/improvise.d.ts +3 -0
- package/dist/server/routes/improvise.d.ts.map +1 -0
- package/dist/server/routes/improvise.js +72 -0
- package/dist/server/routes/improvise.js.map +1 -0
- package/dist/server/routes/index.d.ts +10 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +12 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/instances.d.ts +10 -0
- package/dist/server/routes/instances.d.ts.map +1 -0
- package/dist/server/routes/instances.js +47 -0
- package/dist/server/routes/instances.js.map +1 -0
- package/dist/server/routes/notifications.d.ts +3 -0
- package/dist/server/routes/notifications.d.ts.map +1 -0
- package/dist/server/routes/notifications.js +136 -0
- package/dist/server/routes/notifications.js.map +1 -0
- package/dist/server/services/analytics.d.ts +56 -0
- package/dist/server/services/analytics.d.ts.map +1 -0
- package/dist/server/services/analytics.js +240 -0
- package/dist/server/services/analytics.js.map +1 -0
- package/dist/server/services/auth.d.ts +26 -0
- package/dist/server/services/auth.d.ts.map +1 -0
- package/dist/server/services/auth.js +71 -0
- package/dist/server/services/auth.js.map +1 -0
- package/dist/server/services/client-id.d.ts +10 -0
- package/dist/server/services/client-id.d.ts.map +1 -0
- package/dist/server/services/client-id.js +61 -0
- package/dist/server/services/client-id.js.map +1 -0
- package/dist/server/services/credentials.d.ts +39 -0
- package/dist/server/services/credentials.d.ts.map +1 -0
- package/dist/server/services/credentials.js +110 -0
- package/dist/server/services/credentials.js.map +1 -0
- package/dist/server/services/files.d.ts +119 -0
- package/dist/server/services/files.d.ts.map +1 -0
- package/dist/server/services/files.js +560 -0
- package/dist/server/services/files.js.map +1 -0
- package/dist/server/services/instances.d.ts +52 -0
- package/dist/server/services/instances.d.ts.map +1 -0
- package/dist/server/services/instances.js +241 -0
- package/dist/server/services/instances.js.map +1 -0
- package/dist/server/services/pathUtils.d.ts +47 -0
- package/dist/server/services/pathUtils.d.ts.map +1 -0
- package/dist/server/services/pathUtils.js +124 -0
- package/dist/server/services/pathUtils.js.map +1 -0
- package/dist/server/services/platform.d.ts +72 -0
- package/dist/server/services/platform.d.ts.map +1 -0
- package/dist/server/services/platform.js +368 -0
- package/dist/server/services/platform.js.map +1 -0
- package/dist/server/services/sentry.d.ts +5 -0
- package/dist/server/services/sentry.d.ts.map +1 -0
- package/dist/server/services/sentry.js +71 -0
- package/dist/server/services/sentry.js.map +1 -0
- package/dist/server/services/terminal/pty-manager.d.ts +149 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -0
- package/dist/server/services/terminal/pty-manager.js +377 -0
- package/dist/server/services/terminal/pty-manager.js.map +1 -0
- package/dist/server/services/terminal/tmux-manager.d.ts +82 -0
- package/dist/server/services/terminal/tmux-manager.d.ts.map +1 -0
- package/dist/server/services/terminal/tmux-manager.js +352 -0
- package/dist/server/services/terminal/tmux-manager.js.map +1 -0
- package/dist/server/services/websocket/autocomplete.d.ts +50 -0
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -0
- package/dist/server/services/websocket/autocomplete.js +361 -0
- package/dist/server/services/websocket/autocomplete.js.map +1 -0
- package/dist/server/services/websocket/file-utils.d.ts +44 -0
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -0
- package/dist/server/services/websocket/file-utils.js +272 -0
- package/dist/server/services/websocket/file-utils.js.map +1 -0
- package/dist/server/services/websocket/handler.d.ts +246 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -0
- package/dist/server/services/websocket/handler.js +1771 -0
- package/dist/server/services/websocket/handler.js.map +1 -0
- package/dist/server/services/websocket/index.d.ts +11 -0
- package/dist/server/services/websocket/index.d.ts.map +1 -0
- package/dist/server/services/websocket/index.js +14 -0
- package/dist/server/services/websocket/index.js.map +1 -0
- package/dist/server/services/websocket/types.d.ts +214 -0
- package/dist/server/services/websocket/types.d.ts.map +1 -0
- package/dist/server/services/websocket/types.js +4 -0
- package/dist/server/services/websocket/types.js.map +1 -0
- package/dist/server/utils/agent-manager.d.ts +69 -0
- package/dist/server/utils/agent-manager.d.ts.map +1 -0
- package/dist/server/utils/agent-manager.js +269 -0
- package/dist/server/utils/agent-manager.js.map +1 -0
- package/dist/server/utils/paths.d.ts +25 -0
- package/dist/server/utils/paths.d.ts.map +1 -0
- package/dist/server/utils/paths.js +38 -0
- package/dist/server/utils/paths.js.map +1 -0
- package/dist/server/utils/port-manager.d.ts +10 -0
- package/dist/server/utils/port-manager.d.ts.map +1 -0
- package/dist/server/utils/port-manager.js +60 -0
- package/dist/server/utils/port-manager.js.map +1 -0
- package/dist/server/utils/port.d.ts +26 -0
- package/dist/server/utils/port.d.ts.map +1 -0
- package/dist/server/utils/port.js +83 -0
- package/dist/server/utils/port.js.map +1 -0
- package/hooks/bouncer.sh +138 -0
- package/package.json +74 -0
- package/server/README.md +191 -0
- package/server/cli/headless/claude-invoker.ts +415 -0
- package/server/cli/headless/index.ts +39 -0
- package/server/cli/headless/mcp-config.ts +87 -0
- package/server/cli/headless/output-utils.ts +109 -0
- package/server/cli/headless/prompt-utils.ts +108 -0
- package/server/cli/headless/runner.ts +133 -0
- package/server/cli/headless/types.ts +118 -0
- package/server/cli/improvisation-session-manager.ts +531 -0
- package/server/index.ts +456 -0
- package/server/mcp/README.md +122 -0
- package/server/mcp/bouncer-cli.ts +127 -0
- package/server/mcp/bouncer-integration.ts +430 -0
- package/server/mcp/security-audit.ts +180 -0
- package/server/mcp/security-patterns.ts +290 -0
- package/server/mcp/server.ts +174 -0
- package/server/routes/files.ts +29 -0
- package/server/routes/improvise.ts +82 -0
- package/server/routes/index.ts +13 -0
- package/server/routes/instances.ts +54 -0
- package/server/routes/notifications.ts +158 -0
- package/server/services/analytics.ts +277 -0
- package/server/services/auth.ts +80 -0
- package/server/services/client-id.ts +68 -0
- package/server/services/credentials.ts +134 -0
- package/server/services/files.ts +710 -0
- package/server/services/instances.ts +275 -0
- package/server/services/pathUtils.ts +158 -0
- package/server/services/platform.test.ts +1314 -0
- package/server/services/platform.ts +435 -0
- package/server/services/sentry.ts +81 -0
- package/server/services/terminal/pty-manager.ts +464 -0
- package/server/services/terminal/tmux-manager.ts +426 -0
- package/server/services/websocket/autocomplete.ts +438 -0
- package/server/services/websocket/file-utils.ts +305 -0
- package/server/services/websocket/handler.test.ts +20 -0
- package/server/services/websocket/handler.ts +2047 -0
- package/server/services/websocket/index.ts +40 -0
- package/server/services/websocket/types.ts +339 -0
- package/server/tsconfig.json +19 -0
- package/server/utils/agent-manager.ts +323 -0
- package/server/utils/paths.ts +45 -0
- package/server/utils/port-manager.ts +70 -0
- 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":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACjE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE5D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;AAC3C,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAA;AAE5D,8BAA8B;AAC9B,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAW1D;;GAEG;AACH,SAAS,cAAc;IACrB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAA;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACjC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/C,OAAO,KAAK,CAAA;QACd,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,OAAmC;IAC5D,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAM;IAElB,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACjF,IAAI,EAAE,KAAK;KACZ,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,KAAwB;IAClD,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAA,CAAC,kBAAkB;IAChC,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAA;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,OAAO,GAAG,GAAG,aAAa,GAAG,yBAAyB,CAAA;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,eAAe,GAAG,QAAQ,EAAE,CAAA;IAClC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAA;IACnC,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACnC,MAAM,OAAO,GAAG,IAAI,EAAE,CAAA;IACtB,OAAO,GAAG,eAAe,WAAW,WAAW,IAAI,MAAM,KAAK,OAAO,GAAG,CAAA;AAC1E,CAAC;AAED,2GAA2G;AAC3G,IAAI,aAA+B,CAAA;AACnC,IAAI,OAAO,SAAS,KAAK,WAAW,EAAE,CAAC;IACrC,aAAa,GAAG,SAAS,CAAA;AAC3B,CAAC;KAAM,CAAC;IACN,0EAA0E;IAC1E,8EAA8E;IAC9E,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAA;IACtD,aAAa,GAAG,QAAuC,CAAA;AACzD,CAAC;AAED,MAAM,oBAAoB,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB,CAAA;AAWhF;;GAEG;AACH,MAAM,OAAO,kBAAkB;IACrB,EAAE,GAAqB,IAAI,CAAA;IAC3B,gBAAgB,GAAyC,IAAI,CAAA;IAC7D,iBAAiB,GAAG,CAAC,CAAA;IACrB,oBAAoB,GAAG,EAAE,CAAA;IACzB,qBAAqB,GAAG,KAAK,CAAA;IAC7B,gBAAgB,CAAQ;IACxB,WAAW,CAAQ;IACnB,SAAS,CAAqB;IAC9B,YAAY,GAAkB,IAAI,CAAA;IAClC,WAAW,GAAG,KAAK,CAAA;IACnB,oBAAoB,GAA0C,IAAI,CAAA;IAClE,iBAAiB,GAA0C,IAAI,CAAA;IAEvE,YACE,gBAAwB,EACxB,YAAiC,EAAE,EACnC,WAAoB;QAEpB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;QACxC,IAAI,CAAC,WAAW,GAAG,WAAW,IAAI,oBAAoB,CAAA;QACtD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB;QAC7B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;QAC9B,IAAI,CAAC,KAAK,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,OAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,0BAA0B,EAAE;gBAC1E,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,eAAe,EAAE,UAAU,KAAK,CAAC,KAAK,EAAE;oBACxC,cAAc,EAAE,kBAAkB;iBACnC;aACF,CAAC,CAAA;YAEF,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA6B,CAAA;gBAC7D,iBAAiB,CAAC;oBAChB,KAAK,EAAE,IAAI,CAAC,WAAW;oBACvB,eAAe,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBAC1C,CAAC,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAA;YACnE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,sBAAsB;QAC5B,uBAAuB;QACvB,IAAI,CAAC,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;YAC3C,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACzB,CAAC;IAED;;OAEG;IACK,cAAc;QACpB,sDAAsD;QACtD,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;YACxC,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAChC,IAAI,CAAC;oBACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;gBAChD,CAAC;gBAAC,MAAM,CAAC;oBACP,sDAAsD;gBACxD,CAAC;YACH,CAAC;QACH,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACnB,CAAC;IAED;;OAEG;IACK,aAAa;QACnB,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;YACrC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAA;QAC/B,CAAC;IACH,CAAC;IAED;;OAEG;IACK,qBAAqB;QAC3B,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9B,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAA;YACxC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAA;QAClC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAA;QAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;QAC5C,MAAM,eAAe,GAAG,QAAQ,EAAE,CAAA;QAClC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;QAC9B,MAAM,SAAS,GAAG,oBAAoB,EAAE,CAAA;QACxC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAA;QACnC,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QACnC,MAAM,OAAO,GAAG,IAAI,EAAE,CAAA;QAEtB,kCAAkC;QAClC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAA;QACpC,MAAM,SAAS,GAAG,WAAW,EAAE,KAAK,CAAA;QAEpC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAA;YAC9D,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,yCAAyC,CAAC,CAAA;YACnE,OAAM;QACR,CAAC;QAED,yDAAyD;QACzD,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;QAEjC,0EAA0E;QAC1E,6EAA6E;QAC7E,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,IAAI;YACJ,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,eAAe;YACf,QAAQ;YACR,SAAS;YACT,WAAW;YACX,MAAM;YACN,OAAO;YACP,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;SAChD,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,MAAM,EAAE,CAAA;QAE9E,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAA;YAC5D,gBAAgB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA;YACtG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,+BAA+B,CAAC,CAAA;YACzD,IAAI,CAAC,iBAAiB,EAAE,CAAA;YACxB,OAAM;QACR,CAAC;QAED,8EAA8E;QAC9E,MAAM,iBAAiB,GAAG,UAAU,CAAC,GAAG,EAAE;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,UAAU,CAAA;YACjC,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC,wBAAwB;gBAC7E,OAAO,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAA;gBACzF,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;gBACnE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;gBACf,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,gDAAgD,CAAC,CAAA;YAC5E,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;QAET,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE;YACpB,YAAY,CAAC,iBAAiB,CAAC,CAAA;YAC/B,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YAEvC,wDAAwD;YACxD,IAAI,CAAC,EAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAA;YAEjE,0CAA0C;YAC1C,IAAI,CAAC,iBAAiB,EAAE,CAAA;YACxB,gCAAgC;YAChC,IAAI,CAAC,sBAAsB,EAAE,CAAA;YAC7B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAA;QAC5B,CAAC,CAAA;QAED,IAAI,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE;YAC5B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;gBACjD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;YAC7B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;YACzD,CAAC;QACH,CAAC,CAAA;QAED,sEAAsE;QACtE,IAAI,aAAa,GAAG,KAAK,CAAA;QACzB,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAA;QACtD,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,CAAC,YAAY,EAAE,EAAE;YAC5C,aAAa,GAAG,IAAI,CAAA;YACpB,mBAAmB,EAAE,CAAC,YAAY,CAAC,CAAA;QACrC,CAAC,CAAA;QAED,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YAC1B,8BAA8B;YAC9B,IAAI,CAAC,aAAa,EAAE,CAAA;YACpB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAA;YAExB,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAChC,mFAAmF;gBACnF,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI;oBACvC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;oBACtC,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,CAAA;gBAEzC,IAAI,aAAa,EAAE,CAAC;oBAClB,OAAO,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAA;oBACxF,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;oBACnE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,mDAAmD,CAAC,CAAA;oBAC7E,OAAM;gBACR,CAAC;gBAED,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAA;gBAC1D,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,CAAA;gBACjC,IAAI,CAAC,iBAAiB,EAAE,CAAA;YAC1B,CAAC;QACH,CAAC,CAAA;QAED,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE;YACrB,oCAAoC;QACtC,CAAC,CAAA;IACH,CAAC;IAEO,aAAa,CAAC,OAAY;QAChC,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;gBACvB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAA;gBACxC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;gBACxC,oDAAoD;gBACpD,IAAI,CAAC,cAAc,EAAE,CAAA;gBACrB,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;gBAClD,MAAK;YAEP,KAAK,eAAe;gBAClB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;gBACtC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,CAAA;gBACjC,MAAK;YAEP,KAAK,kBAAkB;gBACrB,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;gBACzC,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,EAAE,CAAA;gBACpC,MAAK;YAEP,KAAK,MAAM;gBACT,6BAA6B;gBAC7B,MAAK;YAEP;gBACE,sCAAsC;gBACtC,qEAAqE;gBACrE,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC,OAAO,CAAC,CAAA;gBAC1C,MAAK;QACT,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,IAAI,IAAI,CAAC,gBAAgB;YAAE,OAAM;QAEjC,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACxD,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAA;YAC/E,OAAM;QACR,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAA;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;QAEvE,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAA;YAC5B,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAY;QACf,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,aAAa,CAAC,IAAI,EAAE,CAAC;YACzD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,qBAAqB;QACnB,OAAO,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE,EAAE,UAAU,KAAK,aAAa,CAAC,IAAI,CAAA;IACvE,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAA;QAEjC,0CAA0C;QAC1C,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,IAAI,CAAC,qBAAqB,EAAE,CAAA;QAE5B,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;YACnC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAA;QAC9B,CAAC;QAED,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;YACf,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;QAChB,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,KAAK,CAAA;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;IAC1B,CAAC;CACF"}
|
|
@@ -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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"}
|