@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,257 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from typing import Union
16
+ import uuid
17
+ import time
18
+ import requests
19
+ from cryptography import x509
20
+ from cryptography.hazmat.primitives import serialization, hashes
21
+ from cryptography.hazmat.primitives.asymmetric import ec
22
+ from cryptography.x509.oid import NameOID
23
+ from agentcp.ca.ca_root import CARoot
24
+ from agentcp.base.log import log_error, log_exception, log_info, log_warning
25
+ from cryptography.hazmat.backends import default_backend
26
+ import os
27
+
28
+
29
+ class AuthClient:
30
+
31
+ success_crt_list = []
32
+
33
+ # HTTP 请求超时配置 (连接超时, 读取超时)
34
+ HTTP_TIMEOUT = (3, 10)
35
+
36
+ def __init__(self, agent_id: str, server_url: str, aid_path: str, seed_password: str):
37
+ """认证客户端类
38
+ Args:
39
+ agent_id: 代理ID
40
+ server_url: 服务器URL
41
+ """
42
+ self.agent_id = agent_id
43
+ self.server_url = server_url
44
+ self.signature = None
45
+ self.aid_path = aid_path
46
+ self.seed_password = seed_password
47
+
48
+ def sign_in(self, max_retry_num: int = 10) -> Union[dict, None]:
49
+ """登录方法,使用循环重试,失败返回 None"""
50
+ for retry_count in range(max_retry_num + 1):
51
+ try:
52
+ if retry_count > 0:
53
+ # 指数退避:2s, 4s, 6s, ..., 最大 30s
54
+ backoff = min(2 * retry_count, 30)
55
+ log_info(f"Sign in retry {retry_count}/{max_retry_num}, waiting {backoff}s...")
56
+ time.sleep(backoff)
57
+
58
+ hb_url = self.server_url + "/sign_in"
59
+ log_info(f"Sign in: {hb_url}")
60
+ request_id = uuid.uuid4().hex
61
+ data = {
62
+ "agent_id": self.agent_id,
63
+ "request_id": request_id,
64
+ }
65
+ headers = {
66
+ 'User-Agent': f'AgentCP/{__import__("agentcp").__version__} (AuthClient; {self.agent_id})'
67
+ }
68
+ response = requests.post(hb_url, json=data, verify=False, headers=headers, proxies={}, timeout=self.HTTP_TIMEOUT)
69
+
70
+ if response.status_code == 200:
71
+ log_info(f"Sign in url: {hb_url}, response: {response.json()}")
72
+ aid_path = os.path.join(self.aid_path, self.agent_id + ".key")
73
+ private_key = self.__load_private_key(aid_path)
74
+ if private_key is None:
75
+ raise Exception("私钥加载失败,请检查加密种子是否一致")
76
+
77
+ aid_path = os.path.join(self.aid_path, self.agent_id + ".crt")
78
+ with open(aid_path, "rb") as f:
79
+ certificate_pem = f.read().decode('utf-8')
80
+
81
+ cert = x509.load_pem_x509_certificate(certificate_pem.encode('utf-8'))
82
+ public_key = cert.public_key()
83
+ public_key_pem = public_key.public_bytes(
84
+ encoding=serialization.Encoding.PEM,
85
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
86
+ ).decode('utf-8')
87
+
88
+ if "nonce" in response.json():
89
+ nonce = response.json()["nonce"]
90
+ server_cert_valid = True
91
+
92
+ if "cert" in response.json() and "signature" in response.json():
93
+ server_cert_valid = False
94
+ try:
95
+ cert_data = response.json()["cert"].encode('utf-8')
96
+ server_cert = x509.load_pem_x509_certificate(cert_data, default_backend())
97
+ server_public_key = server_cert.public_key()
98
+ data_to_verify = (self.agent_id + str(request_id)).lower().encode('utf-8')
99
+ signature_bytes = bytes.fromhex(response.json()["signature"])
100
+ server_public_key.verify(
101
+ signature_bytes,
102
+ data_to_verify,
103
+ ec.ECDSA(hashes.SHA256())
104
+ )
105
+ log_info("服务器签名验证成功")
106
+ server_cert_valid = self.__check_server_cert(server_cert)
107
+ if not server_cert_valid:
108
+ raise Exception("服务器证书验证失败")
109
+ except Exception as e:
110
+ server_cert_valid = False
111
+ raise Exception(f"{e}")
112
+
113
+ if not server_cert_valid:
114
+ raise Exception("服务器证书是无效证书,请注意通信安全")
115
+
116
+ if nonce:
117
+ signature = private_key.sign(
118
+ nonce.encode('utf-8'),
119
+ ec.ECDSA(hashes.SHA256())
120
+ )
121
+ data = {
122
+ "agent_id": self.agent_id,
123
+ "request_id": request_id,
124
+ "nonce": nonce,
125
+ "public_key": public_key_pem,
126
+ "cert": certificate_pem,
127
+ "signature": signature.hex(),
128
+ }
129
+ response = requests.post(hb_url, json=data, verify=False, headers=headers, proxies={}, timeout=self.HTTP_TIMEOUT)
130
+ if response.status_code == 200:
131
+ result = response.json()
132
+ self.signature = result.get("signature")
133
+ log_info("Sign in successful")
134
+ return result
135
+ else:
136
+ log_error(f"Sign in FAILED: {response.status_code} - {response.json().get('error', '')}")
137
+ else:
138
+ log_error(f"Sign in failed: {response.status_code} - {response.json().get('error', '')}")
139
+
140
+ except Exception as e:
141
+ log_warning(f"Sign in exception (retry {retry_count}/{max_retry_num}): {e}")
142
+
143
+ # 所有重试都失败
144
+ log_error(f"Sign in failed after {max_retry_num} retries, please check network connection")
145
+ return None
146
+
147
+
148
+ def __load_private_key(self, aid_path):
149
+ try:
150
+ with open(aid_path, "rb") as f:
151
+ private_key = serialization.load_pem_private_key(
152
+ f.read(),
153
+ password=self.seed_password.encode('utf-8'),
154
+ )
155
+ return private_key
156
+ except Exception as e:
157
+ # 兼容性代码,按照不加密获取 private_key
158
+ return None
159
+
160
+ def sign_out(self) -> None:
161
+ """登出方法"""
162
+ try:
163
+ if self.signature is None:
164
+ return
165
+ hb_url = self.server_url + "/sign_out"
166
+ data = {
167
+ "agent_id": self.agent_id,
168
+ "signature": self.signature,
169
+ }
170
+ headers = {
171
+ 'User-Agent': f'AgentCP/{__import__("agentcp").__version__} (AuthClient; {self.agent_id})'
172
+ }
173
+ response = requests.post(hb_url, json=data, verify=False, headers=headers, proxies={}, timeout=self.HTTP_TIMEOUT)
174
+ if response.status_code == 200:
175
+ log_info(f"Sign out OK: {response.json()}")
176
+ else:
177
+ log_error(f"Sign out failed: {response.json()}")
178
+ except Exception as e:
179
+ log_exception("Sign out exception")
180
+
181
+ def __check_server_cert(self, server_cert):
182
+ try:
183
+ # 尝试获取主体的组织及 common name
184
+ try:
185
+ organization = server_cert.subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)
186
+ common_name = server_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
187
+ if organization:
188
+ log_info(f"__check_server_cert 证书的组织名称为: {organization[0].value}")
189
+ else:
190
+ log_info("__check_server_cert 未找到证书的组织名称")
191
+ if common_name:
192
+ log_info(f"__check_server_cert 证书的通用名称为: {common_name[0].value}")
193
+ else:
194
+ log_info("__check_server_cert 未找到证书的通用名称")
195
+ except AttributeError:
196
+ log_error("获取证书主体信息时出错,可能证书对象不存在或格式不正确。")
197
+ return False
198
+ aia_ext = server_cert.extensions.get_extension_for_class(x509.AuthorityInformationAccess)
199
+ log_info(f"aia_ext:{aia_ext}")
200
+ issuer_url = None
201
+ for desc in aia_ext.value:
202
+ if desc.access_method == x509.oid.AuthorityInformationAccessOID.CA_ISSUERS:
203
+ issuer_url = desc.access_location.value
204
+ break
205
+
206
+ if issuer_url:
207
+ log_info(f"证书颁发者 URL: {issuer_url}")
208
+ if issuer_url in AuthClient.success_crt_list:
209
+ log_info(f"证书之前验证成功 {issuer_url}")
210
+ return True
211
+ try:
212
+ issuer_response = requests.get(issuer_url, verify=False, proxies={}, timeout=self.HTTP_TIMEOUT)
213
+ issuer_response.raise_for_status()
214
+ #TODO:将证书内存以issuer_url为键值缓存在本地,避免重复下载和验证证书
215
+ issuer_cert = x509.load_pem_x509_certificate(issuer_response.content, default_backend())
216
+ # 验证服务器证书的有效性
217
+ issuer_public_key = issuer_cert.public_key()
218
+ issuer_public_key.verify(
219
+ server_cert.signature,
220
+ server_cert.tbs_certificate_bytes,
221
+ ec.ECDSA(server_cert.signature_hash_algorithm)
222
+ )
223
+ #TODO标记证书是受信任的
224
+ log_info(f"证书验证成功 {issuer_url}")
225
+ AuthClient.success_crt_list.append(issuer_url)
226
+ return self.__check_server_cert(issuer_cert)
227
+ except requests.RequestException as e:
228
+ log_error(f"下载证书颁发者证书时出错: {e}")
229
+ return False
230
+ except Exception as e:
231
+ log_error(f"验证证书时出错: {e}")
232
+ return False
233
+ else:
234
+ log_error("未找到证书颁发者信息")
235
+ return False
236
+ except x509.ExtensionNotFound:
237
+ log_error("证书中未包含 AIA 扩展信息")
238
+ try:
239
+ root_cert_pem = CARoot().get_ca_root_crt()
240
+ root_cert = x509.load_pem_x509_certificate(root_cert_pem.encode('utf-8'), default_backend())
241
+ root_public_key = root_cert.public_key()
242
+ root_public_key.verify(
243
+ server_cert.signature,
244
+ server_cert.tbs_certificate_bytes,
245
+ ec.ECDSA(server_cert.signature_hash_algorithm)
246
+ )
247
+ log_info("用root.crt 证书进行验证成功")
248
+ return True
249
+ except FileNotFoundError:
250
+ log_error("未找到 root.crt 文件,请检查文件路径。")
251
+ return False
252
+ except Exception as e:
253
+ log_error(f"加载 root.crt 证书时出错: {e}")
254
+ return False
255
+ except Exception as e:
256
+ log_error(f"服务器证书验证失败: {e}")
257
+ return False
@@ -0,0 +1,112 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import abc
16
+ import requests
17
+
18
+ from agentcp.base.log import log_error, log_info
19
+ from agentcp.utils.proxy_bypass import ensure_no_proxy_for_local_env, get_requests_proxies
20
+
21
+ ensure_no_proxy_for_local_env()
22
+
23
+
24
+ class IClient(abc.ABC):
25
+ def __init__(self):
26
+ self._agent_id_ref = None
27
+
28
+ def set_agent_id_ref(self, agent_id_ref):
29
+ """设置AgentID引用,用于获取代理配置"""
30
+ self._agent_id_ref = agent_id_ref
31
+
32
+ def _get_proxies(self, url: str):
33
+ """获取 requests 代理配置;agentcp 内部本地/远程一律直连。"""
34
+ return get_requests_proxies(False, url)
35
+
36
+ @abc.abstractmethod
37
+ def sign_in(self) -> bool:
38
+ pass
39
+
40
+ @abc.abstractmethod
41
+ def get_headers(self) -> dict:
42
+ pass
43
+
44
+ @abc.abstractmethod
45
+ def sign_out(self):
46
+ pass
47
+
48
+
49
+ def get_request(self,url: str, params: dict = None, headers: dict = None,is_retry=True) -> requests.Response:
50
+ """
51
+ 发送GET请求的通用方法
52
+ Args:
53
+ url (str): 请求的URL
54
+ params (dict, optional): 请求参数. Defaults to None.
55
+ headers (dict, optional): 请求头. Defaults to None.
56
+ Returns:
57
+ requests.Response: 响应对象
58
+ """
59
+ try:
60
+ common_headers = self.get_headers()
61
+ if headers:
62
+ common_headers = {**common_headers, **headers}
63
+ proxies = self._get_proxies(url)
64
+ response = requests.get(url, params=params, headers=common_headers, verify=False, proxies=proxies)
65
+ response.raise_for_status()
66
+ if response.status_code == 200:
67
+ return response
68
+ else:
69
+ error = response.json().get("error", "")
70
+ log_error(f"请求失败,错误详情: {error}")
71
+ if response.status_code == 401 and is_retry:
72
+ log_info("尝试重新登录...")
73
+ if self.sign_in():
74
+ return self.get_request(url, params=params, headers=common_headers, is_retry=False)
75
+ return response
76
+ except requests.exceptions.RequestException as e:
77
+ log_error(f"请求失败: {e}")
78
+ return None
79
+
80
+
81
+ def post_request(self, url: str, data: dict = None, json: dict = None, headers: dict = None, is_retry=True) -> requests.Response:
82
+ """
83
+ 发送POST请求的通用方法
84
+
85
+ Args:
86
+ url (str): 请求的URL
87
+ data (dict, optional): 表单数据. Defaults to None.
88
+ json (dict, optional): JSON数据. Defaults to None.
89
+ headers (dict, optional): 请求头. Defaults to None.
90
+ is_retry (bool, optional): 是否重试. Defaults to True.
91
+ Returns:
92
+ requests.Response: 响应对象
93
+ """
94
+ try:
95
+ common_headers = self.get_headers()
96
+ if headers:
97
+ common_headers = {**common_headers, **headers}
98
+ proxies = self._get_proxies(url)
99
+ response = requests.post(url, data=data, json=json, headers=common_headers, verify=False, proxies=proxies)
100
+ if response.status_code == 200:
101
+ return response
102
+ else:
103
+ error = response.json().get("error", "")
104
+ log_error(f"请求失败,错误详情: {error}")
105
+ if response.status_code == 401 and is_retry:
106
+ log_info("尝试重新登录...")
107
+ if self.sign_in():
108
+ return self.post_request(url, data=data, json=json, headers=common_headers, is_retry=False)
109
+ return response
110
+ except requests.exceptions.RequestException as e:
111
+ log_error(f"请求失败: {e}")
112
+ raise
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import enum
16
+ import os
17
+
18
+ class Environ(enum.Enum):
19
+ """
20
+ Environment for the agent.
21
+ """
22
+ # Define the environments
23
+ ENTRY_SERVER = "ENTRY_SERVER"
24
+ LOG_LEVEL = "LOG_LEVEL"
25
+ CA_SERVER = "CA_SERVER"
26
+
27
+ def __str__(self):
28
+ return self.value
29
+
30
+ def get(self, default=None):
31
+ """
32
+ Get the environment variable value.
33
+ """
34
+ return os.environ.get(self.value, default)