@agentunion/kite 1.4.0 → 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 (235) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/cli.js +44 -5
  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/server.py +33 -17
  7. package/extensions/channels/acp_channel/server.py +33 -17
  8. package/extensions/services/backup/entry.py +23 -16
  9. package/extensions/services/evol/auth_manager.py +443 -0
  10. package/extensions/services/evol/config.yaml +149 -0
  11. package/extensions/services/evol/config_loader.py +117 -0
  12. package/extensions/services/evol/entry.py +406 -0
  13. package/extensions/services/evol/evol_api.py +173 -0
  14. package/extensions/services/evol/evol_config.json5 +29 -0
  15. package/extensions/services/evol/migrate_tokens.py +122 -0
  16. package/extensions/services/evol/module.md +32 -0
  17. package/extensions/services/evol/pairing.py +250 -0
  18. package/extensions/services/evol/pairing_codes.jsonl +1 -0
  19. package/extensions/services/evol/relay.py +682 -0
  20. package/extensions/services/evol/relay_config.json5 +67 -0
  21. package/extensions/services/evol/routes/__init__.py +1 -0
  22. package/extensions/services/evol/routes/routes_management_ws.py +127 -0
  23. package/extensions/services/evol/routes/routes_rpc.py +89 -0
  24. package/extensions/services/evol/routes/routes_test.py +61 -0
  25. package/extensions/services/evol/server.py +875 -0
  26. package/extensions/services/evol/static/css/style.css +1200 -0
  27. package/extensions/services/evol/static/index.html +781 -0
  28. package/extensions/services/evol/static/index_evol.html +14 -0
  29. package/extensions/services/evol/static/js/app.js +6304 -0
  30. package/extensions/services/evol/static/js/auth.js +326 -0
  31. package/extensions/services/evol/static/js/dialog.js +285 -0
  32. package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
  33. package/extensions/services/evol/static/js/evol-app.js +1949 -0
  34. package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
  35. package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
  36. package/extensions/services/evol/static/js/kernel-client.js +396 -0
  37. package/extensions/services/evol/static/js/main.js +141 -0
  38. package/extensions/services/evol/static/js/registry-tests.js +585 -0
  39. package/extensions/services/evol/static/js/stats.js +217 -0
  40. package/extensions/services/evol/static/js/token-manager.js +175 -0
  41. package/extensions/services/evol/static/pairing.html +248 -0
  42. package/extensions/services/evol/static/test_registry.html +262 -0
  43. package/extensions/services/evol/static/test_relay.html +462 -0
  44. package/extensions/services/evol/stats_manager.py +240 -0
  45. package/extensions/services/model_service/entry.py +23 -1
  46. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  47. package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
  48. package/extensions/services/proxy/_fix_prints.py +133 -0
  49. package/extensions/services/proxy/_fix_prints2.py +87 -0
  50. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  51. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  52. package/extensions/services/proxy/agentcp/README.md +260 -0
  53. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  54. package/extensions/services/proxy/agentcp/agent.py +4 -0
  55. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  56. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  57. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  58. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  59. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  60. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  61. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  62. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  63. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  64. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  65. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  66. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  67. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  68. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  69. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  70. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  71. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  72. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  73. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  74. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  75. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  76. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  77. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  78. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  79. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  80. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  81. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  82. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  83. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  84. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  85. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  86. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  87. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  88. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  89. package/extensions/services/proxy/agentcp/message.py +149 -0
  90. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  91. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  92. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  93. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  94. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  95. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  96. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  97. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  98. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  99. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  100. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  101. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  102. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  103. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  104. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  105. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  106. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  107. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  108. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  109. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  110. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  111. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  112. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  113. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  114. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  115. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  116. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  117. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  118. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  119. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  120. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  121. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  122. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  123. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  124. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  125. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  126. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  127. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  128. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  129. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  130. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  131. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  132. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  133. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  134. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  135. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  136. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  137. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  138. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  139. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  140. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  141. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  142. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  143. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  144. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  145. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  146. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  147. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  148. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  149. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  150. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  151. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  152. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  153. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  154. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  155. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  156. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  157. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  158. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  159. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  160. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  161. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  162. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  163. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  164. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  165. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  166. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  167. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  168. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  169. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  170. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  171. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  172. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  173. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  174. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  175. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  176. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  177. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  178. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  179. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  180. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  181. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  182. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  183. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  184. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  185. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  186. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  187. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  188. package/extensions/services/proxy/console_auth.py +109 -0
  189. package/extensions/services/proxy/evol/__init__.py +1 -0
  190. package/extensions/services/proxy/evol/config.py +37 -0
  191. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  192. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  193. package/extensions/services/proxy/evol/log.py +28 -0
  194. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  195. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  196. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
  197. package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
  198. package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
  199. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  200. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
  201. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  202. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  203. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  204. package/extensions/services/proxy/evol/version.py +24 -0
  205. package/extensions/services/proxy/logs/websocket.log +260 -0
  206. package/extensions/services/proxy/main.py +240 -0
  207. package/extensions/services/proxy/requirements.txt +13 -0
  208. package/extensions/services/proxy/server.py +271 -0
  209. package/extensions/services/watchdog/entry.py +42 -16
  210. package/extensions/services/watchdog/module.md +1 -0
  211. package/extensions/services/watchdog/monitor.py +34 -4
  212. package/extensions/services/web/module.md +1 -1
  213. package/extensions/services/web/server.py +30 -18
  214. package/extensions/services/web/static/js/token-manager.js +10 -10
  215. package/kernel/entry.py +1 -1
  216. package/kernel/module.md +25 -1
  217. package/kernel/registry_store.py +2 -26
  218. package/kernel/rpc_router.py +36 -10
  219. package/kernel/server.py +106 -17
  220. package/kite_cli/commands/deps_install.py +67 -0
  221. package/kite_cli/commands/env_check.py +45 -0
  222. package/kite_cli/commands/prepare.py +49 -0
  223. package/kite_cli/commands/venv_setup.py +56 -0
  224. package/kite_cli/main.py +29 -1
  225. package/launcher/entry.py +306 -21
  226. package/launcher/module.md +9 -0
  227. package/launcher/module_scanner.py +11 -1
  228. package/main.py +4 -1
  229. package/package.json +8 -1
  230. package/python_version.json +4 -0
  231. package/requirements.txt +38 -0
  232. package/scripts/env-manager.js +328 -0
  233. package/scripts/python-env.js +79 -0
  234. package/scripts/scan_dependencies.py +461 -0
  235. package/scripts/setup-python-env.js +191 -0
