vibecodingmachine-core 1.0.2 → 2025.11.2-7.1239
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/.babelrc +13 -13
- package/README.md +28 -28
- package/__tests__/applescript-manager-claude-fix.test.js +286 -286
- package/__tests__/requirement-2-auto-start-looping.test.js +69 -69
- package/__tests__/requirement-3-auto-start-looping.test.js +69 -69
- package/__tests__/requirement-4-auto-start-looping.test.js +69 -69
- package/__tests__/requirement-6-auto-start-looping.test.js +73 -73
- package/__tests__/requirement-7-status-tracking.test.js +332 -332
- package/jest.config.js +18 -18
- package/jest.setup.js +12 -12
- package/package.json +48 -48
- package/src/auth/access-denied.html +119 -119
- package/src/auth/shared-auth-storage.js +230 -230
- package/src/autonomous-mode/feature-implementer.cjs +70 -70
- package/src/autonomous-mode/feature-implementer.js +425 -425
- package/src/chat-management/chat-manager.cjs +71 -71
- package/src/chat-management/chat-manager.js +342 -342
- package/src/ide-integration/__tests__/applescript-manager-thread-closure.test.js +227 -227
- package/src/ide-integration/aider-cli-manager.cjs +850 -850
- package/src/ide-integration/applescript-manager.cjs +1088 -1088
- package/src/ide-integration/applescript-manager.js +2802 -2802
- package/src/ide-integration/applescript-utils.js +306 -306
- package/src/ide-integration/cdp-manager.cjs +221 -221
- package/src/ide-integration/cdp-manager.js +321 -321
- package/src/ide-integration/claude-code-cli-manager.cjs +301 -301
- package/src/ide-integration/cline-cli-manager.cjs +2252 -2252
- package/src/ide-integration/continue-cli-manager.js +431 -431
- package/src/ide-integration/provider-manager.cjs +354 -354
- package/src/ide-integration/quota-detector.cjs +34 -34
- package/src/ide-integration/quota-detector.js +349 -349
- package/src/ide-integration/windows-automation-manager.js +262 -262
- package/src/index.cjs +47 -43
- package/src/index.js +17 -17
- package/src/llm/direct-llm-manager.cjs +609 -609
- package/src/ui/ButtonComponents.js +247 -247
- package/src/ui/ChatInterface.js +499 -499
- package/src/ui/StateManager.js +259 -259
- package/src/utils/audit-logger.cjs +116 -116
- package/src/utils/config-helpers.cjs +94 -94
- package/src/utils/config-helpers.js +94 -94
- package/src/utils/electron-update-checker.js +113 -85
- package/src/utils/gcloud-auth.cjs +394 -394
- package/src/utils/logger.cjs +193 -193
- package/src/utils/logger.js +191 -191
- package/src/utils/repo-helpers.cjs +120 -120
- package/src/utils/repo-helpers.js +120 -120
- package/src/utils/requirement-helpers.js +432 -432
- package/src/utils/update-checker.js +227 -167
- package/src/utils/version-checker.js +169 -0
|
@@ -1,342 +1,342 @@
|
|
|
1
|
-
// @vibecodingmachine/core - Chat Manager
|
|
2
|
-
// Handles chat message management, polling, and response detection
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Chat Manager for handling chat interactions
|
|
6
|
-
* Manages message sending, response polling, and state management
|
|
7
|
-
*/
|
|
8
|
-
export class ChatManager {
|
|
9
|
-
constructor(options = {}) {
|
|
10
|
-
this.logger = options.logger || console;
|
|
11
|
-
this.electronAPI = options.electronAPI || null;
|
|
12
|
-
this.onMessageUpdate = options.onMessageUpdate || (() => {});
|
|
13
|
-
this.onStatusUpdate = options.onStatusUpdate || (() => {});
|
|
14
|
-
this.onProgressUpdate = options.onProgressUpdate || (() => {});
|
|
15
|
-
|
|
16
|
-
// State
|
|
17
|
-
this.isPolling = false;
|
|
18
|
-
this.isPaused = false;
|
|
19
|
-
this.isStopped = false;
|
|
20
|
-
this.responseWaiting = false;
|
|
21
|
-
this.currentProgress = 0;
|
|
22
|
-
this.pollingTimer = null;
|
|
23
|
-
this.lastChangeAt = 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Send a chat message to an IDE
|
|
28
|
-
* @param {string} message - The message to send
|
|
29
|
-
* @param {string} ide - The IDE to send to
|
|
30
|
-
* @param {string} tabId - The tab ID for tracking
|
|
31
|
-
* @returns {Promise<Object>} Result of the send operation
|
|
32
|
-
*/
|
|
33
|
-
async sendMessage(message, ide, tabId) {
|
|
34
|
-
this.logger.log('sendMessage called with:', { message: message.substring(0, 50) + '...', ide, tabId });
|
|
35
|
-
|
|
36
|
-
if (!message || !message.trim() || this.isPaused || this.isStopped) {
|
|
37
|
-
this.logger.log('sendMessage early return - message:', !!message, 'isPaused:', this.isPaused, 'isStopped:', this.isStopped);
|
|
38
|
-
return {
|
|
39
|
-
success: false,
|
|
40
|
-
error: 'Cannot send message - invalid state or empty message'
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
this.onStatusUpdate(tabId, 'Sending message...');
|
|
46
|
-
|
|
47
|
-
let result;
|
|
48
|
-
if (this.electronAPI && this.electronAPI.sendChat) {
|
|
49
|
-
result = await this.electronAPI.sendChat(message, ide);
|
|
50
|
-
} else {
|
|
51
|
-
// Fallback for non-Electron environments
|
|
52
|
-
result = { success: true, method: 'simulated', message: 'Simulated message sent' };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (result && result.success) {
|
|
56
|
-
this.logger.log(`Message sent successfully via ${result.method || 'chat'}`);
|
|
57
|
-
this.onStatusUpdate(tabId, `✅ Message sent successfully via ${result.method || 'chat'}`);
|
|
58
|
-
|
|
59
|
-
if (result.method !== 'terminal') {
|
|
60
|
-
this.startPolling(ide, tabId);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
success: true,
|
|
65
|
-
method: result.method,
|
|
66
|
-
message: result.message
|
|
67
|
-
};
|
|
68
|
-
} else {
|
|
69
|
-
this.logger.log('Failed to send message:', result);
|
|
70
|
-
this.onStatusUpdate(tabId, `❌ Failed to send message: ${result?.error || 'Unknown error'}`);
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
success: false,
|
|
74
|
-
error: result?.error || 'Unknown error'
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
this.logger.error('Error sending message:', error);
|
|
79
|
-
this.onStatusUpdate(tabId, `❌ Error sending message: ${error.message}`);
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
success: false,
|
|
83
|
-
error: error.message
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Start polling for responses from an IDE
|
|
90
|
-
* @param {string} ide - The IDE to poll
|
|
91
|
-
* @param {string} tabId - The tab ID for tracking
|
|
92
|
-
*/
|
|
93
|
-
startPolling(ide, tabId) {
|
|
94
|
-
if (this.isPolling) {
|
|
95
|
-
this.logger.log('Polling already in progress');
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this.isPolling = true;
|
|
100
|
-
this.lastChangeAt = Date.now();
|
|
101
|
-
|
|
102
|
-
this.logger.log(`Starting polling for ${ide} (tab: ${tabId})`);
|
|
103
|
-
this.onStatusUpdate(tabId, 'Polling for response...');
|
|
104
|
-
|
|
105
|
-
this.pollingTimer = setInterval(async () => {
|
|
106
|
-
if (this.isStopped || this.isPaused) {
|
|
107
|
-
this.stopPolling();
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const response = await this.readResponse(ide);
|
|
113
|
-
|
|
114
|
-
if (response && response.length > 0) {
|
|
115
|
-
this.lastChangeAt = Date.now();
|
|
116
|
-
|
|
117
|
-
// Check if response is complete
|
|
118
|
-
const completionResult = await this.detectResponseCompletion(ide, response);
|
|
119
|
-
|
|
120
|
-
if (completionResult.completed) {
|
|
121
|
-
this.logger.log('Response completed, stopping polling');
|
|
122
|
-
this.onStatusUpdate(tabId, '✅ Response received');
|
|
123
|
-
this.stopPolling();
|
|
124
|
-
|
|
125
|
-
// Update messages with the complete response
|
|
126
|
-
this.onMessageUpdate(tabId, {
|
|
127
|
-
role: 'assistant',
|
|
128
|
-
text: completionResult.response,
|
|
129
|
-
timestamp: Date.now(),
|
|
130
|
-
progress: completionResult.progress || 100
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Stop polling if no change for 30 seconds
|
|
136
|
-
if (Date.now() - this.lastChangeAt > 30000) {
|
|
137
|
-
this.logger.log('No response change for 30 seconds, stopping polling');
|
|
138
|
-
this.onStatusUpdate(tabId, '⏰ Response timeout');
|
|
139
|
-
this.stopPolling();
|
|
140
|
-
}
|
|
141
|
-
} catch (error) {
|
|
142
|
-
this.logger.error('Error during polling:', error);
|
|
143
|
-
this.onStatusUpdate(tabId, `❌ Polling error: ${error.message}`);
|
|
144
|
-
this.stopPolling();
|
|
145
|
-
}
|
|
146
|
-
}, 2000); // Poll every 2 seconds
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Stop polling for responses
|
|
151
|
-
*/
|
|
152
|
-
stopPolling() {
|
|
153
|
-
if (this.pollingTimer) {
|
|
154
|
-
clearInterval(this.pollingTimer);
|
|
155
|
-
this.pollingTimer = null;
|
|
156
|
-
}
|
|
157
|
-
this.isPolling = false;
|
|
158
|
-
this.responseWaiting = false;
|
|
159
|
-
this.logger.log('Polling stopped');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Read response from an IDE
|
|
164
|
-
* @param {string} ide - The IDE to read from
|
|
165
|
-
* @returns {Promise<string>} The response text
|
|
166
|
-
*/
|
|
167
|
-
async readResponse(ide) {
|
|
168
|
-
try {
|
|
169
|
-
if (this.electronAPI && this.electronAPI.readChat) {
|
|
170
|
-
return await this.electronAPI.readChat(ide);
|
|
171
|
-
} else {
|
|
172
|
-
// Fallback for non-Electron environments
|
|
173
|
-
return 'Simulated response from ' + ide;
|
|
174
|
-
}
|
|
175
|
-
} catch (error) {
|
|
176
|
-
this.logger.error('Error reading response:', error);
|
|
177
|
-
return '';
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Detect if a response is complete
|
|
183
|
-
* @param {string} ide - The IDE name
|
|
184
|
-
* @param {string} response - The current response text
|
|
185
|
-
* @returns {Promise<Object>} Completion detection result
|
|
186
|
-
*/
|
|
187
|
-
async detectResponseCompletion(ide, response) {
|
|
188
|
-
if (ide !== 'windsurf') {
|
|
189
|
-
// For non-Windsurf IDEs, assume response is complete after a delay
|
|
190
|
-
return { completed: true, response };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
this.logger.log('🔍 Detecting Windsurf response completion...');
|
|
194
|
-
this.responseWaiting = true;
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
const responseText = response.toLowerCase();
|
|
198
|
-
|
|
199
|
-
// Check for ongoing status indicators - if these are present, response is NOT complete
|
|
200
|
-
const hasOngoingStatus = responseText.includes('surfing') ||
|
|
201
|
-
responseText.includes('navigating') ||
|
|
202
|
-
responseText.includes('swimming') ||
|
|
203
|
-
responseText.includes('floating') ||
|
|
204
|
-
responseText.includes('proposing code') ||
|
|
205
|
-
responseText.includes('reading file') ||
|
|
206
|
-
responseText.includes('searching') ||
|
|
207
|
-
responseText.includes('updating todo list');
|
|
208
|
-
|
|
209
|
-
if (hasOngoingStatus) {
|
|
210
|
-
this.logger.log('⏳ Response still in progress - waiting for completion...');
|
|
211
|
-
return { completed: false, response };
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Check for completion indicators
|
|
215
|
-
const hasCompletionIndicators = responseText.includes('thumbs up') ||
|
|
216
|
-
responseText.includes('thumbs down') ||
|
|
217
|
-
responseText.includes('👍') ||
|
|
218
|
-
responseText.includes('👎') ||
|
|
219
|
-
responseText.includes('copy icon') ||
|
|
220
|
-
responseText.includes('bookmark icon') ||
|
|
221
|
-
responseText.includes('bar chart icon') ||
|
|
222
|
-
responseText.includes('view response summary') ||
|
|
223
|
-
responseText.includes('ask anything (⌘l)') ||
|
|
224
|
-
responseText.includes('microphone icon') ||
|
|
225
|
-
responseText.includes('send button') ||
|
|
226
|
-
responseText.includes('+ chat') ||
|
|
227
|
-
responseText.includes('+ chat swe-1') ||
|
|
228
|
-
responseText.includes('paper plane') ||
|
|
229
|
-
responseText.includes('send icon') ||
|
|
230
|
-
responseText.includes('chat swe-1') ||
|
|
231
|
-
responseText.includes('ask anything') ||
|
|
232
|
-
responseText.includes('⌘l') ||
|
|
233
|
-
responseText.includes('command+l');
|
|
234
|
-
|
|
235
|
-
if (hasCompletionIndicators) {
|
|
236
|
-
this.logger.log('🎯 Completion indicators found!');
|
|
237
|
-
this.responseWaiting = false;
|
|
238
|
-
return { completed: true, response };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Check for percentage in response
|
|
242
|
-
const percentageMatch = response.match(/(\d+)%/);
|
|
243
|
-
if (percentageMatch) {
|
|
244
|
-
const percentage = parseInt(percentageMatch[1]);
|
|
245
|
-
this.currentProgress = percentage;
|
|
246
|
-
this.onProgressUpdate(percentage);
|
|
247
|
-
this.logger.log(`📊 Progress detected: ${percentage}%`);
|
|
248
|
-
|
|
249
|
-
if (percentage === 100) {
|
|
250
|
-
this.logger.log('🎉 100% completion detected!');
|
|
251
|
-
this.responseWaiting = false;
|
|
252
|
-
return { completed: true, response, progress: 100 };
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Response is not complete
|
|
257
|
-
return { completed: false, response };
|
|
258
|
-
|
|
259
|
-
} catch (error) {
|
|
260
|
-
this.logger.error('Error detecting response completion:', error);
|
|
261
|
-
this.responseWaiting = false;
|
|
262
|
-
return { completed: false, response, error: error.message };
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Pause chat operations
|
|
268
|
-
*/
|
|
269
|
-
pause() {
|
|
270
|
-
this.isPaused = true;
|
|
271
|
-
this.logger.log('Chat operations paused');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Resume chat operations
|
|
276
|
-
*/
|
|
277
|
-
resume() {
|
|
278
|
-
this.isPaused = false;
|
|
279
|
-
this.logger.log('Chat operations resumed');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Stop chat operations
|
|
284
|
-
*/
|
|
285
|
-
stop() {
|
|
286
|
-
this.isStopped = true;
|
|
287
|
-
this.stopPolling();
|
|
288
|
-
this.logger.log('Chat operations stopped');
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Reset chat state
|
|
293
|
-
*/
|
|
294
|
-
reset() {
|
|
295
|
-
this.isStopped = false;
|
|
296
|
-
this.isPaused = false;
|
|
297
|
-
this.isPolling = false;
|
|
298
|
-
this.responseWaiting = false;
|
|
299
|
-
this.currentProgress = 0;
|
|
300
|
-
this.stopPolling();
|
|
301
|
-
this.logger.log('Chat state reset');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Get current chat state
|
|
306
|
-
* @returns {Object} Current chat state
|
|
307
|
-
*/
|
|
308
|
-
getState() {
|
|
309
|
-
return {
|
|
310
|
-
isPolling: this.isPolling,
|
|
311
|
-
isPaused: this.isPaused,
|
|
312
|
-
isStopped: this.isStopped,
|
|
313
|
-
responseWaiting: this.responseWaiting,
|
|
314
|
-
currentProgress: this.currentProgress,
|
|
315
|
-
lastChangeAt: this.lastChangeAt
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Set electron API for Electron environment
|
|
321
|
-
* @param {Object} electronAPI - The electron API object
|
|
322
|
-
*/
|
|
323
|
-
setElectronAPI(electronAPI) {
|
|
324
|
-
this.electronAPI = electronAPI;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Set callback functions
|
|
329
|
-
* @param {Object} callbacks - Object containing callback functions
|
|
330
|
-
*/
|
|
331
|
-
setCallbacks(callbacks) {
|
|
332
|
-
if (callbacks.onMessageUpdate) {
|
|
333
|
-
this.onMessageUpdate = callbacks.onMessageUpdate;
|
|
334
|
-
}
|
|
335
|
-
if (callbacks.onStatusUpdate) {
|
|
336
|
-
this.onStatusUpdate = callbacks.onStatusUpdate;
|
|
337
|
-
}
|
|
338
|
-
if (callbacks.onProgressUpdate) {
|
|
339
|
-
this.onProgressUpdate = callbacks.onProgressUpdate;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
1
|
+
// @vibecodingmachine/core - Chat Manager
|
|
2
|
+
// Handles chat message management, polling, and response detection
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Chat Manager for handling chat interactions
|
|
6
|
+
* Manages message sending, response polling, and state management
|
|
7
|
+
*/
|
|
8
|
+
export class ChatManager {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.logger = options.logger || console;
|
|
11
|
+
this.electronAPI = options.electronAPI || null;
|
|
12
|
+
this.onMessageUpdate = options.onMessageUpdate || (() => {});
|
|
13
|
+
this.onStatusUpdate = options.onStatusUpdate || (() => {});
|
|
14
|
+
this.onProgressUpdate = options.onProgressUpdate || (() => {});
|
|
15
|
+
|
|
16
|
+
// State
|
|
17
|
+
this.isPolling = false;
|
|
18
|
+
this.isPaused = false;
|
|
19
|
+
this.isStopped = false;
|
|
20
|
+
this.responseWaiting = false;
|
|
21
|
+
this.currentProgress = 0;
|
|
22
|
+
this.pollingTimer = null;
|
|
23
|
+
this.lastChangeAt = 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Send a chat message to an IDE
|
|
28
|
+
* @param {string} message - The message to send
|
|
29
|
+
* @param {string} ide - The IDE to send to
|
|
30
|
+
* @param {string} tabId - The tab ID for tracking
|
|
31
|
+
* @returns {Promise<Object>} Result of the send operation
|
|
32
|
+
*/
|
|
33
|
+
async sendMessage(message, ide, tabId) {
|
|
34
|
+
this.logger.log('sendMessage called with:', { message: message.substring(0, 50) + '...', ide, tabId });
|
|
35
|
+
|
|
36
|
+
if (!message || !message.trim() || this.isPaused || this.isStopped) {
|
|
37
|
+
this.logger.log('sendMessage early return - message:', !!message, 'isPaused:', this.isPaused, 'isStopped:', this.isStopped);
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: 'Cannot send message - invalid state or empty message'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
this.onStatusUpdate(tabId, 'Sending message...');
|
|
46
|
+
|
|
47
|
+
let result;
|
|
48
|
+
if (this.electronAPI && this.electronAPI.sendChat) {
|
|
49
|
+
result = await this.electronAPI.sendChat(message, ide);
|
|
50
|
+
} else {
|
|
51
|
+
// Fallback for non-Electron environments
|
|
52
|
+
result = { success: true, method: 'simulated', message: 'Simulated message sent' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (result && result.success) {
|
|
56
|
+
this.logger.log(`Message sent successfully via ${result.method || 'chat'}`);
|
|
57
|
+
this.onStatusUpdate(tabId, `✅ Message sent successfully via ${result.method || 'chat'}`);
|
|
58
|
+
|
|
59
|
+
if (result.method !== 'terminal') {
|
|
60
|
+
this.startPolling(ide, tabId);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
method: result.method,
|
|
66
|
+
message: result.message
|
|
67
|
+
};
|
|
68
|
+
} else {
|
|
69
|
+
this.logger.log('Failed to send message:', result);
|
|
70
|
+
this.onStatusUpdate(tabId, `❌ Failed to send message: ${result?.error || 'Unknown error'}`);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: result?.error || 'Unknown error'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.logger.error('Error sending message:', error);
|
|
79
|
+
this.onStatusUpdate(tabId, `❌ Error sending message: ${error.message}`);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
error: error.message
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Start polling for responses from an IDE
|
|
90
|
+
* @param {string} ide - The IDE to poll
|
|
91
|
+
* @param {string} tabId - The tab ID for tracking
|
|
92
|
+
*/
|
|
93
|
+
startPolling(ide, tabId) {
|
|
94
|
+
if (this.isPolling) {
|
|
95
|
+
this.logger.log('Polling already in progress');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.isPolling = true;
|
|
100
|
+
this.lastChangeAt = Date.now();
|
|
101
|
+
|
|
102
|
+
this.logger.log(`Starting polling for ${ide} (tab: ${tabId})`);
|
|
103
|
+
this.onStatusUpdate(tabId, 'Polling for response...');
|
|
104
|
+
|
|
105
|
+
this.pollingTimer = setInterval(async () => {
|
|
106
|
+
if (this.isStopped || this.isPaused) {
|
|
107
|
+
this.stopPolling();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const response = await this.readResponse(ide);
|
|
113
|
+
|
|
114
|
+
if (response && response.length > 0) {
|
|
115
|
+
this.lastChangeAt = Date.now();
|
|
116
|
+
|
|
117
|
+
// Check if response is complete
|
|
118
|
+
const completionResult = await this.detectResponseCompletion(ide, response);
|
|
119
|
+
|
|
120
|
+
if (completionResult.completed) {
|
|
121
|
+
this.logger.log('Response completed, stopping polling');
|
|
122
|
+
this.onStatusUpdate(tabId, '✅ Response received');
|
|
123
|
+
this.stopPolling();
|
|
124
|
+
|
|
125
|
+
// Update messages with the complete response
|
|
126
|
+
this.onMessageUpdate(tabId, {
|
|
127
|
+
role: 'assistant',
|
|
128
|
+
text: completionResult.response,
|
|
129
|
+
timestamp: Date.now(),
|
|
130
|
+
progress: completionResult.progress || 100
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Stop polling if no change for 30 seconds
|
|
136
|
+
if (Date.now() - this.lastChangeAt > 30000) {
|
|
137
|
+
this.logger.log('No response change for 30 seconds, stopping polling');
|
|
138
|
+
this.onStatusUpdate(tabId, '⏰ Response timeout');
|
|
139
|
+
this.stopPolling();
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
this.logger.error('Error during polling:', error);
|
|
143
|
+
this.onStatusUpdate(tabId, `❌ Polling error: ${error.message}`);
|
|
144
|
+
this.stopPolling();
|
|
145
|
+
}
|
|
146
|
+
}, 2000); // Poll every 2 seconds
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Stop polling for responses
|
|
151
|
+
*/
|
|
152
|
+
stopPolling() {
|
|
153
|
+
if (this.pollingTimer) {
|
|
154
|
+
clearInterval(this.pollingTimer);
|
|
155
|
+
this.pollingTimer = null;
|
|
156
|
+
}
|
|
157
|
+
this.isPolling = false;
|
|
158
|
+
this.responseWaiting = false;
|
|
159
|
+
this.logger.log('Polling stopped');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Read response from an IDE
|
|
164
|
+
* @param {string} ide - The IDE to read from
|
|
165
|
+
* @returns {Promise<string>} The response text
|
|
166
|
+
*/
|
|
167
|
+
async readResponse(ide) {
|
|
168
|
+
try {
|
|
169
|
+
if (this.electronAPI && this.electronAPI.readChat) {
|
|
170
|
+
return await this.electronAPI.readChat(ide);
|
|
171
|
+
} else {
|
|
172
|
+
// Fallback for non-Electron environments
|
|
173
|
+
return 'Simulated response from ' + ide;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.logger.error('Error reading response:', error);
|
|
177
|
+
return '';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Detect if a response is complete
|
|
183
|
+
* @param {string} ide - The IDE name
|
|
184
|
+
* @param {string} response - The current response text
|
|
185
|
+
* @returns {Promise<Object>} Completion detection result
|
|
186
|
+
*/
|
|
187
|
+
async detectResponseCompletion(ide, response) {
|
|
188
|
+
if (ide !== 'windsurf') {
|
|
189
|
+
// For non-Windsurf IDEs, assume response is complete after a delay
|
|
190
|
+
return { completed: true, response };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.logger.log('🔍 Detecting Windsurf response completion...');
|
|
194
|
+
this.responseWaiting = true;
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const responseText = response.toLowerCase();
|
|
198
|
+
|
|
199
|
+
// Check for ongoing status indicators - if these are present, response is NOT complete
|
|
200
|
+
const hasOngoingStatus = responseText.includes('surfing') ||
|
|
201
|
+
responseText.includes('navigating') ||
|
|
202
|
+
responseText.includes('swimming') ||
|
|
203
|
+
responseText.includes('floating') ||
|
|
204
|
+
responseText.includes('proposing code') ||
|
|
205
|
+
responseText.includes('reading file') ||
|
|
206
|
+
responseText.includes('searching') ||
|
|
207
|
+
responseText.includes('updating todo list');
|
|
208
|
+
|
|
209
|
+
if (hasOngoingStatus) {
|
|
210
|
+
this.logger.log('⏳ Response still in progress - waiting for completion...');
|
|
211
|
+
return { completed: false, response };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check for completion indicators
|
|
215
|
+
const hasCompletionIndicators = responseText.includes('thumbs up') ||
|
|
216
|
+
responseText.includes('thumbs down') ||
|
|
217
|
+
responseText.includes('👍') ||
|
|
218
|
+
responseText.includes('👎') ||
|
|
219
|
+
responseText.includes('copy icon') ||
|
|
220
|
+
responseText.includes('bookmark icon') ||
|
|
221
|
+
responseText.includes('bar chart icon') ||
|
|
222
|
+
responseText.includes('view response summary') ||
|
|
223
|
+
responseText.includes('ask anything (⌘l)') ||
|
|
224
|
+
responseText.includes('microphone icon') ||
|
|
225
|
+
responseText.includes('send button') ||
|
|
226
|
+
responseText.includes('+ chat') ||
|
|
227
|
+
responseText.includes('+ chat swe-1') ||
|
|
228
|
+
responseText.includes('paper plane') ||
|
|
229
|
+
responseText.includes('send icon') ||
|
|
230
|
+
responseText.includes('chat swe-1') ||
|
|
231
|
+
responseText.includes('ask anything') ||
|
|
232
|
+
responseText.includes('⌘l') ||
|
|
233
|
+
responseText.includes('command+l');
|
|
234
|
+
|
|
235
|
+
if (hasCompletionIndicators) {
|
|
236
|
+
this.logger.log('🎯 Completion indicators found!');
|
|
237
|
+
this.responseWaiting = false;
|
|
238
|
+
return { completed: true, response };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check for percentage in response
|
|
242
|
+
const percentageMatch = response.match(/(\d+)%/);
|
|
243
|
+
if (percentageMatch) {
|
|
244
|
+
const percentage = parseInt(percentageMatch[1]);
|
|
245
|
+
this.currentProgress = percentage;
|
|
246
|
+
this.onProgressUpdate(percentage);
|
|
247
|
+
this.logger.log(`📊 Progress detected: ${percentage}%`);
|
|
248
|
+
|
|
249
|
+
if (percentage === 100) {
|
|
250
|
+
this.logger.log('🎉 100% completion detected!');
|
|
251
|
+
this.responseWaiting = false;
|
|
252
|
+
return { completed: true, response, progress: 100 };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Response is not complete
|
|
257
|
+
return { completed: false, response };
|
|
258
|
+
|
|
259
|
+
} catch (error) {
|
|
260
|
+
this.logger.error('Error detecting response completion:', error);
|
|
261
|
+
this.responseWaiting = false;
|
|
262
|
+
return { completed: false, response, error: error.message };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Pause chat operations
|
|
268
|
+
*/
|
|
269
|
+
pause() {
|
|
270
|
+
this.isPaused = true;
|
|
271
|
+
this.logger.log('Chat operations paused');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Resume chat operations
|
|
276
|
+
*/
|
|
277
|
+
resume() {
|
|
278
|
+
this.isPaused = false;
|
|
279
|
+
this.logger.log('Chat operations resumed');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Stop chat operations
|
|
284
|
+
*/
|
|
285
|
+
stop() {
|
|
286
|
+
this.isStopped = true;
|
|
287
|
+
this.stopPolling();
|
|
288
|
+
this.logger.log('Chat operations stopped');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Reset chat state
|
|
293
|
+
*/
|
|
294
|
+
reset() {
|
|
295
|
+
this.isStopped = false;
|
|
296
|
+
this.isPaused = false;
|
|
297
|
+
this.isPolling = false;
|
|
298
|
+
this.responseWaiting = false;
|
|
299
|
+
this.currentProgress = 0;
|
|
300
|
+
this.stopPolling();
|
|
301
|
+
this.logger.log('Chat state reset');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get current chat state
|
|
306
|
+
* @returns {Object} Current chat state
|
|
307
|
+
*/
|
|
308
|
+
getState() {
|
|
309
|
+
return {
|
|
310
|
+
isPolling: this.isPolling,
|
|
311
|
+
isPaused: this.isPaused,
|
|
312
|
+
isStopped: this.isStopped,
|
|
313
|
+
responseWaiting: this.responseWaiting,
|
|
314
|
+
currentProgress: this.currentProgress,
|
|
315
|
+
lastChangeAt: this.lastChangeAt
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Set electron API for Electron environment
|
|
321
|
+
* @param {Object} electronAPI - The electron API object
|
|
322
|
+
*/
|
|
323
|
+
setElectronAPI(electronAPI) {
|
|
324
|
+
this.electronAPI = electronAPI;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Set callback functions
|
|
329
|
+
* @param {Object} callbacks - Object containing callback functions
|
|
330
|
+
*/
|
|
331
|
+
setCallbacks(callbacks) {
|
|
332
|
+
if (callbacks.onMessageUpdate) {
|
|
333
|
+
this.onMessageUpdate = callbacks.onMessageUpdate;
|
|
334
|
+
}
|
|
335
|
+
if (callbacks.onStatusUpdate) {
|
|
336
|
+
this.onStatusUpdate = callbacks.onStatusUpdate;
|
|
337
|
+
}
|
|
338
|
+
if (callbacks.onProgressUpdate) {
|
|
339
|
+
this.onProgressUpdate = callbacks.onProgressUpdate;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|