camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__py3-none-any.whl

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.
Files changed (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,319 @@
1
+ const WebSocket = require('ws');
2
+ const { HybridBrowserToolkit } = require('./dist/index.js');
3
+
4
+ class WebSocketBrowserServer {
5
+ constructor() {
6
+ this.toolkit = null;
7
+ this.port = 0; // Let the OS assign a free port
8
+ this.server = null;
9
+ }
10
+
11
+ async start() {
12
+ return new Promise((resolve, reject) => {
13
+ this.server = new WebSocket.Server({
14
+ port: this.port,
15
+ maxPayload: 50 * 1024 * 1024 // 50MB limit instead of default 1MB
16
+ }, () => {
17
+ this.port = this.server.address().port;
18
+ console.log(`WebSocket server started on port ${this.port}`);
19
+ resolve(this.port);
20
+ });
21
+
22
+ this.server.on('connection', (ws) => {
23
+ console.log('Client connected');
24
+
25
+ ws.on('message', async (message) => {
26
+ try {
27
+ const data = JSON.parse(message.toString());
28
+ const { id, command, params } = data;
29
+
30
+ console.log(`Received command: ${command} with id: ${id}`);
31
+
32
+ const result = await this.handleCommand(command, params);
33
+
34
+ const response = {
35
+ id,
36
+ success: true,
37
+ result
38
+ };
39
+
40
+ ws.send(JSON.stringify(response));
41
+ } catch (error) {
42
+ console.error('Error handling command:', error);
43
+
44
+ const errorResponse = {
45
+ id: data?.id || 'unknown',
46
+ success: false,
47
+ error: error.message,
48
+ stack: error.stack
49
+ };
50
+
51
+ ws.send(JSON.stringify(errorResponse));
52
+ }
53
+ });
54
+
55
+ ws.on('close', (code, reason) => {
56
+ console.log('Client disconnected, code:', code, 'reason:', reason?.toString());
57
+ // Clean up resources when client disconnects
58
+ if (this.toolkit) {
59
+ this.toolkit.closeBrowser().catch(err => {
60
+ console.error('Error closing browser on disconnect:', err);
61
+ });
62
+ }
63
+ });
64
+
65
+ ws.on('error', (error) => {
66
+ console.error('WebSocket error:', error);
67
+ });
68
+ });
69
+
70
+ this.server.on('error', (error) => {
71
+ console.error('Server error:', error);
72
+ reject(error);
73
+ });
74
+ });
75
+ }
76
+
77
+ async handleCommand(command, params) {
78
+ switch (command) {
79
+ case 'init':
80
+ console.log('Initializing toolkit with params:', JSON.stringify(params, null, 2));
81
+
82
+ // Check if CDP is available first
83
+ let useCdp = false;
84
+ let cdpUrl = params.cdpUrl || 'http://localhost:9222';
85
+
86
+ // Extract base URL and port for validation
87
+ const baseUrl = cdpUrl.includes('/devtools/') ? cdpUrl.split('/devtools/')[0] : cdpUrl;
88
+
89
+ try {
90
+ // Test if Chrome debug port is accessible and get page URL
91
+ const response = await fetch(`${baseUrl}/json`);
92
+ if (response.ok) {
93
+ const pages = await response.json();
94
+ if (pages && pages.length > 0) {
95
+ // If user provided a specific page URL, use it; otherwise use first available
96
+ if (cdpUrl.includes('/devtools/page/') || cdpUrl.includes('/devtools/browser/')) {
97
+ useCdp = true;
98
+ console.log(`Using provided CDP URL: ${cdpUrl}`);
99
+ } else {
100
+ // Use the first available page
101
+ const firstPage = pages[0];
102
+ const pageUrl = firstPage.devtoolsFrontendUrl;
103
+ const pageId = pageUrl.match(/ws=localhost:\d+(.*)$/)?.[1];
104
+
105
+ if (pageId) {
106
+ useCdp = true;
107
+ cdpUrl = `${baseUrl}${pageId}`;
108
+ console.log(`Chrome debug port detected, using CDP connection to: ${pageId}`);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ } catch (error) {
114
+ console.log('Chrome debug port not accessible, will start new browser instance');
115
+ }
116
+
117
+ const config = {
118
+ connectOverCdp: useCdp,
119
+ cdpUrl: useCdp ? cdpUrl : undefined,
120
+ headless: false,
121
+ ...params
122
+ };
123
+
124
+ console.log('Final config:', JSON.stringify(config, null, 2));
125
+ this.toolkit = new HybridBrowserToolkit(config);
126
+ return { message: 'Toolkit initialized with CDP connection' };
127
+
128
+ case 'open_browser':
129
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
130
+ return await this.toolkit.openBrowser(params.startUrl);
131
+
132
+ case 'close_browser':
133
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
134
+ return await this.toolkit.closeBrowser();
135
+
136
+ case 'visit_page':
137
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
138
+ return await this.toolkit.visitPage(params.url);
139
+
140
+ case 'get_page_snapshot':
141
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
142
+ return await this.toolkit.getPageSnapshot(params.viewport_limit);
143
+
144
+ case 'get_snapshot_for_ai': {
145
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
146
+ // Support includeCoordinates and viewportLimit parameters
147
+ const includeCoordinates = Boolean(params.includeCoordinates);
148
+ const viewportLimit = Boolean(params.viewportLimit);
149
+ return await this.toolkit.session.getSnapshotForAI(includeCoordinates, viewportLimit);
150
+ }
151
+
152
+ case 'get_som_screenshot': {
153
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
154
+ console.log('Starting screenshot...');
155
+ const startTime = Date.now();
156
+ const result = await this.toolkit.getSomScreenshot();
157
+ const endTime = Date.now();
158
+ console.log(`Screenshot completed in ${endTime - startTime}ms`);
159
+ return result;
160
+ }
161
+ case 'click':
162
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
163
+ return await this.toolkit.click(params.ref);
164
+
165
+ case 'type':
166
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
167
+ // Handle both single input and multiple inputs
168
+ if (params.inputs) {
169
+ // Multiple inputs mode - pass inputs array directly
170
+ return await this.toolkit.type(params.inputs);
171
+ } else {
172
+ // Single input mode - pass ref and text
173
+ return await this.toolkit.type(params.ref, params.text);
174
+ }
175
+
176
+ case 'select':
177
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
178
+ return await this.toolkit.select(params.ref, params.value);
179
+
180
+ case 'scroll':
181
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
182
+ return await this.toolkit.scroll(params.direction, params.amount);
183
+
184
+ case 'enter':
185
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
186
+ return await this.toolkit.enter();
187
+
188
+ case 'mouse_control':
189
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
190
+ return await this.toolkit.mouseControl(params.control, params.x, params.y);
191
+
192
+ case 'mouse_drag':
193
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
194
+ return await this.toolkit.mouseDrag(params.from_ref, params.to_ref);
195
+
196
+ case 'press_key':
197
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
198
+ return await this.toolkit.pressKeys(params.keys);
199
+
200
+ case 'batch_keyboard_input':
201
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
202
+ const skipStabilityWait = params.skipStabilityWait !== undefined ? params.skipStabilityWait : true;
203
+ return await this.toolkit.batchKeyboardInput(params.operations, skipStabilityWait);
204
+
205
+ case 'back':
206
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
207
+ return await this.toolkit.back();
208
+
209
+ case 'forward':
210
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
211
+ return await this.toolkit.forward();
212
+
213
+ case 'switch_tab':
214
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
215
+ return await this.toolkit.switchTab(params.tabId);
216
+
217
+ case 'close_tab':
218
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
219
+ return await this.toolkit.closeTab(params.tabId);
220
+
221
+ case 'get_tab_info':
222
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
223
+ return await this.toolkit.getTabInfo();
224
+
225
+ case 'console_view':
226
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
227
+ return await this.toolkit.getConsoleView();
228
+
229
+ case 'console_exec':
230
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
231
+ return await this.toolkit.consoleExecute(params.code);
232
+
233
+ case 'wait_user':
234
+ if (!this.toolkit) throw new Error('Toolkit not initialized');
235
+ return await this.toolkit.waitUser(params.timeout);
236
+
237
+ case 'shutdown': {
238
+ console.log('Shutting down server...');
239
+
240
+ // Close browser first
241
+ if (this.toolkit) {
242
+ try {
243
+ await this.toolkit.closeBrowser();
244
+ } catch (error) {
245
+ console.error('Error closing browser:', error);
246
+ }
247
+ }
248
+
249
+ // Return response immediately
250
+ const shutdownResponse = { message: 'Server shutting down' };
251
+
252
+ // Schedule server shutdown after a short delay to ensure response is sent
253
+ setTimeout(() => {
254
+ // Close the WebSocket server properly
255
+ if (this.server) {
256
+ this.server.close((err) => {
257
+ if (err) {
258
+ console.error('Error closing server:', err);
259
+ } else {
260
+ console.log('WebSocket server closed successfully');
261
+ }
262
+ console.log('Exiting process...');
263
+ process.exit(0);
264
+ });
265
+
266
+ // Fallback timeout in case server close hangs
267
+ setTimeout(() => {
268
+ console.log('Server close timeout, forcing exit...');
269
+ process.exit(0);
270
+ }, 5000); // 5 second fallback
271
+ } else {
272
+ console.log('No server to close, exiting...');
273
+ process.exit(0);
274
+ }
275
+ }, 100); // Delay to ensure response is sent
276
+
277
+ return shutdownResponse;
278
+ }
279
+
280
+ default:
281
+ throw new Error(`Unknown command: ${command}`);
282
+ }
283
+ }
284
+
285
+ async stop() {
286
+ if (this.server) {
287
+ this.server.close();
288
+ console.log('WebSocket server stopped');
289
+ }
290
+ }
291
+ }
292
+
293
+ // Start server if this file is run directly
294
+ if (require.main === module) {
295
+ const server = new WebSocketBrowserServer();
296
+
297
+ server.start().then((port) => {
298
+ // Output the port so the Python client can connect
299
+ console.log(`SERVER_READY:${port}`);
300
+ }).catch((error) => {
301
+ console.error('Failed to start server:', error);
302
+ process.exit(1);
303
+ });
304
+
305
+ // Handle graceful shutdown
306
+ process.on('SIGINT', async () => {
307
+ console.log('Received SIGINT, shutting down gracefully...');
308
+ await server.stop();
309
+ process.exit(0);
310
+ });
311
+
312
+ process.on('SIGTERM', async () => {
313
+ console.log('Received SIGTERM, shutting down gracefully...');
314
+ await server.stop();
315
+ process.exit(0);
316
+ });
317
+ }
318
+
319
+ module.exports = WebSocketBrowserServer;