mstro-app 0.3.0 → 0.3.1
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/bin/mstro.js +65 -2
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +4 -3
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +2 -2
- package/dist/server/cli/headless/mcp-config.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts +6 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +36 -4
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +1 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +2 -2
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +3 -2
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/index.js +6 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.js +53 -14
- package/dist/server/mcp/bouncer-cli.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +70 -7
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/security-audit.d.ts +3 -3
- package/dist/server/mcp/security-audit.d.ts.map +1 -1
- package/dist/server/mcp/security-audit.js.map +1 -1
- package/dist/server/mcp/server.js +3 -2
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/services/analytics.d.ts +2 -2
- package/dist/server/services/analytics.d.ts.map +1 -1
- package/dist/server/services/analytics.js.map +1 -1
- package/dist/server/services/files.js +7 -7
- package/dist/server/services/files.js.map +1 -1
- package/dist/server/services/pathUtils.js +1 -1
- package/dist/server/services/pathUtils.js.map +1 -1
- package/dist/server/services/platform.d.ts +2 -2
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/sentry.d.ts +1 -1
- package/dist/server/services/sentry.d.ts.map +1 -1
- package/dist/server/services/sentry.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts +10 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +32 -4
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-utils.d.ts +4 -0
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/file-utils.js +27 -8
- package/dist/server/services/websocket/file-utils.js.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +17 -17
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.js +3 -3
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.js +10 -10
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler.js +1 -1
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +12 -11
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +1 -1
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.d.ts +22 -2
- package/dist/server/utils/agent-manager.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.js +2 -2
- package/dist/server/utils/agent-manager.js.map +1 -1
- package/dist/server/utils/port-manager.js.map +1 -1
- package/hooks/bouncer.sh +17 -3
- package/package.json +4 -2
- package/server/cli/headless/claude-invoker.ts +21 -16
- package/server/cli/headless/mcp-config.ts +8 -8
- package/server/cli/headless/runner.ts +32 -4
- package/server/cli/headless/types.ts +1 -1
- package/server/cli/improvisation-session-manager.ts +8 -7
- package/server/index.ts +15 -9
- package/server/mcp/bouncer-cli.ts +73 -20
- package/server/mcp/bouncer-integration.ts +99 -16
- package/server/mcp/security-audit.ts +4 -4
- package/server/mcp/server.ts +6 -5
- package/server/services/analytics.ts +3 -3
- package/server/services/files.ts +13 -13
- package/server/services/pathUtils.ts +2 -2
- package/server/services/platform.ts +5 -5
- package/server/services/sentry.ts +1 -1
- package/server/services/terminal/pty-manager.ts +36 -9
- package/server/services/websocket/file-explorer-handlers.ts +1 -1
- package/server/services/websocket/file-utils.ts +28 -9
- package/server/services/websocket/git-handlers.ts +34 -34
- package/server/services/websocket/git-pr-handlers.ts +6 -6
- package/server/services/websocket/git-worktree-handlers.ts +20 -20
- package/server/services/websocket/handler.ts +2 -2
- package/server/services/websocket/session-handlers.ts +31 -30
- package/server/services/websocket/tab-handlers.ts +1 -1
- package/server/services/websocket/terminal-handlers.ts +2 -2
- package/server/services/websocket/types.ts +2 -0
- package/server/utils/agent-manager.ts +6 -6
- package/server/utils/port-manager.ts +1 -1
- package/server/cli/headless/output-utils.test.ts +0 -225
- package/server/cli/headless/stall-assessor.test.ts +0 -165
- package/server/cli/headless/tool-watchdog.test.ts +0 -429
- package/server/mcp/bouncer-integration.test.ts +0 -161
- package/server/mcp/security-patterns.test.ts +0 -258
- package/server/services/platform.test.ts +0 -1304
- package/server/services/websocket/autocomplete.test.ts +0 -194
- package/server/services/websocket/handler.test.ts +0 -20
package/server/services/files.ts
CHANGED
|
@@ -332,9 +332,9 @@ export function listDirectory(
|
|
|
332
332
|
success: true,
|
|
333
333
|
entries: directoryEntries
|
|
334
334
|
}
|
|
335
|
-
} catch (error:
|
|
335
|
+
} catch (error: unknown) {
|
|
336
336
|
// Handle permission errors gracefully
|
|
337
|
-
if (error.code === 'EACCES') {
|
|
337
|
+
if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'EACCES') {
|
|
338
338
|
return {
|
|
339
339
|
success: false,
|
|
340
340
|
error: 'Permission denied'
|
|
@@ -344,7 +344,7 @@ export function listDirectory(
|
|
|
344
344
|
console.error('[FileService] Error listing directory:', error)
|
|
345
345
|
return {
|
|
346
346
|
success: false,
|
|
347
|
-
error: error.message
|
|
347
|
+
error: error instanceof Error ? error.message : 'Failed to list directory'
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
350
|
}
|
|
@@ -408,11 +408,11 @@ export function writeFile(
|
|
|
408
408
|
success: true,
|
|
409
409
|
path: resolvedPath.replace(`${workingDir}/`, '')
|
|
410
410
|
}
|
|
411
|
-
} catch (error:
|
|
411
|
+
} catch (error: unknown) {
|
|
412
412
|
console.error('[FileService] Error writing file:', error)
|
|
413
413
|
return {
|
|
414
414
|
success: false,
|
|
415
|
-
error: error.message
|
|
415
|
+
error: error instanceof Error ? error.message : 'Failed to write file'
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
}
|
|
@@ -471,11 +471,11 @@ export function createFile(
|
|
|
471
471
|
success: true,
|
|
472
472
|
path: resolvedPath.replace(`${workingDir}/`, '')
|
|
473
473
|
}
|
|
474
|
-
} catch (error:
|
|
474
|
+
} catch (error: unknown) {
|
|
475
475
|
console.error('[FileService] Error creating file:', error)
|
|
476
476
|
return {
|
|
477
477
|
success: false,
|
|
478
|
-
error: error.message
|
|
478
|
+
error: error instanceof Error ? error.message : 'Failed to create file'
|
|
479
479
|
}
|
|
480
480
|
}
|
|
481
481
|
}
|
|
@@ -536,11 +536,11 @@ export function createDirectory(
|
|
|
536
536
|
success: true,
|
|
537
537
|
path: resolvedPath.replace(`${workingDir}/`, '')
|
|
538
538
|
}
|
|
539
|
-
} catch (error:
|
|
539
|
+
} catch (error: unknown) {
|
|
540
540
|
console.error('[FileService] Error creating directory:', error)
|
|
541
541
|
return {
|
|
542
542
|
success: false,
|
|
543
|
-
error: error.message
|
|
543
|
+
error: error instanceof Error ? error.message : 'Failed to create directory'
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
}
|
|
@@ -618,11 +618,11 @@ export function deleteFile(
|
|
|
618
618
|
success: true,
|
|
619
619
|
path: resolvedPath.replace(`${workingDir}/`, '')
|
|
620
620
|
}
|
|
621
|
-
} catch (error:
|
|
621
|
+
} catch (error: unknown) {
|
|
622
622
|
console.error('[FileService] Error deleting file:', error)
|
|
623
623
|
return {
|
|
624
624
|
success: false,
|
|
625
|
-
error: error.message
|
|
625
|
+
error: error instanceof Error ? error.message : 'Failed to delete'
|
|
626
626
|
}
|
|
627
627
|
}
|
|
628
628
|
}
|
|
@@ -700,11 +700,11 @@ export function renameFile(
|
|
|
700
700
|
success: true,
|
|
701
701
|
path: resolvedNewPath.replace(`${workingDir}/`, '')
|
|
702
702
|
}
|
|
703
|
-
} catch (error:
|
|
703
|
+
} catch (error: unknown) {
|
|
704
704
|
console.error('[FileService] Error renaming file:', error)
|
|
705
705
|
return {
|
|
706
706
|
success: false,
|
|
707
|
-
error: error.message
|
|
707
|
+
error: error instanceof Error ? error.message : 'Failed to rename'
|
|
708
708
|
}
|
|
709
709
|
}
|
|
710
710
|
}
|
|
@@ -71,12 +71,12 @@ export function validatePathWithinWorkingDir(
|
|
|
71
71
|
valid: true,
|
|
72
72
|
resolvedPath
|
|
73
73
|
};
|
|
74
|
-
} catch (error:
|
|
74
|
+
} catch (error: unknown) {
|
|
75
75
|
console.error('[PathUtils] Error validating path:', error);
|
|
76
76
|
return {
|
|
77
77
|
valid: false,
|
|
78
78
|
resolvedPath: '',
|
|
79
|
-
error: `Invalid path: ${error.message}`
|
|
79
|
+
error: `Invalid path: ${error instanceof Error ? error.message : String(error)}`
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -121,7 +121,7 @@ interface ConnectionCallbacks {
|
|
|
121
121
|
onError?: (error: string) => void
|
|
122
122
|
onWebConnected?: () => void
|
|
123
123
|
onWebDisconnected?: () => void
|
|
124
|
-
onRelayedMessage?: (message:
|
|
124
|
+
onRelayedMessage?: (message: unknown) => void
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
@@ -351,15 +351,15 @@ export class PlatformConnection {
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
private handleMessage(message:
|
|
354
|
+
private handleMessage(message: Record<string, unknown>): void {
|
|
355
355
|
switch (message.type) {
|
|
356
356
|
case 'paired':
|
|
357
357
|
this.isConnected = true
|
|
358
|
-
this.connectionId = message.connectionId
|
|
358
|
+
this.connectionId = message.connectionId as string
|
|
359
359
|
// Connection status printed by onConnected callback
|
|
360
360
|
// Start heartbeat to keep server-side TTL refreshed
|
|
361
361
|
this.startHeartbeat()
|
|
362
|
-
this.callbacks.onConnected?.(message.connectionId)
|
|
362
|
+
this.callbacks.onConnected?.(message.connectionId as string)
|
|
363
363
|
break
|
|
364
364
|
|
|
365
365
|
case 'web_connected':
|
|
@@ -404,7 +404,7 @@ export class PlatformConnection {
|
|
|
404
404
|
/**
|
|
405
405
|
* Send message to platform (will be relayed to web if connected)
|
|
406
406
|
*/
|
|
407
|
-
send(message:
|
|
407
|
+
send(message: unknown): void {
|
|
408
408
|
if (this.ws && this.ws.readyState === WebSocketImpl.OPEN) {
|
|
409
409
|
this.ws.send(JSON.stringify(message))
|
|
410
410
|
}
|
|
@@ -65,7 +65,7 @@ export function initSentry(): void {
|
|
|
65
65
|
})
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function captureException(error: unknown, context?: Record<string,
|
|
68
|
+
export function captureException(error: unknown, context?: Record<string, unknown>): void {
|
|
69
69
|
if (!initialized) return
|
|
70
70
|
Sentry.captureException(error, context ? { extra: context } : undefined)
|
|
71
71
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { EventEmitter } from 'node:events';
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
20
21
|
import { homedir, platform } from 'node:os';
|
|
21
22
|
import { sanitizeEnvForSandbox } from '../sandbox-utils.js';
|
|
22
23
|
|
|
@@ -26,8 +27,8 @@ let _ptyLoadError: string | null = null;
|
|
|
26
27
|
|
|
27
28
|
try {
|
|
28
29
|
pty = await import('node-pty');
|
|
29
|
-
} catch (error:
|
|
30
|
-
_ptyLoadError = error.message
|
|
30
|
+
} catch (error: unknown) {
|
|
31
|
+
_ptyLoadError = error instanceof Error ? error.message : 'Failed to load node-pty';
|
|
31
32
|
console.warn('[PTYManager] node-pty not available - terminal features disabled');
|
|
32
33
|
console.warn('[PTYManager] To enable terminals, run: mstro setup-terminal');
|
|
33
34
|
}
|
|
@@ -39,6 +40,32 @@ export function isPtyAvailable(): boolean {
|
|
|
39
40
|
return pty !== null;
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Re-attempt loading node-pty at runtime.
|
|
45
|
+
* Called after `mstro setup-terminal` compiles the native module
|
|
46
|
+
* so the running server can pick it up without a restart.
|
|
47
|
+
*
|
|
48
|
+
* Uses createRequire (CJS) to bypass ESM's module cache — a failed
|
|
49
|
+
* ESM import is permanently cached, but CJS require cache entries
|
|
50
|
+
* can be deleted and re-required.
|
|
51
|
+
*/
|
|
52
|
+
export async function reloadPty(): Promise<boolean> {
|
|
53
|
+
if (pty) return true;
|
|
54
|
+
try {
|
|
55
|
+
const require = createRequire(import.meta.url);
|
|
56
|
+
// Clear any cached failure so require() retries the native load
|
|
57
|
+
const resolved = require.resolve('node-pty');
|
|
58
|
+
delete require.cache[resolved];
|
|
59
|
+
pty = require('node-pty');
|
|
60
|
+
_ptyLoadError = null;
|
|
61
|
+
console.log('[PTYManager] node-pty loaded successfully after reload');
|
|
62
|
+
return true;
|
|
63
|
+
} catch (error: unknown) {
|
|
64
|
+
_ptyLoadError = error instanceof Error ? error.message : 'Failed to load node-pty';
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
/**
|
|
43
70
|
* Get installation instructions for node-pty based on platform
|
|
44
71
|
*/
|
|
@@ -240,7 +267,7 @@ export class PTYManager extends EventEmitter {
|
|
|
240
267
|
// wraps echoed chars in multi-part ANSI sequences (RPROMPT, syntax highlighting).
|
|
241
268
|
// A longer window on macOS ensures these multi-part sequences arrive as one chunk,
|
|
242
269
|
// which the browser's predictive echo can match correctly.
|
|
243
|
-
const OUTPUT_COALESCE_MS = platform() === 'darwin' ?
|
|
270
|
+
const OUTPUT_COALESCE_MS = platform() === 'darwin' ? 24 : 8;
|
|
244
271
|
// High-water mark: flush immediately when buffer exceeds this size
|
|
245
272
|
// to prevent unbounded memory growth during high-output commands (e.g. `yes`)
|
|
246
273
|
const OUTPUT_HIGH_WATER = 64 * 1024; // 64KB
|
|
@@ -284,9 +311,9 @@ export class PTYManager extends EventEmitter {
|
|
|
284
311
|
});
|
|
285
312
|
|
|
286
313
|
return { shell: session.shell, cwd, isReconnect: false };
|
|
287
|
-
} catch (error:
|
|
314
|
+
} catch (error: unknown) {
|
|
288
315
|
console.error(`[PTYManager] Failed to create terminal ${terminalId}:`, error);
|
|
289
|
-
this.emit('error', terminalId, error.message
|
|
316
|
+
this.emit('error', terminalId, error instanceof Error ? error.message : 'Failed to create terminal');
|
|
290
317
|
throw error;
|
|
291
318
|
}
|
|
292
319
|
}
|
|
@@ -304,9 +331,9 @@ export class PTYManager extends EventEmitter {
|
|
|
304
331
|
try {
|
|
305
332
|
session.pty.write(data);
|
|
306
333
|
return true;
|
|
307
|
-
} catch (error:
|
|
334
|
+
} catch (error: unknown) {
|
|
308
335
|
console.error(`[PTYManager] Error writing to terminal ${terminalId}:`, error);
|
|
309
|
-
this.emit('error', terminalId, error.message
|
|
336
|
+
this.emit('error', terminalId, error instanceof Error ? error.message : 'Write failed');
|
|
310
337
|
return false;
|
|
311
338
|
}
|
|
312
339
|
}
|
|
@@ -324,7 +351,7 @@ export class PTYManager extends EventEmitter {
|
|
|
324
351
|
try {
|
|
325
352
|
session.pty.resize(cols, rows);
|
|
326
353
|
return true;
|
|
327
|
-
} catch (error:
|
|
354
|
+
} catch (error: unknown) {
|
|
328
355
|
console.error(`[PTYManager] Error resizing terminal ${terminalId}:`, error);
|
|
329
356
|
return false;
|
|
330
357
|
}
|
|
@@ -353,7 +380,7 @@ export class PTYManager extends EventEmitter {
|
|
|
353
380
|
session.pty.kill();
|
|
354
381
|
this.terminals.delete(terminalId);
|
|
355
382
|
return true;
|
|
356
|
-
} catch (error:
|
|
383
|
+
} catch (error: unknown) {
|
|
357
384
|
console.error(`[PTYManager] Error closing terminal ${terminalId}:`, error);
|
|
358
385
|
this.terminals.delete(terminalId);
|
|
359
386
|
return false;
|
|
@@ -44,7 +44,7 @@ function handleReadFile(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessag
|
|
|
44
44
|
ctx.send(ws, { type: 'fileContent', tabId, data: readFileContent(msg.data.filePath, workingDir) });
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function sendFileResult(ctx: HandlerContext, ws: WSContext, type: WebSocketResponse['type'], tabId: string, result:
|
|
47
|
+
function sendFileResult(ctx: HandlerContext, ws: WSContext, type: WebSocketResponse['type'], tabId: string, result: { success: boolean; path?: string; error?: string }, successData?: Record<string, unknown>): void {
|
|
48
48
|
const data = result.success
|
|
49
49
|
? { success: true, path: result.path, ...successData }
|
|
50
50
|
: { success: false, path: result.path, error: result.error };
|
|
@@ -226,6 +226,18 @@ export function isImageFile(filePath: string): boolean {
|
|
|
226
226
|
return ext ? imageExtensions.includes(`.${ext}`) : false;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Check if a file is a binary file that should be base64-encoded (images + PDFs)
|
|
231
|
+
*/
|
|
232
|
+
export function isBinaryFile(filePath: string): boolean {
|
|
233
|
+
return isImageFile(filePath) || isPdfFile(filePath);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function isPdfFile(filePath: string): boolean {
|
|
237
|
+
const ext = filePath.toLowerCase().split('.').pop();
|
|
238
|
+
return ext === 'pdf';
|
|
239
|
+
}
|
|
240
|
+
|
|
229
241
|
type FileContentResult = { path: string; fileName: string; content: string; size?: number; modifiedAt?: string; isImage?: boolean; mimeType?: string; error?: string };
|
|
230
242
|
|
|
231
243
|
function readDirectoryContent(fullPath: string, filePath: string, fileName: string): FileContentResult {
|
|
@@ -246,10 +258,17 @@ function readDirectoryContent(fullPath: string, filePath: string, fileName: stri
|
|
|
246
258
|
}
|
|
247
259
|
}
|
|
248
260
|
|
|
249
|
-
function
|
|
261
|
+
function getBinaryMimeType(ext: string): string {
|
|
262
|
+
if (ext === 'svg') return 'image/svg+xml';
|
|
263
|
+
if (ext === 'jpg') return 'image/jpeg';
|
|
264
|
+
if (ext === 'pdf') return 'application/pdf';
|
|
265
|
+
return `image/${ext}`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function readBinaryContent(fullPath: string, filePath: string, fileName: string, stats: { size: number; mtime: Date }): FileContentResult {
|
|
250
269
|
const buffer = readFileSync(fullPath);
|
|
251
270
|
const ext = fullPath.toLowerCase().split('.').pop() || 'png';
|
|
252
|
-
const mimeType = ext
|
|
271
|
+
const mimeType = getBinaryMimeType(ext);
|
|
253
272
|
return {
|
|
254
273
|
path: filePath, fileName,
|
|
255
274
|
content: buffer.toString('base64'),
|
|
@@ -289,17 +308,17 @@ export function readFileContent(filePath: string, workingDir: string): FileConte
|
|
|
289
308
|
return readDirectoryContent(fullPath, filePath, fileName);
|
|
290
309
|
}
|
|
291
310
|
|
|
292
|
-
const
|
|
293
|
-
const MAX_FILE_SIZE =
|
|
311
|
+
const isBin = isBinaryFile(fullPath);
|
|
312
|
+
const MAX_FILE_SIZE = isBin ? 10 * 1024 * 1024 : 1024 * 1024;
|
|
294
313
|
if (stats.size > MAX_FILE_SIZE) {
|
|
295
|
-
return { path: filePath, fileName, content: '', size: stats.size, error: `File too large (${Math.round(stats.size / 1024)}KB). Maximum is ${
|
|
314
|
+
return { path: filePath, fileName, content: '', size: stats.size, error: `File too large (${Math.round(stats.size / 1024)}KB). Maximum is ${isBin ? '10MB' : '1MB'}.` };
|
|
296
315
|
}
|
|
297
316
|
|
|
298
|
-
return
|
|
299
|
-
?
|
|
317
|
+
return isBin
|
|
318
|
+
? readBinaryContent(fullPath, filePath, fileName, stats)
|
|
300
319
|
: readTextContent(fullPath, filePath, fileName, stats);
|
|
301
|
-
} catch (error:
|
|
320
|
+
} catch (error: unknown) {
|
|
302
321
|
console.error('[FileUtils] Error reading file:', error);
|
|
303
|
-
return { path: filePath, fileName: filePath.split(sep).pop() || filePath, content: '', error: error.message || 'Failed to read file' };
|
|
322
|
+
return { path: filePath, fileName: filePath.split(sep).pop() || filePath, content: '', error: (error instanceof Error ? error.message : String(error)) || 'Failed to read file' };
|
|
304
323
|
}
|
|
305
324
|
}
|
|
@@ -270,8 +270,8 @@ export async function handleGitStatus(ctx: HandlerContext, ws: WSContext, tabId:
|
|
|
270
270
|
};
|
|
271
271
|
|
|
272
272
|
ctx.send(ws, { type: 'gitStatus', tabId, data: response });
|
|
273
|
-
} catch (error:
|
|
274
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
273
|
+
} catch (error: unknown) {
|
|
274
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -293,8 +293,8 @@ async function handleGitStage(ctx: HandlerContext, ws: WSContext, msg: WebSocket
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
ctx.send(ws, { type: 'gitStaged', tabId, data: { paths: paths || [] } });
|
|
296
|
-
} catch (error:
|
|
297
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
296
|
+
} catch (error: unknown) {
|
|
297
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
|
|
@@ -313,8 +313,8 @@ async function handleGitUnstage(ctx: HandlerContext, ws: WSContext, msg: WebSock
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
ctx.send(ws, { type: 'gitUnstaged', tabId, data: { paths } });
|
|
316
|
-
} catch (error:
|
|
317
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
316
|
+
} catch (error: unknown) {
|
|
317
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
|
|
@@ -342,8 +342,8 @@ async function handleGitCommit(ctx: HandlerContext, ws: WSContext, msg: WebSocke
|
|
|
342
342
|
|
|
343
343
|
ctx.send(ws, { type: 'gitCommitted', tabId, data: { hash, message } });
|
|
344
344
|
handleGitStatus(ctx, ws, tabId, workingDir);
|
|
345
|
-
} catch (error:
|
|
346
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
345
|
+
} catch (error: unknown) {
|
|
346
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
349
|
|
|
@@ -463,8 +463,8 @@ Respond with ONLY the commit message, nothing else.`;
|
|
|
463
463
|
claude.kill();
|
|
464
464
|
}, 30000);
|
|
465
465
|
|
|
466
|
-
} catch (error:
|
|
467
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
466
|
+
} catch (error: unknown) {
|
|
467
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
|
|
@@ -539,8 +539,8 @@ async function handleGitPush(ctx: HandlerContext, ws: WSContext, tabId: string,
|
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
ctx.send(ws, { type: 'gitPushed', tabId, data: { output: result.stdout || result.stderr } });
|
|
542
|
-
} catch (error:
|
|
543
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
542
|
+
} catch (error: unknown) {
|
|
543
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
|
|
@@ -553,8 +553,8 @@ async function handleGitPull(ctx: HandlerContext, ws: WSContext, tabId: string,
|
|
|
553
553
|
}
|
|
554
554
|
|
|
555
555
|
ctx.send(ws, { type: 'gitPulled', tabId, data: { output: result.stdout || result.stderr } });
|
|
556
|
-
} catch (error:
|
|
557
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
556
|
+
} catch (error: unknown) {
|
|
557
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
558
558
|
}
|
|
559
559
|
}
|
|
560
560
|
|
|
@@ -580,8 +580,8 @@ async function handleGitLog(ctx: HandlerContext, ws: WSContext, msg: WebSocketMe
|
|
|
580
580
|
});
|
|
581
581
|
|
|
582
582
|
ctx.send(ws, { type: 'gitLog', tabId, data: { entries } });
|
|
583
|
-
} catch (error:
|
|
584
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
583
|
+
} catch (error: unknown) {
|
|
584
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
587
|
|
|
@@ -643,8 +643,8 @@ async function handleGitDiscoverRepos(ctx: HandlerContext, ws: WSContext, tabId:
|
|
|
643
643
|
};
|
|
644
644
|
|
|
645
645
|
ctx.send(ws, { type: 'gitReposDiscovered', tabId, data: response });
|
|
646
|
-
} catch (error:
|
|
647
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
646
|
+
} catch (error: unknown) {
|
|
647
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
648
648
|
}
|
|
649
649
|
}
|
|
650
650
|
|
|
@@ -712,8 +712,8 @@ async function handleGitListBranches(ctx: HandlerContext, ws: WSContext, tabId:
|
|
|
712
712
|
.filter(b => b.name !== 'origin/HEAD');
|
|
713
713
|
|
|
714
714
|
ctx.send(ws, { type: 'gitBranchList', tabId, data: { branches, current: currentBranch } });
|
|
715
|
-
} catch (error:
|
|
716
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
715
|
+
} catch (error: unknown) {
|
|
716
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
717
717
|
}
|
|
718
718
|
}
|
|
719
719
|
|
|
@@ -746,8 +746,8 @@ async function handleGitCheckout(ctx: HandlerContext, ws: WSContext, msg: WebSoc
|
|
|
746
746
|
|
|
747
747
|
ctx.send(ws, { type: 'gitCheckedOut', tabId, data: { branch, previous } });
|
|
748
748
|
handleGitStatus(ctx, ws, tabId, workingDir);
|
|
749
|
-
} catch (error:
|
|
750
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
749
|
+
} catch (error: unknown) {
|
|
750
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
751
751
|
}
|
|
752
752
|
}
|
|
753
753
|
|
|
@@ -773,8 +773,8 @@ async function handleGitCreateBranch(ctx: HandlerContext, ws: WSContext, msg: We
|
|
|
773
773
|
}
|
|
774
774
|
|
|
775
775
|
ctx.send(ws, { type: 'gitBranchCreated', tabId, data: { name, hash: hashResult.stdout.trim() } });
|
|
776
|
-
} catch (error:
|
|
777
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
776
|
+
} catch (error: unknown) {
|
|
777
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
778
778
|
}
|
|
779
779
|
}
|
|
780
780
|
|
|
@@ -799,8 +799,8 @@ async function handleGitDeleteBranch(ctx: HandlerContext, ws: WSContext, msg: We
|
|
|
799
799
|
}
|
|
800
800
|
|
|
801
801
|
ctx.send(ws, { type: 'gitBranchDeleted', tabId, data: { name } });
|
|
802
|
-
} catch (error:
|
|
803
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
802
|
+
} catch (error: unknown) {
|
|
803
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
804
804
|
}
|
|
805
805
|
}
|
|
806
806
|
|
|
@@ -833,8 +833,8 @@ async function handleGitDiff(ctx: HandlerContext, ws: WSContext, msg: WebSocketM
|
|
|
833
833
|
tabId,
|
|
834
834
|
data: { path, original, modified, staged: !!staged },
|
|
835
835
|
});
|
|
836
|
-
} catch (error:
|
|
837
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
836
|
+
} catch (error: unknown) {
|
|
837
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
838
838
|
}
|
|
839
839
|
}
|
|
840
840
|
|
|
@@ -863,8 +863,8 @@ async function handleGitListTags(ctx: HandlerContext, ws: WSContext, tabId: stri
|
|
|
863
863
|
});
|
|
864
864
|
|
|
865
865
|
ctx.send(ws, { type: 'gitTagList', tabId, data: { tags } });
|
|
866
|
-
} catch (error:
|
|
867
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
866
|
+
} catch (error: unknown) {
|
|
867
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
868
868
|
}
|
|
869
869
|
}
|
|
870
870
|
|
|
@@ -893,8 +893,8 @@ async function handleGitCreateTag(ctx: HandlerContext, ws: WSContext, msg: WebSo
|
|
|
893
893
|
|
|
894
894
|
const hashResult = await executeGitCommand(['rev-parse', '--short', name], workingDir);
|
|
895
895
|
ctx.send(ws, { type: 'gitTagCreated', tabId, data: { name, hash: hashResult.stdout.trim() } });
|
|
896
|
-
} catch (error:
|
|
897
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
896
|
+
} catch (error: unknown) {
|
|
897
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
898
898
|
}
|
|
899
899
|
}
|
|
900
900
|
|
|
@@ -918,7 +918,7 @@ async function handleGitPushTag(ctx: HandlerContext, ws: WSContext, msg: WebSock
|
|
|
918
918
|
}
|
|
919
919
|
|
|
920
920
|
ctx.send(ws, { type: 'gitTagPushed', tabId, data: { name: name || 'all', output: result.stderr || result.stdout } });
|
|
921
|
-
} catch (error:
|
|
922
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
921
|
+
} catch (error: unknown) {
|
|
922
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
923
923
|
}
|
|
924
924
|
}
|
|
@@ -49,8 +49,8 @@ async function handleGitGetRemoteInfo(ctx: HandlerContext, ws: WSContext, tabId:
|
|
|
49
49
|
preferredBaseBranch,
|
|
50
50
|
},
|
|
51
51
|
});
|
|
52
|
-
} catch (error:
|
|
53
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
52
|
+
} catch (error: unknown) {
|
|
53
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -171,8 +171,8 @@ async function handleGitCreatePR(ctx: HandlerContext, ws: WSContext, msg: WebSoc
|
|
|
171
171
|
} else {
|
|
172
172
|
ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Could not determine remote URL format for PR creation' } });
|
|
173
173
|
}
|
|
174
|
-
} catch (error:
|
|
175
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
174
|
+
} catch (error: unknown) {
|
|
175
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -357,7 +357,7 @@ Respond with ONLY the title and description, nothing else.`;
|
|
|
357
357
|
|
|
358
358
|
setTimeout(() => { claude.kill(); }, 30000);
|
|
359
359
|
|
|
360
|
-
} catch (error:
|
|
361
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
360
|
+
} catch (error: unknown) {
|
|
361
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
362
362
|
}
|
|
363
363
|
}
|
|
@@ -55,8 +55,8 @@ async function handleGitWorktreeList(ctx: HandlerContext, ws: WSContext, tabId:
|
|
|
55
55
|
}
|
|
56
56
|
const worktrees = parseWorktreePorcelain(result.stdout);
|
|
57
57
|
ctx.send(ws, { type: 'gitWorktreeListResult', tabId, data: { worktrees } });
|
|
58
|
-
} catch (error:
|
|
59
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
58
|
+
} catch (error: unknown) {
|
|
59
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -85,8 +85,8 @@ async function handleGitWorktreeCreate(ctx: HandlerContext, ws: WSContext, msg:
|
|
|
85
85
|
tabId,
|
|
86
86
|
data: { path: wtPath, branch: branchName, head: headResult.stdout.trim() },
|
|
87
87
|
});
|
|
88
|
-
} catch (error:
|
|
89
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
88
|
+
} catch (error: unknown) {
|
|
89
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -118,8 +118,8 @@ async function handleGitWorktreeRemove(ctx: HandlerContext, ws: WSContext, msg:
|
|
|
118
118
|
await executeGitCommand(['worktree', 'prune'], workingDir);
|
|
119
119
|
|
|
120
120
|
ctx.send(ws, { type: 'gitWorktreeRemoved', tabId, data: { path: wtPath } });
|
|
121
|
-
} catch (error:
|
|
122
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
121
|
+
} catch (error: unknown) {
|
|
122
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -141,8 +141,8 @@ async function handleTabWorktreeSwitch(ctx: HandlerContext, ws: WSContext, msg:
|
|
|
141
141
|
|
|
142
142
|
ctx.send(ws, { type: 'tabWorktreeSwitched', tabId: resolvedTabId, data: { tabId: resolvedTabId, worktreePath, branch } });
|
|
143
143
|
handleGitStatus(ctx, ws, resolvedTabId, worktreePath);
|
|
144
|
-
} catch (error:
|
|
145
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
144
|
+
} catch (error: unknown) {
|
|
145
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -185,8 +185,8 @@ async function handleGitWorktreePush(ctx: HandlerContext, ws: WSContext, msg: We
|
|
|
185
185
|
return;
|
|
186
186
|
}
|
|
187
187
|
ctx.send(ws, { type: 'gitWorktreePushed', tabId, data: { output: pushResult.output, upstream: `${pushRemote}/${pushBranch}` } });
|
|
188
|
-
} catch (error:
|
|
189
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
188
|
+
} catch (error: unknown) {
|
|
189
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -218,8 +218,8 @@ async function handleGitWorktreeCreatePR(ctx: HandlerContext, ws: WSContext, msg
|
|
|
218
218
|
const prNumber = prNumberMatch ? parseInt(prNumberMatch[1], 10) : 0;
|
|
219
219
|
|
|
220
220
|
ctx.send(ws, { type: 'gitWorktreePRCreated', tabId, data: { prUrl, prNumber } });
|
|
221
|
-
} catch (error:
|
|
222
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
221
|
+
} catch (error: unknown) {
|
|
222
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -263,8 +263,8 @@ async function handleGitMergePreview(ctx: HandlerContext, ws: WSContext, msg: We
|
|
|
263
263
|
tabId,
|
|
264
264
|
data: { clean, conflicts, stat, commits, ahead: commits.length },
|
|
265
265
|
});
|
|
266
|
-
} catch (error:
|
|
267
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
266
|
+
} catch (error: unknown) {
|
|
267
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
|
|
@@ -362,8 +362,8 @@ async function handleGitWorktreeMerge(ctx: HandlerContext, ws: WSContext, msg: W
|
|
|
362
362
|
await cleanupAfterMerge(mainPath, sourceBranch, strategy, !!deleteWorktree, !!deleteBranch);
|
|
363
363
|
|
|
364
364
|
ctx.send(ws, { type: 'gitWorktreeMergeResult', tabId, data: { success: true, mergeCommit: commitHashResult.stdout.trim() } });
|
|
365
|
-
} catch (error:
|
|
366
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
365
|
+
} catch (error: unknown) {
|
|
366
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
|
|
@@ -378,8 +378,8 @@ async function handleGitMergeAbort(ctx: HandlerContext, ws: WSContext, tabId: st
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
ctx.send(ws, { type: 'gitMergeAborted', tabId, data: { aborted: true } });
|
|
381
|
-
} catch (error:
|
|
382
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
381
|
+
} catch (error: unknown) {
|
|
382
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
|
|
@@ -397,7 +397,7 @@ async function handleGitMergeComplete(ctx: HandlerContext, ws: WSContext, _msg:
|
|
|
397
397
|
const mergeCommit = hashResult.stdout.trim();
|
|
398
398
|
|
|
399
399
|
ctx.send(ws, { type: 'gitMergeCompleted', tabId, data: { success: true, mergeCommit } });
|
|
400
|
-
} catch (error:
|
|
401
|
-
ctx.send(ws, { type: 'gitError', tabId, data: { error: error.message } });
|
|
400
|
+
} catch (error: unknown) {
|
|
401
|
+
ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
|
|
402
402
|
}
|
|
403
403
|
}
|
|
@@ -103,12 +103,12 @@ export class WebSocketImproviseHandler implements HandlerContext {
|
|
|
103
103
|
delete msg._permission;
|
|
104
104
|
|
|
105
105
|
await this.dispatchMessage(ws, msg, tabId, workingDir, permission);
|
|
106
|
-
} catch (error:
|
|
106
|
+
} catch (error: unknown) {
|
|
107
107
|
console.error('[WebSocketImproviseHandler] Error handling message:', error);
|
|
108
108
|
captureException(error, { context: 'websocket.handleMessage' });
|
|
109
109
|
this.send(ws, {
|
|
110
110
|
type: 'error',
|
|
111
|
-
data: { message: error.message }
|
|
111
|
+
data: { message: error instanceof Error ? error.message : String(error) }
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
}
|