@agentunion/kite 1.3.2 → 1.5.0

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 (293) hide show
  1. package/CHANGELOG.md +302 -0
  2. package/cli.js +119 -4
  3. package/core/dependency_checker.py +250 -0
  4. package/core/env_checker.py +490 -0
  5. package/dependencies_lock.json +128 -0
  6. package/extensions/agents/assistant/entry.py +111 -1
  7. package/extensions/agents/assistant/server.py +279 -215
  8. package/extensions/channels/acp_channel/entry.py +111 -1
  9. package/extensions/channels/acp_channel/module.md +23 -22
  10. package/extensions/channels/acp_channel/server.py +279 -215
  11. package/extensions/event_hub_bench/entry.py +107 -1
  12. package/extensions/services/backup/entry.py +306 -21
  13. package/extensions/services/backup/module.md +24 -22
  14. package/extensions/services/evol/auth_manager.py +443 -0
  15. package/extensions/services/evol/config.yaml +149 -0
  16. package/extensions/services/evol/config_loader.py +117 -0
  17. package/extensions/services/evol/entry.py +406 -0
  18. package/extensions/services/evol/evol_api.py +173 -0
  19. package/extensions/services/evol/evol_config.json5 +29 -0
  20. package/extensions/services/evol/migrate_tokens.py +122 -0
  21. package/extensions/services/evol/module.md +32 -0
  22. package/extensions/services/evol/pairing.py +250 -0
  23. package/extensions/services/evol/pairing_codes.jsonl +1 -0
  24. package/extensions/services/evol/relay.py +682 -0
  25. package/extensions/services/evol/relay_config.json5 +67 -0
  26. package/extensions/services/evol/routes/__init__.py +1 -0
  27. package/extensions/services/evol/routes/routes_management_ws.py +127 -0
  28. package/extensions/services/evol/routes/routes_rpc.py +89 -0
  29. package/extensions/services/evol/routes/routes_test.py +61 -0
  30. package/extensions/services/evol/server.py +875 -0
  31. package/extensions/services/evol/static/css/style.css +1200 -0
  32. package/extensions/services/evol/static/index.html +781 -0
  33. package/extensions/services/evol/static/index_evol.html +14 -0
  34. package/extensions/services/evol/static/js/app.js +6304 -0
  35. package/extensions/services/evol/static/js/auth.js +326 -0
  36. package/extensions/services/evol/static/js/dialog.js +285 -0
  37. package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
  38. package/extensions/services/evol/static/js/evol-app.js +1949 -0
  39. package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
  40. package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
  41. package/extensions/services/evol/static/js/kernel-client.js +396 -0
  42. package/extensions/services/evol/static/js/main.js +141 -0
  43. package/extensions/services/evol/static/js/registry-tests.js +585 -0
  44. package/extensions/services/evol/static/js/stats.js +217 -0
  45. package/extensions/services/evol/static/js/token-manager.js +175 -0
  46. package/extensions/services/evol/static/pairing.html +248 -0
  47. package/extensions/services/evol/static/test_registry.html +262 -0
  48. package/extensions/services/evol/static/test_relay.html +462 -0
  49. package/extensions/services/evol/stats_manager.py +240 -0
  50. package/extensions/services/model_service/entry.py +167 -19
  51. package/extensions/services/model_service/module.md +21 -22
  52. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  53. package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
  54. package/extensions/services/proxy/_fix_prints.py +133 -0
  55. package/extensions/services/proxy/_fix_prints2.py +87 -0
  56. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  57. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  58. package/extensions/services/proxy/agentcp/README.md +260 -0
  59. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  60. package/extensions/services/proxy/agentcp/agent.py +4 -0
  61. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  62. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  63. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  64. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  65. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  66. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  67. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  68. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  69. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  70. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  71. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  72. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  73. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  74. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  75. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  76. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  77. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  78. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  79. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  80. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  81. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  82. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  83. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  84. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  85. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  86. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  87. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  88. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  89. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  90. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  91. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  92. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  93. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  94. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  95. package/extensions/services/proxy/agentcp/message.py +149 -0
  96. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  97. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  98. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  99. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  100. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  101. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  102. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  103. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  104. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  105. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  106. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  107. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  108. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  109. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  110. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  111. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  112. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  113. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  114. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  115. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  116. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  117. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  118. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  119. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  120. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  121. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  122. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  123. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  124. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  125. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  126. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  127. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  128. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  129. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  130. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  131. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  132. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  133. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  134. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  135. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  136. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  137. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  138. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  139. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  140. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  141. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  142. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  143. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  144. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  145. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  146. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  147. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  148. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  149. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  150. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  151. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  152. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  153. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  154. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  155. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  156. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  157. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  158. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  159. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  160. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  161. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  162. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  163. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  164. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  165. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  166. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  167. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  168. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  169. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  170. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  171. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  172. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  173. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  174. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  175. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  176. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  177. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  178. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  179. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  180. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  181. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  182. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  183. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  184. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  185. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  186. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  187. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  188. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  189. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  190. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  191. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  192. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  193. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  194. package/extensions/services/proxy/console_auth.py +109 -0
  195. package/extensions/services/proxy/evol/__init__.py +1 -0
  196. package/extensions/services/proxy/evol/config.py +37 -0
  197. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  198. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  199. package/extensions/services/proxy/evol/log.py +28 -0
  200. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  201. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  202. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
  203. package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
  204. package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
  205. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  206. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
  207. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  208. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  209. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  210. package/extensions/services/proxy/evol/version.py +24 -0
  211. package/extensions/services/proxy/logs/websocket.log +260 -0
  212. package/extensions/services/proxy/main.py +240 -0
  213. package/extensions/services/proxy/requirements.txt +13 -0
  214. package/extensions/services/proxy/server.py +271 -0
  215. package/extensions/services/watchdog/entry.py +215 -26
  216. package/extensions/services/watchdog/module.md +1 -0
  217. package/extensions/services/watchdog/monitor.py +178 -38
  218. package/extensions/services/web/WEBSOCKET_STATUS.md +143 -0
  219. package/extensions/services/web/config_example.py +35 -0
  220. package/extensions/services/web/config_loader.py +110 -0
  221. package/extensions/services/web/entry.py +114 -26
  222. package/extensions/services/web/module.md +35 -24
  223. package/extensions/services/web/pairing.py +250 -0
  224. package/extensions/services/web/pairing_codes.jsonl +16 -0
  225. package/extensions/services/web/relay.py +643 -0
  226. package/extensions/services/web/relay_config.json5 +67 -0
  227. package/extensions/services/web/routes/routes_management_ws.py +127 -0
  228. package/extensions/services/web/routes/routes_rpc.py +89 -0
  229. package/extensions/services/web/routes/routes_test.py +61 -0
  230. package/extensions/services/web/routes/schemas.py +0 -22
  231. package/extensions/services/web/server.py +434 -99
  232. package/extensions/services/web/static/css/style.css +67 -28
  233. package/extensions/services/web/static/index.html +234 -44
  234. package/extensions/services/web/static/js/app.js +1335 -48
  235. package/extensions/services/web/static/js/kernel-client-example.js +161 -0
  236. package/extensions/services/web/static/js/kernel-client.js +383 -0
  237. package/extensions/services/web/static/js/registry-tests.js +558 -0
  238. package/extensions/services/web/static/js/token-manager.js +175 -0
  239. package/extensions/services/web/static/pairing.html +248 -0
  240. package/extensions/services/web/static/test_registry.html +262 -0
  241. package/extensions/services/web/web_config.json5 +29 -0
  242. package/kernel/entry.py +120 -32
  243. package/kernel/event_hub.py +141 -16
  244. package/kernel/module.md +60 -33
  245. package/kernel/registry_store.py +45 -36
  246. package/kernel/rpc_router.py +152 -59
  247. package/kernel/server.py +322 -26
  248. package/kite_cli/__init__.py +3 -0
  249. package/kite_cli/__main__.py +5 -0
  250. package/kite_cli/commands/__init__.py +1 -0
  251. package/kite_cli/commands/clean.py +101 -0
  252. package/kite_cli/commands/deps_install.py +67 -0
  253. package/kite_cli/commands/doctor.py +35 -0
  254. package/kite_cli/commands/env_check.py +45 -0
  255. package/kite_cli/commands/history.py +111 -0
  256. package/kite_cli/commands/info.py +96 -0
  257. package/kite_cli/commands/install.py +313 -0
  258. package/kite_cli/commands/list.py +143 -0
  259. package/kite_cli/commands/log.py +81 -0
  260. package/kite_cli/commands/prepare.py +49 -0
  261. package/kite_cli/commands/rollback.py +88 -0
  262. package/kite_cli/commands/search.py +73 -0
  263. package/kite_cli/commands/uninstall.py +85 -0
  264. package/kite_cli/commands/update.py +118 -0
  265. package/kite_cli/commands/venv_setup.py +56 -0
  266. package/kite_cli/core/__init__.py +1 -0
  267. package/kite_cli/core/checker.py +142 -0
  268. package/kite_cli/core/dependency.py +229 -0
  269. package/kite_cli/core/downloader.py +209 -0
  270. package/kite_cli/core/install_info.py +40 -0
  271. package/kite_cli/core/tool_installer.py +397 -0
  272. package/kite_cli/core/validator.py +78 -0
  273. package/kite_cli/main.py +317 -0
  274. package/kite_cli/utils/__init__.py +1 -0
  275. package/kite_cli/utils/i18n.py +252 -0
  276. package/kite_cli/utils/interactive.py +63 -0
  277. package/kite_cli/utils/operation_log.py +77 -0
  278. package/kite_cli/utils/paths.py +34 -0
  279. package/kite_cli/utils/version.py +308 -0
  280. package/launcher/entry.py +1124 -178
  281. package/launcher/logging_setup.py +104 -0
  282. package/launcher/module.md +46 -37
  283. package/launcher/module_scanner.py +11 -1
  284. package/main.py +4 -1
  285. package/package.json +9 -1
  286. package/python_version.json +4 -0
  287. package/requirements.txt +38 -0
  288. package/scripts/env-manager.js +328 -0
  289. package/scripts/plan_manager.py +315 -0
  290. package/scripts/python-env.js +79 -0
  291. package/scripts/scan_dependencies.py +461 -0
  292. package/scripts/setup-python-env.js +191 -0
  293. package/extensions/services/web/routes/routes_modules.py +0 -249
