@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
@@ -23,7 +23,113 @@ import websockets
23
23
 
24
24
 
25
25
  # ── Module configuration ──
26
- MODULE_NAME = "model_service"
26
+
27
+ def _load_module_config() -> dict:
28
+ """Load module configuration from module.md frontmatter.
29
+
30
+ Returns:
31
+ Dict with keys: name, preferred_port, advertise_ip
32
+
33
+ Raises:
34
+ SystemExit: If module.md is invalid or name is non-compliant
35
+ """
36
+ _this_dir = os.path.dirname(os.path.abspath(__file__))
37
+ module_md = os.path.join(_this_dir, "module.md")
38
+
39
+ # Calculate relative path for error messages
40
+ project_root = os.environ.get("KITE_PROJECT", "")
41
+ if project_root and _this_dir.startswith(project_root):
42
+ rel_path = os.path.relpath(_this_dir, project_root)
43
+ else:
44
+ rel_path = _this_dir
45
+
46
+ # Default values (will be overridden if valid config exists)
47
+ result = {
48
+ "name": "",
49
+ "preferred_port": 0,
50
+ "advertise_ip": "0.0.0.0"
51
+ }
52
+
53
+ # Check if module.md exists
54
+ if not os.path.exists(module_md):
55
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
56
+ print(f" Path: {rel_path}/module.md")
57
+ print(f" Reason: File not found")
58
+ sys.exit(1)
59
+
60
+ try:
61
+ with open(module_md, encoding="utf-8") as f:
62
+ text = f.read()
63
+
64
+ # Extract YAML frontmatter (between --- markers)
65
+ import re
66
+ m = re.match(r'^---\s*\n(.*?)\n---', text, re.DOTALL)
67
+ if not m:
68
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
69
+ print(f" Path: {rel_path}/module.md")
70
+ print(f" Reason: Missing YAML frontmatter")
71
+ sys.exit(1)
72
+
73
+ # Parse YAML frontmatter
74
+ try:
75
+ import yaml
76
+ fm = yaml.safe_load(m.group(1)) or {}
77
+ except ImportError:
78
+ print(f"[{rel_path}] ERROR: PyYAML not installed, cannot parse module.md")
79
+ sys.exit(1)
80
+ except Exception as e:
81
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
82
+ print(f" Path: {rel_path}/module.md")
83
+ print(f" Reason: YAML parse error: {e}")
84
+ sys.exit(1)
85
+
86
+ # Validate 'name' field (required)
87
+ if "name" not in fm:
88
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
89
+ print(f" Path: {rel_path}/module.md")
90
+ print(f" Reason: Missing 'name' field")
91
+ sys.exit(1)
92
+
93
+ raw_name = str(fm["name"]).strip()
94
+
95
+ if not raw_name:
96
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
97
+ print(f" Path: {rel_path}/module.md")
98
+ print(f" Reason: Empty module name")
99
+ sys.exit(1)
100
+
101
+ # Validate name characters
102
+ sanitized = re.sub(r'[^a-zA-Z0-9_\-]', '', raw_name)
103
+
104
+ if sanitized != raw_name:
105
+ invalid_chars = ''.join(sorted(set(c for c in raw_name if c not in sanitized)))
106
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
107
+ print(f" Path: {rel_path}/module.md")
108
+ print(f" Reason: Invalid characters in name '{raw_name}': {repr(invalid_chars)}")
109
+ sys.exit(1)
110
+
111
+ result["name"] = sanitized
112
+
113
+ # Extract optional fields
114
+ if "preferred_port" in fm:
115
+ try:
116
+ result["preferred_port"] = int(fm["preferred_port"])
117
+ except (ValueError, TypeError):
118
+ pass
119
+
120
+ if "advertise_ip" in fm:
121
+ result["advertise_ip"] = str(fm["advertise_ip"])
122
+
123
+ except SystemExit:
124
+ raise # Re-raise exit to prevent catching by outer except
125
+ except Exception as e:
126
+ print(f"[{rel_path}] ERROR: Failed to read module.md: {e}")
127
+ sys.exit(1)
128
+
129
+ return result
130
+
131
+ _module_config = _load_module_config()
132
+ MODULE_NAME = _module_config["name"]
27
133
 
