@agentunion/kite 1.4.0 → 1.6.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 (718) hide show
  1. package/.claude/skills/kite/checklists/feature-checklist.md +496 -0
  2. package/.claude/skills/kite/references/event-patterns.md +180 -0
  3. package/.claude/skills/kite/references/health-check.md +202 -0
  4. package/.claude/skills/kite/references/http-service.md +199 -0
  5. package/.claude/skills/kite/references/module-md-spec.md +172 -0
  6. package/.claude/skills/kite/references/multi-connection.md +147 -0
  7. package/.claude/skills/kite/references/rpc-patterns.md +199 -0
  8. package/.claude/skills/kite/references/shutdown-sequence.md +146 -0
  9. package/.claude/skills/kite/references/stdin-protocol.md +147 -0
  10. package/.claude/skills/kite/references/test-center-integration.md +178 -0
  11. package/.claude/skills/kite/references/ws-lifecycle.md +301 -0
  12. package/.claude/skills/kite/skill.md +272 -0
  13. package/.claude/skills/kite/templates/go/README.md +20 -0
  14. package/.claude/skills/kite/templates/node/entry.js +134 -0
  15. package/.claude/skills/kite/templates/node/module.md +16 -0
  16. package/.claude/skills/kite/templates/node/server.js +351 -0
  17. package/.claude/skills/kite/templates/node/server_http.js +90 -0
  18. package/.claude/skills/kite/templates/python/entry.py +425 -0
  19. package/.claude/skills/kite/templates/python/module.md +26 -0
  20. package/.claude/skills/kite/templates/python/server.py +447 -0
  21. package/.claude/skills/kite/templates/python/server_http.py +433 -0
  22. package/CHANGELOG.md +102 -0
  23. package/cli.js +78 -5
  24. package/core/dependency_checker.py +250 -0
  25. package/core/env_checker.py +586 -0
  26. package/dependencies_lock.json +128 -0
  27. package/docs/05-/347/237/255/344/277/241/350/256/244/350/257/201/344/270/216/347/224/250/346/210/267/344/277/241/346/201/257/346/216/245/345/217/243/346/226/207/346/241/243.md +507 -0
  28. package/docs/ACP/345/215/217/350/256/256/345/205/274/345/256/271/346/226/271/346/241/210.md +138 -0
  29. package/docs/CI/344/270/216AI/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/346/226/271/346/241/210.md +75 -0
  30. package/docs/CLI/345/274/200/345/217/221/350/256/241/345/210/222.md +595 -0
  31. package/docs/ClaudeCode/350/277/234/347/250/213/345/215/217/344/275/234/347/263/273/347/273/237-/346/212/200/346/234/257/350/257/204/344/274/260.md +535 -0
  32. package/docs/ClaudeCode/350/277/234/347/250/213/345/215/217/344/275/234/347/263/273/347/273/237/350/256/276/350/256/241.md +631 -0
  33. package/docs/Evol-App/344/275/277/347/224/250KernelClient/346/224/271/351/200/240/345/256/214/346/210/220.md +342 -0
  34. package/docs/Evol/346/216/247/345/210/266/345/217/260/346/217/222/344/273/266/345/214/226/346/236/266/346/236/204/346/246/202/350/246/201.md +604 -0
  35. package/docs/Evol/346/216/247/345/210/266/345/217/260/346/217/222/344/273/266/345/214/226/346/236/266/346/236/204/350/256/276/350/256/241.md +1708 -0
  36. package/docs/Evol/346/250/241/345/235/227/350/256/276/350/256/241/346/226/271/346/241/210.md +1154 -0
  37. package/docs/Evol/351/241/265/351/235/242/346/217/222/344/273/266/345/214/226-Evol/346/250/241/345/235/227/345/256/236/346/226/275/346/214/207/345/215/227.md +403 -0
  38. package/docs/Evol/351/241/265/351/235/242/346/217/222/344/273/266/345/214/226-/345/244/226/351/203/250/346/250/241/345/235/227/346/216/245/345/205/245/346/214/207/345/215/227.md +468 -0
  39. package/docs/HTTP-RPC/350/277/201/347/247/273/345/210/260WebSocket/350/256/241/345/210/222.md +318 -0
  40. package/docs/INDEX.md +388 -0
  41. package/docs/KITE_DOCS_GUIDE.md +33 -0
  42. package/docs/Kernel-Client-Kite-Token/346/224/257/346/214/201/345/256/236/346/226/275/345/256/214/346/210/220.md +330 -0
  43. package/docs/Kernel/344/270/273/345/212/250Ping/346/234/272/345/210/266-/346/255/243/347/241/256/345/256/236/347/216/260.md +235 -0
  44. package/docs/Kernel/344/270/273/345/212/250Ping/346/234/272/345/210/266/345/256/236/346/226/275/346/200/273/347/273/223.md +204 -0
  45. package/docs/Kite/345/256/211/350/243/205/351/227/256/351/242/230/350/247/243/345/206/263/346/226/271/346/241/210.md +362 -0
  46. package/docs/Kite/346/216/247/345/210/266/345/217/260/346/217/222/344/273/266/345/214/226/346/236/266/346/236/204/350/256/276/350/256/241-/347/273/210/346/236/201/347/233/256/346/240/207.md +721 -0
  47. package/docs/Kite/346/216/247/345/210/266/345/217/260/347/273/237/344/270/200WebSocket/346/224/271/351/200/240/346/226/271/346/241/210.md +821 -0
  48. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/01-/346/241/206/346/236/266/345/256/232/344/275/215.md +12 -0
  49. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/02-/346/240/270/345/277/203/346/246/202/345/277/265.md +341 -0
  50. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/03-/347/263/273/347/273/237/346/236/266/346/236/204.md +257 -0
  51. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/04-/346/250/241/345/235/227/350/247/204/350/214/203.md +263 -0
  52. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/05-/346/240/270/345/277/203/346/265/201/347/250/213-/346/226/260/347/211/210.md +267 -0
  53. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/05-/346/240/270/345/277/203/346/265/201/347/250/213.md +149 -0
  54. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/06-/347/233/256/345/275/225/347/273/223/346/236/204.md +231 -0
  55. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/07-/346/225/260/346/215/256/346/250/241/345/236/213.md +68 -0
  56. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/08-/346/211/251/345/261/225/346/200/247.md +34 -0
  57. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/09-/344/270/216/345/205/267/344/275/223/345/272/224/347/224/250/347/232/204/345/205/263/347/263/273.md +22 -0
  58. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/README.md +46 -0
  59. package/docs/Kite/347/263/273/347/273/237/345/220/257/345/212/250/346/265/201/347/250/213.md +567 -0
  60. package/docs/Launcher/345/220/257/345/212/250/345/231/250/346/226/207/346/241/243.md +745 -0
  61. package/docs/Polyglot/350/277/220/350/241/214/346/227/266/344/270/216Clawdbot/345/205/274/345/256/271/346/200/247/350/256/276/350/256/241.md +321 -0
  62. package/docs/Redis/344/270/216/346/250/241/345/235/227/345/244/232/345/256/236/344/276/213/346/226/271/346/241/210.md +438 -0
  63. package/docs/Relay-Kite-Token/350/256/244/350/257/201/345/256/236/346/226/275/345/256/214/346/210/220.md +178 -0
  64. package/docs/Relay-Token/346/235/203/351/231/220/351/205/215/347/275/256/351/252/214/350/257/201.md +113 -0
  65. package/docs/Watchdog/345/201/245/345/272/267/346/243/200/346/237/245/344/270/216WebSocket-Ping/346/234/272/345/210/266/345/210/206/346/236/220.md +367 -0
  66. package/docs/Watchdog/350/265/204/346/272/220/347/233/221/346/216/247/347/255/226/347/225/245.md +92 -0
  67. package/docs/WebSocket/346/216/245/346/224/266/345/276/252/347/216/257/346/255/273/351/224/201/351/230/262/350/214/203/350/247/204/350/214/203.md +357 -0
  68. package/docs/WebSocket/350/277/236/346/216/245/351/237/247/346/200/247/344/270/216/351/207/215/350/277/236/346/234/272/345/210/266/345/256/214/346/225/264/346/226/271/346/241/210.md +531 -0
  69. package/docs/WebSocket/350/277/236/346/216/245/351/237/247/346/200/247/346/226/271/346/241/210.md +169 -0
  70. package/docs/WebSocket/351/207/215/350/277/236/346/234/272/345/210/266/346/265/213/350/257/225/346/212/245/345/221/212.md +169 -0
  71. package/docs/WebSocket/351/207/215/350/277/236/351/200/200/351/201/277/346/234/272/345/210/266/346/226/271/346/241/210.md +394 -0
  72. package/docs/Web/346/250/241/345/235/227/344/270/216Evol/346/250/241/345/235/227/351/207/215/346/236/204/345/210/206/346/236/220.md +521 -0
  73. package/docs/audit-api-guide.md +68 -0
  74. package/docs/audit-module-design.md +315 -0
  75. package/docs/audit-module-implementation-summary.md +149 -0
  76. package/docs/llm-context-design.md +52 -0
  77. package/docs/llm-test-enhancement-plan.md +970 -0
  78. package/docs/logs-api-guide.md +42 -0
  79. package/docs/npm/345/214/205Python/347/216/257/345/242/203/347/256/241/347/220/206/346/226/271/346/241/210.md +302 -0
  80. package/docs/npm/345/217/221/345/270/203/344/270/216CLI/344/275/277/347/224/250/346/214/207/345/215/227.md +245 -0
  81. package/docs/stdio/344/270/216/347/253/257/345/217/243/345/217/221/347/216/260/351/207/215/346/236/204.md +480 -0
  82. package/docs/web/346/250/241/345/235/227/344/270/255/350/275/254/346/234/215/345/212/241/350/256/276/350/256/241/346/226/271/346/241/210.md +449 -0
  83. package/docs//344/272/213/344/273/266/345/244/204/347/220/206/346/234/272/345/210/266.md +388 -0
  84. package/docs//344/272/213/344/273/266/345/244/204/347/220/206/350/247/204/350/214/203.md +113 -0
  85. package/docs//344/272/213/344/273/266/350/256/242/351/230/205/351/200/232/351/205/215/347/254/246/350/247/204/350/214/203.md +256 -0
  86. package/docs//344/272/213/344/273/266/351/230/237/345/210/227/345/274/271/346/200/247/347/256/241/347/220/206.md +449 -0
  87. package/docs//344/272/244/344/272/222/345/274/217/347/273/210/347/253/257/346/216/247/345/210/266/346/226/271/346/241/210.md +301 -0
  88. package/docs//344/273/243/347/220/206/345/220/257/345/212/250/345/231/250/344/270/216/345/256/271/345/231/250/345/214/226.md +140 -0
  89. package/docs//344/273/243/347/240/201/347/273/237/350/256/241/345/267/245/345/205/267/344/275/277/347/224/250/350/257/264/346/230/216.md +217 -0
  90. package/docs//344/274/230/351/233/205/351/200/200/345/207/272/350/247/204/350/214/203.md +362 -0
  91. package/docs//344/276/235/350/265/226/347/256/241/347/220/206/350/257/264/346/230/216.md +141 -0
  92. package/docs//344/277/256/345/244/215/346/235/203/351/231/220/351/227/256/351/242/230-evol-RPC/346/235/203/351/231/220.md +268 -0
  93. package/docs//345/210/240/351/231/244kernel-client-example/345/256/214/346/210/220.md +309 -0
  94. package/docs//345/210/240/351/231/244ws-management/345/256/214/346/210/220.md +418 -0
  95. package/docs//345/220/257/345/212/250/344/274/230/345/214/226/346/226/271/346/241/210.md +522 -0
  96. package/docs//345/220/257/345/212/250/344/276/235/350/265/226/344/270/216/346/216/222/345/272/217.md +105 -0
  97. package/docs//345/256/211/350/243/205/350/204/232/346/234/254/345/274/200/345/217/221/346/226/207/346/241/243.md +643 -0
  98. package/docs//345/256/214/346/225/264/345/220/257/345/212/250/346/265/201/347/250/213/350/256/276/350/256/241.md +452 -0
  99. package/docs//345/256/236/347/216/260/350/247/204/345/210/222.md +195 -0
  100. package/docs//345/277/203/350/267/263/346/234/272/345/210/266/351/207/215/346/236/204/346/200/273/347/273/223.md +166 -0
  101. package/docs//346/217/241/346/211/213/350/256/244/350/257/201/346/226/271/346/241/210-/345/256/211/345/205/250/345/256/241/346/237/245.md +176 -0
  102. package/docs//346/217/241/346/211/213/350/256/244/350/257/201/346/226/271/346/241/210.md +908 -0
  103. package/docs//346/226/207/346/241/243/346/233/264/346/226/260/346/270/205/345/215/225.md +83 -0
  104. package/docs//346/227/245/345/277/227/344/270/216/345/274/202/345/270/270/345/244/204/347/220/206/350/247/204/350/214/203.md +829 -0
  105. package/docs//346/227/245/345/277/227/350/260/203/350/257/225/345/256/236/346/210/230/346/214/207/345/215/227.md +25 -0
  106. package/docs//346/236/266/346/236/204/345/200/237/351/211/264/346/214/207/345/215/227.md +977 -0
  107. package/docs//346/236/266/346/236/204/346/224/271/351/200/240-/345/256/214/346/210/220/346/200/273/347/273/223.md +440 -0
  108. package/docs//346/236/266/346/236/204/347/216/260/347/212/266/344/270/216/347/273/210/346/236/201/347/233/256/346/240/207/345/257/271/346/257/224/345/210/206/346/236/220.md +508 -0
  109. package/docs//346/250/241/345/235/227/345/244/232/350/277/236/346/216/245/346/216/247/345/210/266/347/255/226/347/225/245.md +220 -0
  110. package/docs//346/250/241/345/235/227/345/256/211/350/243/205/346/234/272/345/210/266/350/256/276/350/256/241.md +500 -0
  111. package/docs//346/250/241/345/235/227/345/274/200/345/217/221/346/214/207/345/215/227.md +1824 -0
  112. package/docs//346/250/241/345/235/227/347/203/255/346/233/264/346/226/260.md +89 -0
  113. package/docs//346/250/241/345/235/227/350/277/234/347/250/213/351/203/250/347/275/262/345/274/200/345/217/221/350/247/204/350/214/203.md +460 -0
  114. package/docs//346/250/241/345/235/227/351/200/200/345/207/272/346/234/272/345/210/266/345/256/214/346/225/264/346/226/271/346/241/210.md +303 -0
  115. package/docs//346/250/241/345/235/227/351/205/215/347/275/256/345/212/240/350/275/275/344/270/216/347/203/255/351/207/215/350/275/275/350/247/204/350/214/203.md +369 -0
  116. package/docs//346/265/213/350/257/225/344/270/255/345/277/203/346/267/273/345/212/240/346/250/241/345/235/227/346/265/213/350/257/225/346/214/207/345/215/227.md +147 -0
  117. package/docs//347/211/210/346/234/254/351/224/201/345/256/232/347/216/257/345/242/203/347/256/241/347/220/206/346/226/271/346/241/210.md +331 -0
  118. package/docs//347/216/257/345/242/203/345/217/230/351/207/217/344/270/216/350/277/220/350/241/214/346/227/266/347/233/256/345/275/225/350/256/276/350/256/241.md +499 -0
  119. package/docs//347/216/257/345/242/203/347/256/241/347/220/206/345/256/214/346/225/264/346/226/271/346/241/210.md +334 -0
  120. package/docs//350/231/232/346/213/237/346/250/241/345/235/227/344/270/255/350/275/254/346/234/215/345/212/241/345/256/214/346/225/264/350/256/276/350/256/241.md +1496 -0
  121. package/docs//350/231/232/346/213/237/347/216/257/345/242/203/345/267/245/344/275/234/345/216/237/347/220/206.md +163 -0
  122. package/docs//350/256/241/345/210/222/347/256/241/347/220/206/345/231/250/344/275/277/347/224/250/346/214/207/345/215/227.md +196 -0
  123. package/docs//350/256/244/350/257/201/346/250/241/345/235/227/344/270/216Gateway/350/256/276/350/256/241/346/226/271/346/241/210.md +765 -0
  124. package/docs//350/277/234/347/250/213/346/250/241/345/235/227/350/256/276/350/256/241-/346/227/247/347/211/210.md +1117 -0
  125. package/docs//350/277/234/347/250/213/346/250/241/345/235/227/350/256/276/350/256/241.md +451 -0
  126. package/docs//351/207/215/346/236/204/346/234/272/345/210/266/346/270/205/345/215/225.md +192 -0
  127. package/docs//351/223/276/350/267/257/350/277/275/350/270/252/346/226/271/346/241/210.md +242 -0
  128. package/docs//351/231/215/347/272/247/347/255/226/347/225/245/350/256/276/350/256/241/346/226/271/346/241/210.md +618 -0
  129. package/extensions/agents/assistant/entry.py +113 -14
  130. package/extensions/agents/assistant/module.md +27 -22
  131. package/extensions/agents/assistant/server.py +308 -106
  132. package/extensions/channels/acp_channel/entry.py +114 -16
  133. package/extensions/channels/acp_channel/module.md +4 -0
  134. package/extensions/channels/acp_channel/server.py +412 -105
  135. package/extensions/channels/phone_channel/__init__.py +1 -0
  136. package/extensions/channels/phone_channel/entry.py +503 -0
  137. package/extensions/channels/phone_channel/module.md +31 -0
  138. package/extensions/channels/phone_channel/server.py +686 -0
  139. package/extensions/event_hub_bench/entry.py +55 -12
  140. package/extensions/event_hub_bench/module.md +27 -27
  141. package/extensions/services/audit/README.md +134 -0
  142. package/extensions/services/audit/collector.py +73 -0
  143. package/extensions/services/audit/entry.py +444 -0
  144. package/extensions/services/audit/module.md +66 -0
  145. package/extensions/services/audit/query_audit.py +111 -0
  146. package/extensions/services/audit/routes/__init__.py +1 -0
  147. package/extensions/services/audit/routes/routes_audit.py +113 -0
  148. package/extensions/services/audit/schemas/__init__.py +5 -0
  149. package/extensions/services/audit/schemas/audit_event.py +92 -0
  150. package/extensions/services/audit/server.py +542 -0
  151. package/extensions/services/audit/storage.py +95 -0
  152. package/extensions/services/auth/entry.py +1054 -0
  153. package/extensions/services/auth/module.md +31 -0
  154. package/extensions/services/auth/token_store.py +185 -0
  155. package/extensions/services/auth/verifiers/evol_account.py +101 -0
  156. package/extensions/services/auth/verifiers/kite_token.py +38 -0
  157. package/extensions/services/auth/verifiers/pairing_code.py +71 -0
  158. package/extensions/services/backup/entry.py +505 -201
  159. package/extensions/services/backup/module.md +4 -2
  160. package/extensions/services/dataclaw/api/__init__.py +0 -0
  161. package/extensions/services/dataclaw/api/admin.py +367 -0
  162. package/extensions/services/dataclaw/api/copyright.py +175 -0
  163. package/extensions/services/dataclaw/api/credits.py +177 -0
  164. package/extensions/services/dataclaw/api/data.py +179 -0
  165. package/extensions/services/dataclaw/api/demands.py +269 -0
  166. package/extensions/services/dataclaw/api/feeds.py +262 -0
  167. package/extensions/services/dataclaw/api/identity.py +505 -0
  168. package/extensions/services/dataclaw/api/notifications.py +104 -0
  169. package/extensions/services/dataclaw/api/reviews.py +138 -0
  170. package/extensions/services/dataclaw/api/search.py +153 -0
  171. package/extensions/services/dataclaw/api/subscriptions.py +157 -0
  172. package/extensions/services/dataclaw/config.json5 +96 -0
  173. package/extensions/services/dataclaw/core/__init__.py +0 -0
  174. package/extensions/services/dataclaw/core/auth.py +95 -0
  175. package/extensions/services/dataclaw/core/config.py +50 -0
  176. package/extensions/services/dataclaw/core/database.py +70 -0
  177. package/extensions/services/dataclaw/entry.py +416 -0
  178. package/extensions/services/dataclaw/gofeed/351/241/271/347/233/256/346/211/200/346/234/211/346/235/203/350/275/254/347/247/273/346/265/201/347/250/213/350/257/264/346/230/216.md +309 -0
  179. package/extensions/services/dataclaw/migrate.py +283 -0
  180. package/extensions/services/dataclaw/models/__init__.py +0 -0
  181. package/extensions/services/dataclaw/module.md +49 -0
  182. package/extensions/services/dataclaw/requirements.txt +18 -0
  183. package/extensions/services/dataclaw/server.py +759 -0
  184. package/extensions/services/dataclaw/services/__init__.py +0 -0
  185. package/extensions/services/dataclaw/services/agent_service.py +132 -0
  186. package/extensions/services/dataclaw/services/credit_service.py +235 -0
  187. package/extensions/services/dataclaw/services/email_service.py +140 -0
  188. package/extensions/services/dataclaw/services/feed_service.py +259 -0
  189. package/extensions/services/dataclaw/services/notification_service.py +209 -0
  190. package/extensions/services/dataclaw/services/oauth_service.py +275 -0
  191. package/extensions/services/dataclaw/services/pricing.py +102 -0
  192. package/extensions/services/dataclaw/services/quality.py +79 -0
  193. package/extensions/services/dataclaw/services/reputation.py +142 -0
  194. package/extensions/services/dataclaw/services/sms_service.py +174 -0
  195. package/extensions/services/dataclaw/static/css/common.css +853 -0
  196. package/extensions/services/dataclaw/static/css/themes/blue.css +42 -0
  197. package/extensions/services/dataclaw/static/css/themes/dark.css +42 -0
  198. package/extensions/services/dataclaw/static/css/themes/light.css +35 -0
  199. package/extensions/services/dataclaw/static/js/api.js +103 -0
  200. package/extensions/services/dataclaw/static/js/common.js +321 -0
  201. package/extensions/services/dataclaw/static/js/i18n.js +95 -0
  202. package/extensions/services/dataclaw/static/js/pages/admin.js +152 -0
  203. package/extensions/services/dataclaw/static/js/pages/dashboard.js +82 -0
  204. package/extensions/services/dataclaw/static/js/pages/feed-detail.js +180 -0
  205. package/extensions/services/dataclaw/static/js/pages/feed-manage.js +158 -0
  206. package/extensions/services/dataclaw/static/js/theme.js +46 -0
  207. package/extensions/services/dataclaw/static/locales/en-US.json +464 -0
  208. package/extensions/services/dataclaw/static/locales/ja-JP.json +464 -0
  209. package/extensions/services/dataclaw/static/locales/zh-CN.json +464 -0
  210. package/extensions/services/dataclaw/templates/admin/index.html +90 -0
  211. package/extensions/services/dataclaw/templates/base.html +136 -0
  212. package/extensions/services/dataclaw/templates/credits/balance.html +106 -0
  213. package/extensions/services/dataclaw/templates/credits/deposit.html +164 -0
  214. package/extensions/services/dataclaw/templates/credits/history.html +90 -0
  215. package/extensions/services/dataclaw/templates/dashboard.html +52 -0
  216. package/extensions/services/dataclaw/templates/demands/create.html +78 -0
  217. package/extensions/services/dataclaw/templates/demands/detail.html +136 -0
  218. package/extensions/services/dataclaw/templates/demands/list.html +94 -0
  219. package/extensions/services/dataclaw/templates/feeds/create.html +95 -0
  220. package/extensions/services/dataclaw/templates/feeds/detail.html +110 -0
  221. package/extensions/services/dataclaw/templates/feeds/list.html +110 -0
  222. package/extensions/services/dataclaw/templates/feeds/manage.html +88 -0
  223. package/extensions/services/dataclaw/templates/index.html +185 -0
  224. package/extensions/services/dataclaw/templates/login.html +246 -0
  225. package/extensions/services/dataclaw/templates/register.html +164 -0
  226. package/extensions/services/dataclaw/templates/settings/notifications.html +96 -0
  227. package/extensions/services/dataclaw/templates/settings/profile.html +167 -0
  228. package/extensions/services/dataclaw/templates/subscriptions/list.html +64 -0
  229. package/extensions/services/dataclaw/tests/__init__.py +0 -0
  230. package/extensions/services/dataclaw/tests/conftest.py +68 -0
  231. package/extensions/services/dataclaw/tests/integration/__init__.py +0 -0
  232. package/extensions/services/dataclaw/tests/integration/test_workflows.py +239 -0
  233. package/extensions/services/dataclaw/tests/unit/__init__.py +0 -0
  234. package/extensions/services/dataclaw/tests/unit/test_admin.py +70 -0
  235. package/extensions/services/dataclaw/tests/unit/test_copyright.py +63 -0
  236. package/extensions/services/dataclaw/tests/unit/test_credits.py +80 -0
  237. package/extensions/services/dataclaw/tests/unit/test_data.py +98 -0
  238. package/extensions/services/dataclaw/tests/unit/test_demands.py +106 -0
  239. package/extensions/services/dataclaw/tests/unit/test_feeds.py +98 -0
  240. package/extensions/services/dataclaw/tests/unit/test_identity.py +88 -0
  241. package/extensions/services/dataclaw/tests/unit/test_notifications.py +36 -0
  242. package/extensions/services/dataclaw/tests/unit/test_reviews.py +68 -0
  243. package/extensions/services/dataclaw/tests/unit/test_search.py +64 -0
  244. package/extensions/services/dataclaw/tests/unit/test_subscriptions.py +65 -0
  245. package/extensions/services/dataclaw/tests/unit/test_system.py +106 -0
  246. package/extensions/services/dataclaw/utils/__init__.py +0 -0
  247. package/extensions/services/dataclaw/utils/crypto.py +38 -0
  248. package/extensions/services/dataclaw/utils/id_generator.py +52 -0
  249. package/extensions/services/dataclaw/ws/__init__.py +0 -0
  250. package/extensions/services/dataclaw/ws/handler.py +163 -0
  251. package/extensions/services/dataclaw//345/215/217/350/256/2561-/351/241/271/347/233/256/346/235/241/344/273/266/346/216/210/346/235/203/344/270/216/350/202/241/346/235/203/345/257/271/344/273/267/345/215/217/350/256/256.md +243 -0
  252. package/extensions/services/dataclaw//345/215/217/350/256/2562-/351/241/271/347/233/256/350/264/255/344/271/260/346/235/203/344/270/216/345/244/226/345/214/205/345/247/224/346/211/230/345/274/200/345/217/221/345/215/217/350/256/256.md +434 -0
  253. package/extensions/services/evol/__init__.py +1 -0
  254. package/extensions/services/evol/async_http.py +551 -0
  255. package/extensions/services/evol/auth_manager.py +602 -0
  256. package/extensions/services/evol/config.json5 +16 -0
  257. package/extensions/services/evol/config_loader.py +117 -0
  258. package/extensions/services/evol/entry.py +568 -0
  259. package/extensions/services/evol/evol_api.py +969 -0
  260. package/extensions/services/evol/evol_config.json5 +29 -0
  261. package/extensions/services/evol/mfa_totp.py +77 -0
  262. package/extensions/services/evol/migrate_tokens.py +122 -0
  263. package/extensions/services/evol/module.md +150 -0
  264. package/extensions/services/evol/nonce_pool.py +113 -0
  265. package/extensions/services/evol/oauth_manager.py +223 -0
  266. package/extensions/services/evol/pairing.py +251 -0
  267. package/extensions/services/evol/pairing_codes.jsonl +2 -0
  268. package/extensions/services/evol/relay.py +1031 -0
  269. package/extensions/services/evol/relay_config.json5 +85 -0
  270. package/extensions/services/evol/routes/__init__.py +1 -0
  271. package/extensions/services/evol/routes/routes_llm.py +231 -0
  272. package/extensions/services/evol/routes/routes_rpc.py +90 -0
  273. package/extensions/services/evol/routes/routes_test.py +68 -0
  274. package/extensions/services/evol/server.py +2426 -0
  275. package/extensions/services/evol/static/assets/CommissionView-Cs_ys6Gm.js +1 -0
  276. package/extensions/services/evol/static/assets/CommissionView-DACet_Oo.css +1 -0
  277. package/extensions/services/evol/static/assets/IframePage-DbO11U9G.js +1 -0
  278. package/extensions/services/evol/static/assets/IframePage-c572lT8i.css +1 -0
  279. package/extensions/services/evol/static/assets/TeamDetailView-DULrGD7k.css +1 -0
  280. package/extensions/services/evol/static/assets/TeamDetailView-gy_MBEqG.js +139 -0
  281. package/extensions/services/evol/static/assets/element-plus-Bd7pZkkM.js +63 -0
  282. package/extensions/services/evol/static/assets/index-CmMONKzG.css +1 -0
  283. package/extensions/services/evol/static/assets/index-D44bBe__.js +2 -0
  284. package/extensions/services/evol/static/assets/vue-vendor-DtF-__I4.js +29 -0
  285. package/extensions/services/evol/static/index.html +16 -0
  286. package/extensions/services/evol/static/logo.png +0 -0
  287. package/extensions/services/evol/stats_manager.py +243 -0
  288. package/extensions/services/evol/web/README.md +89 -0
  289. package/extensions/services/evol/web/build.bat +44 -0
  290. package/extensions/services/evol/web/index.html +13 -0
  291. package/extensions/services/evol/web/package-lock.json +1718 -0
  292. package/extensions/services/evol/web/package.json +26 -0
  293. package/extensions/services/evol/web/public/logo.png +0 -0
  294. package/extensions/services/evol/web/src/App.vue +7 -0
  295. package/extensions/services/evol/web/src/components/layout/AppHeader.vue +202 -0
  296. package/extensions/services/evol/web/src/components/layout/AppLayout.vue +61 -0
  297. package/extensions/services/evol/web/src/components/layout/AppSidebar.vue +115 -0
  298. package/extensions/services/evol/web/src/components/login/LoginPage.vue +271 -0
  299. package/extensions/services/evol/web/src/components/team/AddMemberModal.vue +181 -0
  300. package/extensions/services/evol/web/src/components/team/GroupTreeNode.vue +156 -0
  301. package/extensions/services/evol/web/src/components/team/TeamAlertConfig.vue +221 -0
  302. package/extensions/services/evol/web/src/components/team/TeamBillModal.vue +165 -0
  303. package/extensions/services/evol/web/src/components/team/TeamMembersAndGroups.vue +499 -0
  304. package/extensions/services/evol/web/src/components/team/TeamStatsPanel.vue +907 -0
  305. package/extensions/services/evol/web/src/components/team/TreeNode.vue +331 -0
  306. package/extensions/services/evol/web/src/components/team/stats/StatsExportProgress.vue +44 -0
  307. package/extensions/services/evol/web/src/components/team/stats/StatsHeader.vue +89 -0
  308. package/extensions/services/evol/web/src/components/team/stats/StatsMemberDetail.vue +415 -0
  309. package/extensions/services/evol/web/src/components/team/stats/StatsSummary.vue +42 -0
  310. package/extensions/services/evol/web/src/components/team/stats/helpers.ts +195 -0
  311. package/extensions/services/evol/web/src/components/team/stats/stats.css +741 -0
  312. package/extensions/services/evol/web/src/components/team/stats/useStatsApi.ts +114 -0
  313. package/extensions/services/evol/web/src/components/team/stats/useStatsCharts.ts +242 -0
  314. package/extensions/services/evol/web/src/components/team/stats/useStatsExport.ts +232 -0
  315. package/extensions/services/evol/web/src/composables/useFormatters.ts +42 -0
  316. package/extensions/services/evol/web/src/composables/useTheme.ts +52 -0
  317. package/extensions/services/evol/web/src/env.d.ts +7 -0
  318. package/extensions/services/evol/web/src/i18n/en.ts +361 -0
  319. package/extensions/services/evol/web/src/i18n/index.ts +36 -0
  320. package/extensions/services/evol/web/src/i18n/zh.ts +379 -0
  321. package/extensions/services/evol/web/src/main.ts +21 -0
  322. package/extensions/services/evol/web/src/router/index.ts +81 -0
  323. package/extensions/services/evol/web/src/services/kernel-client.ts +406 -0
  324. package/extensions/services/evol/web/src/stores/auth.ts +189 -0
  325. package/extensions/services/evol/web/src/stores/connection.ts +134 -0
  326. package/extensions/services/evol/web/src/stores/pages.ts +79 -0
  327. package/extensions/services/evol/web/src/styles/base.css +213 -0
  328. package/extensions/services/evol/web/src/styles/variables.css +138 -0
  329. package/extensions/services/evol/web/src/types/rpc.ts +35 -0
  330. package/extensions/services/evol/web/src/types/token.ts +87 -0
  331. package/extensions/services/evol/web/src/views/AccountView.vue +1532 -0
  332. package/extensions/services/evol/web/src/views/AiServiceView.vue +219 -0
  333. package/extensions/services/evol/web/src/views/CommissionView.vue +1220 -0
  334. package/extensions/services/evol/web/src/views/CreditsView.vue +131 -0
  335. package/extensions/services/evol/web/src/views/EndpointView.vue +163 -0
  336. package/extensions/services/evol/web/src/views/IframePage.vue +120 -0
  337. package/extensions/services/evol/web/src/views/TeamDetailView.vue +473 -0
  338. package/extensions/services/evol/web/src/views/TeamView.vue +332 -0
  339. package/extensions/services/evol/web/tsconfig.json +31 -0
  340. package/extensions/services/evol/web/tsconfig.node.json +10 -0
  341. package/extensions/services/evol/web/vite.config.ts +49 -0
  342. package/extensions/services/evolmem/__init__.py +0 -0
  343. package/extensions/services/evolmem/entry.py +387 -0
  344. package/extensions/services/evolmem/hooks/__init__.py +0 -0
  345. package/extensions/services/evolmem/hooks/assistant_stop.py +228 -0
  346. package/extensions/services/evolmem/hooks/common.py +76 -0
  347. package/extensions/services/evolmem/hooks/pre_tool_use.py +56 -0
  348. package/extensions/services/evolmem/hooks/session_end.py +133 -0
  349. package/extensions/services/evolmem/hooks/session_start.py +229 -0
  350. package/extensions/services/evolmem/hooks/user_prompt.py +122 -0
  351. package/extensions/services/evolmem/module.md +48 -0
  352. package/extensions/services/evolmem/prompts/00-server-info.md +28 -0
  353. package/extensions/services/evolmem/prompts/01-behavior.md +46 -0
  354. package/extensions/services/evolmem/prompts/02-summary-format.md +112 -0
  355. package/extensions/services/evolmem/prompts/03-file-query.md +92 -0
  356. package/extensions/services/evolmem/prompts/04-topic-stats.md +11 -0
  357. package/extensions/services/evolmem/prompts/05-recent-topics.md +84 -0
  358. package/extensions/services/evolmem/scripts/__init__.py +0 -0
  359. package/extensions/services/evolmem/scripts/extract_keywords.py +40 -0
  360. package/extensions/services/evolmem/scripts/search_topics.py +91 -0
  361. package/extensions/services/evolmem/server.py +641 -0
  362. package/extensions/services/gateway/entry.py +964 -0
  363. package/extensions/services/gateway/module.md +29 -0
  364. package/extensions/services/gateway/nonce_pool.py +65 -0
  365. package/extensions/services/gateway/relay.py +133 -0
  366. package/extensions/services/gateway/ws_server.py +285 -0
  367. package/extensions/services/kite_console/auth_manager.py +603 -0
  368. package/extensions/services/kite_console/config.json5 +19 -0
  369. package/extensions/services/kite_console/config_loader.py +117 -0
  370. package/extensions/services/kite_console/entry.py +528 -0
  371. package/extensions/services/kite_console/evol_api.py +179 -0
  372. package/extensions/services/kite_console/evol_config.json5 +29 -0
  373. package/extensions/services/kite_console/mfa_totp.py +77 -0
  374. package/extensions/services/kite_console/migrate_tokens.py +122 -0
  375. package/extensions/services/kite_console/module.md +37 -0
  376. package/extensions/services/kite_console/nonce_pool.py +113 -0
  377. package/extensions/services/kite_console/oauth_manager.py +223 -0
  378. package/extensions/services/kite_console/pairing.py +280 -0
  379. package/extensions/services/kite_console/pairing_codes.jsonl +2 -0
  380. package/extensions/services/kite_console/relay.py +1350 -0
  381. package/extensions/services/kite_console/relay_config.json5 +96 -0
  382. package/extensions/services/kite_console/routes/__init__.py +1 -0
  383. package/extensions/services/kite_console/routes/routes_llm.py +231 -0
  384. package/extensions/services/kite_console/routes/routes_proxy.py +115 -0
  385. package/extensions/services/kite_console/routes/routes_rpc.py +89 -0
  386. package/extensions/services/kite_console/routes/routes_test.py +68 -0
  387. package/extensions/services/kite_console/server.py +1742 -0
  388. package/extensions/services/kite_console/static/css/style.css +1854 -0
  389. package/extensions/services/kite_console/static/index.html +1524 -0
  390. package/extensions/services/kite_console/static/js/dialog.js +292 -0
  391. package/extensions/services/kite_console/static/js/evol-app.js +7740 -0
  392. package/extensions/services/kite_console/static/js/evol-app.js.backup +2777 -0
  393. package/extensions/services/kite_console/static/js/kernel-client.js +560 -0
  394. package/extensions/services/kite_console/static/js/kernel-client.js.backup +434 -0
  395. package/extensions/services/kite_console/static/js/registry-tests.js +592 -0
  396. package/extensions/services/kite_console/static/js/tests/ARCHITECTURE.md +67 -0
  397. package/extensions/services/kite_console/static/js/tests/README.md +140 -0
  398. package/extensions/services/kite_console/static/js/tests/index.js +161 -0
  399. package/extensions/services/kite_console/static/js/tests/integration/auth.js +120 -0
  400. package/extensions/services/kite_console/static/js/tests/integration/channel-interaction.js +188 -0
  401. package/extensions/services/kite_console/static/js/tests/integration/elastic-connection.js +115 -0
  402. package/extensions/services/kite_console/static/js/tests/integration/full-workflow.js +43 -0
  403. package/extensions/services/kite_console/static/js/tests/integration/multi-instance.js +304 -0
  404. package/extensions/services/kite_console/static/js/tests/integration/nested-rpc.js +266 -0
  405. package/extensions/services/kite_console/static/js/tests/integration/pingpong.js +25 -0
  406. package/extensions/services/kite_console/static/js/tests/integration/redis.js +227 -0
  407. package/extensions/services/kite_console/static/js/tests/integration/registry-core.js +52 -0
  408. package/extensions/services/kite_console/static/js/tests/integration/remote-deploy.js +85 -0
  409. package/extensions/services/kite_console/static/js/tests/integration/require-init.js +96 -0
  410. package/extensions/services/kite_console/static/js/tests/integration/scaling-control.js +193 -0
  411. package/extensions/services/kite_console/static/js/tests/integration/trace.js +109 -0
  412. package/extensions/services/kite_console/static/js/tests/modules/acp_channel.js +339 -0
  413. package/extensions/services/kite_console/static/js/tests/modules/auth.js +96 -0
  414. package/extensions/services/kite_console/static/js/tests/modules/backup.js +49 -0
  415. package/extensions/services/kite_console/static/js/tests/modules/gateway.js +41 -0
  416. package/extensions/services/kite_console/static/js/tests/modules/kernel.js +90 -0
  417. package/extensions/services/kite_console/static/js/tests/modules/launcher.js +75 -0
  418. package/extensions/services/kite_console/static/js/tests/modules/multi_instance.js +129 -0
  419. package/extensions/services/kite_console/static/js/tests/modules/phone_channel.js +364 -0
  420. package/extensions/services/kite_console/static/js/tests/modules/redis.js +178 -0
  421. package/extensions/services/kite_console/static/js/tests/modules/watchdog.js +60 -0
  422. package/extensions/services/kite_console/static/js/tests/modules/web.js +70 -0
  423. package/extensions/services/kite_console/static/js/tests/test-runner.js +123 -0
  424. package/extensions/services/kite_console/static/js/virtual-list.js +200 -0
  425. package/extensions/services/kite_console/static/pairing.html +248 -0
  426. package/extensions/services/kite_console/static/test_kernel_client_token.html +352 -0
  427. package/extensions/services/kite_console/static/test_registry.html +262 -0
  428. package/extensions/services/kite_console/static/test_relay.html +462 -0
  429. package/extensions/services/kite_console/stats_manager.py +247 -0
  430. package/extensions/services/logs/README.md +215 -0
  431. package/extensions/services/logs/api_logger.py +37 -0
  432. package/extensions/services/logs/baseline.py +121 -0
  433. package/extensions/services/logs/cleaner.py +76 -0
  434. package/extensions/services/logs/entry.py +449 -0
  435. package/extensions/services/logs/formatter.py +129 -0
  436. package/extensions/services/logs/module.md +38 -0
  437. package/extensions/services/logs/quick_diagnostic.py +128 -0
  438. package/extensions/services/logs/routes/__init__.py +1 -0
  439. package/extensions/services/logs/routes/routes_logs.py +218 -0
  440. package/extensions/services/logs/routes/routes_logs.py.backup +173 -0
  441. package/extensions/services/logs/scanner.py +100 -0
  442. package/extensions/services/logs/searcher.py +263 -0
  443. package/extensions/services/logs/server.py +553 -0
  444. package/extensions/services/logs.zip +0 -0
  445. package/extensions/services/model_service/config.json5 +30 -0
  446. package/extensions/services/model_service/entry.py +633 -162
  447. package/extensions/services/model_service/module.md +11 -2
  448. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  449. package/extensions/services/proxy/__init__.py +0 -0
  450. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  451. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  452. package/extensions/services/proxy/agentcp/README.md +260 -0
  453. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  454. package/extensions/services/proxy/agentcp/agent.py +4 -0
  455. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  456. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  457. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  458. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  459. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  460. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  461. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  462. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  463. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  464. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  465. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  466. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  467. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  468. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  469. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  470. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  471. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  472. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  473. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  474. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  475. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  476. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  477. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  478. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  479. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  480. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  481. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  482. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  483. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  484. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  485. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  486. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  487. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  488. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  489. package/extensions/services/proxy/agentcp/message.py +149 -0
  490. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  491. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  492. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  493. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  494. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  495. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  496. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  497. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  498. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  499. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  500. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  501. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  502. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  503. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  504. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  505. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  506. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  507. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  508. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  509. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  510. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  511. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  512. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  513. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  514. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  515. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  516. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  517. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  518. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  519. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  520. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  521. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  522. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  523. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  524. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  525. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  526. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  527. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  528. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  529. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  530. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  531. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  532. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  533. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  534. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  535. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  536. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  537. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  538. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  539. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  540. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  541. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  542. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  543. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  544. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  545. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  546. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  547. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  548. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  549. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  550. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  551. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  552. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  553. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  554. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  555. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  556. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  557. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  558. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  559. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  560. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  561. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  562. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  563. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  564. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  565. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  566. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  567. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  568. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  569. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  570. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  571. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  572. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  573. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  574. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  575. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  576. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  577. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  578. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  579. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  580. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  581. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  582. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  583. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  584. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  585. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  586. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  587. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  588. package/extensions/services/proxy/aid_manager.py +419 -0
  589. package/extensions/services/proxy/auth_bridge.py +182 -0
  590. package/extensions/services/proxy/config_store.py +79 -0
  591. package/extensions/services/proxy/entry.py +528 -0
  592. package/extensions/services/proxy/evol/__init__.py +1 -0
  593. package/extensions/services/proxy/evol/config.py +37 -0
  594. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  595. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  596. package/extensions/services/proxy/evol/log.py +28 -0
  597. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  598. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  599. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +96 -0
  600. package/extensions/services/proxy/evol/presenter/configPresenter.py +234 -0
  601. package/extensions/services/proxy/evol/presenter/userPresenter.py +71 -0
  602. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  603. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3434 -0
  604. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  605. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  606. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  607. package/extensions/services/proxy/evol/version.py +24 -0
  608. package/extensions/services/proxy/module.md +151 -0
  609. package/extensions/services/proxy/server.py +952 -0
  610. package/extensions/services/redis/ALIGNMENT_CHECKLIST.md +121 -0
  611. package/extensions/services/redis/ALIGNMENT_STATUS.md +548 -0
  612. package/extensions/services/redis/config.json5 +8 -0
  613. package/extensions/services/redis/entry.py +1509 -0
  614. package/extensions/services/redis/entry.py.backup +405 -0
  615. package/extensions/services/redis/module.md +48 -0
  616. package/extensions/services/redis/redis_builtin.py +332 -0
  617. package/extensions/services/redis/redis_external.py +164 -0
  618. package/extensions/services/testUi/entry.py +446 -0
  619. package/extensions/services/testUi/module.md +18 -0
  620. package/extensions/services/testUi/ui/cards.html +131 -0
  621. package/extensions/services/testUi/ui/index.html +22 -0
  622. package/extensions/services/testUi/ui/particles.html +143 -0
  623. package/extensions/services/watchdog/entry.py +1258 -767
  624. package/extensions/services/watchdog/module.md +3 -0
  625. package/extensions/services/watchdog/monitor.py +483 -75
  626. package/extensions/services/web/auth_manager.py +602 -0
  627. package/extensions/services/web/config.json5 +11 -0
  628. package/extensions/services/web/entry.py +598 -478
  629. package/extensions/services/web/mfa_totp.py +77 -0
  630. package/extensions/services/web/module.md +17 -14
  631. package/extensions/services/web/nonce_pool.py +113 -0
  632. package/extensions/services/web/oauth_manager.py +223 -0
  633. package/extensions/services/web/pairing.py +3 -2
  634. package/extensions/services/web/pairing_codes.jsonl +1 -0
  635. package/extensions/services/web/relay.py +442 -63
  636. package/extensions/services/web/relay_config.json5 +1 -2
  637. package/extensions/services/web/routes/routes_rpc.py +6 -6
  638. package/extensions/services/web/server.py +380 -181
  639. package/extensions/services/web/static/index.html +1752 -1738
  640. package/extensions/services/web/static/js/app.js +32 -0
  641. package/extensions/services/web/static/js/kernel-client.js +48 -9
  642. package/extensions/services/web/static/js/token-manager.js +10 -10
  643. package/extensions/services/web/vendor/bluetooth/audio.py +1 -1
  644. package/extensions/services/web/vendor/config.py +2 -2
  645. package/extensions/services/web/vendor/storage/identity.py +1 -1
  646. package/kernel/entry.py +77 -23
  647. package/kernel/event_hub.py +1122 -74
  648. package/kernel/module.md +26 -1
  649. package/kernel/registry_store.py +209 -36
  650. package/kernel/rpc_router.py +1400 -465
  651. package/kernel/server.py +1084 -108
  652. package/kite_cli/builders/__init__.py +4 -0
  653. package/kite_cli/builders/base.py +67 -0
  654. package/kite_cli/builders/custom.py +31 -0
  655. package/kite_cli/builders/detector.py +56 -0
  656. package/kite_cli/builders/go.py +34 -0
  657. package/kite_cli/builders/gradle.py +41 -0
  658. package/kite_cli/builders/maven.py +36 -0
  659. package/kite_cli/builders/npm.py +44 -0
  660. package/kite_cli/builders/python.py +37 -0
  661. package/kite_cli/commands/BUILD_GUIDE.md +109 -0
  662. package/kite_cli/commands/build.py +142 -0
  663. package/kite_cli/commands/check.py +60 -0
  664. package/kite_cli/commands/config.py +156 -0
  665. package/kite_cli/commands/deps.py +58 -0
  666. package/kite_cli/commands/deps_install.py +67 -0
  667. package/kite_cli/commands/disable.py +162 -0
  668. package/kite_cli/commands/enable.py +162 -0
  669. package/kite_cli/commands/env_check.py +45 -0
  670. package/kite_cli/commands/export.py +96 -0
  671. package/kite_cli/commands/import_cmd.py +110 -0
  672. package/kite_cli/commands/install.py +50 -23
  673. package/kite_cli/commands/install_skill.py +107 -0
  674. package/kite_cli/commands/list.py +128 -31
  675. package/kite_cli/commands/outdated.py +202 -0
  676. package/kite_cli/commands/prepare.py +49 -0
  677. package/kite_cli/commands/search.py +33 -17
  678. package/kite_cli/commands/update.py +115 -2
  679. package/kite_cli/commands/venv_setup.py +56 -0
  680. package/kite_cli/commands/why.py +48 -0
  681. package/kite_cli/core/config_manager.py +145 -0
  682. package/kite_cli/core/downloader.py +32 -2
  683. package/kite_cli/main.py +179 -5
  684. package/kite_cli/utils/colors.py +153 -0
  685. package/kite_cli/utils/dependency_graph.py +209 -0
  686. package/kite_cli/utils/process.py +55 -0
  687. package/kite_cli/utils/progress.py +207 -0
  688. package/kite_cli/utils/table.py +101 -0
  689. package/launcher/count_lines.py +192 -43
  690. package/launcher/entry.py +4543 -2517
  691. package/launcher/logging_setup.py +54 -1
  692. package/launcher/module.md +37 -2
  693. package/launcher/module_scanner.py +103 -20
  694. package/launcher/process_manager.py +355 -76
  695. package/main.py +10 -1
  696. package/package.json +11 -1
  697. package/python_version.json +4 -0
  698. package/requirements.txt +41 -0
  699. package/scripts/auto-fix-deps.py +128 -0
  700. package/scripts/env-manager.js +351 -0
  701. package/scripts/final-test.js +78 -0
  702. package/scripts/python-env.js +79 -0
  703. package/scripts/scan_dependencies.py +461 -0
  704. package/scripts/setup-python-env.js +700 -0
  705. package/scripts/test-alluser.js +48 -0
  706. package/scripts/test-different-version.js +86 -0
  707. package/scripts/test-direct.js +63 -0
  708. package/scripts/test-extract-installer.js +28 -0
  709. package/scripts/test-install-log.js +54 -0
  710. package/scripts/test-installer.js +39 -0
  711. package/scripts/test-integration.js +250 -0
  712. package/scripts/test-real-install.js +210 -0
  713. package/scripts/test-targetdir.js +49 -0
  714. package/scripts/test-venv-real.js +47 -0
  715. package/scripts/test-venv-simple.js +57 -0
  716. package/scripts/test-wait.js +49 -0
  717. package/scripts/test-with-log.js +63 -0
  718. package/extensions/services/web/config.yaml +0 -149
