@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,406 @@
1
+ """
2
+ Evol module entry point.
3
+ Reads boot_info from stdin, starts HTTP server for Evol account management.
4
+ """
5
+
6
+ import builtins
7
+ import json
8
+ import os
9
+ import re
10
+ import socket
11
+ import sys
12
+ import threading
13
+ import time
14
+ import traceback
15
+ from datetime import datetime, timezone
16
+
17
+ import uvicorn
18
+
19
+
20
+ # ── Module configuration ──
21
+
22
+ def _load_module_config() -> dict:
23
+ """Load module configuration from module.md frontmatter."""
24
+ _this_dir = os.path.dirname(os.path.abspath(__file__))
25
+ module_md = os.path.join(_this_dir, "module.md")
26
+
27
+ project_root = os.environ.get("KITE_PROJECT", "")
28
+ if project_root and _this_dir.startswith(project_root):
29
+ rel_path = os.path.relpath(_this_dir, project_root)
30
+ else:
31
+ rel_path = _this_dir
32
+
33
+ result = {
34
+ "name": "",
35
+ "preferred_port": 0,
36
+ "advertise_ip": "0.0.0.0"
37
+ }
38
+
39
+ if not os.path.exists(module_md):
40
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
41
+ print(f" Path: {rel_path}/module.md")
42
+ print(f" Reason: File not found")
43
+ sys.exit(1)
44
+
45
+ try:
46
+ with open(module_md, encoding="utf-8") as f:
47
+ text = f.read()
48
+
49
+ m = re.match(r'^---\s*\n(.*?)\n---', text, re.DOTALL)
50
+ if not m:
51
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
52
+ print(f" Path: {rel_path}/module.md")
53
+ print(f" Reason: Missing YAML frontmatter")
54
+ sys.exit(1)
55
+
56
+ import yaml
57
+ fm = yaml.safe_load(m.group(1)) or {}
58
+
59
+ if "name" not in fm:
60
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
61
+ print(f" Path: {rel_path}/module.md")
62
+ print(f" Reason: Missing 'name' field")
63
+ sys.exit(1)
64
+
65
+ raw_name = str(fm["name"]).strip()
66
+
67
+ if not raw_name:
68
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
69
+ print(f" Path: {rel_path}/module.md")
70
+ print(f" Reason: Empty module name")
71
+ sys.exit(1)
72
+
73
+ sanitized = re.sub(r'[^a-zA-Z0-9_\-]', '', raw_name)
74
+
75
+ if sanitized != raw_name:
76
+ invalid_chars = ''.join(sorted(set(c for c in raw_name if c not in sanitized)))
77
+ print(f"[{rel_path}] ERROR: Invalid module configuration in module.md")
78
+ print(f" Path: {rel_path}/module.md")
79
+ print(f" Reason: Invalid characters in name '{raw_name}': {repr(invalid_chars)}")
80
+ sys.exit(1)
81
+
82
+ result["name"] = sanitized
83
+
84
+ if "preferred_port" in fm:
85
+ try:
86
+ result["preferred_port"] = int(fm["preferred_port"])
87
+ except (ValueError, TypeError):
88
+ pass
89
+
90
+ if "advertise_ip" in fm:
91
+ result["advertise_ip"] = str(fm["advertise_ip"])
92
+
93
+ except SystemExit:
94
+ raise
95
+ except Exception as e:
96
+ print(f"[{rel_path}] ERROR: Failed to read module.md: {e}")
97
+ sys.exit(1)
98
+
99
+ return result
100
+
101
+ _module_config = _load_module_config()
102
+ MODULE_NAME = _module_config["name"]
103
+
104
+
105
+ # ── Safe stdout/stderr ──
106
+
107
+ class _SafeWriter:
108
+ def __init__(self, stream):
109
+ self._stream = stream
110
+
111
+ def write(self, s):
112
+ try:
113
+ self._stream.write(s)
114
+ except (BrokenPipeError, OSError):
115
+ pass
116
+
117
+ def flush(self):
118
+ try:
119
+ self._stream.flush()
120
+ except (BrokenPipeError, OSError):
121
+ pass
122
+
123
+ def __getattr__(self, name):
124
+ return getattr(self._stream, name)
125
+
126
+ sys.stdout = _SafeWriter(sys.stdout)
127
+ sys.stderr = _SafeWriter(sys.stderr)
128
+
129
+
130
+ # ── Timestamped print + log file writer ──
131
+
132
+ _builtin_print = builtins.print
133
+ _start_ts = time.monotonic()
134
+ _last_ts = time.monotonic()
135
+ _ANSI_RE = re.compile(r"\033\[[0-9;]*m")
136
+ _log_lock = threading.Lock()
137
+ _log_latest_path = None
138
+ _log_daily_path = None
139
+ _log_daily_date = ""
140
+ _log_dir = None
141
+ _crash_log_path = None
142
+
143
+ def _strip_ansi(s: str) -> str:
144
+ return _ANSI_RE.sub("", s)
145
+
146
+ def _resolve_daily_log_path():
147
+ """Resolve daily log path based on current date."""
148
+ global _log_daily_path, _log_daily_date
149
+ if not _log_dir:
150
+ return
151
+ today = datetime.now().strftime("%Y-%m-%d")
152
+ if today == _log_daily_date and _log_daily_path:
153
+ return
154
+ month_dir = os.path.join(_log_dir, today[:7])
155
+ os.makedirs(month_dir, exist_ok=True)
156
+ _log_daily_path = os.path.join(month_dir, f"{today}.log")
157
+ _log_daily_date = today
158
+
159
+ def _write_log(plain_line: str):
160
+ """Write a plain-text line to both latest.log and daily log."""
161
+ with _log_lock:
162
+ if _log_latest_path:
163
+ try:
164
+ with open(_log_latest_path, "a", encoding="utf-8") as f:
165
+ f.write(plain_line)
166
+ except Exception:
167
+ pass
168
+ _resolve_daily_log_path()
169
+ if _log_daily_path:
170
+ try:
171
+ with open(_log_daily_path, "a", encoding="utf-8") as f:
172
+ f.write(plain_line)
173
+ except Exception:
174
+ pass
175
+
176
+ def _write_crash(exc_type, exc_value, exc_tb, thread_name=None, severity="critical", handled=False):
177
+ """Write crash record to crashes.jsonl + daily crash archive."""
178
+ record = {
179
+ "timestamp": datetime.now(timezone.utc).isoformat(),
180
+ "module": MODULE_NAME,
181
+ "thread": thread_name or threading.current_thread().name,
182
+ "exception_type": exc_type.__name__ if exc_type else "Unknown",
183
+ "exception_message": str(exc_value),
184
+ "traceback": "".join(traceback.format_exception(exc_type, exc_value, exc_tb)),
185
+ "severity": severity,
186
+ "handled": handled,
187
+ "process_id": os.getpid(),
188
+ "platform": sys.platform,
189
+ "runtime_version": f"Python {sys.version.split()[0]}",
190
+ }
191
+
192
+ if exc_tb:
193
+ tb_entries = traceback.extract_tb(exc_tb)
194
+ if tb_entries:
195
+ last = tb_entries[-1]
196
+ record["context"] = {
197
+ "function": last.name,
198
+ "file": os.path.basename(last.filename),
199
+ "line": last.lineno,
200
+ }
201
+
202
+ line = json.dumps(record, ensure_ascii=False) + "\n"
203
+
204
+ if _crash_log_path:
205
+ try:
206
+ with open(_crash_log_path, "a", encoding="utf-8") as f:
207
+ f.write(line)
208
+ except Exception:
209
+ pass
210
+
211
+ if _log_dir:
212
+ try:
213
+ today = datetime.now().strftime("%Y-%m-%d")
214
+ archive_dir = os.path.join(_log_dir, "crashes", today[:7])
215
+ os.makedirs(archive_dir, exist_ok=True)
216
+ archive_path = os.path.join(archive_dir, f"{today}.jsonl")
217
+ with open(archive_path, "a", encoding="utf-8") as f:
218
+ f.write(line)
219
+ except Exception:
220
+ pass
221
+
222
+ def _setup_exception_hooks():
223
+ """Set up global exception hooks."""
224
+ _orig_excepthook = sys.excepthook
225
+
226
+ def _excepthook(exc_type, exc_value, exc_tb):
227
+ _write_crash(exc_type, exc_value, exc_tb, severity="critical", handled=False)
228
+ _orig_excepthook(exc_type, exc_value, exc_tb)
229
+
230
+ sys.excepthook = _excepthook
231
+
232
+ if hasattr(threading, "excepthook"):
233
+ def _thread_excepthook(args):
234
+ _write_crash(args.exc_type, args.exc_value, args.exc_traceback,
235
+ thread_name=args.thread.name if args.thread else "unknown",
236
+ severity="error", handled=False)
237
+ threading.excepthook = _thread_excepthook
238
+
239
+ def _tprint(*args, **kwargs):
240
+ global _last_ts
241
+ now = time.monotonic()
242
+ elapsed = now - _start_ts
243
+ delta = now - _last_ts
244
+ _last_ts = now
245
+
246
+ if elapsed < 1:
247
+ elapsed_str = f"{elapsed * 1000:.0f}ms"
248
+ elif elapsed < 100:
249
+ elapsed_str = f"{elapsed:.1f}s"
250
+ else:
251
+ elapsed_str = f"{elapsed:.0f}s"
252
+
253
+ if delta < 0.001:
254
+ delta_str = ""
255
+ elif delta < 1:
256
+ delta_str = f"+{delta * 1000:.0f}ms"
257
+ elif delta < 100:
258
+ delta_str = f"+{delta:.1f}s"
259
+ else:
260
+ delta_str = f"+{delta:.0f}s"
261
+
262
+ ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]
263
+
264
+ _builtin_print(*args, **kwargs)
265
+
266
+ if _log_latest_path or _log_daily_path:
267
+ sep = kwargs.get("sep", " ")
268
+ end = kwargs.get("end", "\n")
269
+ text = sep.join(str(a) for a in args)
270
+ prefix = f"[{elapsed_str:>6}] {ts} {delta_str:>8} [{MODULE_NAME}] "
271
+ _write_log(prefix + _strip_ansi(text) + end)
272
+
273
+ builtins.print = _tprint
274
+
275
+
276
+ # ── Path setup ──
277
+
278
+ _this_dir = os.path.dirname(os.path.abspath(__file__))
279
+ _project_root = os.environ.get("KITE_PROJECT") or os.path.dirname(os.path.dirname(os.path.dirname(_this_dir)))
280
+ if _project_root not in sys.path:
281
+ sys.path.insert(0, _project_root)
282
+
283
+
284
+ # ── Import server ──
285
+
286
+ from extensions.services.evol.server import EvolServer
287
+
288
+
289
+ def _fmt_elapsed(t0: float) -> str:
290
+ d = time.monotonic() - t0
291
+ if d < 1:
292
+ return f"{d * 1000:.0f}ms"
293
+ if d < 10:
294
+ return f"{d:.1f}s"
295
+ return f"{d:.0f}s"
296
+
297
+
298
+ def _bind_port(preferred: int, host: str, max_attempts: int = 10) -> int | None:
299
+ """Try to bind to preferred port, then port+1, port+2, ..."""
300
+ if not preferred:
301
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
302
+ s.bind((host, 0))
303
+ return s.getsockname()[1]
304
+
305
+ for attempt in range(max_attempts):
306
+ port = preferred + attempt
307
+ try:
308
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
309
+ s.bind((host, port))
310
+ if attempt > 0:
311
+ print(f"Bound to port {port} (preferred {preferred} was occupied)")
312
+ return port
313
+ except OSError:
314
+ if attempt < max_attempts - 1:
315
+ continue
316
+ else:
317
+ print(f"ERROR: Failed to bind port after {max_attempts} attempts")
318
+ return None
319
+
320
+ return None
321
+
322
+
323
+ def main():
324
+ # Initialize log file paths
325
+ global _log_dir, _log_latest_path, _crash_log_path
326
+ module_data = os.environ.get("KITE_MODULE_DATA")
327
+ if module_data:
328
+ _log_dir = os.path.join(module_data, "log")
329
+ os.makedirs(_log_dir, exist_ok=True)
330
+ suffix = os.environ.get("KITE_INSTANCE_SUFFIX", "")
331
+
332
+ _log_latest_path = os.path.join(_log_dir, f"latest{suffix}.log")
333
+ try:
334
+ with open(_log_latest_path, "w", encoding="utf-8") as f:
335
+ pass
336
+ except Exception:
337
+ _log_latest_path = None
338
+
339
+ _crash_log_path = os.path.join(_log_dir, f"crashes{suffix}.jsonl")
340
+ try:
341
+ with open(_crash_log_path, "w", encoding="utf-8") as f:
342
+ pass
343
+ except Exception:
344
+ _crash_log_path = None
345
+
346
+ _resolve_daily_log_path()
347
+
348
+ _setup_exception_hooks()
349
+
350
+ _t0 = time.monotonic()
351
+
352
+ # Read boot_info from stdin
353
+ token = ""
354
+ try:
355
+ line = sys.stdin.readline().strip()
356
+ if line:
357
+ boot_info = json.loads(line)
358
+ token = boot_info.get("token", "")
359
+ except Exception:
360
+ pass
361
+
362
+ kernel_port = int(os.environ.get("KITE_KERNEL_PORT", "0"))
363
+
364
+ if not token or not kernel_port:
365
+ print("ERROR: Missing token or KITE_KERNEL_PORT")
366
+ sys.exit(1)
367
+
368
+ print(f"Token received ({len(token)} chars), kernel port: {kernel_port} ({_fmt_elapsed(_t0)})")
369
+
370
+ host = _module_config["advertise_ip"]
371
+ port = _bind_port(_module_config["preferred_port"], host)
372
+
373
+ if port is None:
374
+ print("ERROR: Cannot bind to any port, exiting")
375
+ sys.exit(1)
376
+
377
+ server = EvolServer(
378
+ token=token,
379
+ kernel_port=kernel_port,
380
+ host=host,
381
+ port=port,
382
+ boot_t0=_t0,
383
+ )
384
+
385
+ display_host = "localhost" if host == "0.0.0.0" else host
386
+ url = f"http://{display_host}:{port}"
387
+ print(f"Starting on {host}:{port} ({_fmt_elapsed(_t0)})")
388
+ print(f"\033[32m✓ Evol UI ready: {url}\033[0m")
389
+
390
+ try:
391
+ config = uvicorn.Config(server.app, host=host, port=port, log_level="warning")
392
+ uvi_server = uvicorn.Server(config)
393
+ server._uvicorn_server = uvi_server
394
+ uvi_server.run()
395
+ except Exception as e:
396
+ print(f"ERROR: {e}")
397
+ traceback.print_exc()
398
+ sys.exit(1)
399
+
400
+ # Check if server requested exit with non-zero code
401
+ if server._exit_code != 0:
402
+ sys.exit(server._exit_code)
403
+
404
+
405
+ if __name__ == "__main__":
406
+ main()
@@ -0,0 +1,173 @@
1
+ """
2
+ Evol API 调用封装
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import httpx
8
+ from typing import Optional
9
+
10
+
11
+ class EvolAPI:
12
+ """Evol API 客户端"""
13
+
14
+ def __init__(self, base_url: str = "https://api.evolai.cn:8550"):
15
+ self.base_url = base_url
16
+ self.timeout = 30.0
17
+
18
+ def _log_request(self, method: str, url: str, **kwargs):
19
+ """记录请求日志"""
20
+ log_data = {
21
+ "method": method,
22
+ "url": url,
23
+ }
24
+ if "json" in kwargs:
25
+ # 隐藏敏感信息
26
+ data = kwargs["json"].copy()
27
+ if "code" in data:
28
+ data["code"] = "***"
29
+ log_data["body"] = data
30
+ if "headers" in kwargs:
31
+ headers = kwargs["headers"].copy()
32
+ if "Authorization" in headers:
33
+ headers["Authorization"] = "Bearer ***"
34
+ log_data["headers"] = headers
35
+
36
+ print(f"[evol-api] → {method} {url}")
37
+ print(f"[evol-api] Request: {json.dumps(log_data, ensure_ascii=False)}")
38
+
39
+ def _log_response(self, url: str, status_code: int, response_data: dict):
40
+ """记录响应日志"""
41
+ # 隐藏敏感信息
42
+ safe_data = response_data.copy() if isinstance(response_data, dict) else {}
43
+ if "data" in safe_data and isinstance(safe_data["data"], dict):
44
+ data = safe_data["data"].copy()
45
+ if "token" in data:
46
+ data["token"] = data["token"][:20] + "..." if data["token"] else None
47
+ if "apiKey" in data:
48
+ data["apiKey"] = data["apiKey"][:10] + "..." if data["apiKey"] else None
49
+ safe_data["data"] = data
50
+
51
+ print(f"[evol-api] ← {status_code} {url}")
52
+ print(f"[evol-api] Response: {json.dumps(safe_data, ensure_ascii=False)}")
53
+
54
+ def _log_error(self, url: str, error: Exception):
55
+ """记录错误日志"""
56
+ print(f"[evol-api] ✗ Error {url}: {type(error).__name__}: {str(error)}")
57
+
58
+ async def send_sms(self, phone: str) -> dict:
59
+ """发送短信验证码"""
60
+ url = f"{self.base_url}/api/auth/sms/send"
61
+ data = {
62
+ "phone": phone,
63
+ "codeType": "LOGIN"
64
+ }
65
+
66
+ self._log_request("POST", url, json=data)
67
+
68
+ try:
69
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
70
+ response = await client.post(url, json=data)
71
+ result = response.json()
72
+
73
+ self._log_response(url, response.status_code, result)
74
+
75
+ if result.get("code") == 200:
76
+ return {"success": True, "msg": result.get("msg", "验证码发送成功")}
77
+ else:
78
+ return {"success": False, "msg": result.get("msg", "发送失败")}
79
+
80
+ except Exception as e:
81
+ self._log_error(url, e)
82
+ return {"success": False, "msg": f"网络错误: {str(e)}"}
83
+
84
+ async def verify_sms(self, phone: str, code: str) -> dict:
85
+ """验证短信并登录"""
86
+ url = f"{self.base_url}/api/auth/sms/login"
87
+ data = {
88
+ "phone": phone,
89
+ "code": code,
90
+ "source": "WEB"
91
+ }
92
+
93
+ self._log_request("POST", url, json=data)
94
+
95
+ try:
96
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
97
+ response = await client.post(url, json=data)
98
+ result = response.json()
99
+
100
+ self._log_response(url, response.status_code, result)
101
+
102
+ if result.get("code") == 200:
103
+ return {
104
+ "success": True,
105
+ "data": result.get("data", {})
106
+ }
107
+ else:
108
+ return {"success": False, "msg": result.get("msg", "验证失败")}
109
+
110
+ except Exception as e:
111
+ self._log_error(url, e)
112
+ return {"success": False, "msg": f"网络错误: {str(e)}"}
113
+
114
+ async def get_user_info(self, token: str) -> dict:
115
+ """获取用户信息(带指数退避重试)"""
116
+ url = f"{self.base_url}/api/auth/sms/userInfo"
117
+ headers = {
118
+ "Authorization": f"Bearer {token}"
119
+ }
120
+
121
+ max_retries = 6
122
+ base_delay = 0.5 # 第一次重试间隔 0.5 秒
123
+
124
+ for attempt in range(max_retries):
125
+ self._log_request("GET", url, headers=headers)
126
+
127
+ try:
128
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
129
+ response = await client.get(url, headers=headers)
130
+ result = response.json()
131
+
132
+ self._log_response(url, response.status_code, result)
133
+
134
+ if result.get("code") == 200:
135
+ data = result.get("data", {})
136
+ # API Key 在 gatewayInfo 中
137
+ gateway_info = data.get("gatewayInfo", {})
138
+ api_key = gateway_info.get("apiKey", "")
139
+
140
+ if api_key:
141
+ # 有 API Key,直接返回
142
+ return {
143
+ "success": True,
144
+ "data": data
145
+ }
146
+ else:
147
+ # 没有 API Key,重试几次后也返回(不阻塞登录)
148
+ if attempt < 3: # 只重试 3 次
149
+ delay = base_delay * (2 ** attempt)
150
+ print(f"[evol-api] API Key not ready, retry {attempt + 1}/3 after {delay}s")
151
+ await asyncio.sleep(delay)
152
+ continue
153
+ else:
154
+ # 3 次后放弃等待,返回没有 API Key 的数据
155
+ print(f"[evol-api] API Key not available after 3 retries, proceeding without it")
156
+ return {
157
+ "success": True,
158
+ "data": data
159
+ }
160
+ else:
161
+ return {"success": False, "msg": result.get("msg", "获取失败")}
162
+
163
+ except Exception as e:
164
+ self._log_error(url, e)
165
+ if attempt < max_retries - 1:
166
+ delay = base_delay * (2 ** attempt)
167
+ print(f"[evol-api] Network error, retry {attempt + 1}/{max_retries} after {delay}s: {e}")
168
+ await asyncio.sleep(delay)
169
+ continue
170
+ else:
171
+ return {"success": False, "msg": f"网络错误: {str(e)}"}
172
+
173
+ return {"success": False, "msg": "获取用户信息失败,已达最大重试次数"}
@@ -0,0 +1,29 @@
1
+ {
2
+ // Web 服务配置
3
+ server: {
4
+ host: "0.0.0.0",
5
+ port: 18766,
6
+ ssl: false,
7
+ // SSL 证书路径(如果启用 SSL)
8
+ ssl_cert: null,
9
+ ssl_key: null
10
+ },
11
+
12
+ // 静态文件配置
13
+ static: {
14
+ directory: "static",
15
+ cache_max_age: 3600 // 秒
16
+ },
17
+
18
+ // CORS 配置
19
+ cors: {
20
+ enabled: false,
21
+ origins: ["*"]
22
+ },
23
+
24
+ // 日志配置
25
+ logging: {
26
+ level: "INFO",
27
+ format: "%(asctime)s [%(name)s] %(levelname)s: %(message)s"
28
+ }
29
+ }