28
134
 
29
135
  class _SafeWriter:
@@ -264,6 +370,7 @@ def _read_stdin_kite_message(expected_type: str, timeout: float = 10) -> dict |
264
370
  # Global WS reference for publish_event callback
265
371
  _ws_global = None
266
372
  _shutting_down = False
373
+ _exit_code = 0 # Exit code for main() to use
267
374
 
268
375
 
269
376
  def _is_auth_failure(e: Exception) -> bool:
@@ -333,7 +440,7 @@ async def main():
333
440
 
334
441
  async def _ws_loop(token: str, kernel_port: int, _t0: float):
335
442
  """Connect to Kernel with exponential backoff reconnection."""
336
- global _shutting_down
443
+ global _shutting_down, _exit_code
337
444
  retry_delay = 0.3
338
445
  max_delay = 5.0
339
446
  max_retries = 10
@@ -349,10 +456,14 @@ async def _ws_loop(token: str, kernel_port: int, _t0: float):
349
456
  attempt += 1
350
457
  if _is_auth_failure(e):
351
458
  print(f"[model_service] Kernel 认证失败,退出")
352
- sys.exit(1)
459
+ _exit_code = 1
460
+ _shutting_down = True
461
+ return
353
462
  if attempt >= max_retries:
354
463
  print(f"[model_service] 重连失败 {max_retries} 次,退出")
355
- sys.exit(1)
464
+ _exit_code = 1
465
+ _shutting_down = True
466
+ return
356
467
  _write_crash(type(e), e, e.__traceback__, severity="error", handled=True)
357
468
  print(f"[model_service] 连接错误: {e}, {retry_delay:.1f}s 后重试 ({attempt}/{max_retries})")
358
469
  _ws_global_clear()
@@ -374,7 +485,7 @@ async def _ws_connect(token: str, kernel_port: int, _t0: float):
374
485
  ws_url = f"ws://127.0.0.1:{kernel_port}/ws?token={token}&id=model_service"
375
486
  print(f"[model_service] Connecting to Kernel: {ws_url}")
376
487
 
377
- async with websockets.connect(ws_url, open_timeout=5, ping_interval=None, ping_timeout=None, close_timeout=10) as ws:
488
+ async with websockets.connect(ws_url, open_timeout=5, ping_interval=None, close_timeout=10) as ws:
378
489
  _ws_global = ws
379
490
  print(f"[model_service] Connected to Kernel ({_fmt_elapsed(_t0)})")
380
491
 
@@ -393,7 +504,9 @@ async def _ws_connect(token: str, kernel_port: int, _t0: float):
393
504
  "module_id": "model_service",
394
505
  "module_type": "service",
395
506
  "events_publish": {
396
- "model_service.test": {"description": "Test event from model_service module"},
507
+ "model_service": {
508
+ "test": {"description": "Test event from model_service module"},
509
+ }
397
510
  },