@@ -0,0 +1,2777 @@
1
+ // Evol App - Main Application Logic
2
+
3
+ class EvolApp {
4
+ constructor() {
5
+ // 优先从 Cookie 恢复 kiteToken(防止 Ctrl+F5 清除 localStorage)
6
+ this.kiteToken = this._getKiteToken();
7
+ this.userInfo = null;
8
+ this.kernelClient = null; // 使用 kernelClient 替代 ws
9
+ this.currentPage = sessionStorage.getItem('currentPage') || 'account'; // 从 sessionStorage 恢复当前页面
10
+ this.deviceInfo = this._getDeviceInfo();
11
+ this.statsRefreshTimer = null;
12
+ this.moduleActionPending = new Map(); // 模块操作防抖
13
+ this.moduleStateChangePending = new Map(); // 模块状态变更防抖 {moduleName: {newState, timestamp}}
14
+ this.currentModuleName = null; // 当前查看的模块名
15
+
16
+ // 模块列表自动刷新
17
+ this.lastModuleListUpdate = 0;
18
+ this.moduleListRefreshTimer = null;
19
+ this.refreshDebounceTimer = null;
20
+ this.MODULE_LIST_REFRESH_INTERVAL = 300000; // 5分钟
21
+ this.REFRESH_DEBOUNCE_DELAY = 300; // 300ms 防抖
22
+
23
+ // 秒定时器 - 负责所有需要每秒更新的内容
24
+ this.secondTimer = null;
25
+ this.modulesData = []; // 缓存模块数据用于秒更新
26
+
27
+ // Ping 数据更新定时器(独立于模块列表刷新)
28
+ this.pingRefreshTimer = null;
29
+ this.lastPingData = {}; // 缓存最后一次的 ping 数据
30
+
31
+ // 事件日志相关
32
+ this.eventLogs = []; // 存储所有事件日志 {timestamp, module, event, data, raw}
33
+ this.consoleExpanded = false; // 控制台是否展开
34
+ this.eventSubscribed = false; // 是否已订阅全部事件
35
+ this.knownModules = new Set(); // 已知的模块列表
36
+ this.knownEvents = new Set(); // 已知的事件类型
37
+
38
+ // WebSocket 流量统计
39
+ this.reconnectCount = 0; // 重连次数
40
+ this.trafficStats = {
41
+ rxTotal: 0, // 总接收字节数
42
+ txTotal: 0, // 总发送字节数
43
+ rxRate: 0, // 接收速率 (B/s)
44
+ txRate: 0, // 发送速率 (B/s)
45
+ lastRxBytes: 0, // 上次接收字节数
46
+ lastTxBytes: 0, // 上次发送字节数
47
+ lastUpdateTime: Date.now(),
48
+ history: [] // 历史数据 [{time, rxRate, txRate}]
49
+ };
50
+ this.trafficUpdateTimer = null;
51
+ this.trafficChartCtx = null;
52
+
53
+ // 账户信息缓存(5秒有效期,持久化到 localStorage)
54
+ this.CACHE_DURATION = 5000; // 5秒
55
+ this._loadUserInfoCache();
56
+ }
57
+
58
+ _loadUserInfoCache() {
59
+ try {
60
+ const raw = localStorage.getItem('evolUserInfoCache');
61
+ if (raw) {
62
+ const cached = JSON.parse(raw);
63
+ this.userInfoCache = cached.data;
64
+ this.userInfoCacheTime = cached.time;
65
+ } else {
66
+ this.userInfoCache = null;
67
+ this.userInfoCacheTime = 0;
68
+ }
69
+ } catch {
70
+ this.userInfoCache = null;
71
+ this.userInfoCacheTime = 0;
72
+ }
73
+ }
74
+
75
+ _saveUserInfoCache(data) {
76
+ this.userInfoCache = data;
77
+ this.userInfoCacheTime = Date.now();
78
+ localStorage.setItem('evolUserInfoCache', JSON.stringify({
79
+ data: data,
80
+ time: this.userInfoCacheTime
81
+ }));
82
+ }
83
+
84
+ _getKiteToken() {
85
+ // 1. 先从 localStorage 读取
86
+ let token = localStorage.getItem('kiteToken');
87
+ if (token) return token;
88
+
89
+ // 2. 如果 localStorage 没有,尝试从 Cookie 恢复
90
+ token = this._getCookie('kiteToken');
91
+ if (token) {
92
+ // 恢复到 localStorage
93
+ localStorage.setItem('kiteToken', token);
94
+ console.log('Restored kiteToken from cookie');
95
+ return token;
96
+ }
97
+
98
+ return null;
99
+ }
100
+
101
+ _getCookie(name) {
102
+ const value = `; ${document.cookie}`;
103
+ const parts = value.split(`; ${name}=`);
104
+ if (parts.length === 2) return parts.pop().split(';').shift();
105
+ return null;
106
+ }
107
+
108
+ _setCookie(name, value, days = 30) {
109
+ const expires = new Date(Date.now() + days * 864e5).toUTCString();
110
+ document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Strict`;
111
+ }
112
+
113
+ _saveKiteToken(token) {
114
+ // 同时保存到 localStorage 和 Cookie
115
+ localStorage.setItem('kiteToken', token);
116
+ this._setCookie('kiteToken', token, 30);
117
+ }
118
+
119
+ _clearKiteToken() {
120
+ // 同时清除 localStorage 和 Cookie
121
+ localStorage.removeItem('kiteToken');
122
+ this._setCookie('kiteToken', '', -1);
123
+ }
124
+
125
+ _getDeviceInfo() {
126
+ // 生成或获取持久化的设备 ID
127
+ let deviceId = localStorage.getItem('deviceId');
128
+ if (!deviceId) {
129
+ deviceId = 'device_' + this._randomString(16) + '_' + Date.now();
130
+ localStorage.setItem('deviceId', deviceId);
131
+ }
132
+
133
+ // 检测设备类型和名称
134
+ const ua = navigator.userAgent;
135
+ let deviceType = 'Desktop';
136
+ let osName = 'Unknown OS';
137
+
138
+ if (/Android/i.test(ua)) {
139
+ deviceType = 'Mobile';
140
+ osName = 'Android';
141
+ } else if (/iPhone|iPad|iPod/i.test(ua)) {
142
+ deviceType = 'Mobile';
143
+ osName = 'iOS';
144
+ } else if (/Windows/i.test(ua)) {
145
+ osName = 'Windows';
146
+ } else if (/Mac/i.test(ua)) {
147
+ osName = 'macOS';
148
+ } else if (/Linux/i.test(ua)) {
149
+ osName = 'Linux';
150
+ }
151
+
152
+ let browserName = 'Unknown Browser';
153
+ if (/Chrome/i.test(ua) && !/Edge/i.test(ua)) {
154
+ browserName = 'Chrome';
155
+ } else if (/Firefox/i.test(ua)) {
156
+ browserName = 'Firefox';
157
+ } else if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) {
158
+ browserName = 'Safari';
159
+ } else if (/Edge/i.test(ua)) {
160
+ browserName = 'Edge';
161
+ }
162
+
163
+ return {
164
+ deviceId: deviceId,
165
+ deviceName: `${browserName} on ${osName}`,
166
+ deviceType: deviceType,
167
+ browser: browserName,
168
+ os: osName,
169
+ userAgent: ua
170
+ };
171
+ }
172
+
173
+ _randomString(length) {
174
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
175
+ let result = '';
176
+ for (let i = 0; i < length; i++) {
177
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
178
+ }
179
+ return result;
180
+ }
181
+
182
+ async init() {
183
+ if (this.kiteToken) {
184
+ // 有 kiteToken,立即显示主界面和占位信息
185
+ this.showMainApp();
186
+ this.showUserInfoPlaceholder();
187
+
188
+ // 异步加载 Evol 账户信息(不阻塞,失败不影响主界面)
189
+ this.loadUserInfo().catch(err => {
190
+ console.warn('Failed to load Evol user info:', err);
191
+ // evolToken 失效,显示提示但不影响 Kite 功能
192
+ this.showEvolTokenWarning();
193
+ });
194
+ } else {
195
+ // 没有 kiteToken,显示登录页
196
+ this.showLoginPage();
197
+ }
198
+ }
199
+
200
+ showUserInfoPlaceholder() {
201
+ // 立即显示占位信息,避免延迟
202
+ const loginBtn = document.getElementById('login-btn');
203
+ const userInfoArea = document.getElementById('user-info-area');
204
+
205
+ if (loginBtn) loginBtn.classList.add('hidden');
206
+ if (userInfoArea) {
207
+ userInfoArea.classList.remove('hidden');
208
+ userInfoArea.style.display = 'flex';
209
+
210
+ // 显示加载中占位
211
+ const userNameEl = userInfoArea.querySelector('.user-name');
212
+ const creditsEl = userInfoArea.querySelector('.user-credits');
213
+ if (userNameEl) userNameEl.textContent = '加载中...';
214
+ if (creditsEl) creditsEl.textContent = '';
215
+ }
216
+ }
217
+
218
+ showLoginPage() {
219
+ // 移除 has-token 类,触发 CSS 切换
220
+ document.documentElement.classList.remove('has-token');
221
+ document.getElementById('login-page').classList.remove('hidden');
222
+ document.getElementById('main-app').classList.add('hidden');
223
+ }
224
+
225
+ showMainApp() {
226
+ // 添加 has-token 类,触发 CSS 切换
227
+ document.documentElement.classList.add('has-token');
228
+ document.getElementById('login-page').classList.add('hidden');
229
+ document.getElementById('main-app').classList.remove('hidden');
230
+ this.initMainApp();
231
+ }
232
+
233
+ async loadUserInfo() {
234
+ // 检查缓存是否有效(5秒内)
235
+ const now = Date.now();
236
+ const cacheAge = now - this.userInfoCacheTime;
237
+
238
+ if (this.userInfoCache && cacheAge < this.CACHE_DURATION) {
239
+ // 缓存有效,直接使用
240
+ this.userInfo = this.userInfoCache;
241
+ this.updateUserInfoDisplay();
242
+ this.loadAccountInfo();
243
+ return;
244
+ }
245
+
246
+ // 如果有缓存数据,先渲染缓存(即使过期)
247
+ if (this.userInfoCache) {
248
+ this.userInfo = this.userInfoCache;
249
+ this.updateUserInfoDisplay();
250
+ this.loadAccountInfo();
251
+ }
252
+
253
+ // 等待 kernelClient 连接成功
254
+ if (!this.kernelClient || !this.kernelClient.connected) {
255
+ console.log('[Evol] Waiting for kernelClient to connect...');
256
+ // 等待最多 5 秒
257
+ const maxWait = 5000;
258
+ const startTime = Date.now();
259
+ while ((!this.kernelClient || !this.kernelClient.authenticated) && (Date.now() - startTime < maxWait)) {
260
+ await new Promise(resolve => setTimeout(resolve, 100));
261
+ }
262
+ if (!this.kernelClient || !this.kernelClient.authenticated) {
263
+ console.error('[Evol] kernelClient not authenticated after 5s');
264
+ throw new Error('KernelClient not authenticated');
265
+ }
266
+ }
267
+
268
+ // 然后异步更新数据
269
+ try {
270
+ const data = await this.kernelClient.call('evol.get_user_info', {
271
+ kiteToken: this.kiteToken
272
+ });
273
+
274
+ if (data.success) {
275
+ this.userInfo = data.data;
276
+ this._saveUserInfoCache(data.data);
277
+ this.updateUserInfoDisplay();
278
+ // 更新账户信息页面
279
+ this.loadAccountInfo();
280
+ } else if (data.code === 'INVALID_KITE_TOKEN') {
281
+ // kiteToken 失效,需要重新配对
282
+ console.warn('kiteToken invalid, need re-pairing');
283
+ this._clearKiteToken();
284
+ this.kiteToken = null;
285
+ this.showLoginPage();
286
+ throw new Error('KITE_TOKEN_INVALID');
287
+ } else {
288
+ // evolToken 失效或其他错误,不影响 Kite 功能
289
+ throw new Error(data.message || 'EVOL_TOKEN_INVALID');
290
+ }
291
+ } catch (err) {
292
+ console.error('Load user info failed:', err);
293
+ throw err;
294
+ }
295
+ }
296
+
297
+ updateUserInfoDisplay() {
298
+ // 更新右上角用户信息显示
299
+ if (this.userInfo) {
300
+ const userInfo = this.userInfo.userInfo || this.userInfo;
301
+ const accountInfo = this.userInfo.accountInfo || {};
302
+
303
+ // 显示用户信息区域
304
+ const loginBtn = document.getElementById('login-btn');
305
+ const userInfoArea = document.getElementById('user-info-area');
306
+
307
+ if (loginBtn) loginBtn.classList.add('hidden');
308
+ if (userInfoArea) {
309
+ userInfoArea.classList.remove('hidden');
310
+ userInfoArea.style.display = 'flex';
311
+
312
+ // 更新用户名
313
+ const userNameEl = userInfoArea.querySelector('.user-name');
314
+ if (userNameEl) {
315
+ userNameEl.textContent = userInfo.nickName || userInfo.userName || '未知用户';
316
+ }
317
+
318
+ // 更新积分
319
+ const creditsEl = userInfoArea.querySelector('.user-credits');
320
+ if (creditsEl) {
321
+ creditsEl.textContent = `积分: ${accountInfo.credits || 0}`;
322
+ }
323
+ }
324
+ }
325
+ }
326
+
327
+ showEvolTokenWarning() {
328
+ // 右上角显示 Evol 未登录状态
329
+ const loginBtn = document.getElementById('login-btn');
330
+ const userInfoArea = document.getElementById('user-info-area');
331
+
332
+ if (loginBtn) {
333
+ loginBtn.classList.remove('hidden');
334
+ loginBtn.style.display = 'inline';
335
+ loginBtn.textContent = '⚠️ 未登录 Evol';
336
+ loginBtn.style.color = '#ff9800';
337
+ loginBtn.style.fontWeight = '500';
338
+ }
339
+ if (userInfoArea) {
340
+ userInfoArea.classList.add('hidden');
341
+ userInfoArea.style.display = 'none';
342
+ }
343
+
344
+ // 绑定右上角登录按钮点击事件
345
+ if (loginBtn) {
346
+ loginBtn.onclick = (e) => {
347
+ e.preventDefault();
348
+ this.logout();
349
+ };
350
+ }
351
+ }
352
+
353
+ async sendSMS(phone) {
354
+ const res = await fetch('/api/send_sms', {
355
+ method: 'POST',
356
+ headers: { 'Content-Type': 'application/json' },
357
+ body: JSON.stringify({ phone })
358
+ });
359
+ return await res.json();
360
+ }
361
+
362
+ async login(phone, code) {
363
+ const res = await fetch('/api/verify_sms', {
364
+ method: 'POST',
365
+ headers: { 'Content-Type': 'application/json' },
366
+ body: JSON.stringify({ phone, code, deviceInfo: this.deviceInfo })
367
+ });
368
+ const data = await res.json();
369
+
370
+ if (data.success) {
371
+ this.kiteToken = data.kiteToken;
372
+ this.userInfo = data.data;
373
+ this._saveKiteToken(this.kiteToken);
374
+ this.showMainApp();
375
+ // 登录成功后立即更新用户信息显示
376
+ this.updateUserInfoDisplay();
377
+ // 更新账户信息页面
378
+ this.updateAccountPage();
379
+ }
380
+
381
+ return data;
382
+ }
383
+
384
+ async logout() {
385
+ try {
386
+ await this.kernelClient.call('evol.logout', {
387
+ kiteToken: this.kiteToken
388
+ });
389
+ } catch (err) {}
390
+
391
+ this._clearKiteToken();
392
+ location.reload();
393
+ }
394
+
395
+ initMainApp() {
396
+ // 初始化 Kernel 客户端和导航
397
+ this.initKernelClient();
398
+ this.loadAccountInfo();
399
+ this.setupNavigation();
400
+ this.restorePageState(); // 恢复页面状态
401
+
402
+ // 监听页面可见性变化,页面重新可见时尝试重连
403
+ document.addEventListener('visibilitychange', () => {
404
+ if (!document.hidden && this.kernelClient && !this.kernelClient.connected) {
405
+ console.log('[Evol] Page visible again, retrying connection');
406
+ this.initKernelClient();
407
+ }
408
+ });
409
+ }
410
+
411
+ updateConnectionStatus(status, message) {
412
+ const indicator = document.getElementById('ws-indicator');
413
+ const reconnectCount = document.getElementById('ws-reconnect-count');
414
+ const trafficEl = document.getElementById('ws-traffic');
415
+ if (!indicator) return;
416
+
417
+ switch (status) {
418
+ case 'connected':
419
+ indicator.innerHTML = '● Kite 已连接';
420
+ indicator.style.color = '#27ae60';
421
+ // 显示重连次数和流量统计
422
+ if (reconnectCount) {
423
+ reconnectCount.style.display = this.reconnectCount > 0 ? 'inline' : 'none';
424
+ reconnectCount.textContent = `重连: ${this.reconnectCount}`;
425
+ }
426
+ if (trafficEl) {
427
+ trafficEl.style.display = 'inline';
428
+ }
429
+ // 启动流量统计更新
430
+ this.startTrafficMonitoring();
431
+ break;
432
+ case 'disconnected':
433
+ indicator.innerHTML = '○ 未连接';
434
+ indicator.style.color = '#e74c3c';
435
+ // 隐藏流量统计
436
+ if (trafficEl) trafficEl.style.display = 'none';
437
+ // 停止流量统计更新
438
+ this.stopTrafficMonitoring();
439
+ break;
440
+ case 'reconnecting':
441
+ indicator.innerHTML = `⟳ ${message || '正在重连...'}`;
442
+ indicator.style.color = '#f39c12';
443
+ // 增加重连次数
444
+ this.reconnectCount++;
445
+ if (reconnectCount) {
446
+ reconnectCount.style.display = 'inline';
447
+ reconnectCount.textContent = `重连: ${this.reconnectCount}`;
448
+ }
449
+ break;
450
+ case 'error':
451
+ indicator.innerHTML = `✗ ${message || '连接失败'}`;
452
+ indicator.style.color = '#e74c3c';
453
+ break;
454
+ default:
455
+ indicator.innerHTML = '○ 未知状态';
456
+ indicator.style.color = '#95a5a6';
457
+ }
458
+ }
459
+
460
+ async initKernelClient() {
461
+ // 如果已经有客户端且正在连接或已连接,直接返回
462
+ if (this.kernelClient) {
463
+ if (this.kernelClient.connected) {
464
+ console.log('[Evol] KernelClient already connected');
465
+ return;
466
+ }
467
+ // 如果正在重连,等待重连完成
468
+ if (this.kernelClient.reconnectTimer) {
469
+ console.log('[Evol] KernelClient reconnecting, waiting...');
470
+ return;
471
+ }
472
+ }
473
+
474
+ const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
475
+
476
+ // 如果已有客户端,先断开
477
+ if (this.kernelClient) {
478
+ try {
479
+ this.kernelClient.disconnect();
480
+ } catch (err) {
481
+ console.warn('[Evol] Failed to disconnect old client:', err);
482
+ }
483
+ }
484
+
485
+ this.kernelClient = new KernelClient(`${proto}//${location.host}/ws/relay`);
486
+
487
+ // 监听连接状态变化
488
+ this.kernelClient.onConnectionChange = (status, message) => {
489
+ console.log(`[Evol] Connection status: ${status} - ${message}`);
490
+ this.updateConnectionStatus(status, message);
491
+ };
492
+
493
+ // 监听认证成功(用于重新订阅事件)
494
+ this.kernelClient.onAuthenticated = async (reconnected) => {
495
+ console.log(`[Evol] Authenticated (reconnected: ${reconnected})`);
496
+
497
+ // 订阅事件
498
+ try {
499
+ await this.kernelClient.subscribe([
500
+ 'module.starting',
501
+ 'module.started',
502
+ 'module.ready',
503
+ 'module.stopped',
504
+ 'module.exiting',
505
+ 'module.shutdown'
506
+ ]);
507
+ console.log('[Evol] Events subscribed');
508
+ } catch (err) {
509
+ console.error('[Evol] Failed to subscribe events:', err);
510
+ }
511
+
512
+ // 如果是重连,刷新当前页面数据
513
+ if (reconnected && this.currentPage === 'modules') {
514
+ console.log('[Evol] Refreshing modules after reconnection');
515
+ this.loadModules();
516
+ }
517
+ };
518
+
519
+ try {
520
+ console.log('[Evol] Connecting to Kernel via /ws/relay');
521
+ this.updateConnectionStatus('reconnecting', '正在连接...');
522
+
523
+ // 连接并认证
524
+ const result = await this.kernelClient.connect();
525
+ console.log('[Evol] KernelClient connected:', result);
526
+
527
+ // 注意:事件订阅已在 onAuthenticated 回调中处理
528
+
529
+ // 监听所有模块状态变化事件(只需注册一次)
530
+ const moduleEvents = [
531
+ 'module.starting',
532
+ 'module.started',
533
+ 'module.ready',
534
+ 'module.stopped',
535
+ 'module.exiting',
536
+ 'module.shutdown'
537
+ ];
538
+
539
+ moduleEvents.forEach(eventName => {
540
+ this.kernelClient.on(eventName, (data) => {
541
+ console.log(`[Evol] ${eventName}:`, data.module_id);
542
+ if (this.currentPage === 'modules') {
543
+ // 检查是否在列表页
544
+ const isDetailView = !document.getElementById('module-detail')?.classList.contains('hidden');
545
+
546
+ if (isDetailView) {
547
+ // 在详情页:只更新详情页状态(如果是当前查看的模块)
548
+ if (this.currentModuleName === data.module_id) {
549
+ this._updateModuleDetailStatus(data.module_id);
550
+ }
551
+ } else {
552
+ // 在列表页:刷新模块列表
553
+ this.refreshModuleList();
554
+ }
555
+ }
556
+ });
557
+ });
558
+
559
+ // 更新连接状态
560
+ this.updateConnectionStatus('connected', 'Kite 已连接');
561
+
562
+ } catch (err) {
563
+ console.error('[Evol] KernelClient connection failed:', err);
564
+ this.updateConnectionStatus('error', err.message);
565
+
566
+ // 如果是 token 无效,跳转到登录页面
567
+ if (err.message.includes('Invalid') || err.message.includes('expired')) {
568
+ console.warn('[Evol] Token invalid, redirecting to login');
569
+ this._clearKiteToken();
570
+ this.kiteToken = null;
571
+ this.showLoginPage();
572
+ }
573
+ }
574
+ }
575
+
576
+ async callRpc(method, params = {}) {
577
+ try {
578
+ // 改用 WebSocket RPC
579
+ if (!this.kernelClient || !this.kernelClient.connected) {
580
+ throw new Error('Not connected to Kernel');
581
+ }
582
+ // 等待认证完成(最多 5 秒)
583
+ if (!this.kernelClient.authenticated) {
584
+ const maxWait = 5000;
585
+ const startTime = Date.now();
586
+ while (!this.kernelClient.authenticated && (Date.now() - startTime < maxWait)) {
587
+ await new Promise(resolve => setTimeout(resolve, 100));
588
+ }
589
+ if (!this.kernelClient.authenticated) {
590
+ throw new Error('Not authenticated after 5s');
591
+ }
592
+ }
593
+ return await this.kernelClient.call(method, params);
594
+ } catch (err) {
595
+ console.error(`[Evol] RPC ${method} failed:`, err);
596
+ throw err;
597
+ }
598
+ }
599
+
600
+ setupNavigation() {
601
+ const navItems = document.querySelectorAll('.nav-item');
602
+ const pages = document.querySelectorAll('.page');
603
+
604
+ navItems.forEach(item => {
605
+ item.addEventListener('click', async () => {
606
+ const pageName = item.dataset.page;
607
+
608
+ // 离开模块页面时清除定时器
609
+ if (this.currentPage === 'modules' && pageName !== 'modules') {
610
+ if (this.moduleListRefreshTimer) {
611
+ clearTimeout(this.moduleListRefreshTimer);
612
+ this.moduleListRefreshTimer = null;
613
+ }
614
+ if (this.refreshDebounceTimer) {
615
+ clearTimeout(this.refreshDebounceTimer);
616
+ this.refreshDebounceTimer = null;
617
+ }
618
+ this.stopSecondTimer(); // 停止秒定时器
619
+ this.stopPingRefresh(); // 停止 ping 刷新定时器
620
+ }
621
+
622
+ this.currentPage = pageName;
623
+ sessionStorage.setItem('currentPage', pageName); // 保存当前页面到 sessionStorage
624
+
625
+ navItems.forEach(i => i.classList.remove('active'));
626
+ pages.forEach(p => p.classList.remove('active'));
627
+
628
+ item.classList.add('active');
629
+ document.getElementById('page-' + pageName).classList.add('active');
630
+
631
+ // 检查连接状态,如果未连接则尝试重连
632
+ if (!this.kernelClient || !this.kernelClient.connected) {
633
+ console.log('[Evol] Not connected, attempting to reconnect...');
634
+ try {
635
+ await this.initKernelClient();
636
+ } catch (err) {
637
+ console.error('[Evol] Reconnect failed:', err);
638
+ // 继续执行,让页面加载逻辑自己处理错误
639
+ }
640
+ }
641
+
642
+ if (pageName === 'modules') this.refreshModuleList();
643
+ else if (pageName === 'credits') this.loadCreditsStats();
644
+ else if (pageName === 'tokens') this.loadTokens();
645
+ });
646
+ });
647
+
648
+ document.getElementById('sidebar-toggle').addEventListener('click', () => {
649
+ document.getElementById('sidebar').classList.toggle('collapsed');
650
+ });
651
+ }
652
+
653
+ restorePageState() {
654
+ // 恢复页面状态
655
+ const savedPage = this.currentPage; // 已在 constructor 中从 sessionStorage 恢复
656
+ console.log(`[Evol] Restoring page state: ${savedPage}`);
657
+
658
+ // 更新导航和页面显示
659
+ const navItems = document.querySelectorAll('.nav-item');
660
+ const pages = document.querySelectorAll('.page');
661
+
662
+ navItems.forEach(item => {
663
+ if (item.dataset.page === savedPage) {
664
+ item.classList.add('active');
665
+ } else {
666
+ item.classList.remove('active');
667
+ }
668
+ });
669
+
670
+ pages.forEach(page => {
671
+ const pageId = page.id.replace('page-', '');
672
+ if (pageId === savedPage) {
673
+ page.classList.add('active');
674
+ } else {
675
+ page.classList.remove('active');
676
+ }
677
+ });
678
+
679
+ // 加载对应页面的数据
680
+ if (savedPage === 'modules') {
681
+ this.refreshModuleList();
682
+ } else if (savedPage === 'credits') {
683
+ this.loadCreditsStats();
684
+ } else if (savedPage === 'tokens') {
685
+ this.loadTokens();
686
+ }
687
+ }
688
+
689
+ loadAccountInfo() {
690
+ // 如果 userInfo 还没加载,显示加载中
691
+ if (!this.userInfo) {
692
+ const accountInfoEl = document.getElementById('account-info');
693
+ if (accountInfoEl) {
694
+ accountInfoEl.innerHTML = '<div style="padding:20px;text-align:center;color:#999;">加载中...</div>';
695
+ }
696
+ return;
697
+ }
698
+
699
+ const ui = this.userInfo.userInfo || {};
700
+ const ai = this.userInfo.accountInfo || {};
701
+ const ti = this.userInfo.teamInfo || {};
702
+ const gi = this.userInfo.gatewayInfo || {};
703
+
704
+ // 格式化 VIP 过期时间
705
+ const formatDate = (dateStr) => {
706
+ if (!dateStr) return '-';
707
+ const date = new Date(dateStr);
708
+ return date.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' });
709
+ };
710
+
711
+ // 积分包汇总
712
+ const pkg = ai.creditsPackageSummary || {};
713
+ const packageInfo = pkg.totalPackageCount > 0
714
+ ? `共 ${pkg.totalPackageCount} 个积分包`
715
+ : '无积分包';
716
+
717
+ const html = `
718
+ <div style="display:grid;grid-template-columns:repeat(2,1fr);gap:20px;">
719
+ <!-- 基本信息 -->
720
+ <div style="background:#f9f9f9;padding:16px;border-radius:6px;">
721
+ <h3 style="margin:0 0 12px 0;font-size:14px;font-weight:600;color:#666;">基本信息</h3>
722
+ <table style="width:100%;border-collapse:collapse;font-size:13px;">
723
+ <tr><td style="padding:8px 0;color:#666;">手机号</td><td style="padding:8px 0;font-weight:500;">${ui.phone || '-'}</td></tr>
724
+ <tr><td style="padding:8px 0;color:#666;">昵称</td><td style="padding:8px 0;font-weight:500;">${ui.nickName || '-'}</td></tr>
725
+ <tr><td style="padding:8px 0;color:#666;">用户ID</td><td style="padding:8px 0;font-weight:500;">${ui.userId || '-'}</td></tr>
726
+ <tr><td style="padding:8px 0;color:#666;">AID</td><td style="padding:8px 0;font-family:monospace;font-size:11px;">${ui.aid || '-'}</td></tr>
727
+ </table>
728
+ </div>
729
+
730
+ <!-- 积分信息 -->
731
+ <div style="background:#f9f9f9;padding:16px;border-radius:6px;">
732
+ <h3 style="margin:0 0 12px 0;font-size:14px;font-weight:600;color:#666;">积分信息</h3>
733
+ <table style="width:100%;border-collapse:collapse;font-size:13px;">
734
+ <tr><td style="padding:8px 0;color:#666;">当前积分</td><td style="padding:8px 0;font-weight:600;color:#27ae60;font-size:16px;">${ai.credits || 0}</td></tr>
735
+ <tr><td style="padding:8px 0;color:#666;">积分上限</td><td style="padding:8px 0;font-weight:500;">${ai.creditsLimit || 0}</td></tr>
736
+ <tr><td style="padding:8px 0;color:#666;">恢复速率</td><td style="padding:8px 0;font-weight:500;">${ai.creditsRecoveryRate || 0} / 小时</td></tr>
737
+ <tr><td style="padding:8px 0;color:#666;">积分包</td><td style="padding:8px 0;font-weight:500;">${packageInfo}</td></tr>
738
+ </table>
739
+ </div>
740
+
741
+ <!-- VIP 信息 -->
742
+ <div style="background:#f9f9f9;padding:16px;border-radius:6px;">
743
+ <h3 style="margin:0 0 12px 0;font-size:14px;font-weight:600;color:#666;">VIP 信息</h3>
744
+ <table style="width:100%;border-collapse:collapse;font-size:13px;">
745
+ <tr><td style="padding:8px 0;color:#666;">VIP 类型</td><td style="padding:8px 0;font-weight:500;">${ai.vipTypeName || 'Unknown'}</td></tr>
746
+ <tr><td style="padding:8px 0;color:#666;">过期时间</td><td style="padding:8px 0;font-weight:500;">${formatDate(ai.vipExpireTime)}</td></tr>
747
+ <tr><td style="padding:8px 0;color:#666;">剩余天数</td><td style="padding:8px 0;font-weight:600;color:#ff9800;">${ai.vipRemainingDays || 0} 天</td></tr>
748
+ <tr><td style="padding:8px 0;color:#666;">账户余额</td><td style="padding:8px 0;font-weight:500;">¥ ${(ai.balance || 0).toFixed(2)}</td></tr>
749
+ </table>
750
+ </div>
751
+
752
+ <!-- 团队信息 -->
753
+ <div style="background:#f9f9f9;padding:16px;border-radius:6px;">
754
+ <h3 style="margin:0 0 12px 0;font-size:14px;font-weight:600;color:#666;">团队信息</h3>
755
+ <table style="width:100%;border-collapse:collapse;font-size:13px;">
756
+ <tr><td style="padding:8px 0;color:#666;">当前团队</td><td style="padding:8px 0;font-weight:500;">${ti.currentTeamName || '-'}</td></tr>
757
+ <tr><td style="padding:8px 0;color:#666;">团队角色</td><td style="padding:8px 0;font-weight:500;">${ti.currentTeamRole === '1' ? '管理员' : '成员'}</td></tr>
758
+ <tr><td style="padding:8px 0;color:#666;">团队数量</td><td style="padding:8px 0;font-weight:500;">${(ti.teams || []).length} 个</td></tr>
759
+ </table>
760
+ </div>
761
+
762
+ <!-- 网关信息 -->
763
+ <div style="background:#f9f9f9;padding:16px;border-radius:6px;grid-column:1/-1;">
764
+ <h3 style="margin:0 0 12px 0;font-size:14px;font-weight:600;color:#666;">网关信息</h3>
765
+ <table style="width:100%;border-collapse:collapse;font-size:13px;">
766
+ <tr><td style="padding:8px 0;color:#666;width:120px;">网关名称</td><td style="padding:8px 0;font-weight:500;">${gi.gatewayName || '-'}</td></tr>
767
+ <tr><td style="padding:8px 0;color:#666;">模型地址</td><td style="padding:8px 0;font-family:monospace;font-size:11px;">${gi.modelBaseUrl || '-'}</td></tr>
768
+ <tr><td style="padding:8px 0;color:#666;">API Key</td><td style="padding:8px 0;"><code style="background:#fff;padding:4px 8px;border-radius:4px;font-size:11px;">${gi.apiKey || '未获取'}</code></td></tr>
769
+ </table>
770
+ </div>
771
+ </div>
772
+ `;
773
+ document.getElementById('account-info').innerHTML = html;
774
+ }
775
+
776
+ async loadCreditsStats() {
777
+ try {
778
+ const data = await this.kernelClient.call('evol.get_credits_stats', {
779
+ kiteToken: this.kiteToken,
780
+ period: 'day'
781
+ });
782
+
783
+ if (data.success) {
784
+ const html = '<h4>今日积分消耗</h4><p style="font-size:24px;font-weight:600;color:#667eea;">' +
785
+ (data.data?.total || 0) + ' 积分</p>';
786
+ document.getElementById('credits-stats').innerHTML = html;
787
+ } else {
788
+ document.getElementById('credits-stats').innerHTML = '<p style="color:#e74c3c;">' + data.msg + '</p>';
789
+ }
790
+ } catch (err) {
791
+ document.getElementById('credits-stats').innerHTML = '<p style="color:#e74c3c;">加载失败</p>';
792
+ }
793
+ }
794
+
795
+ refreshModuleList() {
796
+ // 检查是否在列表页(详情页隐藏 = 在列表页)
797
+ const isListView = document.getElementById('module-detail')?.classList.contains('hidden');
798
+ if (!isListView) {
799
+ console.log('[Evol] In detail view, skipping list refresh');
800
+ return;
801
+ }
802
+
803
+ // 防抖:300ms 内多次调用只执行最后一次
804
+ if (this.refreshDebounceTimer) {
805
+ clearTimeout(this.refreshDebounceTimer);
806
+ }
807
+
808
+ this.refreshDebounceTimer = setTimeout(() => {
809
+ // 再次检查是否还在列表页
810
+ const stillInListView = document.getElementById('module-detail')?.classList.contains('hidden');
811
+ if (!stillInListView) {
812
+ console.log('[Evol] Switched to detail view during debounce, skipping list refresh');
813
+ return;
814
+ }
815
+
816
+ // 记录更新时间
817
+ this.lastModuleListUpdate = Date.now();
818
+
819
+ // 重置 1 分钟定时器
820
+ if (this.moduleListRefreshTimer) {
821
+ clearTimeout(this.moduleListRefreshTimer);
822
+ }
823
+
824
+ // 设置新的定时器(1分钟后刷新)
825
+ this.moduleListRefreshTimer = setTimeout(() => {
826
+ if (this.currentPage === 'modules') {
827
+ // 检查是否在列表页
828
+ const isListView = document.getElementById('module-detail')?.classList.contains('hidden');
829
+ if (isListView) {
830
+ console.log('[Evol] Auto-refreshing module list (1 minute timeout)');
831
+ this.loadModules(false);
832
+ }
833
+ }
834
+ }, this.MODULE_LIST_REFRESH_INTERVAL);
835
+
836
+ // 执行刷新(不重置视图)
837
+ this.loadModules(false);
838
+ }, this.REFRESH_DEBOUNCE_DELAY);
839
+ }
840
+
841
+ async loadModules(resetView = true) {
842
+ // 检查是否在列表页(详情页隐藏 = 在列表页)
843
+ const isListView = document.getElementById('module-detail')?.classList.contains('hidden');
844
+
845
+ // 如果不在列表页且不需要重置视图,直接返回
846
+ if (!isListView && !resetView) {
847
+ return;
848
+ }
849
+
850
+ // 只在需要时重置视图(避免在详情页时被强制切回列表页)
851
+ if (resetView) {
852
+ document.getElementById('modules-list-header')?.classList.remove('hidden');
853
+ document.getElementById('modules-table')?.closest('.panel')?.classList.remove('hidden');
854
+ document.getElementById('module-detail')?.classList.add('hidden');
855
+ document.getElementById('statistics-panel')?.classList.remove('hidden');
856
+ document.getElementById('registry-test-section')?.classList.remove('hidden');
857
+ document.getElementById('registry-test-output')?.classList.remove('hidden');
858
+
859
+ // Load statistics and start auto-refresh
860
+ this.loadModuleStats();
861
+ this.startStatsAutoRefresh();
862
+ }
863
+
864
+ try {
865
+ // 获取模块列表和运行状态
866
+ const result = await this.callRpc('launcher.list_modules', {});
867
+ const modules = result.modules || [];
868
+
869
+ // RPC 返回后再次检查是否还在列表页
870
+ const stillInListView = document.getElementById('module-detail')?.classList.contains('hidden');
871
+ if (!stillInListView && !resetView) {
872
+ console.log('[Evol] Switched to detail view during RPC, skipping list update');
873
+ return;
874
+ }
875
+
876
+ const tbody = document.getElementById('modules-tbody');
877
+ if (modules.length === 0) {
878
+ tbody.innerHTML = '<tr><td colspan="12" class="text-muted" style="text-align:center;padding:40px;">暂无模块</td></tr>';
879
+ return;
880
+ }
881
+
882
+ let html = '';
883
+ modules.forEach(mod => {
884
+ // 状态圆点
885
+ const stateClass = mod.state === 'enabled' ? 'enabled' : mod.state === 'manual' ? 'manual' : 'disabled';
886
+
887
+ // 运行状态判断
888
+ const running = mod.actual_state ? mod.actual_state.startsWith('running') : false;
889
+ const pending = this.moduleActionPending.has(mod.name);
890
+ const pendingAction = this.moduleActionPending.get(mod.name);
891
+
892
+ const runningStatus = pending
893
+ ? `<span style="color:var(--warning);">${pendingAction === 'start' ? '启动中…' : '停止中…'}</span>`
894
+ : running
895
+ ? '<span style="color:var(--gray-400);">运行中</span>'
896
+ : '<span style="color:var(--gray-400);">已停止</span>';
897
+
898
+ // 默认状态下拉选项
899
+ // 检查是否有 pending 的状态变更
900
+ const stateChangePending = this.moduleStateChangePending.has(mod.name);
901
+ const pendingState = stateChangePending ? this.moduleStateChangePending.get(mod.name).newState : mod.state;
902
+
903
+ const stateOptions = `
904
+ <option value="enabled" ${pendingState === 'enabled' ? 'selected' : ''}>自动</option>
905
+ <option value="manual" ${pendingState === 'manual' ? 'selected' : ''}>手动</option>
906
+ <option value="disabled" ${pendingState === 'disabled' ? 'selected' : ''}>禁用</option>
907
+ `;
908
+
909
+ const stateSelectDisabled = stateChangePending ? 'disabled' : '';
910
+
911
+ // 统一操作按钮逻辑
912
+ const isCore = ['kernel', 'launcher'].includes(mod.name);
913
+ const displayOrder = mod.display_order || 0;
914
+ const isHighOrder = displayOrder >= 80;
915
+ const isDisabledState = mod.state === 'disabled';
916
+
917
+ // 完全禁止操作的条件:核心模块 或 display_order>=80 或 disabled状态
918
+ const fullyDisabled = isCore || isHighOrder || isDisabledState;
919
+
920
+ let btnHtml = '';
921
+ if (!fullyDisabled) {
922
+ // 只有非禁用模块才显示按钮
923
+ let btnClass, btnLabel, btnAction, btnDisabled;
924
+
925
+ if (pending) {
926
+ // 操作中
927
+ btnClass = 'btn-warning';
928
+ btnLabel = pendingAction === 'start' ? '启动中…' : '停止中…';
929
+ btnAction = '';
930
+ btnDisabled = 'disabled';
931
+ } else if (running) {
932
+ // 运行中 → 可停止
933
+ btnClass = 'btn-danger';
934
+ btnLabel = '停止';
935
+ btnAction = `onclick="app.stopModule('${mod.name}')"`;
936
+ btnDisabled = '';
937
+ } else {
938
+ // 已停止 → 可启动
939
+ btnClass = 'btn-success';
940
+ btnLabel = '启动';
941
+ btnAction = `onclick="app.startModule('${mod.name}')"`;
942
+ btnDisabled = '';
943
+ }
944
+
945
+ btnHtml = `<button class="btn btn-sm ${btnClass}" ${btnAction} ${btnDisabled} style="min-width:82px;">${btnLabel}</button>`;
946
+ }
947
+
948
+ // 格式化运行时长
949
+ const formatUptime = (seconds) => {
950
+ if (!seconds) return '-';
951
+ if (seconds < 60) return `${Math.floor(seconds)}秒`;
952
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}分钟`;
953
+ const h = Math.floor(seconds / 3600);
954
+ const m = Math.floor((seconds % 3600) / 60);
955
+ return m > 0 ? `${h}小时${m}分` : `${h}小时`;
956
+ };
957
+
958
+ // 格式化启动耗时
959
+ const formatStartupTime = (seconds) => {
960
+ if (!seconds) return '-';
961
+ if (seconds < 1) return `${Math.floor(seconds * 1000)}ms`;
962
+ return `${seconds.toFixed(1)}秒`;
963
+ };
964
+
965
+ // 计算启动耗时(ready_at - started_at)
966
+ // 只有在模块运行中且两个时间戳都存在时才计算
967
+ const startupTime = (running && mod.ready_at && mod.started_at)
968
+ ? formatStartupTime(mod.ready_at - mod.started_at)
969
+ : '-';
970
+
971
+ // 计算运行时长(只有运行中才显示)
972
+ const uptime = (running && mod.started_at)
973
+ ? formatUptime(Date.now() / 1000 - mod.started_at)
974
+ : '-';
975
+
976
+ // Ping 状态(智能使用后端数据和缓存)
977
+ let pingStatus = 'never';
978
+ let latencyInfo = {};
979
+
980
+ // 1. 尝试从后端获取数据
981
+ if (mod.latency && mod.latency.status) {
982
+ pingStatus = mod.latency.status;
983
+ latencyInfo = mod.latency;
984
+ } else if (mod.ping_status) {
985
+ // 兼容旧格式
986
+ pingStatus = mod.ping_status;
987
+ latencyInfo = mod.latency || {};
988
+ }
989
+
990
+ // 2. 如果后端数据无效,优先使用缓存
991
+ const cached = this.lastPingData[mod.name];
992
+ if (cached && cached.status && cached.last_update) {
993
+ const now = Date.now() / 1000;
994
+ const cacheAge = now - cached.last_update;
995
+ const backendAge = latencyInfo.last_update ? now - latencyInfo.last_update : Infinity;
996
+
997
+ // 如果后端数据是 never,但缓存有有效数据且不太旧(10秒内),使用缓存
998
+ if (pingStatus === 'never' && cached.status !== 'never' && cacheAge < 10) {
999
+ pingStatus = cached.status;
1000
+ latencyInfo = cached;
1001
+ }
1002
+ // 如果后端数据是 timeout,但缓存是 ok 且更新(缓存比后端新),使用缓存
1003
+ else if (pingStatus === 'timeout' && cached.status === 'ok' && cacheAge < backendAge) {
1004
+ pingStatus = cached.status;
1005
+ latencyInfo = cached;
1006
+ }
1007
+ // 如果缓存比后端数据更新(缓存时间戳更大),使用缓存
1008
+ else if (cached.last_update > (latencyInfo.last_update || 0)) {
1009
+ pingStatus = cached.status;
1010
+ latencyInfo = cached;
1011
+ }
1012
+ }
1013
+
1014
+ const outbound = latencyInfo.outbound;
1015
+ const inbound = latencyInfo.inbound;
1016
+
1017
+ let pingText = '-';
1018
+ let pingClass = ''; // 默认无类名,使用 td 默认颜色
1019
+
1020
+ if (pingStatus === 'ok' && outbound !== undefined && inbound !== undefined) {
1021
+ const totalLatency = outbound + inbound;
1022
+ pingText = `${totalLatency.toFixed(1)}ms`;
1023
+
1024
+ // 根据延迟设置颜色
1025
+ if (totalLatency <= 100) {
1026
+ pingClass = 'success'; // 绿色
1027
+ } else if (totalLatency <= 500) {
1028
+ pingClass = 'warning'; // 黄色
1029
+ } else {
1030
+ pingClass = 'error'; // 红色
1031
+ }
1032
+ } else if (pingStatus === 'timeout') {
1033
+ pingText = '超时';
1034
+ pingClass = 'error';
1035
+ }
1036
+
1037
+ html += `<tr data-module="${mod.name}" class="module-row" onclick="app.showModuleDetails('${mod.name}')">
1038
+ <td><span class="module-state-dot ${stateClass}"></span></td>
1039
+ <td><strong>${mod.display_name || mod.name}</strong> <span style="color:#999;font-size:12px;">(${mod.name})</span></td>
1040
+ <td><span class="module-type-badge type-${mod.type || 'unknown'}">${mod.type || '?'}</span></td>
1041
+ <td>${mod.version || '-'}</td>
1042
+ <td>${mod.preferred_port || '-'}</td>
1043
+ <td>${mod.pid || '-'}</td>
1044
+ <td>${startupTime}</td>
1045
+ <td class="uptime-cell" data-started-at="${running && mod.started_at ? mod.started_at : ''}">${uptime}</td>
1046
+ <td>${mod.launch_count || 0}</td>
1047
+ <td class="ping-cell"><span class="text-${pingClass}">${pingText}</span></td>
1048
+ <td>${runningStatus}</td>
1049
+ <td onclick="event.stopPropagation()">
1050
+ <select class="module-state-select" data-module="${mod.name}" onchange="app.onModuleStateChange(this)" ${stateSelectDisabled}>
1051
+ ${stateOptions}
1052
+ </select>
1053
+ </td>
1054
+ <td onclick="event.stopPropagation()">
1055
+ ${btnHtml}
1056
+ </td>
1057
+ </tr>`;
1058
+ });
1059
+
1060
+ // 如果有下拉框正在交互(打开状态),延迟渲染,避免打断用户操作
1061
+ const activeSelect = tbody.querySelector('.module-state-select:focus');
1062
+ if (activeSelect) {
1063
+ this._pendingModulesHtml = html;
1064
+ const applyPending = () => {
1065
+ if (this._pendingModulesHtml) {
1066
+ document.getElementById('modules-tbody').innerHTML = this._pendingModulesHtml;
1067
+ this._pendingModulesHtml = null;
1068
+ }
1069
+ };
1070
+ activeSelect.addEventListener('blur', applyPending, { once: true });
1071
+ } else {
1072
+ this._pendingModulesHtml = null;
1073
+ tbody.innerHTML = html;
1074
+ }
1075
+
1076
+ // 缓存模块数据用于秒定时器更新
1077
+ this.modulesData = modules;
1078
+
1079
+ // 启动秒定时器(如果还没启动)
1080
+ this.startSecondTimer();
1081
+
1082
+ // 启动 ping 数据刷新定时器(如果还没启动)
1083
+ this.startPingRefresh();
1084
+ } catch (err) {
1085
+ console.error('[Evol] Load modules failed:', err);
1086
+ document.getElementById('modules-tbody').innerHTML =
1087
+ `<tr><td colspan="12" style="text-align:center;padding:40px;color:#e74c3c;">加载失败: ${err.message}</td></tr>`;
1088
+ }
1089
+ }
1090
+
1091
+ async loadModuleStats() {
1092
+ try {
1093
+ // 对齐 Web 模块:使用 kernel.health 获取运行时长
1094
+ const health = await this.callRpc('kernel.health', {});
1095
+ const eventStats = health.event_stats || {};
1096
+
1097
+ // 对齐 Web 模块:使用 kernel.stats 获取统计数据
1098
+ const stats = await this.callRpc('kernel.stats', {});
1099
+ const counters = stats.counters || {};
1100
+ const rpcStats = stats.rpc || {};
1101
+
1102
+ // 获取模块列表(用于计算模块数量和注册记录)
1103
+ let modules = [];
1104
+ try {
1105
+ const modulesRes = await this.callRpc('launcher.list_modules', {});
1106
+ modules = modulesRes.modules || [];
1107
+ } catch (err) {
1108
+ if (!err.message.includes('not ready')) {
1109
+ console.warn('[Evol] Failed to get modules for stats:', err);
1110
+ }
1111
+ }
1112
+
1113
+ // 记录 Kernel 运行时长基准(用于秒定时器实时更新)
1114
+ const kernelUptime = eventStats.uptime_seconds || 0;
1115
+ if (kernelUptime > 0) {
1116
+ this.kernelUptimeBase = kernelUptime;
1117
+ this.kernelUptimeTimestamp = Date.now() / 1000; // 记录获取时刻
1118
+ }
1119
+
1120
+ // 计算模块数量
1121
+ const moduleCount = modules.length;
1122
+
1123
+ // 获取所有注册记录并分类统计
1124
+ let registryByCategory = {
1125
+ modules: 0, // module.* 字段
1126
+ rpc: 0, // tools.rpc.* 字段
1127
+ hook: 0, // tools.hook.* 字段
1128
+ api: 0 // tools.api.* 字段
1129
+ };
1130
+ let totalRecords = 0;
1131
+
1132
+ for (const mod of modules) {
1133
+ try {
1134
+ // 对齐 Web 模块:使用 registry.lookup 查询每个模块的注册记录
1135
+ const regRes = await this.callRpc('registry.lookup', { module: mod.name });
1136
+ const records = regRes.results || [];
1137
+ totalRecords += records.length;
1138
+
1139
+ // 按字段路径分类
1140
+ for (const rec of records) {
1141
+ const field = rec.field || '';
1142
+ if (field.startsWith('module.')) {
1143
+ registryByCategory.modules++;
1144
+ } else if (field.startsWith('tools.rpc.')) {
1145
+ registryByCategory.rpc++;
1146
+ } else if (field.startsWith('tools.hook.')) {
1147
+ registryByCategory.hook++;
1148
+ } else if (field.startsWith('tools.api.')) {
1149
+ registryByCategory.api++;
1150
+ }
1151
+ }
1152
+ } catch (e) {
1153
+ // 模块可能还未注册,忽略错误
1154
+ }
1155
+ }
1156
+
1157
+ // 事件统计
1158
+ const eventsRouted = counters.events_routed || 0;
1159
+
1160
+ // RPC 调用统计
1161
+ const rpcCalls = rpcStats.total || 0;
1162
+
1163
+ // 更新 UI
1164
+ document.getElementById('stat-uptime').textContent = this.formatUptime(kernelUptime);
1165
+ document.getElementById('stat-modules').textContent = moduleCount;
1166
+ document.getElementById('stat-registry').textContent = totalRecords;
1167
+ document.getElementById('stat-rpc').textContent = registryByCategory.rpc;
1168
+ document.getElementById('stat-hooks').textContent = registryByCategory.hook;
1169
+ document.getElementById('stat-api').textContent = registryByCategory.api;
1170
+ document.getElementById('stat-events').textContent = eventsRouted;
1171
+ document.getElementById('stat-rpc-calls').textContent = rpcCalls;
1172
+ } catch (err) {
1173
+ console.error('[Evol] Load stats failed:', err);
1174
+ // 不清空 UI,保留上次的值
1175
+ }
1176
+ }
1177
+
1178
+ formatUptime(seconds) {
1179
+ // 对齐 Web 模块的格式化逻辑
1180
+ if (seconds < 60) return `${Math.floor(seconds)}秒`;
1181
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}分钟`;
1182
+ if (seconds < 86400) {
1183
+ const h = Math.floor(seconds / 3600);
1184
+ const m = Math.floor((seconds % 3600) / 60);
1185
+ return m > 0 ? `${h}小时${m}分` : `${h}小时`;
1186
+ }
1187
+ const d = Math.floor(seconds / 86400);
1188
+ const h = Math.floor((seconds % 86400) / 3600);
1189
+ return h > 0 ? `${d}天${h}小时` : `${d}天`;
1190
+ }
1191
+
1192
+ startStatsAutoRefresh() {
1193
+ if (this.statsRefreshTimer) clearInterval(this.statsRefreshTimer);
1194
+ this.statsRefreshTimer = setInterval(() => {
1195
+ if (this.currentPage === 'modules') {
1196
+ this.loadModuleStats();
1197
+ }
1198
+ }, 5000);
1199
+ }
1200
+
1201
+ startSecondTimer() {
1202
+ if (this.secondTimer) return; // 已经启动,不重复启动
1203
+
1204
+ let lastMinuteUpdate = 0; // 记录上次分钟更新的时间戳
1205
+
1206
+ this.secondTimer = setInterval(() => {
1207
+ if (this.currentPage !== 'modules') return; // 不在模块页面时不更新
1208
+
1209
+ const now = Date.now() / 1000;
1210
+ const currentMinute = Math.floor(now / 60);
1211
+
1212
+ // ① 更新统计面板的 Kernel 运行时长
1213
+ if (this.kernelUptimeBase && this.kernelUptimeTimestamp) {
1214
+ const kernelUptime = this.kernelUptimeBase + (now - this.kernelUptimeTimestamp);
1215
+ const el = document.getElementById('stat-uptime');
1216
+ if (el) {
1217
+ if (kernelUptime < 60 || currentMinute !== lastMinuteUpdate) {
1218
+ el.textContent = this.formatUptime(kernelUptime);
1219
+ }
1220
+ }
1221
+ }
1222
+
1223
+ // ② 更新模块列表的运行时长
1224
+ document.querySelectorAll('.uptime-cell').forEach(cell => {
1225
+ const startedAt = parseFloat(cell.getAttribute('data-started-at'));
1226
+ if (!startedAt) return;
1227
+
1228
+ const uptime = now - startedAt;
1229
+
1230
+ // 小于 1 分钟:每秒更新
1231
+ // 大于等于 1 分钟:每分钟更新(检查分钟是否变化)
1232
+ if (uptime < 60 || currentMinute !== lastMinuteUpdate) {
1233
+ cell.textContent = this.formatUptime(uptime);
1234
+ }
1235
+ });
1236
+
1237
+ // 更新分钟标记
1238
+ if (currentMinute !== lastMinuteUpdate) {
1239
+ lastMinuteUpdate = currentMinute;
1240
+ }
1241
+ }, 1000); // 每秒执行
1242
+ }
1243
+
1244
+ stopSecondTimer() {
1245
+ if (this.secondTimer) {
1246
+ clearInterval(this.secondTimer);
1247
+ this.secondTimer = null;
1248
+ }
1249
+ }
1250
+
1251
+ async updatePingData() {
1252
+ try {
1253
+ // 检查连接状态
1254
+ if (!this.kernelClient || !this.kernelClient.authenticated) {
1255
+ console.warn('[Evol] Cannot update ping data: not authenticated');
1256
+ return;
1257
+ }
1258
+
1259
+ // 直接调用 kernel.latencies 获取最新 ping 数据
1260
+ const result = await this.callRpc('kernel.latencies', {});
1261
+ const latencies = result.latencies || {};
1262
+
1263
+ console.log('[Evol] Ping data received:', Object.keys(latencies).length, 'modules');
1264
+
1265
+ // 更新缓存
1266
+ this.lastPingData = latencies;
1267
+
1268
+ // 更新模块列表中的 ping 状态
1269
+ document.querySelectorAll('.module-row').forEach(row => {
1270
+ const moduleName = row.getAttribute('data-module');
1271
+ if (!moduleName) return;
1272
+
1273
+ const latencyInfo = latencies[moduleName] || {};
1274
+ const pingStatus = latencyInfo.status || 'never';
1275
+ const outbound = latencyInfo.outbound;
1276
+ const inbound = latencyInfo.inbound;
1277
+
1278
+ // 找到 ping 状态单元格
1279
+ const pingCell = row.querySelector('.ping-cell');
1280
+ if (!pingCell) return;
1281
+
1282
+ let pingText = '-';
1283
+ let pingClass = ''; // 默认无类名,使用 td 默认颜色
1284
+
1285
+ if (pingStatus === 'ok' && outbound !== undefined && inbound !== undefined) {
1286
+ const totalLatency = outbound + inbound;
1287
+ pingText = `${totalLatency.toFixed(1)}ms`;
1288
+
1289
+ // 根据延迟设置颜色
1290
+ if (totalLatency <= 100) {
1291
+ pingClass = 'success'; // 绿色
1292
+ } else if (totalLatency <= 500) {
1293
+ pingClass = 'warning'; // 黄色
1294
+ } else {
1295
+ pingClass = 'error'; // 红色
1296
+ }
1297
+ } else if (pingStatus === 'timeout') {
1298
+ pingText = '超时';
1299
+ pingClass = 'error';
1300
+ }
1301
+
1302
+ pingCell.innerHTML = `<span class="text-${pingClass}">${pingText}</span>`;
1303
+ });
1304
+ } catch (err) {
1305
+ console.warn('[Evol] Failed to update ping data:', err);
1306
+ }
1307
+ }
1308
+
1309
+ startPingRefresh() {
1310
+ if (this.pingRefreshTimer) return; // 已经启动
1311
+
1312
+ // 延迟 2 秒后执行第一次(等待连接稳定)
1313
+ setTimeout(() => {
1314
+ if (this.kernelClient && this.kernelClient.authenticated) {
1315
+ this.updatePingData();
1316
+ }
1317
+ }, 2000);
1318
+
1319
+ // 每 5 秒更新一次(触发 Kernel 使用 5 秒 ping 间隔)
1320
+ this.pingRefreshTimer = setInterval(() => {
1321
+ if (this.currentPage === 'modules' && this.kernelClient && this.kernelClient.authenticated) {
1322
+ this.updatePingData();
1323
+ }
1324
+ }, 5000);
1325
+ }
1326
+
1327
+ stopPingRefresh() {
1328
+ if (this.pingRefreshTimer) {
1329
+ clearInterval(this.pingRefreshTimer);
1330
+ this.pingRefreshTimer = null;
1331
+ }
1332
+ }
1333
+
1334
+ async showModuleDetails(moduleName) {
1335
+ try {
1336
+ // 对齐 Web 模块:使用 launcher.get_module_config 获取完整配置
1337
+ const mod = await this.callRpc('launcher.get_module_config', { module_name: moduleName });
1338
+
1339
+ // 隐藏列表,显示详情
1340
+ document.getElementById('modules-list-header')?.classList.add('hidden');
1341
+ document.getElementById('modules-table')?.closest('.panel')?.classList.add('hidden');
1342
+ document.getElementById('statistics-panel')?.classList.add('hidden');
1343
+ document.getElementById('registry-test-section')?.classList.add('hidden');
1344
+ document.getElementById('registry-test-output')?.classList.add('hidden');
1345
+ document.getElementById('module-detail')?.classList.remove('hidden');
1346
+
1347
+ // 保存当前模块名(用于后续操作)
1348
+ this.currentModuleName = moduleName;
1349
+
1350
+ // Header
1351
+ document.getElementById('module-detail-name').textContent = mod.display_name || mod.name;
1352
+
1353
+ // 【区块1:模块标识】
1354
+ this._setVal('mod-source-path', mod.source_path || '');
1355
+ this._setVal('mod-meta-name', mod.name || '');
1356
+ this._setVal('mod-meta-type', mod.type || '');
1357
+ this._setVal('mod-meta-runtime', mod.runtime || '');
1358
+ this._setVal('mod-meta-entry', mod.entry || '');
1359
+ this._setVal('mod-meta-display-name', mod.display_name || '');
1360
+ this._setVal('mod-meta-version', mod.version || '');
1361
+
1362
+ // 【区块2:启动配置】
1363
+ this._setVal('mod-meta-state', mod.state || 'enabled');
1364
+ const monitorValue = mod.monitor != null ? String(mod.monitor) : 'true';
1365
+ this._setVal('mod-meta-monitor', monitorValue);
1366
+
1367
+ // 【区块3:网络配置】
1368
+ this._setVal('mod-meta-port', mod.preferred_port != null ? mod.preferred_port : '');
1369
+
1370
+ // 监听地址:根据模块设置默认值
1371
+ let defaultIp = '127.0.0.1';
1372
+ if (mod.name === 'web' || mod.name === 'evol') {
1373
+ defaultIp = '0.0.0.0'; // web/evol 模块默认允许远程
1374
+ }
1375
+ this._setVal('mod-meta-ip', mod.advertise_ip || defaultIp);
1376
+
1377
+ // 运行状态
1378
+ await this._updateModuleDetailStatus(moduleName);
1379
+
1380
+ // 【区块4:模块配置文件】
1381
+ const configSection = document.getElementById('module-config-section');
1382
+ const configTree = document.getElementById('module-config-tree');
1383
+ if (mod.has_config && mod.config) {
1384
+ configSection?.classList.remove('hidden');
1385
+ if (configTree) {
1386
+ configTree.innerHTML = '';
1387
+ this._renderConfigTree(mod.config, configTree, '');
1388
+ }
1389
+ } else {
1390
+ configSection?.classList.add('hidden');
1391
+ }
1392
+
1393
+ // 【区块5:事件信息】
1394
+ await this._loadModuleEvents(moduleName);
1395
+
1396
+ // 绑定自动保存事件监听器
1397
+ this._bindAutoSaveListeners();
1398
+
1399
+ // 绑定事件刷新按钮
1400
+ const refreshBtn = document.getElementById('btn-refresh-events');
1401
+ if (refreshBtn) {
1402
+ refreshBtn.onclick = () => this._loadModuleEvents(moduleName);
1403
+ }
1404
+
1405
+ } catch (err) {
1406
+ alert('加载模块详情失败: ' + err.message);
1407
+ console.error('[Evol] showModuleDetails failed:', err);
1408
+ }
1409
+ }
1410
+
1411
+ // 辅助方法:设置表单值
1412
+ _setVal(id, value) {
1413
+ const el = document.getElementById(id);
1414
+ if (el) {
1415
+ if (el.tagName === 'SELECT') {
1416
+ el.value = value;
1417
+ } else if (el.type === 'number') {
1418
+ el.value = value === '' ? '' : value;
1419
+ } else {
1420
+ el.value = value;
1421
+ }
1422
+ }
1423
+ }
1424
+
1425
+ // 更新模块详情页的运行状态
1426
+ async _updateModuleDetailStatus(moduleName) {
1427
+ try {
1428
+ const result = await this.callRpc('launcher.list_modules', {});
1429
+ const modules = result.modules || [];
1430
+ const mod = modules.find(m => m.name === moduleName);
1431
+
1432
+ if (mod) {
1433
+ const running = mod.actual_state ? mod.actual_state.startsWith('running') : false;
1434
+ const pending = this.moduleActionPending.has(mod.name);
1435
+ const pendingAction = this.moduleActionPending.get(mod.name);
1436
+
1437
+ // 更新运行状态文本和状态点
1438
+ const statusEl = document.getElementById('module-detail-run-status');
1439
+ const dotEl = document.getElementById('module-detail-status-dot');
1440
+
1441
+ if (statusEl && dotEl) {
1442
+ if (pending) {
1443
+ statusEl.textContent = pendingAction === 'start' ? '启动中…' : '停止中…';
1444
+ statusEl.style.color = 'var(--warning)';
1445
+ dotEl.style.background = 'var(--warning)';
1446
+ } else if (running) {
1447
+ statusEl.textContent = '运行中';
1448
+ statusEl.style.color = 'var(--success)';
1449
+ dotEl.style.background = 'var(--success)';
1450
+ } else {
1451
+ statusEl.textContent = '已停止';
1452
+ statusEl.style.color = 'var(--gray-400)';
1453
+ dotEl.style.background = 'var(--gray-400)';
1454
+ }
1455
+ }
1456
+
1457
+ // 更新统一操作按钮
1458
+ const actionBtn = document.getElementById('btn-detail-action');
1459
+ if (actionBtn) {
1460
+ const isCore = ['kernel', 'launcher'].includes(mod.name);
1461
+ const displayOrder = mod.display_order || 0;
1462
+ const isHighOrder = displayOrder >= 80;
1463
+ const isDisabledState = mod.state === 'disabled';
1464
+ const fullyDisabled = isCore || isHighOrder || isDisabledState;
1465
+
1466
+ if (fullyDisabled) {
1467
+ // 完全禁用 - 隐藏按钮
1468
+ actionBtn.style.display = 'none';
1469
+ } else {
1470
+ actionBtn.style.display = '';
1471
+
1472
+ if (pending) {
1473
+ // 操作中
1474
+ actionBtn.className = 'btn btn-sm btn-warning';
1475
+ actionBtn.textContent = pendingAction === 'start' ? '启动中…' : '停止中…';
1476
+ actionBtn.disabled = true;
1477
+ actionBtn.onclick = null;
1478
+ } else if (running) {
1479
+ // 运行中 → 可停止
1480
+ actionBtn.className = 'btn btn-sm btn-danger';
1481
+ actionBtn.textContent = '停止';
1482
+ actionBtn.disabled = false;
1483
+ actionBtn.onclick = () => this.stopModuleFromDetail();
1484
+ } else {
1485
+ // 已停止 → 可启动
1486
+ actionBtn.className = 'btn btn-sm btn-success';
1487
+ actionBtn.textContent = '启动';
1488
+ actionBtn.disabled = false;
1489
+ actionBtn.onclick = () => this.startModuleFromDetail();
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+ } catch (err) {
1495
+ console.error('[Evol] Update detail status failed:', err);
1496
+ }
1497
+ }
1498
+
1499
+ // 渲染配置树(简化版)
1500
+ _renderConfigTree(config, container, prefix) {
1501
+ if (!config || typeof config !== 'object') return;
1502
+
1503
+ for (const [key, value] of Object.entries(config)) {
1504
+ const fullKey = prefix ? `${prefix}.${key}` : key;
1505
+ const div = document.createElement('div');
1506
+ div.style.marginBottom = '8px';
1507
+
1508
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1509
+ // 嵌套对象
1510
+ div.innerHTML = `<strong style="color:#666;">${key}:</strong>`;
1511
+ container.appendChild(div);
1512
+ const subContainer = document.createElement('div');
1513
+ subContainer.style.marginLeft = '20px';
1514
+ container.appendChild(subContainer);
1515
+ this._renderConfigTree(value, subContainer, fullKey);
1516
+ } else {
1517
+ // 简单值
1518
+ div.innerHTML = `
1519
+ <label style="display:inline-block;width:200px;font-size:13px;color:#666;">${key}:</label>
1520
+ <input type="text" value="${value}" data-config-key="${fullKey}"
1521
+ style="padding:4px 8px;border:1px solid #ddd;border-radius:4px;width:300px;">
1522
+ `;
1523
+ container.appendChild(div);
1524
+ }
1525
+ }
1526
+ }
1527
+
1528
+ // 加载模块事件信息
1529
+ async _loadModuleEvents(moduleName) {
1530
+ const container = document.getElementById('module-events-content');
1531
+ if (!container) return;
1532
+
1533
+ try {
1534
+ container.innerHTML = '<div style="text-align:center;padding:20px;color:#999;">加载中...</div>';
1535
+
1536
+ const result = await this.callRpc('kernel.get_module_events', {
1537
+ module_name: moduleName
1538
+ });
1539
+
1540
+ this._renderModuleEvents(result);
1541
+ } catch (err) {
1542
+ container.innerHTML = `<div style="text-align:center;padding:20px;color:#e74c3c;">加载失败: ${err.message}</div>`;
1543
+ console.error('[Evol] Load module events failed:', err);
1544
+ }
1545
+ }
1546
+
1547
+ // 渲染模块事件信息
1548
+ _renderModuleEvents(data) {
1549
+ const container = document.getElementById('module-events-content');
1550
+ if (!container) return;
1551
+
1552
+ const { subscriptions, publications } = data;
1553
+ const activeSubs = new Set(subscriptions.active || []);
1554
+ const declaredSubs = subscriptions.declared || [];
1555
+ const declaredPubs = publications.declared || [];
1556
+
1557
+ let html = '';
1558
+
1559
+ // 订阅的事件
1560
+ html += '<div style="margin-bottom:20px;">';
1561
+ html += '<h5 style="margin:0 0 8px 0;font-size:13px;font-weight:600;color:#555;">订阅的事件</h5>';
1562
+
1563
+ if (declaredSubs.length === 0) {
1564
+ html += '<div style="color:#999;font-size:12px;">无</div>';
1565
+ } else {
1566
+ html += '<div style="display:flex;flex-wrap:wrap;gap:8px;">';
1567
+
1568
+ for (const event of declaredSubs) {
1569
+ const isActive = activeSubs.has(event);
1570
+ const isDynamic = !declaredSubs.includes(event) && activeSubs.has(event);
1571
+
1572
+ let badgeClass = 'event-badge';
1573
+ let badgeColor = '#95a5a6'; // 灰色 - 仅声明
1574
+ let badgeText = '未激活';
1575
+
1576
+ if (isActive) {
1577
+ badgeColor = '#27ae60'; // 绿色 - 已订阅
1578
+ badgeText = '已订阅';
1579
+ }
1580
+
1581
+ html += `
1582
+ <div class="event-badge" style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:#f5f5f5;border-radius:4px;font-size:12px;">
1583
+ <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${badgeColor};" title="${badgeText}"></span>
1584
+ <span style="color:#333;">${event}</span>
1585
+ </div>
1586
+ `;
1587
+ }
1588
+
1589
+ // 动态订阅(运行时订阅但未在 manifest 声明)
1590
+ for (const event of subscriptions.active) {
1591
+ if (!declaredSubs.includes(event)) {
1592
+ html += `
1593
+ <div class="event-badge" style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:#fff3cd;border-radius:4px;font-size:12px;">
1594
+ <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#ff9800;" title="动态订阅"></span>
1595
+ <span style="color:#333;">${event}</span>
1596
+ </div>
1597
+ `;
1598
+ }
1599
+ }
1600
+
1601
+ html += '</div>';
1602
+ }
1603
+ html += '</div>';
1604
+
1605
+ // 发布的事件
1606
+ html += '<div>';
1607
+ html += '<h5 style="margin:0 0 8px 0;font-size:13px;font-weight:600;color:#555;">发布的事件</h5>';
1608
+
1609
+ if (declaredPubs.length === 0) {
1610
+ html += '<div style="color:#999;font-size:12px;">无</div>';
1611
+ } else {
1612
+ html += '<div style="display:flex;flex-wrap:wrap;gap:8px;">';
1613
+
1614
+ for (const event of declaredPubs) {
1615
+ html += `
1616
+ <div class="event-badge" style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:#e3f2fd;border-radius:4px;font-size:12px;">
1617
+ <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#2196f3;" title="声明发布"></span>
1618
+ <span style="color:#333;">${event}</span>
1619
+ </div>
1620
+ `;
1621
+ }
1622
+
1623
+ html += '</div>';
1624
+ }
1625
+ html += '</div>';
1626
+
1627
+ container.innerHTML = html;
1628
+ }
1629
+
1630
+ // 绑定自动保存监听器
1631
+ _bindAutoSaveListeners() {
1632
+ // 防抖保存函数
1633
+ if (!this._debouncedSave) {
1634
+ this._debouncedSave = this._debounce(() => this._saveModuleConfig(), 500);
1635
+ }
1636
+
1637
+ // 绑定元数据字段
1638
+ document.querySelectorAll('#module-detail [data-field]').forEach(el => {
1639
+ el.removeEventListener('input', this._debouncedSave);
1640
+ el.removeEventListener('change', this._debouncedSave);
1641
+ const event = (el.tagName === 'SELECT') ? 'change' : 'input';
1642
+ el.addEventListener(event, this._debouncedSave);
1643
+ });
1644
+
1645
+ // 绑定配置树字段
1646
+ document.querySelectorAll('#module-config-tree [data-config-key]').forEach(el => {
1647
+ el.removeEventListener('input', this._debouncedSave);
1648
+ el.addEventListener('input', this._debouncedSave);
1649
+ });
1650
+ }
1651
+
1652
+ // 防抖函数
1653
+ _debounce(func, wait) {
1654
+ let timeout;
1655
+ return function executedFunction(...args) {
1656
+ const later = () => {
1657
+ clearTimeout(timeout);
1658
+ func(...args);
1659
+ };
1660
+ clearTimeout(timeout);
1661
+ timeout = setTimeout(later, wait);
1662
+ };
1663
+ }
1664
+
1665
+ // 保存模块配置
1666
+ async _saveModuleConfig() {
1667
+ if (!this.currentModuleName) return;
1668
+
1669
+ try {
1670
+ // 收集元数据
1671
+ const metadata = {};
1672
+ document.querySelectorAll('#module-detail [data-field]').forEach(el => {
1673
+ const field = el.dataset.field;
1674
+ let value = el.value;
1675
+
1676
+ // 类型转换
1677
+ if (el.type === 'number') {
1678
+ value = value === '' ? null : parseInt(value);
1679
+ } else if (field === 'monitor') {
1680
+ value = value === 'true';
1681
+ }
1682
+
1683
+ metadata[field] = value;
1684
+ });
1685
+
1686
+ // 收集配置文件
1687
+ const config = {};
1688
+ document.querySelectorAll('#module-config-tree [data-config-key]').forEach(el => {
1689
+ const key = el.dataset.configKey;
1690
+ config[key] = el.value;
1691
+ });
1692
+
1693
+ // 调用更新 RPC
1694
+ await this.callRpc('launcher.update_module_config', {
1695
+ module_name: this.currentModuleName,
1696
+ metadata: metadata,
1697
+ config: Object.keys(config).length > 0 ? config : undefined
1698
+ });
1699
+
1700
+ // 显示保存成功提示(简单版)
1701
+ console.log('[Evol] Module config saved successfully');
1702
+
1703
+ } catch (err) {
1704
+ console.error('[Evol] Save module config failed:', err);
1705
+ alert('保存失败: ' + err.message);
1706
+ }
1707
+ }
1708
+
1709
+ async startModule(moduleName) {
1710
+ if (['kernel', 'launcher'].includes(moduleName)) return; // 静默拦截核心模块
1711
+ if (this.moduleActionPending.has(moduleName)) return; // 防抖
1712
+
1713
+ this.moduleActionPending.set(moduleName, 'start');
1714
+ this.updateModuleButtons(moduleName);
1715
+
1716
+ try {
1717
+ await this.callRpc('launcher.start_module', { name: moduleName });
1718
+ // 延迟刷新,等待模块实际启动后再查询状态
1719
+ setTimeout(async () => {
1720
+ this.moduleActionPending.delete(moduleName);
1721
+ await this.loadModules();
1722
+ // 检查启动结果
1723
+ const result = await this.callRpc('launcher.list_modules', {});
1724
+ const mod = (result.modules || []).find(m => m.name === moduleName);
1725
+ const running = mod?.actual_state?.startsWith('running');
1726
+ if (running) {
1727
+ this.showToast(`${moduleName} 启动成功`, 'success');
1728
+ } else {
1729
+ this.showToast(`${moduleName} 启动超时,请检查日志`, 'error');
1730
+ }
1731
+ }, 1500);
1732
+ } catch (err) {
1733
+ this.moduleActionPending.delete(moduleName);
1734
+ this.updateModuleButtons(moduleName);
1735
+ this.showToast(`启动失败: ${err.message}`, 'error');
1736
+ }
1737
+ }
1738
+
1739
+ async stopModule(moduleName) {
1740
+ if (['kernel', 'launcher'].includes(moduleName)) return; // 静默拦截核心模块
1741
+ if (this.moduleActionPending.has(moduleName)) return; // 防抖
1742
+
1743
+ this.moduleActionPending.set(moduleName, 'stop');
1744
+ this.updateModuleButtons(moduleName);
1745
+
1746
+ try {
1747
+ await this.callRpc('launcher.stop_module', { name: moduleName, reason: 'user_request' });
1748
+ // 延迟刷新,等待模块实际停止后再查询状态
1749
+ setTimeout(async () => {
1750
+ this.moduleActionPending.delete(moduleName);
1751
+ await this.loadModules();
1752
+ // 检查停止结果
1753
+ const result = await this.callRpc('launcher.list_modules', {});
1754
+ const mod = (result.modules || []).find(m => m.name === moduleName);
1755
+ const running = mod?.actual_state?.startsWith('running');
1756
+ if (!running) {
1757
+ this.showToast(`${moduleName} 停止成功`, 'success');
1758
+ } else {
1759
+ this.showToast(`${moduleName} 停止超时,请检查日志`, 'error');
1760
+ }
1761
+ }, 1500);
1762
+ } catch (err) {
1763
+ this.moduleActionPending.delete(moduleName);
1764
+ this.updateModuleButtons(moduleName);
1765
+ this.showToast(`停止失败: ${err.message}`, 'error');
1766
+ }
1767
+ }
1768
+
1769
+ updateModuleButtons(moduleName) {
1770
+ // 判断当前是否在详情页
1771
+ const detailPanel = document.getElementById('module-detail');
1772
+ const isDetailPage = detailPanel && !detailPanel.classList.contains('hidden');
1773
+
1774
+ if (isDetailPage && this.currentModuleName === moduleName) {
1775
+ // 在详情页,只更新详情页状态
1776
+ this._updateModuleDetailStatus(moduleName);
1777
+ } else {
1778
+ // 在列表页,重新渲染列表
1779
+ this.loadModules();
1780
+ }
1781
+ }
1782
+
1783
+ async onModuleStateChange(selectEl) {
1784
+ const moduleName = selectEl.dataset.module;
1785
+ const newState = selectEl.value;
1786
+
1787
+ // 防抖检查:如果该模块正在变更状态,忽略新的操作
1788
+ if (this.moduleStateChangePending.has(moduleName)) {
1789
+ console.log(`[Evol] Module ${moduleName} state change already pending, ignoring`);
1790
+ const pending = this.moduleStateChangePending.get(moduleName);
1791
+ selectEl.value = pending.newState;
1792
+ return;
1793
+ }
1794
+
1795
+ // 记录 pending 状态
1796
+ this.moduleStateChangePending.set(moduleName, { newState, timestamp: Date.now() });
1797
+
1798
+ // 禁用下拉,防止重复操作
1799
+ selectEl.disabled = true;
1800
+
1801
+ // 超时保护(5秒)
1802
+ const timeoutId = setTimeout(() => {
1803
+ console.warn(`[Evol] Module ${moduleName} state change timeout, force cleanup`);
1804
+ this.moduleStateChangePending.delete(moduleName);
1805
+ const sel = document.querySelector(`.module-state-select[data-module="${moduleName}"]`);
1806
+ if (sel) sel.disabled = false;
1807
+ }, 5000);
1808
+
1809
+ try {
1810
+ await this.callRpc('launcher.update_module_config', {
1811
+ module_name: moduleName,
1812
+ metadata: { state: newState }
1813
+ });
1814
+ this.showToast(`模块 ${moduleName} 默认状态已更新为 ${newState}`, 'success');
1815
+
1816
+ const row = selectEl.closest('tr');
1817
+ const dot = row?.querySelector('.module-state-dot');
1818
+ if (dot) {
1819
+ dot.className = `module-state-dot ${newState === 'enabled' ? 'enabled' : newState === 'manual' ? 'manual' : 'disabled'}`;
1820
+ }
1821
+ } catch (err) {
1822
+ this.showToast('更新失败: ' + err.message, 'error');
1823
+ await this.loadModules();
1824
+ } finally {
1825
+ clearTimeout(timeoutId);
1826
+ this.moduleStateChangePending.delete(moduleName);
1827
+ // 重新查找元素(可能已被 loadModules 重新渲染)
1828
+ const currentSelect = document.querySelector(`.module-state-select[data-module="${moduleName}"]`);
1829
+ if (currentSelect) currentSelect.disabled = false;
1830
+ }
1831
+ }
1832
+
1833
+ async restartKite() {
1834
+ // 禁用按钮,防止重复点击
1835
+ const btn = document.getElementById('btn-restart-kite');
1836
+ if (btn) {
1837
+ btn.disabled = true;
1838
+ btn.innerHTML = '<span>⏳</span><span>重启中...</span>';
1839
+ }
1840
+
1841
+ try {
1842
+ const data = await this.callRpc('launcher.restart_launcher', { reason: 'user_request' });
1843
+
1844
+ // 检查是否有错误
1845
+ if (data.error) {
1846
+ this.showToast(`重启失败: ${data.error}`, 'error');
1847
+ if (btn) {
1848
+ btn.disabled = false;
1849
+ btn.innerHTML = '<span>🔄</span><span>重启 Kite</span>';
1850
+ }
1851
+ return;
1852
+ }
1853
+
1854
+ // 成功 - 显示重启提示并轮询检测重启完成
1855
+ this.showToast('Kite 正在重启...', 'success');
1856
+
1857
+ // 轮询检测 Kite 是否重启完成(每 0.5s 检查一次,最多 30s)
1858
+ let attempts = 0;
1859
+ const maxAttempts = 60; // 30s
1860
+ const checkInterval = setInterval(async () => {
1861
+ attempts++;
1862
+ try {
1863
+ // 尝试调用 RPC,如果成功说明重启完成
1864
+ await this.callRpc('kernel.health', {});
1865
+ clearInterval(checkInterval);
1866
+ this.showToast('Kite 重启完成', 'success');
1867
+ window.location.reload();
1868
+ } catch (err) {
1869
+ // 还在重启中,继续等待
1870
+ if (attempts >= maxAttempts) {
1871
+ clearInterval(checkInterval);
1872
+ this.showToast('重启超时,请手动刷新页面', 'warning');
1873
+ if (btn) {
1874
+ btn.disabled = false;
1875
+ btn.innerHTML = '<span>🔄</span><span>重启 Kite</span>';
1876
+ }
1877
+ }
1878
+ }
1879
+ }, 500);
1880
+ } catch (err) {
1881
+ this.showToast(`重启失败: ${err.message}`, 'error');
1882
+ if (btn) {
1883
+ btn.disabled = false;
1884
+ btn.innerHTML = '<span>🔄</span><span>重启 Kite</span>';
1885
+ }
1886
+ }
1887
+ }
1888
+
1889
+ // 详情页操作方法
1890
+ async startModuleFromDetail() {
1891
+ if (!this.currentModuleName) return;
1892
+ const moduleName = this.currentModuleName;
1893
+
1894
+ if (['kernel', 'launcher'].includes(moduleName)) return;
1895
+ if (this.moduleActionPending.has(moduleName)) return;
1896
+
1897
+ this.moduleActionPending.set(moduleName, 'start');
1898
+ await this._updateModuleDetailStatus(moduleName);
1899
+
1900
+ try {
1901
+ await this.callRpc('launcher.start_module', { name: moduleName });
1902
+ // 延迟刷新,等待模块实际启动后再查询状态
1903
+ setTimeout(async () => {
1904
+ this.moduleActionPending.delete(moduleName);
1905
+ await this._updateModuleDetailStatus(moduleName);
1906
+ // 检查启动结果
1907
+ const result = await this.callRpc('launcher.list_modules', {});
1908
+ const mod = (result.modules || []).find(m => m.name === moduleName);
1909
+ const running = mod?.actual_state?.startsWith('running');
1910
+ if (running) {
1911
+ this.showToast(`${moduleName} 启动成功`, 'success');
1912
+ } else {
1913
+ this.showToast(`${moduleName} 启动超时,请检查日志`, 'error');
1914
+ }
1915
+ }, 1500);
1916
+ } catch (err) {
1917
+ this.moduleActionPending.delete(moduleName);
1918
+ await this._updateModuleDetailStatus(moduleName);
1919
+ this.showToast(`启动失败: ${err.message}`, 'error');
1920
+ }
1921
+ }
1922
+
1923
+ async stopModuleFromDetail() {
1924
+ if (!this.currentModuleName) return;
1925
+ const moduleName = this.currentModuleName;
1926
+
1927
+ if (['kernel', 'launcher'].includes(moduleName)) return;
1928
+ if (this.moduleActionPending.has(moduleName)) return;
1929
+
1930
+ this.moduleActionPending.set(moduleName, 'stop');
1931
+ await this._updateModuleDetailStatus(moduleName);
1932
+
1933
+ try {
1934
+ await this.callRpc('launcher.stop_module', { name: moduleName, reason: 'user_request' });
1935
+ // 延迟刷新,等待模块实际停止后再查询状态
1936
+ setTimeout(async () => {
1937
+ this.moduleActionPending.delete(moduleName);
1938
+ await this._updateModuleDetailStatus(moduleName);
1939
+ // 检查停止结果
1940
+ const result = await this.callRpc('launcher.list_modules', {});
1941
+ const mod = (result.modules || []).find(m => m.name === moduleName);
1942
+ const running = mod?.actual_state?.startsWith('running');
1943
+ if (!running) {
1944
+ this.showToast(`${moduleName} 停止成功`, 'success');
1945
+ } else {
1946
+ this.showToast(`${moduleName} 停止超时,请检查日志`, 'error');
1947
+ }
1948
+ }, 1500);
1949
+ } catch (err) {
1950
+ this.moduleActionPending.delete(moduleName);
1951
+ await this._updateModuleDetailStatus(moduleName);
1952
+ this.showToast(`停止失败: ${err.message}`, 'error');
1953
+ }
1954
+ }
1955
+
1956
+ async resetModuleDefaults() {
1957
+ if (!this.currentModuleName) return;
1958
+ if (!confirm(`确定要恢复模块 ${this.currentModuleName} 的默认配置吗?`)) return;
1959
+
1960
+ try {
1961
+ await this.callRpc('launcher.reset_module_config', { module_name: this.currentModuleName });
1962
+ this.showToast('配置已恢复为默认值', 'success');
1963
+ // 重新加载详情
1964
+ await this.showModuleDetails(this.currentModuleName);
1965
+ } catch (err) {
1966
+ this.showToast(`恢复默认值失败: ${err.message}`, 'error');
1967
+ }
1968
+ }
1969
+
1970
+ async toggleConsole() {
1971
+ const console = document.getElementById('realtime-console');
1972
+ const text = document.getElementById('console-toggle-text');
1973
+
1974
+ if (console.style.display === 'none') {
1975
+ // 展开控制台
1976
+ console.style.display = 'block';
1977
+ text.textContent = '收起控制台';
1978
+ this.consoleExpanded = true;
1979
+
1980
+ // 订阅全部事件
1981
+ await this.subscribeAllEvents();
1982
+ } else {
1983
+ // 收起控制台
1984
+ console.style.display = 'none';
1985
+ text.textContent = '展开控制台';
1986
+ this.consoleExpanded = false;
1987
+
1988
+ // 清空事件日志
1989
+ this.eventLogs = [];
1990
+ document.getElementById('console-output').textContent = '';
1991
+
1992
+ // 恢复默认订阅(只订阅 module.* 事件)
1993
+ await this.subscribeDefaultEvents();
1994
+ }
1995
+ }
1996
+
1997
+ async subscribeAllEvents() {
1998
+ if (this.eventSubscribed) return;
1999
+
2000
+ try {
2001
+ // 订阅所有事件(使用通配符)
2002
+ await this.kernelClient.subscribe(['*']);
2003
+ this.eventSubscribed = true;
2004
+ console.log('[Evol] Subscribed to all events');
2005
+ } catch (err) {
2006
+ console.error('[Evol] Failed to subscribe all events:', err);
2007
+ }
2008
+ }
2009
+
2010
+ async subscribeDefaultEvents() {
2011
+ try {
2012
+ // 只订阅 module.* 事件(用于刷新模块列表)
2013
+ await this.kernelClient.subscribe([
2014
+ 'module.started',
2015
+ 'module.stopped',
2016
+ 'module.crashed',
2017
+ 'module.ready',
2018
+ 'module.exiting',
2019
+ 'module.shutdown',
2020
+ 'module.shutdown.ack',
2021
+ 'module.shutdown.ready'
2022
+ ]);
2023
+ this.eventSubscribed = false;
2024
+ console.log('[Evol] Subscribed to default events (module.*)');
2025
+ } catch (err) {
2026
+ console.error('[Evol] Failed to subscribe default events:', err);
2027
+ }
2028
+ }
2029
+
2030
+ clearConsole() {
2031
+ this.eventLogs = [];
2032
+ this.renderEventLogs();
2033
+ }
2034
+
2035
+ // 添加事件日志
2036
+ addEventLog(event, data) {
2037
+ const timestamp = Date.now();
2038
+ const module = this.extractModuleFromEvent(event);
2039
+
2040
+ // 记录已知的模块和事件
2041
+ if (module) this.knownModules.add(module);
2042
+ this.knownEvents.add(event);
2043
+
2044
+ // 添加到日志数组
2045
+ this.eventLogs.unshift({
2046
+ timestamp,
2047
+ module,
2048
+ event,
2049
+ data,
2050
+ raw: JSON.stringify(data)
2051
+ });
2052
+
2053
+ // 限制日志数量(最多保留1000条)
2054
+ if (this.eventLogs.length > 1000) {
2055
+ this.eventLogs = this.eventLogs.slice(0, 1000);
2056
+ }
2057
+
2058
+ // 更新筛选器选项
2059
+ this.updateFilterOptions();
2060
+
2061
+ // 重新渲染
2062
+ this.renderEventLogs();
2063
+ }
2064
+
2065
+ // 从事件名提取模块名
2066
+ extractModuleFromEvent(event) {
2067
+ // 事件格式通常是 module.event_name 或 category.module.event_name
2068
+ const parts = event.split('.');
2069
+ if (parts.length >= 2) {
2070
+ return parts[0]; // 返回第一部分作为模块名
2071
+ }
2072
+ return null;
2073
+ }
2074
+
2075
+ // 更新筛选器选项
2076
+ updateFilterOptions() {
2077
+ // 更新模块筛选器
2078
+ const moduleSelect = document.getElementById('filter-module');
2079
+ if (moduleSelect) {
2080
+ const currentValue = moduleSelect.value;
2081
+ const modules = Array.from(this.knownModules).sort();
2082
+
2083
+ let html = '<option value="">全部模块</option>';
2084
+ modules.forEach(mod => {
2085
+ html += `<option value="${mod}">${mod}</option>`;
2086
+ });
2087
+
2088
+ moduleSelect.innerHTML = html;
2089
+ moduleSelect.value = currentValue;
2090
+ }
2091
+
2092
+ // 更新事件筛选器
2093
+ const eventSelect = document.getElementById('filter-event');
2094
+ if (eventSelect) {
2095
+ const currentValue = eventSelect.value;
2096
+ const events = Array.from(this.knownEvents).sort();
2097
+
2098
+ let html = '<option value="">全部事件</option>';
2099
+ events.forEach(evt => {
2100
+ html += `<option value="${evt}">${evt}</option>`;
2101
+ });
2102
+
2103
+ eventSelect.innerHTML = html;
2104
+ eventSelect.value = currentValue;
2105
+ }
2106
+ }
2107
+
2108
+ // 渲染事件日志
2109
+ renderEventLogs() {
2110
+ const output = document.getElementById('console-output');
2111
+ if (!output) return;
2112
+
2113
+ // 获取筛选条件
2114
+ const filterModule = document.getElementById('filter-module')?.value || '';
2115
+ const filterEvent = document.getElementById('filter-event')?.value || '';
2116
+ const filterKeyword = document.getElementById('filter-keyword')?.value || '';
2117
+ const filterTime = parseInt(document.getElementById('filter-time')?.value || '0');
2118
+
2119
+ // 筛选日志
2120
+ const now = Date.now();
2121
+ const filtered = this.eventLogs.filter(log => {
2122
+ // 时间筛选
2123
+ if (filterTime > 0) {
2124
+ const age = (now - log.timestamp) / 1000; // 秒
2125
+ if (age > filterTime) return false;
2126
+ }
2127
+
2128
+ // 模块筛选
2129
+ if (filterModule && log.module !== filterModule) return false;
2130
+
2131
+ // 事件筛选
2132
+ if (filterEvent && log.event !== filterEvent) return false;
2133
+
2134
+ // 关键词筛选(glob 模式)
2135
+ if (filterKeyword) {
2136
+ const pattern = this.globToRegex(filterKeyword);
2137
+ const searchText = `${log.event} ${log.raw}`.toLowerCase();
2138
+ if (!pattern.test(searchText)) return false;
2139
+ }
2140
+
2141
+ return true;
2142
+ });
2143
+
2144
+ // 渲染(时间倒序,最新的在最上面)
2145
+ let html = '';
2146
+ filtered.forEach(log => {
2147
+ const time = this.formatTimestamp(log.timestamp);
2148
+ const module = log.module || '?';
2149
+ const event = log.event;
2150
+ const data = this.formatEventData(log.data);
2151
+
2152
+ // 根据事件类型设置颜色
2153
+ let color = '#d4d4d4';
2154
+ if (event.includes('error') || event.includes('failed')) {
2155
+ color = '#f48771';
2156
+ } else if (event.includes('warning')) {
2157
+ color = '#dcdcaa';
2158
+ } else if (event.includes('started') || event.includes('success')) {
2159
+ color = '#4ec9b0';
2160
+ }
2161
+
2162
+ html += `<span style="color:#858585;">${time}</span> `;
2163
+ html += `<span style="color:#569cd6;">[${module}]</span> `;
2164
+ html += `<span style="color:${color};">${event}</span> `;
2165
+ html += `<span style="color:#9cdcfe;">${data}</span>\n`;
2166
+ });
2167
+
2168
+ output.innerHTML = html || '<span style="color:#858585;">暂无事件日志</span>';
2169
+
2170
+ // 保持滚动在顶部(因为是倒序显示)
2171
+ output.scrollTop = 0;
2172
+ }
2173
+
2174
+ // 格式化时间戳
2175
+ formatTimestamp(timestamp) {
2176
+ const date = new Date(timestamp);
2177
+ const h = String(date.getHours()).padStart(2, '0');
2178
+ const m = String(date.getMinutes()).padStart(2, '0');
2179
+ const s = String(date.getSeconds()).padStart(2, '0');
2180
+ const ms = String(date.getMilliseconds()).padStart(3, '0');
2181
+ return `${h}:${m}:${s}.${ms}`;
2182
+ }
2183
+
2184
+ // 格式化事件数据(人类可读)
2185
+ formatEventData(data) {
2186
+ if (!data || typeof data !== 'object') {
2187
+ return JSON.stringify(data);
2188
+ }
2189
+
2190
+ // 特殊格式化处理
2191
+ const formatted = [];
2192
+
2193
+ // 常见字段的友好显示
2194
+ if (data.module) formatted.push(`module=${data.module}`);
2195
+ if (data.state) formatted.push(`state=${data.state}`);
2196
+ if (data.status) formatted.push(`status=${data.status}`);
2197
+ if (data.port) formatted.push(`port=${data.port}`);
2198
+ if (data.pid) formatted.push(`pid=${data.pid}`);
2199
+ if (data.error) formatted.push(`error="${data.error}"`);
2200
+ if (data.message) formatted.push(`msg="${data.message}"`);
2201
+ if (data.duration !== undefined) formatted.push(`duration=${data.duration}ms`);
2202
+
2203
+ // 如果有格式化的字段,返回格式化结果
2204
+ if (formatted.length > 0) {
2205
+ return formatted.join(' ');
2206
+ }
2207
+
2208
+ // 否则返回紧凑的 JSON
2209
+ return JSON.stringify(data);
2210
+ }
2211
+
2212
+ // Glob 转正则表达式
2213
+ globToRegex(pattern) {
2214
+ const escaped = pattern
2215
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // 转义特殊字符
2216
+ .replace(/\*/g, '.*') // * 匹配任意字符
2217
+ .replace(/\?/g, '.'); // ? 匹配单个字符
2218
+ return new RegExp(escaped, 'i'); // 不区分大小写
2219
+ }
2220
+
2221
+ appendConsoleLog(message) {
2222
+ // 保留旧方法以兼容其他代码
2223
+ const output = document.getElementById('console-output');
2224
+ const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false });
2225
+ output.textContent += `[${timestamp}] ${message}\n`;
2226
+ output.scrollTop = output.scrollHeight;
2227
+ }
2228
+
2229
+ async runRegistryTest() {
2230
+ // 调用 registry-tests.js 中的 runAllTests() 函数
2231
+ if (typeof runAllTests === 'function') {
2232
+ await runAllTests();
2233
+ } else {
2234
+ const output = document.getElementById('test-output');
2235
+ output.textContent = '错误: registry-tests.js 未加载或 runAllTests 函数不存在';
2236
+ }
2237
+ }
2238
+
2239
+ clearTestOutput() {
2240
+ document.getElementById('test-output').textContent = '';
2241
+ }
2242
+
2243
+ async loadTokens() {
2244
+ // 根据当前激活的标签页加载对应的 token
2245
+ const activeTab = document.querySelector('.tab.active')?.dataset.tab || 'kite';
2246
+ if (activeTab === 'kite') {
2247
+ await this.loadKiteTokens();
2248
+ } else {
2249
+ await this.loadEvolTokens();
2250
+ }
2251
+ }
2252
+
2253
+ async loadKiteTokens() {
2254
+ try {
2255
+ const result = await this.callRpc('evol.list_kite_tokens', {});
2256
+ const tokens = result.tokens || [];
2257
+
2258
+ const tbody = document.getElementById('kite-tokens-tbody');
2259
+ if (tokens.length === 0) {
2260
+ tbody.innerHTML = '<tr><td colspan="9" class="text-muted" style="text-align:center;padding:40px;">暂无 Kite Token</td></tr>';
2261
+ return;
2262
+ }
2263
+
2264
+ // 缩略显示函数:头部6位...尾部8位
2265
+ const truncate = (str, headLen = 6, tailLen = 8) => {
2266
+ if (!str || str.length <= headLen + tailLen + 3) return str;
2267
+ return `${str.substring(0, headLen)}...${str.substring(str.length - tailLen)}`;
2268
+ };
2269
+
2270
+ let html = '';
2271
+ tokens.forEach(token => {
2272
+ // Token 缩略显示(前6位...后8位)
2273
+ const tokenDisplay = truncate(token.token, 6, 8);
2274
+
2275
+ // 设备信息(设备名 + 设备ID缩略)
2276
+ const deviceId = token.deviceId || 'unknown';
2277
+ const deviceIdShort = truncate(deviceId, 6, 6);
2278
+ const deviceInfo = `${token.deviceName || 'Unknown'}<br><span style="font-size:11px;color:#999;">${deviceIdShort}</span>`;
2279
+
2280
+ // 手机号显示
2281
+ const phone = token.phone || '<span style="color:#999;">未绑定</span>';
2282
+
2283
+ // 时间格式化
2284
+ const formatTime = (isoStr) => {
2285
+ if (!isoStr) return '-';
2286
+ const date = new Date(isoStr);
2287
+ return date.toLocaleString('zh-CN', {
2288
+ month: '2-digit',
2289
+ day: '2-digit',
2290
+ hour: '2-digit',
2291
+ minute: '2-digit'
2292
+ });
2293
+ };
2294
+
2295
+ const createdAt = formatTime(token.createdAt);
2296
+ const lastUsedAt = formatTime(token.lastUsedAt);
2297
+ const expiresAt = formatTime(token.expiresAt);
2298
+
2299
+ // 计算剩余有效期
2300
+ const now = new Date();
2301
+ const expireDate = token.expiresAt ? new Date(token.expiresAt) : null;
2302
+ let remainingTime = '-';
2303
+ let isExpired = false;
2304
+
2305
+ if (expireDate) {
2306
+ const diffMs = expireDate - now;
2307
+ isExpired = diffMs <= 0;
2308
+
2309
+ if (isExpired) {
2310
+ remainingTime = '已过期';
2311
+ } else {
2312
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
2313
+ const diffHours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
2314
+
2315
+ if (diffDays > 0) {
2316
+ remainingTime = `${diffDays}天`;
2317
+ } else if (diffHours > 0) {
2318
+ remainingTime = `${diffHours}小时`;
2319
+ } else {
2320
+ const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
2321
+ remainingTime = `${diffMinutes}分钟`;
2322
+ }
2323
+ }
2324
+ }
2325
+
2326
+ const status = isExpired ? '已过期' : '有效';
2327
+ const statusColor = isExpired ? '#e74c3c' : '#27ae60';
2328
+ const remainingColor = isExpired ? '#e74c3c' : (expireDate && (expireDate - now) < 7 * 24 * 60 * 60 * 1000 ? '#ff9800' : '#666');
2329
+
2330
+ html += `<tr>
2331
+ <td><code style="font-size:11px;" title="${token.token}">${tokenDisplay}</code></td>
2332
+ <td style="font-size:12px;">${deviceInfo}</td>
2333
+ <td style="font-size:12px;">${phone}</td>
2334
+ <td style="font-size:12px;">${createdAt}</td>
2335
+ <td style="font-size:12px;">${lastUsedAt}</td>
2336
+ <td style="font-size:12px;">${expiresAt}</td>
2337
+ <td style="font-size:12px;color:${remainingColor};font-weight:500;">${remainingTime}</td>
2338
+ <td style="color:${statusColor};font-weight:500;">${status}</td>
2339
+ <td>
2340
+ <button class="btn btn-sm btn-danger" onclick="app.deleteToken('${token.token}')" ${isExpired ? 'disabled' : ''}>吊销</button>
2341
+ </td>
2342
+ </tr>`;
2343
+ });
2344
+
2345
+ tbody.innerHTML = html;
2346
+ } catch (err) {
2347
+ console.error('[evol] Load Kite tokens failed:', err);
2348
+ document.getElementById('kite-tokens-tbody').innerHTML =
2349
+ `<tr><td colspan="9" style="text-align:center;padding:40px;color:#e74c3c;">加载失败: ${err.message}</td></tr>`;
2350
+ }
2351
+ }
2352
+
2353
+ async loadEvolTokens() {
2354
+ try {
2355
+ const result = await this.callRpc('evol.list_evol_tokens', {});
2356
+ const tokens = result.tokens || [];
2357
+
2358
+ const tbody = document.getElementById('evol-tokens-tbody');
2359
+ if (tokens.length === 0) {
2360
+ tbody.innerHTML = '<tr><td colspan="8" class="text-muted" style="text-align:center;padding:40px;">暂无 Evol Token</td></tr>';
2361
+ return;
2362
+ }
2363
+
2364
+ // 缩略显示函数
2365
+ const truncate = (str, headLen = 6, tailLen = 8) => {
2366
+ if (!str || str.length <= headLen + tailLen + 3) return str;
2367
+ return `${str.substring(0, headLen)}...${str.substring(str.length - tailLen)}`;
2368
+ };
2369
+
2370
+ // 时间格式化
2371
+ const formatTime = (isoStr) => {
2372
+ if (!isoStr) return '-';
2373
+ const date = new Date(isoStr);
2374
+ return date.toLocaleString('zh-CN', {
2375
+ month: '2-digit',
2376
+ day: '2-digit',
2377
+ hour: '2-digit',
2378
+ minute: '2-digit'
2379
+ });
2380
+ };
2381
+
2382
+ let html = '';
2383
+ tokens.forEach(token => {
2384
+ const tokenDisplay = truncate(token.token, 6, 8);
2385
+ const phone = token.phone || '-';
2386
+ const nickName = token.nickName || '-';
2387
+ const credits = `${token.credits || 0} / ${token.creditsLimit || 0}`;
2388
+ const vipInfo = `${token.vipTypeName || 'Unknown'}<br><span style="font-size:11px;color:#999;">剩余${token.vipRemainingDays || 0}天</span>`;
2389
+ const obtainedAt = formatTime(token.obtainedAt);
2390
+ const lastUsedAt = formatTime(token.lastUsedAt);
2391
+ const expiresAt = formatTime(token.expiresAt);
2392
+
2393
+ html += `<tr>
2394
+ <td><code style="font-size:11px;" title="${token.token}">${tokenDisplay}</code></td>
2395
+ <td style="font-size:12px;">${phone}</td>
2396
+ <td style="font-size:12px;">${nickName}</td>
2397
+ <td style="font-size:12px;">${credits}</td>
2398
+ <td style="font-size:12px;">${vipInfo}</td>
2399
+ <td style="font-size:12px;">${obtainedAt}</td>
2400
+ <td style="font-size:12px;">${lastUsedAt}</td>
2401
+ <td style="font-size:12px;">${expiresAt}</td>
2402
+ </tr>`;
2403
+ });
2404
+
2405
+ tbody.innerHTML = html;
2406
+ } catch (err) {
2407
+ console.error('[evol] Load Evol tokens failed:', err);
2408
+ document.getElementById('evol-tokens-tbody').innerHTML =
2409
+ `<tr><td colspan="8" style="text-align:center;padding:40px;color:#e74c3c;">加载失败: ${err.message}</td></tr>`;
2410
+ }
2411
+ }
2412
+
2413
+ async deleteToken(token) {
2414
+ const confirmed = await window.dialog.confirm('确定要吊销此 Token 吗?删除后该设备将无法访问。', '吊销 Token');
2415
+ if (!confirmed) return;
2416
+
2417
+ try {
2418
+ await this.callRpc('evol.revoke_token', { token });
2419
+ window.dialog.success('Token 已成功吊销');
2420
+ this.loadTokens();
2421
+ } catch (err) {
2422
+ window.dialog.error('吊销失败: ' + err.message);
2423
+ }
2424
+ }
2425
+
2426
+ async refreshTokens() {
2427
+ this.loadTokens();
2428
+ }
2429
+
2430
+ showToast(message, type = 'info') {
2431
+ // 使用全局 showMessage 函数
2432
+ showMessage(message, type);
2433
+ }
2434
+
2435
+ // ========== 流量统计相关方法 ==========
2436
+
2437
+ startTrafficMonitoring() {
2438
+ if (this.trafficUpdateTimer) return; // 已经在运行
2439
+
2440
+ // 每秒更新一次流量统计
2441
+ this.trafficUpdateTimer = setInterval(() => {
2442
+ this.updateTrafficStats();
2443
+ }, 1000);
2444
+ }
2445
+
2446
+ stopTrafficMonitoring() {
2447
+ if (this.trafficUpdateTimer) {
2448
+ clearInterval(this.trafficUpdateTimer);
2449
+ this.trafficUpdateTimer = null;
2450
+ }
2451
+ }
2452
+
2453
+ updateTrafficStats() {
2454
+ if (!this.kernelClient || !this.kernelClient.connected) return;
2455
+
2456
+ const now = Date.now();
2457
+ const elapsed = (now - this.trafficStats.lastUpdateTime) / 1000; // 秒
2458
+
2459
+ // 从 kernelClient 获取流量数据
2460
+ const rxBytes = this.kernelClient.bytesReceived || 0;
2461
+ const txBytes = this.kernelClient.bytesSent || 0;
2462
+
2463
+ // 计算速率
2464
+ const rxDelta = rxBytes - this.trafficStats.lastRxBytes;
2465
+ const txDelta = txBytes - this.trafficStats.lastTxBytes;
2466
+
2467
+ this.trafficStats.rxRate = elapsed > 0 ? rxDelta / elapsed : 0;
2468
+ this.trafficStats.txRate = elapsed > 0 ? txDelta / elapsed : 0;
2469
+ this.trafficStats.rxTotal = rxBytes;
2470
+ this.trafficStats.txTotal = txBytes;
2471
+ this.trafficStats.lastRxBytes = rxBytes;
2472
+ this.trafficStats.lastTxBytes = txBytes;
2473
+ this.trafficStats.lastUpdateTime = now;
2474
+
2475
+ // 保存历史数据(最多保留 60 个点,即 1 分钟)
2476
+ this.trafficStats.history.push({
2477
+ time: now,
2478
+ rxRate: this.trafficStats.rxRate,
2479
+ txRate: this.trafficStats.txRate
2480
+ });
2481
+ if (this.trafficStats.history.length > 60) {
2482
+ this.trafficStats.history.shift();
2483
+ }
2484
+
2485
+ // 更新显示
2486
+ this.updateTrafficDisplay();
2487
+ }
2488
+
2489
+ updateTrafficDisplay() {
2490
+ const trafficEl = document.getElementById('ws-traffic');
2491
+ if (!trafficEl) return;
2492
+
2493
+ // 显示总速率(接收 + 发送)
2494
+ const totalRate = this.trafficStats.rxRate + this.trafficStats.txRate;
2495
+ trafficEl.textContent = `↓↑ ${this.formatBytes(totalRate)}/s`;
2496
+
2497
+ // 更新弹窗中的详细信息
2498
+ const popup = document.getElementById('traffic-popup');
2499
+ if (popup && popup.style.display === 'block') {
2500
+ document.getElementById('traffic-rx-total').textContent = this.formatBytes(this.trafficStats.rxTotal);
2501
+ document.getElementById('traffic-tx-total').textContent = this.formatBytes(this.trafficStats.txTotal);
2502
+ document.getElementById('traffic-rx-rate').textContent = this.formatBytes(this.trafficStats.rxRate) + '/s';
2503
+ document.getElementById('traffic-tx-rate').textContent = this.formatBytes(this.trafficStats.txRate) + '/s';
2504
+
2505
+ // 更新图表
2506
+ this.updateTrafficChart();
2507
+ }
2508
+ }
2509
+
2510
+ formatBytes(bytes) {
2511
+ if (bytes === 0) return '0 B';
2512
+ const k = 1024;
2513
+ const sizes = ['B', 'KB', 'MB', 'GB'];
2514
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2515
+ return (bytes / Math.pow(k, i)).toFixed(i === 0 ? 0 : 1) + ' ' + sizes[i];
2516
+ }
2517
+
2518
+ initTrafficChart() {
2519
+ const canvas = document.getElementById('traffic-chart');
2520
+ if (!canvas) return;
2521
+
2522
+ this.trafficChartCtx = canvas.getContext('2d');
2523
+ this.updateTrafficChart();
2524
+ }
2525
+
2526
+ updateTrafficChart() {
2527
+ if (!this.trafficChartCtx) return;
2528
+
2529
+ const ctx = this.trafficChartCtx;
2530
+ const canvas = ctx.canvas;
2531
+ const width = canvas.width;
2532
+ const height = canvas.height;
2533
+
2534
+ // 清空画布
2535
+ ctx.clearRect(0, 0, width, height);
2536
+
2537
+ if (this.trafficStats.history.length < 2) return;
2538
+
2539
+ // 找到最大值用于缩放
2540
+ let maxRate = 1024; // 最小 1KB
2541
+ this.trafficStats.history.forEach(point => {
2542
+ maxRate = Math.max(maxRate, point.rxRate, point.txRate);
2543
+ });
2544
+
2545
+ // 绘制网格线
2546
+ ctx.strokeStyle = '#e0e0e0';
2547
+ ctx.lineWidth = 1;
2548
+ for (let i = 0; i <= 4; i++) {
2549
+ const y = (height / 4) * i;
2550
+ ctx.beginPath();
2551
+ ctx.moveTo(0, y);
2552
+ ctx.lineTo(width, y);
2553
+ ctx.stroke();
2554
+ }
2555
+
2556
+ // 绘制接收速率曲线(绿色)
2557
+ ctx.strokeStyle = '#27ae60';
2558
+ ctx.lineWidth = 2;
2559
+ ctx.beginPath();
2560
+ this.trafficStats.history.forEach((point, i) => {
2561
+ const x = (i / (this.trafficStats.history.length - 1)) * width;
2562
+ const y = height - (point.rxRate / maxRate) * height;
2563
+ if (i === 0) {
2564
+ ctx.moveTo(x, y);
2565
+ } else {
2566
+ ctx.lineTo(x, y);
2567
+ }
2568
+ });
2569
+ ctx.stroke();
2570
+
2571
+ // 绘制发送速率曲线(蓝色)
2572
+ ctx.strokeStyle = '#3498db';
2573
+ ctx.lineWidth = 2;
2574
+ ctx.beginPath();
2575
+ this.trafficStats.history.forEach((point, i) => {
2576
+ const x = (i / (this.trafficStats.history.length - 1)) * width;
2577
+ const y = height - (point.txRate / maxRate) * height;
2578
+ if (i === 0) {
2579
+ ctx.moveTo(x, y);
2580
+ } else {
2581
+ ctx.lineTo(x, y);
2582
+ }
2583
+ });
2584
+ ctx.stroke();
2585
+
2586
+ // 绘制图例
2587
+ ctx.font = '11px sans-serif';
2588
+ ctx.fillStyle = '#27ae60';
2589
+ ctx.fillText('↓ 接收', 10, 15);
2590
+ ctx.fillStyle = '#3498db';
2591
+ ctx.fillText('↑ 发送', 60, 15);
2592
+ ctx.fillStyle = '#666';
2593
+ ctx.fillText(`最大: ${this.formatBytes(maxRate)}/s`, width - 100, 15);
2594
+ }
2595
+ }
2596
+
2597
+
2598
+ // Global app instance
2599
+ let app;
2600
+
2601
+ // Initialize app on load
2602
+ document.addEventListener('DOMContentLoaded', () => {
2603
+ app = new EvolApp();
2604
+ window.app = app; // 暴露到全局,供 registry-tests.js 使用
2605
+ app.init();
2606
+
2607
+ // Login form handlers
2608
+ document.getElementById('btn-send-code').addEventListener('click', async () => {
2609
+ const phone = document.getElementById('login-phone').value.trim();
2610
+
2611
+ if (!phone) {
2612
+ showMessage('请输入手机号', 'error');
2613
+ return;
2614
+ }
2615
+
2616
+ if (!/^1[3-9]\d{9}$/.test(phone)) {
2617
+ showMessage('请输入有效的中国大陆手机号(11位数字,以1开头)', 'error');
2618
+ return;
2619
+ }
2620
+
2621
+ const btn = document.getElementById('btn-send-code');
2622
+ btn.disabled = true;
2623
+ btn.textContent = '发送中...';
2624
+
2625
+ try {
2626
+ const data = await app.sendSMS(phone);
2627
+ console.log('SMS API response:', data);
2628
+
2629
+ if (data.success) {
2630
+ showMessage('验证码已发送,请查收短信', 'success');
2631
+ // 焦点移到验证码输入框
2632
+ document.getElementById('login-code').focus();
2633
+ let countdown = 60;
2634
+ const timer = setInterval(() => {
2635
+ countdown--;
2636
+ btn.textContent = countdown + '秒后重试';
2637
+ if (countdown <= 0) {
2638
+ clearInterval(timer);
2639
+ btn.disabled = false;
2640
+ btn.textContent = '发送验证码';
2641
+ }
2642
+ }, 1000);
2643
+ } else {
2644
+ const errorMsg = data.msg || data.message || '发送失败,请稍后重试';
2645
+ showMessage('发送失败: ' + errorMsg, 'error');
2646
+ console.error('SMS API error:', data);
2647
+ btn.disabled = false;
2648
+ btn.textContent = '发送验证码';
2649
+ }
2650
+ } catch (err) {
2651
+ console.error('SMS request error:', err);
2652
+ showMessage('网络错误: ' + err.message, 'error');
2653
+ btn.disabled = false;
2654
+ btn.textContent = '发送验证码';
2655
+ }
2656
+ });
2657
+
2658
+ document.getElementById('btn-login').addEventListener('click', async () => {
2659
+ const phone = document.getElementById('login-phone').value.trim();
2660
+ const code = document.getElementById('login-code').value.trim();
2661
+
2662
+ if (!phone || !code) {
2663
+ showMessage('请输入手机号和验证码', 'error');
2664
+ return;
2665
+ }
2666
+
2667
+ const btn = document.getElementById('btn-login');
2668
+ btn.disabled = true;
2669
+ btn.textContent = '登录中...';
2670
+
2671
+ try {
2672
+ const data = await app.login(phone, code);
2673
+ if (data.success) {
2674
+ showMessage('登录成功', 'success');
2675
+ } else {
2676
+ showMessage(data.msg || '登录失败', 'error');
2677
+ btn.disabled = false;
2678
+ btn.textContent = '登录';
2679
+ }
2680
+ } catch (err) {
2681
+ showMessage('网络错误', 'error');
2682
+ btn.disabled = false;
2683
+ btn.textContent = '登录';
2684
+ }
2685
+ });
2686
+
2687
+ document.getElementById('btn-logout').addEventListener('click', () => {
2688
+ app.logout();
2689
+ });
2690
+
2691
+ // Module management event listeners
2692
+ document.getElementById('btn-toggle-console')?.addEventListener('click', () => {
2693
+ app.toggleConsole();
2694
+ });
2695
+
2696
+ document.getElementById('btn-clear-console')?.addEventListener('click', () => {
2697
+ app.clearConsole();
2698
+ });
2699
+
2700
+ document.getElementById('btn-restart-kite')?.addEventListener('click', () => {
2701
+ app.restartKite();
2702
+ });
2703
+
2704
+ document.getElementById('btn-test-registry')?.addEventListener('click', () => {
2705
+ app.runRegistryTest();
2706
+ });
2707
+
2708
+ document.getElementById('btn-clear-test-output')?.addEventListener('click', () => {
2709
+ app.clearTestOutput();
2710
+ });
2711
+
2712
+ document.getElementById('btn-module-back')?.addEventListener('click', () => {
2713
+ app.loadModules();
2714
+ });
2715
+
2716
+ document.getElementById('btn-reset-defaults')?.addEventListener('click', () => {
2717
+ app.resetModuleDefaults();
2718
+ });
2719
+
2720
+ // Token management event listeners
2721
+ document.getElementById('btn-refresh-tokens')?.addEventListener('click', () => {
2722
+ app.refreshTokens();
2723
+ });
2724
+
2725
+ // Tab switching event listeners
2726
+ document.querySelectorAll('.tab').forEach(tab => {
2727
+ tab.addEventListener('click', () => {
2728
+ // 移除所有 active 类
2729
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
2730
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
2731
+
2732
+ // 添加 active 类到当前标签
2733
+ tab.classList.add('active');
2734
+ const tabName = tab.dataset.tab;
2735
+ document.getElementById(`tab-${tabName}`).classList.add('active');
2736
+
2737
+ // 加载对应的数据
2738
+ if (tabName === 'kite') {
2739
+ app.loadKiteTokens();
2740
+ } else if (tabName === 'evol') {
2741
+ app.loadEvolTokens();
2742
+ }
2743
+ });
2744
+ });
2745
+
2746
+ // 流量统计弹窗事件
2747
+ document.getElementById('ws-traffic')?.addEventListener('click', () => {
2748
+ const popup = document.getElementById('traffic-popup');
2749
+ if (popup) {
2750
+ popup.style.display = popup.style.display === 'none' ? 'block' : 'none';
2751
+ if (popup.style.display === 'block') {
2752
+ app.initTrafficChart();
2753
+ }
2754
+ }
2755
+ });
2756
+
2757
+ document.getElementById('close-traffic-popup')?.addEventListener('click', () => {
2758
+ const popup = document.getElementById('traffic-popup');
2759
+ if (popup) popup.style.display = 'none';
2760
+ });
2761
+
2762
+ // 点击弹窗外部关闭
2763
+ document.addEventListener('click', (e) => {
2764
+ const popup = document.getElementById('traffic-popup');
2765
+ const trafficEl = document.getElementById('ws-traffic');
2766
+ if (popup && popup.style.display === 'block' &&
2767
+ !popup.contains(e.target) && e.target !== trafficEl) {
2768
+ popup.style.display = 'none';
2769
+ }
2770
+ });
2771
+ });
2772
+
2773
+ function showMessage(msg, type) {
2774
+ const el = document.getElementById('login-message');
2775
+ el.textContent = msg;
2776
+ el.className = type === 'error' ? 'error-msg' : 'success-msg';
2777
+ }