@@ -0,0 +1,267 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import json
16
+ import asyncio
17
+ import queue
18
+ import ssl
19
+ import threading
20
+ import time
21
+ import urllib.parse
22
+ from typing import Optional
23
+
24
+ import websocket
25
+
26
+ from agentcp.base.log import log_debug, log_error, log_exception, log_info
27
+ from agentcp.msg.wss_binary_message import *
28
+ from agentcp.utils.proxy_bypass import ensure_no_proxy_for_local_env, without_proxy_env
29
+
30
+ from ..context import ErrorContext, exceptions
31
+
32
+ ensure_no_proxy_for_local_env()
33
+
34
+ @dataclass
35
+ class FileChunk:
36
+ offset: int
37
+ chunk_size: int
38
+ chunk: bytes
39
+
40
+ class StreamClient:
41
+ def __init__(self, agent_id: str, session_id, server_url: str, signature: str):
42
+ """消息客户端类
43
+ Args:
44
+ agent_id: 代理ID
45
+ server_url: 服务器URL
46
+ """
47
+ self.session_id = session_id
48
+ self.agent_id = agent_id
49
+ self.server_url = server_url
50
+ self.signature = signature
51
+ self.connected_event = threading.Event()
52
+ self.ws = None
53
+ self.ws_url = ""
54
+ self.ws_thread: Optional[threading.Thread] = None
55
+ self.ws_is_running = False
56
+ self.ws_chunks = ""
57
+ self.file_stream_chunk_queue = queue.Queue()
58
+ self.file_stream_push_cache_left_space = 65536
59
+
60
+ def set_message_handler(self, message_handler):
61
+ """设置消息处理器"""
62
+ self.message_handler = message_handler
63
+
64
+ async def start_websocket_client(self):
65
+ if self.connected_event.is_set():
66
+ return True
67
+
68
+ # 确保URL格式正确
69
+ ws_url = self.server_url.rstrip("/") # 移除末尾斜杠
70
+ ws_url = ws_url.replace("https://", "wss://").replace("http://", "ws://")
71
+ ws_url = ws_url + f"&agent_id={self.agent_id}"
72
+
73
+ log_debug(f"message Connecting to WebSocket URL: {ws_url}") # 调试日志
74
+ self.ws_url = ws_url
75
+ self.ws_thread = threading.Thread(target=self.__ws_handler, daemon=True)
76
+ self.ws_thread.start()
77
+ wait = 0
78
+ while not self.connected_event.is_set() and wait < 5:
79
+ log_debug("WebSocket client is reconnect...")
80
+ await asyncio.sleep(0.01) # 使用 asyncio.sleep 避免阻塞事件循环
81
+ wait += 0.01
82
+ return self.connected_event.is_set()
83
+
84
+ def send_wss_message(self, msg):
85
+ """发送WebSocket消息,如果连接断开则尝试重连"""
86
+ # 检查连接状态,如果断开则尝试重连
87
+ if not self.ws or not self.ws.sock or not self.ws.sock.connected:
88
+ try:
89
+ # 使用事件循环在另一个线程中运行异步重连
90
+ loop = asyncio.new_event_loop()
91
+ def reconnect_sync():
92
+ asyncio.set_event_loop(loop)
93
+ try:
94
+ loop.run_until_complete(self.start_websocket_client())
95
+ finally:
96
+ loop.close()
97
+
98
+ # 在新线程中执行重连
99
+ reconnect_thread = threading.Thread(target=reconnect_sync, daemon=True)
100
+ reconnect_thread.start()
101
+ reconnect_thread.join(timeout=5.0)
102
+
103
+ if not self.connected_event.is_set():
104
+ log_error("[StreamClient] WebSocket重连超时")
105
+ return False
106
+ except Exception as e:
107
+ log_error(f"[StreamClient] WebSocket重连失败: {e}")
108
+ return False
109
+
110
+ # 发送消息
111
+ self.__send_wss_message_p(msg)
112
+ return True
113
+
114
+ def __send_wss_message_p(self, msg):
115
+ if self.ws.sock and self.ws.sock.connected:
116
+ bytes_msg = encode_wss_binary_message(msg)
117
+ self.ws.send(bytes_msg, websocket.ABNF.OPCODE_BINARY)
118
+
119
+ def stop_websocket_client(self):
120
+ self.connected_event.clear() # 清除连接事件
121
+ if self.ws:
122
+ self.ws.close()
123
+ self.ws_thread.join()
124
+ self.ws = None
125
+ self.ws_thread = None
126
+
127
+ def send_msg(self, msg):
128
+ try:
129
+ self.ws.send(msg)
130
+ return True
131
+ except Exception as e:
132
+ log_exception(f"send message: {msg}")
133
+ trace_id = msg.get("trace_id", "") if isinstance(msg, dict) else ""
134
+ ErrorContext.publish(exceptions.SendMsgError(f"发送消息时发生错误: {e}", trace_id=trace_id))
135
+
136
+ def send_chunk_to_stream(self, chunk, type="text/event-stream"):
137
+
138
+ if self.ws is None or self.ws.sock is None or not self.ws.sock.connected:
139
+ log_error("WebSocket connection is not established @send_chunk_to_stream.")
140
+ self.ws_chunks += chunk
141
+ return False
142
+ try:
143
+ if type=="text/event-stream":
144
+ chunk_quote = urllib.parse.quote_plus(chunk)
145
+ data = {
146
+ "cmd": "push_text_stream_req",
147
+ "data": {
148
+ "chunk": f"{chunk_quote}",
149
+ },
150
+ }
151
+ msg = json.dumps(data)
152
+ self.send_msg(msg) # 发送消息到 WebSocket 服务器
153
+ return True
154
+ return False
155
+ except Exception as e:
156
+ import traceback
157
+ log_error(f"发送消息时发生错误: {str(e)}\n{traceback.format_exc()}")
158
+ ErrorContext.publish(exceptions.SendChunkToStreamError(f"发送消息时发生错误: {e}"))
159
+ return False
160
+
161
+ def send_chunk_to_file_stream(self, offset: int, chunk: bytes) -> bool:
162
+ try:
163
+ if self.ws is None or self.ws.sock is None or not self.ws.sock.connected:
164
+ file_chunk = FileChunk()
165
+ file_chunk.offset = offset
166
+ file_chunk.size = len(chunk)
167
+ file_chunk.chunk = chunk
168
+ self.file_stream_chunk_queue.put(file_chunk)
169
+ time.sleep(1)
170
+ return False
171
+ msg = WssBinaryMessage()
172
+ msg.magic_byte1 = ord('M')
173
+ msg.magic_byte2 = ord('U')
174
+ msg.version = 0x101
175
+ msg.flags = 0
176
+ msg.msg_type = 0x5
177
+ msg.msg_seq = 0
178
+ msg.content_type = 0x5
179
+ msg.compressed = 0
180
+ msg.reserved = offset
181
+ if self.ws and self.ws.sock and self.ws.sock.connected: # 检查WebSocket连接状态是否正常
182
+ bytes_msg = encode_wss_binary_buffer(chunk, msg)
183
+ self.ws.send(bytes_msg, websocket.ABNF.OPCODE_BINARY)
184
+ print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送文件数据: {offset} + {len(chunk)}")
185
+ self.file_stream_push_cache_left_space -= len(chunk)
186
+ return self.file_stream_push_cache_left_space >= 16384
187
+ except Exception as e:
188
+ print(f"发送文件数据时发生错误: {str(e)}")
189
+ return False
190
+
191
+ def __ws_handler(self):
192
+ """
193
+ WebSocket客户端定时发送消息函数
194
+ :param url: WebSocket服务器URL(ws://或wss://开头)
195
+ :param message: 要定时发送的消息内容
196
+ :param interval: 发送间隔时间(秒),默认5秒
197
+ """
198
+ try:
199
+
200
+ def on_message(ws, message):
201
+ """接收到服务器消息时的处理函数"""
202
+ if isinstance(message, bytes):
203
+ txt_msg = decode_wss_binary_message(message)
204
+ if txt_msg is not None and len(txt_msg) > 0:
205
+ log_info(f"Text_Stream收到消息收到二进制消息: {txt_msg}")
206
+ on_message(ws, txt_msg)
207
+ elif isinstance(message, str):
208
+ log_info(f"Text_Stream收到消息: {self.agent_id}\t{message}")
209
+
210
+ def on_error(ws, error):
211
+ """连接发生错误时的处理函数"""
212
+ log_error(f"Text_Stream连接错误: {error}")
213
+ self.connected_event.clear() # 清除连接事件,允许重连
214
+ ErrorContext.publish(exceptions.SDKError(f"websocket on_error: {error}"))
215
+
216
+ def on_close(ws, close_status_code, close_msg):
217
+ """连接关闭时的处理函数"""
218
+ log_info("Text_Stream WebSocket 连接已关闭")
219
+ self.connected_event.clear() # 清除连接事件,允许重连
220
+
221
+ def on_open(ws):
222
+ """连接建立后的处理函数,用于发送初始消息"""
223
+ log_info("Text_Stream WebSocket 连接已建立")
224
+ self.connected_event.set() # 设置连接事件
225
+ # if self.ws_chunks is not None and len(self.ws_chunks) > 0:
226
+ # self.send_chunk_to_stream(self.ws_chunks)
227
+ # self.ws_chunks = ""
228
+
229
+ # 创建 WebSocket 客户端实例
230
+ self.ws = websocket.WebSocketApp(
231
+ self.ws_url,
232
+ on_open=on_open,
233
+ on_message=on_message,
234
+ on_error=on_error,
235
+ on_close=on_close,
236
+ )
237
+ # 启动WebSocket连接(阻塞当前线程)
238
+ with without_proxy_env(enabled=True):
239
+ self.ws.run_forever(
240
+ sslopt={
241
+ "cert_reqs": ssl.CERT_NONE, # 禁用证书验证
242
+ "check_hostname": False, # 忽略主机名不匹配
243
+ },
244
+ )
245
+ except Exception as e:
246
+ log_exception(f"WebSocket连接失败: {e}")
247
+ ErrorContext.publish(exceptions.SDKError(f"WebSocket连接失败: {e}"))
248
+
249
+ def close_stream(self, stream_url: str):
250
+ if self.ws and self.ws.sock and self.ws.sock.connected: # 检查WebSocket连接状态是否正常
251
+ data = {
252
+ "cmd": "close_stream_req",
253
+ }
254
+ msg = json.dumps(data)
255
+ self.ws.send(msg)
256
+ # self.__send_wss_message(self.ws, msg) # 发送消息到 WebSocket 服务器
257
+ log_info(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息: {msg}")
258
+
259
+ if self.ws_is_running:
260
+ self.ws_is_running = False
261
+ self.ws.close()
262
+ self.ws = None
263
+
264
+ def __send_wss_message(self, msg):
265
+ if self.ws.sock and self.ws.sock.connected:
266
+ bytes_msg = encode_wss_binary_message(msg)
267
+ self.ws.send(bytes_msg, websocket.ABNF.OPCODE_BINARY)
@@ -0,0 +1,89 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import websocket
16
+ import os
17
+ import ssl
18
+ from agentcp.msg.wss_binary_message import decode_wss_binary_buffer
19
+ from agentcp.utils.proxy_bypass import ensure_no_proxy_for_local_env, without_proxy_env
20
+
21
+ ensure_no_proxy_for_local_env()
22
+ def download_file_via_websocket(wss_url, save_path):
23
+ """
24
+ 通过WebSocket下载文件并保存到指定路径
25
+ :param wss_url: WebSocket连接地址
26
+ :param file_extension: 文件扩展名
27
+ :param save_path: 文件保存路径
28
+ """
29
+
30
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
31
+ file_stream_pulled_file = open(save_path, "wb")
32
+ print(f"文件保存路径:{save_path}")
33
+ def on_message(ws, message):
34
+ try:
35
+ if isinstance(message, bytes):
36
+ bin_msg = decode_wss_binary_buffer(message)
37
+ if bin_msg is None:
38
+ return
39
+ if bin_msg.msg_type == 0x5 and bin_msg.content_type == 0x5:
40
+ print(f"收到文件流:offset={bin_msg.reserved}, size={bin_msg.payload_length}, crc32={bin_msg.crc32}")
41
+ if file_stream_pulled_file is not None:
42
+ file_stream_pulled_file.write(bin_msg.payload)
43
+ else:
44
+ txt_msg = bin_msg.payload.decode()
45
+ if txt_msg is not None and len(txt_msg) > 0:
46
+ print(f"File_Stream pull handler 收到消息收到二进制消息: {txt_msg}")
47
+ on_message(ws, txt_msg)
48
+ elif isinstance(message, str):
49
+ import json
50
+ js = json.loads(message)
51
+ if "cmd" not in js or "data" not in js:
52
+ return
53
+ cmd = js["cmd"]
54
+ data = js["data"]
55
+ if cmd == "close_file_stream_req":
56
+ if file_stream_pulled_file is not None:
57
+ file_stream_pulled_file.close()
58
+ file_stream_pulled_file = None
59
+ ws.close()
60
+
61
+ except Exception as e:
62
+ print(f"file_stream_pull_handler在处理收到的消息时发生异常 {str(e)}")
63
+
64
+ def on_error(ws, error):
65
+ print(f"发生错误: {error}")
66
+ ws.close() # 发生错误时主动关闭连接
67
+
68
+ def on_close(ws, close_status_code, close_msg):
69
+ print("连接已关闭")
70
+ ws = None # 确保引用被清除
71
+
72
+ def on_open(ws):
73
+ print("连接已建立,等待文件数据...")
74
+
75
+ # 创建WebSocket连接,禁用SSL验证
76
+ ws = websocket.WebSocketApp(wss_url,
77
+ on_open=on_open,
78
+ on_message=on_message,
79
+ on_error=on_error,
80
+ on_close=on_close)
81
+
82
+ # 运行时不验证SSL证书
83
+ with without_proxy_env(enabled=True):
84
+ ws.run_forever(
85
+ sslopt={
86
+ "cert_reqs": ssl.CERT_NONE, # 禁用证书验证
87
+ "check_hostname": False, # 忽略主机名不匹配
88
+ },
89
+ )