398
511
  "events_subscribe": [
399
512
  "module.started",
@@ -405,12 +518,14 @@ async def _ws_connect(token: str, kernel_port: int, _t0: float):
405
518
 
406
519
  # Publish module.ready (every reconnect)
407
520
  if not _shutting_down:
521
+ startup_time = time.monotonic() - _t0
408
522
  await _rpc_call(ws, "event.publish", {
409
523
  "event_id": str(uuid.uuid4()),
410
524
  "event": "module.ready",
411
525
  "data": {
412
526
  "module_id": "model_service",
413
527
  "graceful_shutdown": True,
528
+ "startup_time": startup_time,
414
529
  },
415
530
  })
416
531
  print(f"[model_service] module.ready published ({_fmt_elapsed(_t0)})")
@@ -419,6 +534,11 @@ async def _ws_connect(token: str, kernel_port: int, _t0: float):
419
534
  test_task = asyncio.create_task(_test_event_loop(ws))
420
535
 
421
536
  # Message loop: handle incoming RPC + events
537
+ # CRITICAL: RPC 死锁防范
538
+ # - 入站 RPC 请求必须用 create_task() 异步执行,不可 await
539
+ # - 原因:如果 handler 内部调用 rpc_call() 发出站请求,出站响应需要本接收循环来分发
540
+ # - 如果接收循环被 await handler 阻塞,出站响应永远收不到 → 超时死锁
541
+ # - 事件通知和 RPC 响应可以同步处理(它们不会反向调用 rpc_call)
422
542
  async for raw in ws:
423
543
  try:
424
544
  msg = json.loads(raw)
@@ -433,8 +553,8 @@ async def _ws_connect(token: str, kernel_port: int, _t0: float):
433
553
  # Event Notification
434
554
  await _handle_event_notification(msg)
435
555
  elif has_method and has_id:
436
- # Incoming RPC request
437
- await _handle_rpc_request(ws, msg)
556
+ # Incoming RPC request — run in background to prevent deadlock
557
+ asyncio.create_task(_handle_rpc_request(ws, msg))
438
558
  # Ignore RPC responses (we don't await them in this simple impl)
439
559
  except Exception as e:
440
560
  print(f"[model_service] 消息处理异常(已忽略): {e}")
@@ -457,12 +577,32 @@ async def _publish_event(ws, event: dict):
457
577
  })
458
578
 
459
579
 
580
+ async def _handle_ping_event(data: dict):
581
+ """Handle system.ping event and reply with system.pong."""
582
+ t1 = data.get("ping_time")
583
+ t2 = time.time()
584
+
585
+ await _publish_event(_ws_global, {
586
+ "event": "system.pong",
587
+ "data": {
588
+ "module_id": MODULE_NAME,
589
+ "ping_time": t1,
590
+ "pong_time": t2,
591
+ },
592
+ })
593
+
594
+
460
595
  async def _handle_event_notification(msg: dict):
461
596
  """Handle an event notification (JSON-RPC 2.0 Notification with method='event')."""
462
597
  params = msg.get("params", {})
463
598
  event_type = params.get("event", "")
464
599
  data = params.get("data", {})
465
600
 
601
+ # Handle system.ping event
602
+ if event_type == "system.ping":
603
+ await _handle_ping_event(data)
604
+ return
605
+
466
606
  # Special handling for module.shutdown
467
607
  if event_type == "module.shutdown":
468
608
  target = data.get("module_id", "")
@@ -523,29 +663,37 @@ async def _rpc_status() -> dict:
523
663
 
524
664
 
525
665
  async def _handle_shutdown():
526
- """Handle module.shutdown event — exitingack → cleanup → ready → exit."""
666
+ """Handle module.shutdown event — ackexiting → cleanup → ready → exit."""
527
667
  global _shutting_down
528
668
  print("[model_service] Received shutdown request")
529
669
  _shutting_down = True
530
- # Step 0: Send module.exiting
670
+ # Step 1: Send ack (立即确认收到)
531
671
  await _publish_event(_ws_global, {
532
- "event": "module.exiting",
533
- "data": {"module_id": "model_service", "action": "none"},
672
+ "event": "module.shutdown.ack",
673
+ "data": {"module_id": "model_service"},
534
674
  })
535
- # Step 1: Send ack
675
+ # Step 2: Send module.exiting (开始清理)
536
676
  await _publish_event(_ws_global, {
537
- "event": "module.shutdown.ack",
538
- "data": {"module_id": "model_service", "estimated_cleanup": 2},
677
+ "event": "module.exiting",
678
+ "data": {
679
+ "module_id": "model_service",
680
+ "type": "passive",
681
+ "reason": "shutdown_requested",
682
+ "restart": "auto",
683
+ "action": "none",
684
+ "timeout": 2.0,
685
+ "restart_delay": 0.0,
686
+ },
539
687
  })
