mstro-app 0.3.5 → 0.3.6
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/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +5 -10
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +4 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +39 -1
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts +19 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +48 -1
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.d.ts +44 -0
- package/dist/server/services/websocket/file-upload-handler.d.ts.map +1 -0
- package/dist/server/services/websocket/file-upload-handler.js +185 -0
- package/dist/server/services/websocket/file-upload-handler.js.map +1 -0
- package/dist/server/services/websocket/git-handlers.d.ts +1 -1
- package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +3 -3
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.d.ts +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.js +40 -2
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler-context.d.ts +3 -0
- package/dist/server/services/websocket/handler-context.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +4 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +31 -0
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +69 -20
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/session-registry.d.ts +6 -0
- package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
- package/dist/server/services/websocket/session-registry.js +16 -0
- package/dist/server/services/websocket/session-registry.js.map +1 -1
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js +33 -24
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.d.ts +4 -0
- package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +35 -4
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +2 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/server/cli/headless/claude-invoker.ts +5 -11
- package/server/cli/improvisation-session-manager.ts +42 -1
- package/server/services/terminal/pty-manager.ts +57 -2
- package/server/services/websocket/file-upload-handler.ts +259 -0
- package/server/services/websocket/git-handlers.ts +3 -3
- package/server/services/websocket/git-worktree-handlers.ts +47 -3
- package/server/services/websocket/handler-context.ts +3 -0
- package/server/services/websocket/handler.ts +33 -0
- package/server/services/websocket/session-handlers.ts +79 -20
- package/server/services/websocket/session-registry.ts +18 -0
- package/server/services/websocket/tab-handlers.ts +44 -23
- package/server/services/websocket/terminal-handlers.ts +40 -4
- package/server/services/websocket/types.ts +14 -2
|
@@ -7,6 +7,43 @@ import type { HandlerContext } from './handler-context.js';
|
|
|
7
7
|
import { buildOutputHistory, setupSessionListeners } from './session-handlers.js';
|
|
8
8
|
import type { WebSocketMessage, WSContext } from './types.js';
|
|
9
9
|
|
|
10
|
+
function buildActiveTabData(
|
|
11
|
+
regTab: { tabName: string; createdAt: string; order: number; hasUnviewedCompletion?: boolean; sessionId: string },
|
|
12
|
+
session: ImprovisationSessionManager,
|
|
13
|
+
worktreePath: string | undefined,
|
|
14
|
+
worktreeBranch: string | undefined,
|
|
15
|
+
): Record<string, unknown> {
|
|
16
|
+
return {
|
|
17
|
+
tabName: regTab.tabName,
|
|
18
|
+
createdAt: regTab.createdAt,
|
|
19
|
+
order: regTab.order,
|
|
20
|
+
hasUnviewedCompletion: regTab.hasUnviewedCompletion,
|
|
21
|
+
sessionInfo: session.getSessionInfo(),
|
|
22
|
+
isExecuting: session.isExecuting,
|
|
23
|
+
outputHistory: buildOutputHistory(session),
|
|
24
|
+
executionEvents: session.isExecuting ? session.getExecutionEventLog() : undefined,
|
|
25
|
+
...(session.isExecuting && session.executionStartTimestamp ? { executionStartTimestamp: session.executionStartTimestamp } : {}),
|
|
26
|
+
...(worktreePath ? { worktreePath, worktreeBranch } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildInactiveTabData(
|
|
31
|
+
regTab: { tabName: string; createdAt: string; order: number; hasUnviewedCompletion?: boolean; sessionId: string },
|
|
32
|
+
worktreePath: string | undefined,
|
|
33
|
+
worktreeBranch: string | undefined,
|
|
34
|
+
): Record<string, unknown> {
|
|
35
|
+
return {
|
|
36
|
+
tabName: regTab.tabName,
|
|
37
|
+
createdAt: regTab.createdAt,
|
|
38
|
+
order: regTab.order,
|
|
39
|
+
hasUnviewedCompletion: regTab.hasUnviewedCompletion,
|
|
40
|
+
sessionId: regTab.sessionId,
|
|
41
|
+
isExecuting: false,
|
|
42
|
+
outputHistory: [],
|
|
43
|
+
...(worktreePath ? { worktreePath, worktreeBranch } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
10
47
|
export function handleGetActiveTabs(ctx: HandlerContext, ws: WSContext, workingDir: string): void {
|
|
11
48
|
const registry = ctx.getRegistry(workingDir);
|
|
12
49
|
const allTabs = registry.getAllTabs();
|
|
@@ -14,29 +51,11 @@ export function handleGetActiveTabs(ctx: HandlerContext, ws: WSContext, workingD
|
|
|
14
51
|
const tabs: Record<string, unknown> = {};
|
|
15
52
|
for (const [tabId, regTab] of Object.entries(allTabs)) {
|
|
16
53
|
const session = ctx.sessions.get(regTab.sessionId);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
hasUnviewedCompletion: regTab.hasUnviewedCompletion,
|
|
23
|
-
sessionInfo: session.getSessionInfo(),
|
|
24
|
-
isExecuting: session.isExecuting,
|
|
25
|
-
outputHistory: buildOutputHistory(session),
|
|
26
|
-
executionEvents: session.isExecuting ? session.getExecutionEventLog() : undefined,
|
|
27
|
-
...(session.isExecuting && session.executionStartTimestamp ? { executionStartTimestamp: session.executionStartTimestamp } : {}),
|
|
28
|
-
};
|
|
29
|
-
} else {
|
|
30
|
-
tabs[tabId] = {
|
|
31
|
-
tabName: regTab.tabName,
|
|
32
|
-
createdAt: regTab.createdAt,
|
|
33
|
-
order: regTab.order,
|
|
34
|
-
hasUnviewedCompletion: regTab.hasUnviewedCompletion,
|
|
35
|
-
sessionId: regTab.sessionId,
|
|
36
|
-
isExecuting: false,
|
|
37
|
-
outputHistory: [],
|
|
38
|
-
};
|
|
39
|
-
}
|
|
54
|
+
const worktreePath = ctx.gitDirectories.get(tabId);
|
|
55
|
+
const worktreeBranch = ctx.gitBranches.get(tabId);
|
|
56
|
+
tabs[tabId] = session
|
|
57
|
+
? buildActiveTabData(regTab, session, worktreePath, worktreeBranch)
|
|
58
|
+
: buildInactiveTabData(regTab, worktreePath, worktreeBranch);
|
|
40
59
|
}
|
|
41
60
|
|
|
42
61
|
ctx.send(ws, { type: 'activeTabs', data: { tabs } });
|
|
@@ -65,6 +84,8 @@ export function handleSyncPromptText(ctx: HandlerContext, _ws: WSContext, msg: W
|
|
|
65
84
|
export function handleRemoveTab(ctx: HandlerContext, _ws: WSContext, tabId: string, workingDir: string): void {
|
|
66
85
|
const registry = ctx.getRegistry(workingDir);
|
|
67
86
|
registry.unregisterTab(tabId);
|
|
87
|
+
ctx.gitDirectories.delete(tabId);
|
|
88
|
+
ctx.gitBranches.delete(tabId);
|
|
68
89
|
|
|
69
90
|
ctx.broadcastToAll({
|
|
70
91
|
type: 'tabRemoved',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
2
|
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
3
|
|
|
4
|
+
import { platform } from 'node:os';
|
|
4
5
|
import { AnalyticsEvents, trackEvent } from '../analytics.js';
|
|
5
6
|
import { getPTYManager } from '../terminal/pty-manager.js';
|
|
6
7
|
import type { HandlerContext } from './handler-context.js';
|
|
@@ -58,7 +59,7 @@ function handleTerminalInit(
|
|
|
58
59
|
setupTerminalBroadcastListeners(ctx, terminalId);
|
|
59
60
|
|
|
60
61
|
try {
|
|
61
|
-
const { shell, cwd, isReconnect } = ptyManager.create(
|
|
62
|
+
const { shell, cwd, isReconnect, platform } = ptyManager.create(
|
|
62
63
|
terminalId,
|
|
63
64
|
workingDir,
|
|
64
65
|
cols || 80,
|
|
@@ -77,8 +78,17 @@ function handleTerminalInit(
|
|
|
77
78
|
ctx.send(ws, {
|
|
78
79
|
type: 'terminalReady',
|
|
79
80
|
terminalId,
|
|
80
|
-
data: { shell, cwd, isReconnect }
|
|
81
|
+
data: { shell, cwd, isReconnect, platform }
|
|
81
82
|
});
|
|
83
|
+
|
|
84
|
+
// Send scrollback buffer so reconnecting clients see prior output
|
|
85
|
+
if (isReconnect) {
|
|
86
|
+
const scrollback = ptyManager.getScrollback(terminalId);
|
|
87
|
+
if (scrollback) {
|
|
88
|
+
ctx.send(ws, { type: 'terminalScrollback', terminalId, data: { scrollback } });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
82
92
|
trackEvent(AnalyticsEvents.TERMINAL_SESSION_CREATED, {
|
|
83
93
|
shell,
|
|
84
94
|
is_reconnect: isReconnect,
|
|
@@ -116,10 +126,17 @@ function handleTerminalReconnect(ctx: HandlerContext, ws: WSContext, terminalId:
|
|
|
116
126
|
data: {
|
|
117
127
|
shell: sessionInfo.shell,
|
|
118
128
|
cwd: sessionInfo.cwd,
|
|
119
|
-
isReconnect: true
|
|
129
|
+
isReconnect: true,
|
|
130
|
+
platform: platform(),
|
|
120
131
|
}
|
|
121
132
|
});
|
|
122
133
|
|
|
134
|
+
// Send scrollback buffer so reconnecting clients see prior output
|
|
135
|
+
const scrollback = ptyManager.getScrollback(terminalId);
|
|
136
|
+
if (scrollback) {
|
|
137
|
+
ctx.send(ws, { type: 'terminalScrollback', terminalId, data: { scrollback } });
|
|
138
|
+
}
|
|
139
|
+
|
|
123
140
|
ptyManager.resize(terminalId, sessionInfo.cols, sessionInfo.rows);
|
|
124
141
|
}
|
|
125
142
|
|
|
@@ -269,9 +286,28 @@ function setupTerminalBroadcastListeners(ctx: HandlerContext, terminalId: string
|
|
|
269
286
|
/**
|
|
270
287
|
* Clean up terminal subscribers for a disconnected WS context.
|
|
271
288
|
* Called from handler.ts handleClose().
|
|
289
|
+
*
|
|
290
|
+
* After removing the ws, also cleans up any subscriber Sets that are now empty
|
|
291
|
+
* and their associated PTY event listeners — preventing stale state accumulation
|
|
292
|
+
* across repeated connect/disconnect cycles (WebSocket hiccups).
|
|
272
293
|
*/
|
|
273
294
|
export function cleanupTerminalSubscribers(ctx: HandlerContext, ws: WSContext): void {
|
|
274
|
-
|
|
295
|
+
const emptyTerminals: string[] = [];
|
|
296
|
+
|
|
297
|
+
for (const [terminalId, subs] of ctx.terminalSubscribers) {
|
|
275
298
|
subs.delete(ws);
|
|
299
|
+
if (subs.size === 0) {
|
|
300
|
+
emptyTerminals.push(terminalId);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Clean up empty subscriber sets and their PTY event listeners
|
|
305
|
+
for (const terminalId of emptyTerminals) {
|
|
306
|
+
ctx.terminalSubscribers.delete(terminalId);
|
|
307
|
+
const cleanup = ctx.terminalListenerCleanups.get(terminalId);
|
|
308
|
+
if (cleanup) {
|
|
309
|
+
cleanup();
|
|
310
|
+
ctx.terminalListenerCleanups.delete(terminalId);
|
|
311
|
+
}
|
|
276
312
|
}
|
|
277
313
|
}
|
|
@@ -86,6 +86,7 @@ export interface WebSocketMessage {
|
|
|
86
86
|
// Worktree operations
|
|
87
87
|
| 'gitWorktreeList'
|
|
88
88
|
| 'gitWorktreeCreate'
|
|
89
|
+
| 'gitWorktreeCreateAndAssign'
|
|
89
90
|
| 'gitWorktreeRemove'
|
|
90
91
|
| 'tabWorktreeSwitch'
|
|
91
92
|
| 'gitWorktreePush'
|
|
@@ -105,7 +106,12 @@ export interface WebSocketMessage {
|
|
|
105
106
|
| 'markTabViewed'
|
|
106
107
|
// Settings message types
|
|
107
108
|
| 'getSettings'
|
|
108
|
-
| 'updateSettings'
|
|
109
|
+
| 'updateSettings'
|
|
110
|
+
// File upload message types (chunked remote uploads)
|
|
111
|
+
| 'fileUploadStart'
|
|
112
|
+
| 'fileUploadChunk'
|
|
113
|
+
| 'fileUploadComplete'
|
|
114
|
+
| 'fileUploadCancel';
|
|
109
115
|
tabId?: string;
|
|
110
116
|
terminalId?: string;
|
|
111
117
|
// biome-ignore lint/suspicious/noExplicitAny: message envelope carries heterogeneous payloads
|
|
@@ -158,6 +164,7 @@ export interface WebSocketResponse {
|
|
|
158
164
|
| 'contentSearchError'
|
|
159
165
|
| 'definitionResult'
|
|
160
166
|
| 'fileError'
|
|
167
|
+
| 'terminalScrollback'
|
|
161
168
|
// Terminal sync response types
|
|
162
169
|
| 'terminalCreated'
|
|
163
170
|
| 'terminalClosed'
|
|
@@ -190,6 +197,7 @@ export interface WebSocketResponse {
|
|
|
190
197
|
// Worktree response types
|
|
191
198
|
| 'gitWorktreeListResult'
|
|
192
199
|
| 'gitWorktreeCreated'
|
|
200
|
+
| 'gitWorktreeCreatedAndAssigned'
|
|
193
201
|
| 'gitWorktreeRemoved'
|
|
194
202
|
| 'tabWorktreeSwitched'
|
|
195
203
|
| 'gitWorktreePushed'
|
|
@@ -210,7 +218,11 @@ export interface WebSocketResponse {
|
|
|
210
218
|
| 'tabStateChanged'
|
|
211
219
|
// Settings response types
|
|
212
220
|
| 'settings'
|
|
213
|
-
| 'settingsUpdated'
|
|
221
|
+
| 'settingsUpdated'
|
|
222
|
+
// File upload response types
|
|
223
|
+
| 'fileUploadAck'
|
|
224
|
+
| 'fileUploadReady'
|
|
225
|
+
| 'fileUploadError';
|
|
214
226
|
tabId?: string;
|
|
215
227
|
terminalId?: string;
|
|
216
228
|
// biome-ignore lint/suspicious/noExplicitAny: message envelope carries heterogeneous payloads
|