@@ -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
+ }
@@ -0,0 +1,122 @@
1
+ """
2
+ 迁移工具:将旧的 token 文件转换为 JSONL 格式
3
+
4
+ 使用方法:
5
+ python migrate_tokens.py
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import sys
11
+
12
+
13
+ def migrate_kite_tokens(data_dir: str):
14
+ """迁移 Kite Token 文件格式"""
15
+ auth_dir = os.path.join(data_dir, "auth")
16
+ old_file = os.path.join(auth_dir, "kite_tokens.json")
17
+ new_file = os.path.join(auth_dir, "kite_tokens.jsonl")
18
+ backup_file = os.path.join(auth_dir, "kite_tokens.json.backup")
19
+
20
+ if not os.path.exists(old_file):
21
+ print(f"Kite tokens 旧文件不存在: {old_file}")
22
+ return False
23
+
24
+ if os.path.exists(new_file):
25
+ print(f"Kite tokens 新文件已存在: {new_file}")
26
+ return False
27
+
28
+ try:
29
+ with open(old_file, "r", encoding="utf-8") as f:
30
+ data = json.load(f)
31
+ tokens = data.get("tokens", [])
32
+ except Exception as e:
33
+ print(f"读取 Kite tokens 旧文件失败: {e}")
34
+ return False
35
+
36
+ if not tokens:
37
+ print("Kite tokens 旧文件中没有记录")
38
+ return False
39
+
40
+ print(f"找到 {len(tokens)} 个 Kite Token 记录")
41
+
42
+ try:
43
+ with open(new_file, "w", encoding="utf-8") as f:
44
+ for token in tokens:
45
+ token["action"] = "migrated"
46
+ token["timestamp"] = token.get("createdAt", 0)
47
+ f.write(json.dumps(token, ensure_ascii=False) + "\n")
48
+ print(f"成功写入 Kite tokens 新文件: {new_file}")
49
+ except Exception as e:
50
+ print(f"写入 Kite tokens 新文件失败: {e}")
51
+ return False
52
+
53
+ try:
54
+ os.rename(old_file, backup_file)
55
+ print(f"Kite tokens 旧文件已备份到: {backup_file}")
56
+ except Exception as e:
57
+ print(f"备份 Kite tokens 旧文件失败: {e}")
58
+
59
+ return True
60
+
61
+
62
+ def migrate_evol_token(data_dir: str):
63
+ """迁移 Evol Token 文件格式"""
64
+ auth_dir = os.path.join(data_dir, "auth")
65
+ old_file = os.path.join(auth_dir, "evol_token.json")
66
+ new_file = os.path.join(auth_dir, "evol_tokens.jsonl")
67
+ backup_file = os.path.join(auth_dir, "evol_token.json.backup")
68
+
69
+ if not os.path.exists(old_file):
70
+ print(f"Evol token 旧文件不存在: {old_file}")
71
+ return False
72
+
73
+ if os.path.exists(new_file):
74
+ print(f"Evol token 新文件已存在: {new_file}")
75
+ return False
76
+
77
+ try:
78
+ with open(old_file, "r", encoding="utf-8") as f:
79
+ record = json.load(f)
80
+ except Exception as e:
81
+ print(f"读取 Evol token 旧文件失败: {e}")
82
+ return False
83
+
84
+ print(f"找到 Evol Token 记录: {record.get('phone', 'unknown')}")
85
+
86
+ try:
87
+ record["action"] = "migrated"
88
+ record["timestamp"] = record.get("obtainedAt", 0)
89
+ record["lastUsedAt"] = record.get("obtainedAt", 0)
90
+
91
+ with open(new_file, "w", encoding="utf-8") as f:
92
+ f.write(json.dumps(record, ensure_ascii=False) + "\n")
93
+ print(f"成功写入 Evol token 新文件: {new_file}")
94
+ except Exception as e:
95
+ print(f"写入 Evol token 新文件失败: {e}")
96
+ return False
97
+
98
+ try:
99
+ os.rename(old_file, backup_file)
100
+ print(f"Evol token 旧文件已备份到: {backup_file}")
101
+ except Exception as e:
102
+ print(f"备份 Evol token 旧文件失败: {e}")
103
+
104
+ return True
105
+
106
+
107
+ if __name__ == "__main__":
108
+ data_dir = os.environ.get("KITE_DATA", os.path.expanduser("~/.kite/data"))
109
+ evol_data_dir = os.path.join(data_dir, "evol")
110
+
111
+ print(f"数据目录: {evol_data_dir}")
112
+ print()
113
+
114
+ kite_ok = migrate_kite_tokens(evol_data_dir)
115
+ print()
116
+ evol_ok = migrate_evol_token(evol_data_dir)
117
+ print()
118
+
119
+ if kite_ok or evol_ok:
120
+ print("迁移完成!")
121
+ else:
122
+ print("没有需要迁移的文件")
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: evol
3
+ display_name: Evol Account Management
4
+ version: '1.0'
5
+ type: service
6
+ state: enabled
7
+ runtime: python
8
+ entry: entry.py
9
+ preferred_port: 18766
10
+ advertise_ip: 0.0.0.0
11
+ display_order: 90
12
+ businesses:
13
+ - name: relay_service
14
+ type: relay
15
+ description: Kernel WebSocket relay service for web clients
16
+ config_file: relay_config.json5
17
+ ---
18
+
19
+ # Evol 模块
20
+
21
+ Evol AI 平台账户管理模块。
22
+
23
+ ## 功能
24
+
25
+ - 手机号短信登录
26
+ - 用户信息查看
27
+ - 积分监控与统计
28
+ - 设备管理
29
+
30
+ ## 端口
31
+
32
+ 默认端口:18766
@@ -0,0 +1,250 @@
1
+ """
2
+ 配对码管理模块
3
+
4
+ 负责配对码的生成、验证、使用和 frontend_token 的管理。
5
+
6
+ 零共享代码依赖 - 此文件可以独立拷贝到其他模块使用。
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import secrets
12
+ import string
13
+ import time
14
+ from datetime import datetime, timezone
15
+ from typing import Optional
16
+
17
+
18
+ class PairingManager:
19
+ """配对码管理器"""
20
+
21
+ def __init__(self, pairing_file: str, code_length: int = 6, token_expiry: int = 2592000):
22
+ """
23
+ 初始化配对码管理器。
24
+
25
+ Args:
26
+ pairing_file: 配对码文件路径(JSONL 格式)
27
+ code_length: 配对码长度(默认 6)
28
+ token_expiry: frontend_token 有效期(秒,默认 30 天)
29
+ """
30
+ self.pairing_file = pairing_file
31
+ self.code_length = code_length
32
+ self.token_expiry = token_expiry
33
+
34
+ # 临时配对码(未持久化)
35
+ self.pending_codes = {} # {code: {"role": "admin", "created_at": timestamp}}
36
+
37
+ # 确保文件存在
38
+ if not os.path.exists(pairing_file):
39
+ os.makedirs(os.path.dirname(pairing_file), exist_ok=True)
40
+
41
+ def generate_pairing_code(self, role: str = "admin") -> str:
42
+ """
43
+ 生成临时配对码(不持久化)。
44
+
45
+ Args:
46
+ role: 用户角色
47
+
48
+ Returns:
49
+ 配对码
50
+ """
51
+ code = self._generate_code()
52
+ self.pending_codes[code] = {
53
+ "role": role,
54
+ "created_at": time.time()
55
+ }
56
+
57
+ # 清理过期的配对码(超过 5 分钟)
58
+ self._cleanup_expired_codes()
59
+
60
+ return code
61
+
62
+ def _cleanup_expired_codes(self):
63
+ """清理过期的临时配对码"""
64
+ now = time.time()
65
+ expired = [
66
+ code for code, info in self.pending_codes.items()
67
+ if now - info["created_at"] > 300 # 5 分钟
68
+ ]
69
+ for code in expired:
70
+ del self.pending_codes[code]
71
+
72
+ def _ensure_active_code(self):
73
+ """确保有 active 状态的配对码,如果没有则生成(已废弃)"""
74
+ pass
75
+
76
+ def _print_pairing_code(self, code: str, is_new: bool):
77
+ """在控制台绿色高亮显示配对码(已废弃,改为通过事件发送给 Launcher)"""
78
+ pass
79
+
80
+ def _create_initial_code(self):
81
+ """创建初始配对码(已废弃,由 _ensure_active_code 替代)"""
82
+ pass
83
+
84
+ def _generate_code(self) -> str:
85
+ """生成随机配对码"""
86
+ chars = string.ascii_uppercase + string.digits
87
+ return ''.join(secrets.choice(chars) for _ in range(self.code_length))
88
+
89
+ def _generate_token(self) -> str:
90
+ """生成 frontend_token"""
91
+ return "tok_" + secrets.token_urlsafe(32)
92
+
93
+ def _read_codes(self) -> list[dict]:
94
+ """读取所有配对码记录"""
95
+ if not os.path.exists(self.pairing_file):
96
+ return []
97
+
98
+ codes = []
99
+ with open(self.pairing_file, "r", encoding="utf-8") as f:
100
+ for line in f:
101
+ line = line.strip()
102
+ if line:
103
+ try:
104
+ codes.append(json.loads(line))
105
+ except json.JSONDecodeError:
106
+ pass
107
+ return codes
108
+
109
+ def _write_code(self, code_record: dict):
110
+ """追加配对码记录"""
111
+ with open(self.pairing_file, "a", encoding="utf-8") as f:
112
+ f.write(json.dumps(code_record, ensure_ascii=False) + "\n")
113
+
114
+ def verify_code(self, code: str) -> Optional[dict]:
115
+ """
116
+ 验证配对码(包括临时配对码和持久化配对码)。
117
+
118
+ Args:
119
+ code: 配对码
120
+
121
+ Returns:
122
+ 如果有效,返回配对码信息(包含 role);否则返回 None
123
+ """
124
+ # 先检查临时配对码
125
+ if code in self.pending_codes:
126
+ info = self.pending_codes[code]
127
+ # 检查是否过期(5 分钟)
128
+ if time.time() - info["created_at"] < 300:
129
+ return {"code": code, "role": info["role"], "status": "pending"}
130
+
131
+ # 再检查持久化配对码(向后兼容)
132
+ codes = self._read_codes()
133
+ for record in reversed(codes):
134
+ if record.get("code") == code and record.get("status") == "active":
135
+ return record
136
+
137
+ return None
138
+
139
+ def pair(self, code: str) -> Optional[dict]:
140
+ """
141
+ 使用配对码进行配对。
142
+
143
+ Args:
144
+ code: 配对码
145
+
146
+ Returns:
147
+ 如果成功,返回 {"token": "...", "role": "..."};否则返回 None
148
+ """
149
+ # 验证配对码
150
+ code_info = self.verify_code(code)
151
+ if not code_info:
152
+ return None
153
+
154
+ # 生成 frontend_token
155
+ token = self._generate_token()
156
+ role = code_info.get("role", "viewer")
157
+
158
+ # 持久化保存 token
159
+ used_record = {
160
+ "code": code,
161
+ "role": role,
162
+ "status": "used",
163
+ "token": token,
164
+ "paired_at": datetime.now(timezone.utc).isoformat(),
165
+ "expires_at": datetime.fromtimestamp(
166
+ time.time() + self.token_expiry, tz=timezone.utc
167
+ ).isoformat()
168
+ }
169
+ self._write_code(used_record)
170
+
171
+ # 从临时配对码中删除
172
+ if code in self.pending_codes:
173
+ del self.pending_codes[code]
174
+
175
+ return {
176
+ "token": token,
177
+ "role": role,
178
+ "expires_at": used_record["expires_at"]
179
+ }
180
+
181
+ def verify_token(self, token: str) -> Optional[dict]:
182
+ """
183
+ 验证 frontend_token。
184
+
185
+ Args:
186
+ token: frontend_token
187
+
188
+ Returns:
189
+ 如果有效,返回 {"role": "...", "expires_at": "..."};否则返回 None
190
+ """
191
+ codes = self._read_codes()
192
+
193
+ # 检查 token 是否被吊销
194
+ for record in reversed(codes):
195
+ if record.get("token") == token and record.get("status") == "revoked":
196
+ return None # Token 已被吊销
197
+
198
+ # 查找 token 对应的记录
199
+ for record in reversed(codes):
200
+ if record.get("token") == token and record.get("status") == "used":
201
+ # 检查是否过期
202
+ expires_at = record.get("expires_at")
203
+ if expires_at:
204
+ expire_time = datetime.fromisoformat(expires_at)
205
+ if datetime.now(timezone.utc) < expire_time:
206
+ return {
207
+ "role": record.get("role", "viewer"),
208
+ "expires_at": expires_at
209
+ }
210
+
211
+ return None
212
+
213
+ def renew_token(self, old_token: str) -> Optional[str]:
214
+ """
215
+ 续期 frontend_token。
216
+
217
+ Args:
218
+ old_token: 旧的 frontend_token
219
+
220
+ Returns:
221
+ 如果成功,返回新的 token;否则返回 None
222
+ """
223
+ # 验证旧 token
224
+ token_info = self.verify_token(old_token)
225
+ if not token_info:
226
+ return None
227
+
228
+ # 生成新 token
229
+ new_token = self._generate_token()
230
+ role = token_info["role"]
231
+
232
+ # 写入新 token 记录
233
+ new_record = {
234
+ "token": new_token,
235
+ "role": role,
236
+ "status": "used",
237
+ "renewed_from": old_token,
238
+ "paired_at": datetime.now(timezone.utc).isoformat(),
239
+ "expires_at": datetime.fromtimestamp(
240
+ time.time() + self.token_expiry, tz=timezone.utc
241
+ ).isoformat()
242
+ }
243
+ self._write_code(new_record)
244
+
245
+ return new_token
246
+
247
+ def get_active_codes(self) -> list[dict]:
248
+ """获取所有 active 状态的配对码"""
249
+ codes = self._read_codes()
250
+ return [c for c in codes if c.get("status") == "active"]
@@ -0,0 +1 @@
1
+ {"code": "EAJU5I", "role": "admin", "status": "used", "token": "tok_eO59UiqTxQZvUXKPMkLzV293iwQR2EfU806I_4wjuqc", "paired_at": "2026-03-08T13:25:35.375089+00:00", "expires_at": "2026-04-07T13:25:35.375099+00:00"}