snow-ai 0.3.27 → 0.3.29
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/api/systemPrompt.js +4 -3
- package/dist/hooks/useCommandHandler.js +5 -3
- package/dist/hooks/useConversation.js +54 -42
- package/dist/mcp/filesystem.d.ts +11 -0
- package/dist/mcp/filesystem.js +95 -2
- package/dist/mcp/utils/aceCodeSearch/language.utils.js +354 -35
- package/dist/ui/components/MessageList.d.ts +1 -0
- package/dist/ui/pages/ChatScreen.js +44 -7
- package/dist/ui/pages/SubAgentConfigScreen.js +27 -4
- package/dist/utils/sessionConverter.js +68 -21
- package/dist/utils/subAgentConfig.d.ts +3 -1
- package/dist/utils/subAgentConfig.js +3 -1
- package/dist/utils/subAgentExecutor.js +6 -1
- package/dist/utils/toolExecutor.d.ts +3 -1
- package/dist/utils/toolExecutor.js +90 -2
- package/dist/utils/vscodeConnection.d.ts +7 -0
- package/dist/utils/vscodeConnection.js +79 -2
- package/package.json +1 -1
|
@@ -18,27 +18,30 @@ export function convertSessionMessagesToUI(sessionMessages) {
|
|
|
18
18
|
// Handle sub-agent internal tool call messages
|
|
19
19
|
if (msg.subAgentInternal && msg.role === 'assistant' && msg.tool_calls) {
|
|
20
20
|
for (const toolCall of msg.tool_calls) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
toolArgs
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
// 只有耗时工具才创建"进行中"消息
|
|
22
|
+
if (isToolNeedTwoStepDisplay(toolCall.function.name)) {
|
|
23
|
+
const toolDisplay = formatToolCallMessage(toolCall);
|
|
24
|
+
let toolArgs;
|
|
25
|
+
try {
|
|
26
|
+
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
toolArgs = {};
|
|
30
|
+
}
|
|
31
|
+
uiMessages.push({
|
|
32
|
+
role: 'subagent',
|
|
33
|
+
content: `\x1b[38;2;184;122;206m⚇⚡ ${toolDisplay.toolName}\x1b[0m`,
|
|
34
|
+
streaming: false,
|
|
35
|
+
toolCall: {
|
|
36
|
+
name: toolCall.function.name,
|
|
37
|
+
arguments: toolArgs,
|
|
38
|
+
},
|
|
39
|
+
toolDisplay,
|
|
40
|
+
toolCallId: toolCall.id,
|
|
41
|
+
toolPending: false,
|
|
42
|
+
subAgentInternal: true,
|
|
43
|
+
});
|
|
28
44
|
}
|
|
29
|
-
uiMessages.push({
|
|
30
|
-
role: 'subagent',
|
|
31
|
-
content: `\x1b[38;2;184;122;206m⚇⚡ ${toolDisplay.toolName}\x1b[0m`,
|
|
32
|
-
streaming: false,
|
|
33
|
-
toolCall: {
|
|
34
|
-
name: toolCall.function.name,
|
|
35
|
-
arguments: toolArgs,
|
|
36
|
-
},
|
|
37
|
-
toolDisplay,
|
|
38
|
-
toolCallId: toolCall.id,
|
|
39
|
-
toolPending: false,
|
|
40
|
-
subAgentInternal: true,
|
|
41
|
-
});
|
|
42
45
|
processedToolCalls.add(toolCall.id);
|
|
43
46
|
}
|
|
44
47
|
continue;
|
|
@@ -103,6 +106,12 @@ export function convertSessionMessagesToUI(sessionMessages) {
|
|
|
103
106
|
msg.tool_calls &&
|
|
104
107
|
msg.tool_calls.length > 0 &&
|
|
105
108
|
!msg.subAgentInternal) {
|
|
109
|
+
// Generate parallel group ID for non-time-consuming tools
|
|
110
|
+
const hasMultipleTools = msg.tool_calls.length > 1;
|
|
111
|
+
const hasNonTimeConsumingTool = msg.tool_calls.some(tc => !isToolNeedTwoStepDisplay(tc.function.name));
|
|
112
|
+
const parallelGroupId = hasMultipleTools && hasNonTimeConsumingTool
|
|
113
|
+
? `parallel-${i}-${Math.random()}`
|
|
114
|
+
: undefined;
|
|
106
115
|
for (const toolCall of msg.tool_calls) {
|
|
107
116
|
// Skip if already processed
|
|
108
117
|
if (processedToolCalls.has(toolCall.id))
|
|
@@ -130,7 +139,15 @@ export function convertSessionMessagesToUI(sessionMessages) {
|
|
|
130
139
|
toolDisplay,
|
|
131
140
|
});
|
|
132
141
|
}
|
|
133
|
-
|
|
142
|
+
// Store parallel group info for this tool call
|
|
143
|
+
if (parallelGroupId && !needTwoSteps) {
|
|
144
|
+
processedToolCalls.add(toolCall.id);
|
|
145
|
+
// Mark this tool call with parallel group (will be used when processing tool results)
|
|
146
|
+
toolCall.parallelGroupId = parallelGroupId;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
processedToolCalls.add(toolCall.id);
|
|
150
|
+
}
|
|
134
151
|
}
|
|
135
152
|
continue;
|
|
136
153
|
}
|
|
@@ -219,6 +236,23 @@ export function convertSessionMessagesToUI(sessionMessages) {
|
|
|
219
236
|
}
|
|
220
237
|
}
|
|
221
238
|
}
|
|
239
|
+
// Check if this tool result is part of a parallel group
|
|
240
|
+
let parallelGroupId;
|
|
241
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
242
|
+
const prevMsg = sessionMessages[j];
|
|
243
|
+
if (!prevMsg)
|
|
244
|
+
continue;
|
|
245
|
+
if (prevMsg.role === 'assistant' &&
|
|
246
|
+
prevMsg.tool_calls &&
|
|
247
|
+
!prevMsg.subAgentInternal) {
|
|
248
|
+
const tc = prevMsg.tool_calls.find(t => t.id === msg.tool_call_id);
|
|
249
|
+
if (tc) {
|
|
250
|
+
parallelGroupId = tc.parallelGroupId;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const isNonTimeConsuming = !isToolNeedTwoStepDisplay(toolName);
|
|
222
256
|
uiMessages.push({
|
|
223
257
|
role: 'assistant',
|
|
224
258
|
content: `${statusIcon} ${toolName}${statusText}`,
|
|
@@ -231,6 +265,19 @@ export function convertSessionMessagesToUI(sessionMessages) {
|
|
|
231
265
|
}
|
|
232
266
|
: undefined,
|
|
233
267
|
terminalResult: terminalResultData,
|
|
268
|
+
// Add toolDisplay for non-time-consuming tools
|
|
269
|
+
toolDisplay: isNonTimeConsuming && !editDiffData
|
|
270
|
+
? formatToolCallMessage({
|
|
271
|
+
id: msg.tool_call_id || '',
|
|
272
|
+
type: 'function',
|
|
273
|
+
function: {
|
|
274
|
+
name: toolName,
|
|
275
|
+
arguments: JSON.stringify(toolArgs),
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
: undefined,
|
|
279
|
+
// Mark parallel group for non-time-consuming tools
|
|
280
|
+
parallelGroup: isNonTimeConsuming && parallelGroupId ? parallelGroupId : undefined,
|
|
234
281
|
});
|
|
235
282
|
continue;
|
|
236
283
|
}
|
|
@@ -2,6 +2,7 @@ export interface SubAgent {
|
|
|
2
2
|
id: string;
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
+
role?: string;
|
|
5
6
|
tools: string[];
|
|
6
7
|
createdAt: string;
|
|
7
8
|
updatedAt: string;
|
|
@@ -20,13 +21,14 @@ export declare function getSubAgent(id: string): SubAgent | null;
|
|
|
20
21
|
/**
|
|
21
22
|
* Create a new sub-agent
|
|
22
23
|
*/
|
|
23
|
-
export declare function createSubAgent(name: string, description: string, tools: string[]): SubAgent;
|
|
24
|
+
export declare function createSubAgent(name: string, description: string, tools: string[], role?: string): SubAgent;
|
|
24
25
|
/**
|
|
25
26
|
* Update an existing sub-agent
|
|
26
27
|
*/
|
|
27
28
|
export declare function updateSubAgent(id: string, updates: {
|
|
28
29
|
name?: string;
|
|
29
30
|
description?: string;
|
|
31
|
+
role?: string;
|
|
30
32
|
tools?: string[];
|
|
31
33
|
}): SubAgent | null;
|
|
32
34
|
/**
|
|
@@ -53,13 +53,14 @@ function saveSubAgents(agents) {
|
|
|
53
53
|
/**
|
|
54
54
|
* Create a new sub-agent
|
|
55
55
|
*/
|
|
56
|
-
export function createSubAgent(name, description, tools) {
|
|
56
|
+
export function createSubAgent(name, description, tools, role) {
|
|
57
57
|
const agents = getSubAgents();
|
|
58
58
|
const now = new Date().toISOString();
|
|
59
59
|
const newAgent = {
|
|
60
60
|
id: generateId(),
|
|
61
61
|
name,
|
|
62
62
|
description,
|
|
63
|
+
role,
|
|
63
64
|
tools,
|
|
64
65
|
createdAt: now,
|
|
65
66
|
updatedAt: now,
|
|
@@ -85,6 +86,7 @@ export function updateSubAgent(id, updates) {
|
|
|
85
86
|
id: existingAgent.id,
|
|
86
87
|
name: updates.name ?? existingAgent.name,
|
|
87
88
|
description: updates.description ?? existingAgent.description,
|
|
89
|
+
role: updates.role ?? existingAgent.role,
|
|
88
90
|
tools: updates.tools ?? existingAgent.tools,
|
|
89
91
|
createdAt: existingAgent.createdAt,
|
|
90
92
|
updatedAt: new Date().toISOString(),
|
|
@@ -50,10 +50,15 @@ export async function executeSubAgent(agentId, prompt, onMessage, abortSignal, r
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
// Build conversation history for sub-agent
|
|
53
|
+
// Append role to prompt if configured
|
|
54
|
+
let finalPrompt = prompt;
|
|
55
|
+
if (agent.role) {
|
|
56
|
+
finalPrompt = `${prompt}\n\n${agent.role}`;
|
|
57
|
+
}
|
|
53
58
|
const messages = [
|
|
54
59
|
{
|
|
55
60
|
role: 'user',
|
|
56
|
-
content:
|
|
61
|
+
content: finalPrompt,
|
|
57
62
|
},
|
|
58
63
|
];
|
|
59
64
|
// Stream sub-agent execution
|
|
@@ -27,6 +27,8 @@ export interface AddToAlwaysApprovedCallback {
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function executeToolCall(toolCall: ToolCall, abortSignal?: AbortSignal, onTokenUpdate?: (tokenCount: number) => void, onSubAgentMessage?: SubAgentMessageCallback, requestToolConfirmation?: ToolConfirmationCallback, isToolAutoApproved?: ToolApprovalChecker, yoloMode?: boolean, addToAlwaysApproved?: AddToAlwaysApprovedCallback): Promise<ToolResult>;
|
|
29
29
|
/**
|
|
30
|
-
* Execute multiple tool calls
|
|
30
|
+
* Execute multiple tool calls with intelligent sequencing
|
|
31
|
+
* - Tools modifying the same resource execute sequentially
|
|
32
|
+
* - Independent tools execute in parallel
|
|
31
33
|
*/
|
|
32
34
|
export declare function executeToolCalls(toolCalls: ToolCall[], abortSignal?: AbortSignal, onTokenUpdate?: (tokenCount: number) => void, onSubAgentMessage?: SubAgentMessageCallback, requestToolConfirmation?: ToolConfirmationCallback, isToolAutoApproved?: ToolApprovalChecker, yoloMode?: boolean, addToAlwaysApproved?: AddToAlwaysApprovedCallback): Promise<ToolResult[]>;
|
|
@@ -63,8 +63,96 @@ export async function executeToolCall(toolCall, abortSignal, onTokenUpdate, onSu
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Categorize tools by their resource type for proper execution sequencing
|
|
67
|
+
*/
|
|
68
|
+
function getToolResourceType(toolName) {
|
|
69
|
+
// TODO tools all modify the same TODO file - must be sequential
|
|
70
|
+
if (toolName === 'todo-create' ||
|
|
71
|
+
toolName === 'todo-update' ||
|
|
72
|
+
toolName === 'todo-add' ||
|
|
73
|
+
toolName === 'todo-delete') {
|
|
74
|
+
return 'todo-state';
|
|
75
|
+
}
|
|
76
|
+
// Terminal commands must be sequential to avoid race conditions
|
|
77
|
+
// (e.g., npm install -> npm build, port conflicts, file locks)
|
|
78
|
+
if (toolName === 'terminal-execute') {
|
|
79
|
+
return 'terminal-execution';
|
|
80
|
+
}
|
|
81
|
+
// Each file is a separate resource
|
|
82
|
+
if (toolName === 'filesystem-edit' ||
|
|
83
|
+
toolName === 'filesystem-edit_search' ||
|
|
84
|
+
toolName === 'filesystem-create' ||
|
|
85
|
+
toolName === 'filesystem-delete') {
|
|
86
|
+
return 'filesystem'; // Will be further refined by file path
|
|
87
|
+
}
|
|
88
|
+
// Other tools are independent
|
|
89
|
+
return 'independent';
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get resource identifier for a tool call
|
|
93
|
+
* Tools modifying the same resource will have the same identifier
|
|
94
|
+
*/
|
|
95
|
+
function getResourceIdentifier(toolCall) {
|
|
96
|
+
const toolName = toolCall.function.name;
|
|
97
|
+
const resourceType = getToolResourceType(toolName);
|
|
98
|
+
if (resourceType === 'todo-state') {
|
|
99
|
+
return 'todo-state'; // All TODO operations share same resource
|
|
100
|
+
}
|
|
101
|
+
if (resourceType === 'terminal-execution') {
|
|
102
|
+
return 'terminal-execution'; // All terminal commands share same execution context
|
|
103
|
+
}
|
|
104
|
+
if (resourceType === 'filesystem') {
|
|
105
|
+
try {
|
|
106
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
107
|
+
// Support both single file and array of files
|
|
108
|
+
const filePath = args.filePath;
|
|
109
|
+
if (typeof filePath === 'string') {
|
|
110
|
+
return `filesystem:${filePath}`;
|
|
111
|
+
}
|
|
112
|
+
else if (Array.isArray(filePath)) {
|
|
113
|
+
// For batch operations, treat as independent (already handling multiple files)
|
|
114
|
+
return `filesystem-batch:${toolCall.id}`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Parsing error, treat as independent
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Each independent tool gets its own unique identifier
|
|
122
|
+
return `independent:${toolCall.id}`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute multiple tool calls with intelligent sequencing
|
|
126
|
+
* - Tools modifying the same resource execute sequentially
|
|
127
|
+
* - Independent tools execute in parallel
|
|
67
128
|
*/
|
|
68
129
|
export async function executeToolCalls(toolCalls, abortSignal, onTokenUpdate, onSubAgentMessage, requestToolConfirmation, isToolAutoApproved, yoloMode, addToAlwaysApproved) {
|
|
69
|
-
|
|
130
|
+
// Group tool calls by their resource identifier
|
|
131
|
+
const resourceGroups = new Map();
|
|
132
|
+
for (const toolCall of toolCalls) {
|
|
133
|
+
const resourceId = getResourceIdentifier(toolCall);
|
|
134
|
+
const group = resourceGroups.get(resourceId) || [];
|
|
135
|
+
group.push(toolCall);
|
|
136
|
+
resourceGroups.set(resourceId, group);
|
|
137
|
+
}
|
|
138
|
+
// Execute each resource group sequentially, but execute different groups in parallel
|
|
139
|
+
const results = await Promise.all(Array.from(resourceGroups.values()).map(async (group) => {
|
|
140
|
+
// Within the same resource group, execute sequentially
|
|
141
|
+
const groupResults = [];
|
|
142
|
+
for (const toolCall of group) {
|
|
143
|
+
const result = await executeToolCall(toolCall, abortSignal, onTokenUpdate, onSubAgentMessage, requestToolConfirmation, isToolAutoApproved, yoloMode, addToAlwaysApproved);
|
|
144
|
+
groupResults.push(result);
|
|
145
|
+
}
|
|
146
|
+
return groupResults;
|
|
147
|
+
}));
|
|
148
|
+
// Flatten results and restore original order
|
|
149
|
+
const flatResults = results.flat();
|
|
150
|
+
const resultMap = new Map(flatResults.map(r => [r.tool_call_id, r]));
|
|
151
|
+
return toolCalls.map(tc => {
|
|
152
|
+
const result = resultMap.get(tc.id);
|
|
153
|
+
if (!result) {
|
|
154
|
+
throw new Error(`Result not found for tool call ${tc.id}`);
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
});
|
|
70
158
|
}
|
|
@@ -30,7 +30,14 @@ declare class VSCodeConnectionManager {
|
|
|
30
30
|
private editorContext;
|
|
31
31
|
private listeners;
|
|
32
32
|
private currentWorkingDirectory;
|
|
33
|
+
private connectingPromise;
|
|
34
|
+
private connectionTimeout;
|
|
35
|
+
private readonly CONNECTION_TIMEOUT;
|
|
33
36
|
start(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Clean up connection state and resources
|
|
39
|
+
*/
|
|
40
|
+
private cleanupConnection;
|
|
34
41
|
/**
|
|
35
42
|
* Normalize path for cross-platform compatibility
|
|
36
43
|
* - Converts Windows backslashes to forward slashes
|
|
@@ -89,15 +89,44 @@ class VSCodeConnectionManager {
|
|
|
89
89
|
writable: true,
|
|
90
90
|
value: process.cwd()
|
|
91
91
|
});
|
|
92
|
+
// Connection state management
|
|
93
|
+
Object.defineProperty(this, "connectingPromise", {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
configurable: true,
|
|
96
|
+
writable: true,
|
|
97
|
+
value: null
|
|
98
|
+
});
|
|
99
|
+
Object.defineProperty(this, "connectionTimeout", {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
writable: true,
|
|
103
|
+
value: null
|
|
104
|
+
});
|
|
105
|
+
Object.defineProperty(this, "CONNECTION_TIMEOUT", {
|
|
106
|
+
enumerable: true,
|
|
107
|
+
configurable: true,
|
|
108
|
+
writable: true,
|
|
109
|
+
value: 10000
|
|
110
|
+
}); // 10 seconds timeout for initial connection
|
|
92
111
|
}
|
|
93
112
|
async start() {
|
|
94
113
|
// If already connected, just return success
|
|
95
114
|
if (this.client?.readyState === WebSocket.OPEN) {
|
|
96
115
|
return Promise.resolve();
|
|
97
116
|
}
|
|
117
|
+
// If already connecting, return the existing promise to avoid duplicate connections
|
|
118
|
+
if (this.connectingPromise) {
|
|
119
|
+
return this.connectingPromise;
|
|
120
|
+
}
|
|
98
121
|
// Try to find the correct port for this workspace
|
|
99
122
|
const targetPort = this.findPortForWorkspace();
|
|
100
|
-
|
|
123
|
+
// Create a new connection promise and store it
|
|
124
|
+
this.connectingPromise = new Promise((resolve, reject) => {
|
|
125
|
+
// Set connection timeout
|
|
126
|
+
this.connectionTimeout = setTimeout(() => {
|
|
127
|
+
this.cleanupConnection();
|
|
128
|
+
reject(new Error('Connection timeout after 10 seconds'));
|
|
129
|
+
}, this.CONNECTION_TIMEOUT);
|
|
101
130
|
const tryConnect = (port) => {
|
|
102
131
|
// Check both VSCode and JetBrains port ranges
|
|
103
132
|
if (port > this.VSCODE_MAX_PORT && port < this.JETBRAINS_BASE_PORT) {
|
|
@@ -106,6 +135,7 @@ class VSCodeConnectionManager {
|
|
|
106
135
|
return;
|
|
107
136
|
}
|
|
108
137
|
if (port > this.JETBRAINS_MAX_PORT) {
|
|
138
|
+
this.cleanupConnection();
|
|
109
139
|
reject(new Error(`Failed to connect: no IDE server found on ports ${this.VSCODE_BASE_PORT}-${this.VSCODE_MAX_PORT} or ${this.JETBRAINS_BASE_PORT}-${this.JETBRAINS_MAX_PORT}`));
|
|
110
140
|
return;
|
|
111
141
|
}
|
|
@@ -115,6 +145,12 @@ class VSCodeConnectionManager {
|
|
|
115
145
|
// Reset reconnect attempts on successful connection
|
|
116
146
|
this.reconnectAttempts = 0;
|
|
117
147
|
this.port = port;
|
|
148
|
+
// Clear connection state
|
|
149
|
+
if (this.connectionTimeout) {
|
|
150
|
+
clearTimeout(this.connectionTimeout);
|
|
151
|
+
this.connectionTimeout = null;
|
|
152
|
+
}
|
|
153
|
+
this.connectingPromise = null;
|
|
118
154
|
resolve();
|
|
119
155
|
});
|
|
120
156
|
this.client.on('message', message => {
|
|
@@ -148,6 +184,34 @@ class VSCodeConnectionManager {
|
|
|
148
184
|
};
|
|
149
185
|
tryConnect(targetPort);
|
|
150
186
|
});
|
|
187
|
+
// Return the promise and clean up state when it completes or fails
|
|
188
|
+
return this.connectingPromise.finally(() => {
|
|
189
|
+
this.connectingPromise = null;
|
|
190
|
+
if (this.connectionTimeout) {
|
|
191
|
+
clearTimeout(this.connectionTimeout);
|
|
192
|
+
this.connectionTimeout = null;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Clean up connection state and resources
|
|
198
|
+
*/
|
|
199
|
+
cleanupConnection() {
|
|
200
|
+
this.connectingPromise = null;
|
|
201
|
+
if (this.connectionTimeout) {
|
|
202
|
+
clearTimeout(this.connectionTimeout);
|
|
203
|
+
this.connectionTimeout = null;
|
|
204
|
+
}
|
|
205
|
+
if (this.client) {
|
|
206
|
+
try {
|
|
207
|
+
this.client.removeAllListeners();
|
|
208
|
+
this.client.close();
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
// Ignore errors during cleanup
|
|
212
|
+
}
|
|
213
|
+
this.client = null;
|
|
214
|
+
}
|
|
151
215
|
}
|
|
152
216
|
/**
|
|
153
217
|
* Normalize path for cross-platform compatibility
|
|
@@ -228,8 +292,21 @@ class VSCodeConnectionManager {
|
|
|
228
292
|
clearTimeout(this.reconnectTimer);
|
|
229
293
|
this.reconnectTimer = null;
|
|
230
294
|
}
|
|
295
|
+
// Clear connection timeout
|
|
296
|
+
if (this.connectionTimeout) {
|
|
297
|
+
clearTimeout(this.connectionTimeout);
|
|
298
|
+
this.connectionTimeout = null;
|
|
299
|
+
}
|
|
300
|
+
// Clear connecting promise
|
|
301
|
+
this.connectingPromise = null;
|
|
231
302
|
if (this.client) {
|
|
232
|
-
|
|
303
|
+
try {
|
|
304
|
+
this.client.removeAllListeners();
|
|
305
|
+
this.client.close();
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
// Ignore errors during cleanup
|
|
309
|
+
}
|
|
233
310
|
this.client = null;
|
|
234
311
|
}
|
|
235
312
|
this.reconnectAttempts = 0;
|