@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,175 @@
1
+ /**
2
+ * Token Management
3
+ * Handles token listing, revocation, and statistics via WebSocket RPC
4
+ */
5
+
6
+ // Load tokens on page load
7
+ async function loadTokens() {
8
+ console.log('[web] loadTokens() called');
9
+
10
+ // 立即显示加载中
11
+ const tbody = document.getElementById('tokens-tbody');
12
+ tbody.innerHTML = `
13
+ <tr><td colspan="6" class="text-muted" style="text-align:center;padding:40px;">
14
+ 加载中...
15
+ </td></tr>
16
+ `;
17
+
18
+ try {
19
+ // Use kernel client to call RPC
20
+ if (!kernelClient || !kernelClient.connected) {
21
+ console.error('[web] kernelClient not ready:', { kernelClient, connected: kernelClient?.connected });
22
+ throw new Error('WebSocket 未连接');
23
+ }
24
+
25
+ console.log('[web] Calling web.list_tokens...');
26
+ const result = await kernelClient.call('web.list_tokens', {});
27
+ console.log('[web] Got result:', result);
28
+ renderTokens(result.tokens || []);
29
+ } catch (err) {
30
+ console.error('[web] Failed to load tokens:', err);
31
+
32
+ // 根据错误类型显示不同的提示
33
+ let errorMsg = err.message;
34
+ if (errorMsg.includes('Permission denied')) {
35
+ errorMsg = '权限不足:当前用户没有查看 Token 的权限';
36
+ } else if (errorMsg.includes('not connected') || errorMsg.includes('未连接')) {
37
+ errorMsg = 'WebSocket 未连接,请刷新页面重试';
38
+ }
39
+
40
+ tbody.innerHTML = `
41
+ <tr><td colspan="6" class="text-danger" style="text-align:center;padding:40px;">
42
+ 加载失败: ${errorMsg}
43
+ </td></tr>
44
+ `;
45
+ }
46
+ }
47
+
48
+ // Render tokens table
49
+ function renderTokens(tokens) {
50
+ const tbody = document.getElementById('tokens-tbody');
51
+
52
+ if (tokens.length === 0) {
53
+ tbody.innerHTML = `
54
+ <tr><td colspan="6" class="text-muted" style="text-align:center;padding:40px;">
55
+ 暂无 Token
56
+ </td></tr>
57
+ `;
58
+ return;
59
+ }
60
+
61
+ tbody.innerHTML = tokens.map(token => {
62
+ const isExpired = new Date(token.expires_at) < new Date();
63
+ const statusClass = isExpired ? 'text-danger' : 'text-success';
64
+ const statusText = isExpired ? '已过期' : '活跃';
65
+
66
+ // Mask token for security (show first 8 and last 4 chars)
67
+ const maskedToken = token.token.length > 12
68
+ ? `${token.token.substring(0, 8)}...${token.token.substring(token.token.length - 4)}`
69
+ : token.token;
70
+
71
+ return `
72
+ <tr>
73
+ <td style="font-family:monospace;font-size:12px;" title="${token.token}">${maskedToken}</td>
74
+ <td><span class="badge badge-${getRoleBadgeClass(token.role)}">${token.role}</span></td>
75
+ <td style="font-size:12px;">${formatDateTime(token.paired_at)}</td>
76
+ <td style="font-size:12px;">${formatDateTime(token.expires_at)}</td>
77
+ <td><span class="${statusClass}">${statusText}</span></td>
78
+ <td>
79
+ <button class="btn btn-sm btn-danger" onclick="revokeToken('${token.token}')" ${isExpired ? 'disabled' : ''}>
80
+ 吊销
81
+ </button>
82
+ </td>
83
+ </tr>
84
+ `;
85
+ }).join('');
86
+ }
87
+
88
+ // Get role badge class
89
+ function getRoleBadgeClass(role) {
90
+ const classes = {
91
+ 'admin': 'primary',
92
+ 'operator': 'warning',
93
+ 'viewer': 'secondary'
94
+ };
95
+ return classes[role] || 'secondary';
96
+ }
97
+
98
+ // Format datetime
99
+ function formatDateTime(isoString) {
100
+ if (!isoString) return '-';
101
+ const date = new Date(isoString);
102
+ return date.toLocaleString('zh-CN', {
103
+ year: 'numeric',
104
+ month: '2-digit',
105
+ day: '2-digit',
106
+ hour: '2-digit',
107
+ minute: '2-digit'
108
+ });
109
+ }
110
+
111
+ // Revoke token
112
+ async function revokeToken(token) {
113
+ try {
114
+ if (!kernelClient || !kernelClient.connected) {
115
+ throw new Error('WebSocket 未连接');
116
+ }
117
+
118
+ // 立即变灰并禁用
119
+ const row = event.target.closest('tr');
120
+ if (row) {
121
+ row.style.opacity = '0.3';
122
+ row.style.pointerEvents = 'none';
123
+ }
124
+
125
+ // 后台调用 RPC 吊销
126
+ await kernelClient.call('web.revoke_token', { token });
127
+
128
+ } catch (err) {
129
+ console.error('[web] Failed to revoke token:', err);
130
+
131
+ // 失败后恢复该行
132
+ const row = event.target.closest('tr');
133
+ if (row) {
134
+ row.style.opacity = '1';
135
+ row.style.pointerEvents = 'auto';
136
+ }
137
+
138
+ // 显示错误提示
139
+ const errorMsg = err.message.includes('Permission denied')
140
+ ? '权限不足:无法吊销 Token'
141
+ : `吊销失败: ${err.message}`;
142
+
143
+ console.error('[web]', errorMsg);
144
+ }
145
+ }
146
+
147
+ // Refresh tokens
148
+ function refreshTokens() {
149
+ loadTokens();
150
+ }
151
+
152
+ // Auto-load tokens when modules page is shown
153
+ document.addEventListener('DOMContentLoaded', () => {
154
+ // Load tokens when switching to modules page
155
+ const modulesNav = document.querySelector('[data-page="modules"]');
156
+ if (modulesNav) {
157
+ modulesNav.addEventListener('click', () => {
158
+ // Wait a bit for page switch, then load tokens
159
+ setTimeout(() => {
160
+ console.log('[web] Modules page clicked, loading tokens...');
161
+ loadTokens();
162
+ }, 100);
163
+ });
164
+ }
165
+
166
+ // Wait for kernelClient to be ready before loading tokens
167
+ window.addEventListener('kernelClientReady', () => {
168
+ console.log('[web] kernelClient ready');
169
+ // Load tokens if modules page is active on load
170
+ if (document.getElementById('page-modules').classList.contains('active')) {
171
+ console.log('[web] Modules page is active, loading tokens...');
172
+ loadTokens();
173
+ }
174
+ });
175
+ });
@@ -0,0 +1,248 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Kite 配对</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ padding: 20px;
22
+ }
23
+
24
+ .container {
25
+ background: white;
26
+ border-radius: 16px;
27
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
28
+ padding: 40px;
29
+ max-width: 400px;
30
+ width: 100%;
31
+ }
32
+
33
+ h1 {
34
+ font-size: 28px;
35
+ color: #333;
36
+ margin-bottom: 10px;
37
+ text-align: center;
38
+ }
39
+
40
+ .subtitle {
41
+ color: #666;
42
+ text-align: center;
43
+ margin-bottom: 30px;
44
+ font-size: 14px;
45
+ }
46
+
47
+ .form-group {
48
+ margin-bottom: 20px;
49
+ }
50
+
51
+ label {
52
+ display: block;
53
+ color: #555;
54
+ font-size: 14px;
55
+ margin-bottom: 8px;
56
+ font-weight: 500;
57
+ }
58
+
59
+ input {
60
+ width: 100%;
61
+ padding: 12px 16px;
62
+ border: 2px solid #e0e0e0;
63
+ border-radius: 8px;
64
+ font-size: 16px;
65
+ transition: border-color 0.3s;
66
+ }
67
+
68
+ input:focus {
69
+ outline: none;
70
+ border-color: #667eea;
71
+ }
72
+
73
+ button {
74
+ width: 100%;
75
+ padding: 14px;
76
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
77
+ color: white;
78
+ border: none;
79
+ border-radius: 8px;
80
+ font-size: 16px;
81
+ font-weight: 600;
82
+ cursor: pointer;
83
+ transition: transform 0.2s, box-shadow 0.2s;
84
+ }
85
+
86
+ button:hover {
87
+ transform: translateY(-2px);
88
+ box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
89
+ }
90
+
91
+ button:active {
92
+ transform: translateY(0);
93
+ }
94
+
95
+ button:disabled {
96
+ opacity: 0.6;
97
+ cursor: not-allowed;
98
+ transform: none;
99
+ }
100
+
101
+ .message {
102
+ margin-top: 20px;
103
+ padding: 12px;
104
+ border-radius: 8px;
105
+ font-size: 14px;
106
+ text-align: center;
107
+ }
108
+
109
+ .message.error {
110
+ background: #fee;
111
+ color: #c33;
112
+ border: 1px solid #fcc;
113
+ }
114
+
115
+ .message.success {
116
+ background: #efe;
117
+ color: #3c3;
118
+ border: 1px solid #cfc;
119
+ }
120
+
121
+ .message.info {
122
+ background: #eef;
123
+ color: #33c;
124
+ border: 1px solid #ccf;
125
+ }
126
+
127
+ .hint {
128
+ margin-top: 20px;
129
+ padding: 12px;
130
+ background: #f5f5f5;
131
+ border-radius: 8px;
132
+ font-size: 13px;
133
+ color: #666;
134
+ }
135
+
136
+ .hint strong {
137
+ color: #333;
138
+ }
139
+ </style>
140
+ </head>
141
+ <body>
142
+ <div class="container">
143
+ <h1>🚀 Kite 配对</h1>
144
+ <p class="subtitle">输入配对码以连接到 Kite 系统</p>
145
+
146
+ <div class="form-group">
147
+ <label for="code">配对码</label>
148
+ <input type="text" id="code" placeholder="输入 6 位配对码" maxlength="6" autocomplete="off">
149
+ </div>
150
+
151
+ <button id="pairBtn">配对</button>
152
+
153
+ <div id="message"></div>
154
+
155
+ <div class="hint">
156
+ <strong>如何获取配对码?</strong><br>
157
+ 配对码会在 Kite 启动时自动生成并显示在控制台(绿色高亮)<br>
158
+ 每个配对码只能使用一次,使用后会自动生成新的配对码
159
+ </div>
160
+ </div>
161
+
162
+ <script src="js/kernel-client.js"></script>
163
+ <script>
164
+ const codeInput = document.getElementById('code');
165
+ const pairBtn = document.getElementById('pairBtn');
166
+ const messageDiv = document.getElementById('message');
167
+
168
+ // 页面加载时请求配对码
169
+ window.addEventListener('DOMContentLoaded', async () => {
170
+ showMessage('正在生成配对码...', 'info');
171
+
172
+ try {
173
+ // 连接 WebSocket 请求配对码
174
+ const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
175
+ const ws = new WebSocket(`${proto}//${location.host}/ws/relay`);
176
+
177
+ ws.onopen = () => {
178
+ // 请求配对码
179
+ ws.send(JSON.stringify({ type: 'request_code' }));
180
+ };
181
+
182
+ ws.onmessage = (event) => {
183
+ const msg = JSON.parse(event.data);
184
+
185
+ if (msg.type === 'code_generated') {
186
+ showMessage(`配对码已生成,请查看控制台`, 'success');
187
+ codeInput.focus();
188
+ } else if (msg.type === 'error') {
189
+ showMessage(`生成配对码失败: ${msg.message}`, 'error');
190
+ }
191
+ };
192
+
193
+ ws.onerror = () => {
194
+ showMessage('连接失败,请刷新页面重试', 'error');
195
+ };
196
+
197
+ } catch (err) {
198
+ showMessage(`生成配对码失败: ${err.message}`, 'error');
199
+ }
200
+ });
201
+
202
+ // 回车提交
203
+ codeInput.addEventListener('keypress', (e) => {
204
+ if (e.key === 'Enter') {
205
+ pairBtn.click();
206
+ }
207
+ });
208
+
209
+ // 配对
210
+ pairBtn.addEventListener('click', async () => {
211
+ const code = codeInput.value.trim();
212
+
213
+ if (!code) {
214
+ showMessage('请输入配对码', 'error');
215
+ return;
216
+ }
217
+
218
+ pairBtn.disabled = true;
219
+ showMessage('正在配对...', 'info');
220
+
221
+ try {
222
+ // 创建 Kernel 客户端
223
+ const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
224
+ const client = new KernelClient(`${proto}//${location.host}/ws/relay`);
225
+
226
+ // 配对
227
+ const result = await client.pair(code);
228
+
229
+ showMessage(`配对成功!模块 ID: ${result.moduleId}`, 'success');
230
+
231
+ // 2 秒后跳转
232
+ setTimeout(() => {
233
+ window.location.href = '/';
234
+ }, 2000);
235
+
236
+ } catch (err) {
237
+ showMessage(`配对失败: ${err.message}`, 'error');
238
+ pairBtn.disabled = false;
239
+ }
240
+ });
241
+
242
+ function showMessage(text, type) {
243
+ messageDiv.textContent = text;
244
+ messageDiv.className = `message ${type}`;
245
+ }
246
+ </script>
247
+ </body>
248
+ </html>
@@ -0,0 +1,262 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>注册中心验证测试</title>
6
+ <style>
7
+ body {
8
+ font-family: monospace;
9
+ padding: 20px;
10
+ background: #1e1e1e;
11
+ color: #d4d4d4;
12
+ }
13
+ .test-section {
14
+ margin: 20px 0;
15
+ padding: 15px;
16
+ border: 1px solid #3c3c3c;
17
+ border-radius: 4px;
18
+ }
19
+ .test-title {
20
+ font-size: 18px;
21
+ font-weight: bold;
22
+ margin-bottom: 10px;
23
+ color: #4ec9b0;
24
+ }
25
+ .test-item {
26
+ margin: 5px 0;
27
+ padding: 5px;
28
+ }
29
+ .pass { color: #4ec9b0; }
30
+ .fail { color: #f48771; }
31
+ .info { color: #9cdcfe; }
32
+ button {
33
+ background: #0e639c;
34
+ color: white;
35
+ border: none;
36
+ padding: 10px 20px;
37
+ border-radius: 4px;
38
+ cursor: pointer;
39
+ font-size: 14px;
40
+ }
41
+ button:hover {
42
+ background: #1177bb;
43
+ }
44
+ #results {
45
+ margin-top: 20px;
46
+ }
47
+ </style>
48
+ </head>
49
+ <body>
50
+ <h1>Kite 注册中心修复验证</h1>
51
+ <button onclick="runAllTests()">运行所有测试</button>
52
+ <div id="results"></div>
53
+
54
+ <script src="/static/js/kernel-client.js"></script>
55
+ <script>
56
+ const resultsDiv = document.getElementById('results');
57
+ let kernelClient;
58
+
59
+ async function init() {
60
+ // 尝试从 localStorage 获取 token,如果没有则使用临时 token
61
+ let token = localStorage.getItem('kite_token');
62
+
63
+ if (!token) {
64
+ // 尝试从主页面获取 token
65
+ try {
66
+ const response = await fetch('/api/pairing/request', {
67
+ method: 'POST',
68
+ headers: {'Content-Type': 'application/json'},
69
+ body: JSON.stringify({role: 'admin'})
70
+ });
71
+ const data = await response.json();
72
+ token = data.token;
73
+ console.log('使用临时 token:', token.substring(0, 16) + '...');
74
+ } catch (err) {
75
+ resultsDiv.innerHTML = '<div class="fail">错误: 无法获取 token</div>';
76
+ return false;
77
+ }
78
+ }
79
+
80
+ kernelClient = new KernelClient(token);
81
+ try {
82
+ await kernelClient.connect();
83
+ return true;
84
+ } catch (err) {
85
+ resultsDiv.innerHTML = `<div class="fail">连接失败: ${err.message}</div>`;
86
+ return false;
87
+ }
88
+ }
89
+
90
+ async function testWatchdogRpc() {
91
+ const section = document.createElement('div');
92
+ section.className = 'test-section';
93
+ section.innerHTML = '<div class="test-title">测试 1: Watchdog RPC 方法注册</div>';
94
+
95
+ try {
96
+ // 测试 health 方法
97
+ let result = await kernelClient.call('registry.lookup', {
98
+ field: 'tools.rpc.module.health',
99
+ module: 'watchdog'
100
+ });
101
+
102
+ if (result.results && result.results.length > 0) {
103
+ section.innerHTML += `<div class="test-item pass">✓ 找到 watchdog.health: ${JSON.stringify(result.results[0].value)}</div>`;
104
+ } else {
105
+ section.innerHTML += '<div class="test-item fail">✗ 未找到 watchdog.health</div>';
106
+ }
107
+
108
+ // 测试 status 方法
109
+ result = await kernelClient.call('registry.lookup', {
110
+ field: 'tools.rpc.module.status',
111
+ module: 'watchdog'
112
+ });
113
+
114
+ if (result.results && result.results.length > 0) {
115
+ section.innerHTML += `<div class="test-item pass">✓ 找到 watchdog.status: ${JSON.stringify(result.results[0].value)}</div>`;
116
+ } else {
117
+ section.innerHTML += '<div class="test-item fail">✗ 未找到 watchdog.status</div>';
118
+ }
119
+ } catch (err) {
120
+ section.innerHTML += `<div class="test-item fail">✗ 测试失败: ${err.message}</div>`;
121
+ }
122
+
123
+ resultsDiv.appendChild(section);
124
+ }
125
+
126
+ async function testWatchdogEvents() {
127
+ const section = document.createElement('div');
128
+ section.className = 'test-section';
129
+ section.innerHTML = '<div class="test-title">测试 2: Watchdog 事件注册</div>';
130
+
131
+ const events = [
132
+ 'watchdog.module.unhealthy',
133
+ 'watchdog.module.recovered',
134
+ 'watchdog.module.resource_critical',
135
+ 'watchdog.module.resource_warning',
136
+ 'watchdog.module.resource_recovered'
137
+ ];
138
+
139
+ try {
140
+ for (const event of events) {
141
+ const parts = event.split('.');
142
+ const field = `events_publish.${parts[0]}.${parts[1]}.${parts[2]}`;
143
+
144
+ const result = await kernelClient.call('registry.lookup', {
145
+ field: field,
146
+ module: 'watchdog'
147
+ });
148
+
149
+ if (result.results && result.results.length > 0) {
150
+ section.innerHTML += `<div class="test-item pass">✓ 找到事件: ${event}</div>`;
151
+ } else {
152
+ section.innerHTML += `<div class="test-item fail">✗ 未找到事件: ${event}</div>`;
153
+ }
154
+ }
155
+ } catch (err) {
156
+ section.innerHTML += `<div class="test-item fail">✗ 测试失败: ${err.message}</div>`;
157
+ }
158
+
159
+ resultsDiv.appendChild(section);
160
+ }
161
+
162
+ async function testWebRpc() {
163
+ const section = document.createElement('div');
164
+ section.className = 'test-section';
165
+ section.innerHTML = '<div class="test-title">测试 3: Web RPC 方法注册</div>';
166
+
167
+ const methods = [
168
+ ['tools.rpc.module.health', 'health'],
169
+ ['tools.rpc.module.status', 'status'],
170
+ ['tools.rpc.web.list_tokens', 'list_tokens'],
171
+ ['tools.rpc.web.revoke_token', 'revoke_token']
172
+ ];
173
+
174
+ try {
175
+ for (const [field, name] of methods) {
176
+ const result = await kernelClient.call('registry.lookup', {
177
+ field: field,
178
+ module: 'web'
179
+ });
180
+
181
+ if (result.results && result.results.length > 0) {
182
+ section.innerHTML += `<div class="test-item pass">✓ 找到 web.${name}: ${JSON.stringify(result.results[0].value)}</div>`;
183
+ } else {
184
+ section.innerHTML += `<div class="test-item fail">✗ 未找到 web.${name}</div>`;
185
+ }
186
+ }
187
+ } catch (err) {
188
+ section.innerHTML += `<div class="test-item fail">✗ 测试失败: ${err.message}</div>`;
189
+ }
190
+
191
+ resultsDiv.appendChild(section);
192
+ }
193
+
194
+ async function testLauncherEvents() {
195
+ const section = document.createElement('div');
196
+ section.className = 'test-section';
197
+ section.innerHTML = '<div class="test-title">测试 4: Launcher 事件注册</div>';
198
+
199
+ const events = [
200
+ 'system.ready',
201
+ 'module.starting',
202
+ 'module.started',
203
+ 'module.ready',
204
+ 'module.stopped',
205
+ 'module.exiting',
206
+ 'module.shutdown'
207
+ ];
208
+
209
+ try {
210
+ for (const event of events) {
211
+ const parts = event.split('.');
212
+ const field = `events_publish.${parts[0]}.${parts[1]}`;
213
+
214
+ const result = await kernelClient.call('registry.lookup', {
215
+ field: field,
216
+ module: 'launcher'
217
+ });
218
+
219
+ if (result.results && result.results.length > 0) {
220
+ section.innerHTML += `<div class="test-item pass">✓ 找到事件: ${event}</div>`;
221
+ } else {
222
+ section.innerHTML += `<div class="test-item fail">✗ 未找到事件: ${event}</div>`;
223
+ }
224
+ }
225
+
226
+ // 验证 state_changed 已删除
227
+ const result = await kernelClient.call('registry.lookup', {
228
+ field: 'events_publish.module.state_changed',
229
+ module: 'launcher'
230
+ });
231
+
232
+ if (!result.results || result.results.length === 0) {
233
+ section.innerHTML += '<div class="test-item pass">✓ 已删除未使用的事件: module.state_changed</div>';
234
+ } else {
235
+ section.innerHTML += '<div class="test-item fail">✗ module.state_changed 仍然存在(应该已删除)</div>';
236
+ }
237
+ } catch (err) {
238
+ section.innerHTML += `<div class="test-item fail">✗ 测试失败: ${err.message}</div>`;
239
+ }
240
+
241
+ resultsDiv.appendChild(section);
242
+ }
243
+
244
+ async function runAllTests() {
245
+ resultsDiv.innerHTML = '<div class="info">正在初始化...</div>';
246
+
247
+ if (!await init()) {
248
+ return;
249
+ }
250
+
251
+ resultsDiv.innerHTML = '<div class="info">开始测试...</div>';
252
+
253
+ await testWatchdogRpc();
254
+ await testWatchdogEvents();
255
+ await testWebRpc();
256
+ await testLauncherEvents();
257
+
258
+ resultsDiv.innerHTML += '<div class="test-section"><div class="test-title pass">所有测试完成</div></div>';
259
+ }
260
+ </script>
261
+ </body>
262
+ </html>