540
- # Step 2: Cleanup (nothing to clean up for model_service)
541
- # Step 3: Send ready
688
+ # Step 3: Cleanup (nothing to clean up for model_service)
689
+ # Step 4: Send ready (清理完成)
542
690
  await _publish_event(_ws_global, {
543
691
  "event": "module.shutdown.ready",
544
692
  "data": {"module_id": "model_service"},
545
693
  })
546
694
  print("[model_service] Shutdown ready, exiting")
547
- # Step 4: Exit
548
- sys.exit(0)
695
+ # Step 5: Exit
696
+ sys.exit(_exit_code)
549
697
 
550
698
 
551
699
  async def _test_event_loop(ws):
@@ -1,22 +1,21 @@
1
- ---
2
- name: model_service
3
- display_name: Model Service
4
- version: "1.0"
5
- type: service
6
- state: enabled
7
- runtime: python
8
- entry: entry.py
9
- events:
10
- - model_service.test
11
- subscriptions:
12
- - module.started
13
- - module.stopped
14
- - module.shutdown
15
- ---
16
-
17
- # Model Service(大模型服务)
18
-
19
- 大模型服务模块,提供统一的 LLM 调用接口。
20
-
21
- - 模型调用封装多种大模型 API 的统一调用接口
22
- - 事件通知 — 通过 Event Hub 发布模型服务状态事件
1
+ ---
2
+ name: model_service
3
+ display_name: Model Service
4
+ version: '1.0'
5
+ type: service
6
+ state: manual
7
+ runtime: python
8
+ entry: entry.py
9
+ events:
10
+ - model_service.test
11
+ subscriptions:
12
+ - module.started
13
+ - module.stopped
14
+ - module.shutdown
15
+ ---
16
+ # Model Service(大模型服务)
17
+
18
+ 大模型服务模块,提供统一的 LLM 调用接口。
19
+
20
+ - 模型调用 — 封装多种大模型 API 的统一调用接口
21
+ - 事件通知通过 Event Hub 发布模型服务状态事件
@@ -0,0 +1,13 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(echo agentcp not found directly:*)",
5
+ "Bash(grep:*)",
6
+ "Bash(head:*)",
7
+ "Bash(find H:\\\\project\\\\evol_main\\\\evol-electron-app\\\\python_sample_backend:*)",
8
+ "Bash(python3:*)",
9
+ "Bash(python:*)",
10
+ "Bash(cd H:\\\\project\\\\evol_main\\\\evol-electron-app\\\\python_sample_backend:*)"
11
+ ]
12
+ }
13
+ }
@@ -0,0 +1,258 @@
1
+ # Evol Proxy 服务修改说明
2
+
3
+ ## 修改日期
4
+ 2026-03-08
5
+
6
+ ## 修改内容
7
+
8
+ ### 1. 端口改为系统自动分配
9
+
10
+ **修改文件:**
11
+ - `main.py`
12
+ - `server.py`
13
+
14
+ **改动说明:**
15
+
16
+ 原来使用固定端口 12655,容易导致端口冲突。现在改为:
17
+ - 使用 `port=0` 让系统自动分配可用端口
18
+ - 在启动前先获取一个可用端口
19
+ - 启动后显示实际使用的端口号
20
+
21
+ **实现方式:**
22
+
23
+ ```python
24
+ # 获取系统分配的可用端口
25
+ import socket
26
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
27
+ sock.bind(('', 0))
28
+ actual_port = sock.getsockname()[1]
29
+ sock.close()
30
+
31
+ # 使用该端口启动服务器
32
+ await run_server(port=actual_port)
33
+ ```
34
+
35
+ ### 2. 登录后自动拉取模型列表和用户信息
36
+
37
+ **修改文件:**
38
+ - `main.py`
39
+
40
+ **新增功能:**
41
+
42
+ 在 AID 上线成功后,自动执行以下操作:
43
+
44
+ 1. **拉取模型列表**
45
+ - 调用 `get_models_list()` 获取可用模型
46
+ - 显示模型数量和前 5 个模型名称
47
+ - 示例输出:
48
+ ```
49
+ [Models] Found 15 available models:
50
+ - gpt-4
51
+ - gpt-3.5-turbo
52
+ - claude-3-opus
53
+ - claude-3-sonnet
54
+ - gemini-pro
55
+ ... and 10 more
56
+ ```
57
+
58
+ 2. **获取用户信息**
59
+ - 调用 `userPresenter.get_user_info()` 获取用户信息
60
+ - 显示手机号和用户 ID
61
+ - 示例输出:
62
+ ```
63
+ [User] Logged in as: 138****1234
64
+ [User] User ID: user_abc123
65
+ ```
66
+
67
+ **代码位置:**
68
+
69
+ 在 `async_main()` 函数中,Step 3.6 部分:
70
+
71
+ ```python
72
+ # Step 3.6: 拉取模型列表和用户信息
73
+ print("\n[Info] Fetching models and user info...")
74
+ try:
75
+ # 拉取模型列表
76
+ from evol.server.openclaw_proxy import get_models_list
77
+ models_data = get_models_list()
78
+ model_count = len(models_data.get("data", []))
79
+ print(f"[Models] Found {model_count} available models:")
80
+ for model in models_data.get("data", [])[:5]:
81
+ print(f" - {model.get('id', 'unknown')}")
82
+ if model_count > 5:
83
+ print(f" ... and {model_count - 5} more")
84
+
85
+ # 获取用户信息
86
+ from evol.presenter.userPresenter import userPresenter
87
+ user_info = userPresenter.get_user_info()
88
+ if user_info:
89
+ print(f"[User] Logged in as: {user_info.get('phone', 'unknown')}")
90
+ print(f"[User] User ID: {user_info.get('id', 'unknown')}")
91
+
92
+ except Exception as e:
93
+ print(f"[Info] Warning: 获取信息失败: {e}")
94
+ ```
95
+
96
+ ## 启动流程变化
97
+
98
+ ### 修改前
99
+
100
+ ```
101
+ 1. 初始化配置目录
102
+ 2. 检查本地 token
103
+ 3. 若无有效 token → 控制台登录
104
+ 4. 同步用户信息 & AID 上线
105
+ 5. 获取 Evol 本地密钥
106
+ 6. 启动 FastAPI 服务器(固定端口 12655)
107
+ ```
108
+
109
+ ### 修改后
110
+
111
+ ```
112
+ 1. 初始化配置目录
113
+ 2. 检查本地 token
114
+ 3. 若无有效 token → 控制台登录
115
+ 4. 同步用户信息 & AID 上线
116
+ 5. 获取 Evol 本地密钥
117
+ 6. 拉取模型列表和用户信息 ← 新增
118
+ 7. 启动 FastAPI 服务器(系统自动分配端口)← 修改
119
+ ```
120
+
121
+ ## 输出示例
122
+
123
+ ### 修改前
124
+
125
+ ```
126
+ ============================================================
127
+ Evol Sample Backend
128
+ Port: 12655
129
+ ============================================================
130
+
131
+ [Init] Config dir: C:\Users\xxx\.evol
132
+ [Auth] Token valid, skip login
133
+ [AID] Starting AID login...
134
+ Evol: aid_xxx is online
135
+ [Key] Evol API Key: evol-xxx
136
+
137
+ [Server] Starting on port 12655...
138
+ [Server] ANTHROPIC_BASE_URL = http://127.0.0.1:12655/claude-proxy
139
+ [Server] OPENAI_BASE_URL = http://127.0.0.1:12655/openclaw-proxy/v1
140
+ [Server] Health check = http://127.0.0.1:12655/health
141
+ [Server] Evol API Key = evol-xxx
142
+
143
+ Press Ctrl+C to stop.
144
+ ```
145
+
146
+ ### 修改后
147
+
148
+ ```
149
+ ============================================================
150
+ Evol Sample Backend
151
+ Port: Auto-assigned by system
152
+ ============================================================
153
+
154
+ [Init] Config dir: C:\Users\xxx\.evol
155
+ [Auth] Token valid, skip login
156
+ [AID] Starting AID login...
157
+ Evol: aid_xxx is online
158
+ [Key] Evol API Key: evol-xxx
159
+
160
+ [Info] Fetching models and user info...
161
+ [Models] Found 15 available models:
162
+ - gpt-4
163
+ - gpt-3.5-turbo
164
+ - claude-3-opus
165
+ - claude-3-sonnet
166
+ - gemini-pro
167
+ ... and 10 more
168
+ [User] Logged in as: 138****1234
169
+ [User] User ID: user_abc123
170
+
171
+ [Server] Starting server...
172
+
173
+ [Server] Server will start on port 54321
174
+ [Server] ANTHROPIC_BASE_URL = http://127.0.0.1:54321/claude-proxy
175
+ [Server] OPENAI_BASE_URL = http://127.0.0.1:54321/openclaw-proxy/v1
176
+ [Server] Health check = http://127.0.0.1:54321/health
177
+ [Server] Evol API Key = evol-xxx
178
+
179
+ Press Ctrl+C to stop.
180
+ ```
181
+
182
+ ## 优点
183
+
184
+ ### 1. 端口自动分配
185
+
186
+ - **避免端口冲突** - 不会与其他服务冲突
187
+ - **支持多实例** - 可以同时运行多个代理服务
188
+ - **更灵活** - 适应不同的部署环境
189
+
190
+ ### 2. 自动拉取信息
191
+
192
+ - **验证登录状态** - 确认模型列表可以正常获取
193
+ - **显示可用模型** - 用户一目了然有哪些模型可用
194
+ - **确认用户身份** - 显示当前登录的用户信息
195
+ - **便于调试** - 启动时就能发现配置问题
196
+
197
+ ## 注意事项
198
+
199
+ 1. **端口号变化** - 每次启动端口可能不同,需要从启动日志中获取
200
+ 2. **防火墙规则** - 如果需要外部访问,需要动态配置防火墙
201
+ 3. **错误处理** - 如果拉取模型列表失败,只会显示警告,不会中断启动
202
+
203
+ ## 测试建议
204
+
205
+ 1. **基本功能测试**
206
+ ```bash
207
+ # 启动服务
208
+ python main.py
209
+
210
+ # 查看启动日志,获取实际端口(假设是 54321)
211
+ # 测试健康检查
212
+ curl http://127.0.0.1:54321/health
213
+
214
+ # 测试模型列表
215
+ curl http://127.0.0.1:54321/openclaw-proxy/v1/models \
216
+ -H "Authorization: Bearer evol-xxx"
217
+ ```
218
+
219
+ 2. **多实例测试**
220
+ ```bash
221
+ # 同时启动多个实例,验证端口不冲突
222
+ python main.py &
223
+ python main.py &
224
+ ```
225
+
226
+ 3. **端口冲突测试**
227
+ ```bash
228
+ # 验证不会出现端口占用错误
229
+ # 之前固定端口时可能出现的问题现在应该不会发生
230
+ ```
231
+
232
+ ## 回滚方案
233
+
234
+ 如果需要回滚到固定端口模式,修改 `main.py`:
235
+
236
+ ```python
237
+ # 改回固定端口
238
+ PORT = 12655 # 或其他固定端口
239
+
240
+ # 恢复端口清理逻辑
241
+ kill_port(PORT)
242
+
243
+ # 直接使用固定端口启动
244
+ await run_server(port=PORT)
245
+ ```
246
+
247
+ ## 相关文件
248
+
249
+ - `main.py` - 主程序入口
250
+ - `server.py` - FastAPI 服务器
251
+ - `console_auth.py` - 控制台认证
252
+ - `evol/server/openclaw_proxy.py` - OpenClaw 代理实现
253
+ - `evol/presenter/userPresenter.py` - 用户信息管理
254
+
255
+ ---
256
+
257
+ **修改者:** Claude (Kite Assistant)
258
+ **审核者:** 待审核