@agentunion/kite 1.5.0 → 1.6.1

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 (574) 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/cli.js +38 -4
  23. package/core/env_checker.py +96 -0
  24. 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
  25. package/docs/ACP/345/215/217/350/256/256/345/205/274/345/256/271/346/226/271/346/241/210.md +138 -0
  26. 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
  27. package/docs/CLI/345/274/200/345/217/221/350/256/241/345/210/222.md +595 -0
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. package/docs/Evol/346/250/241/345/235/227/350/256/276/350/256/241/346/226/271/346/241/210.md +1154 -0
  34. 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
  35. 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
  36. package/docs/HTTP-RPC/350/277/201/347/247/273/345/210/260WebSocket/350/256/241/345/210/222.md +318 -0
  37. package/docs/INDEX.md +388 -0
  38. package/docs/KITE_DOCS_GUIDE.md +33 -0
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/README.md +46 -0
  56. package/docs/Kite/347/263/273/347/273/237/345/220/257/345/212/250/346/265/201/347/250/213.md +567 -0
  57. package/docs/Launcher/345/220/257/345/212/250/345/231/250/346/226/207/346/241/243.md +745 -0
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. package/docs/Watchdog/350/265/204/346/272/220/347/233/221/346/216/247/347/255/226/347/225/245.md +92 -0
  64. 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
  65. 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
  66. package/docs/WebSocket/350/277/236/346/216/245/351/237/247/346/200/247/346/226/271/346/241/210.md +169 -0
  67. 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
  68. 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
  69. 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
  70. package/docs/audit-api-guide.md +68 -0
  71. package/docs/audit-module-design.md +315 -0
  72. package/docs/audit-module-implementation-summary.md +149 -0
  73. package/docs/llm-context-design.md +52 -0
  74. package/docs/llm-test-enhancement-plan.md +970 -0
  75. package/docs/logs-api-guide.md +42 -0
  76. 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
  77. 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
  78. 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
  79. 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
  80. package/docs//344/272/213/344/273/266/345/244/204/347/220/206/346/234/272/345/210/266.md +388 -0
  81. package/docs//344/272/213/344/273/266/345/244/204/347/220/206/350/247/204/350/214/203.md +113 -0
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. package/docs//344/274/230/351/233/205/351/200/200/345/207/272/350/247/204/350/214/203.md +362 -0
  88. package/docs//344/276/235/350/265/226/347/256/241/347/220/206/350/257/264/346/230/216.md +141 -0
  89. 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
  90. package/docs//345/210/240/351/231/244kernel-client-example/345/256/214/346/210/220.md +309 -0
  91. package/docs//345/210/240/351/231/244ws-management/345/256/214/346/210/220.md +418 -0
  92. package/docs//345/220/257/345/212/250/344/274/230/345/214/226/346/226/271/346/241/210.md +522 -0
  93. 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
  94. 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
  95. 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
  96. package/docs//345/256/236/347/216/260/350/247/204/345/210/222.md +195 -0
  97. 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
  98. 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
  99. package/docs//346/217/241/346/211/213/350/256/244/350/257/201/346/226/271/346/241/210.md +908 -0
  100. package/docs//346/226/207/346/241/243/346/233/264/346/226/260/346/270/205/345/215/225.md +83 -0
  101. 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
  102. 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
  103. package/docs//346/236/266/346/236/204/345/200/237/351/211/264/346/214/207/345/215/227.md +977 -0
  104. 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
  105. 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
  106. 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
  107. 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
  108. package/docs//346/250/241/345/235/227/345/274/200/345/217/221/346/214/207/345/215/227.md +1824 -0
  109. package/docs//346/250/241/345/235/227/347/203/255/346/233/264/346/226/260.md +89 -0
  110. 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
  111. 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
  112. 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
  113. 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
  114. 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
  115. 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
  116. 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
  117. 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
  118. 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
  119. 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
  120. 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
  121. 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
  122. package/docs//350/277/234/347/250/213/346/250/241/345/235/227/350/256/276/350/256/241.md +451 -0
  123. package/docs//351/207/215/346/236/204/346/234/272/345/210/266/346/270/205/345/215/225.md +192 -0
  124. package/docs//351/223/276/350/267/257/350/277/275/350/270/252/346/226/271/346/241/210.md +242 -0
  125. 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
  126. package/extensions/agents/assistant/entry.py +113 -14
  127. package/extensions/agents/assistant/module.md +27 -22
  128. package/extensions/agents/assistant/server.py +291 -105
  129. package/extensions/channels/acp_channel/entry.py +114 -16
  130. package/extensions/channels/acp_channel/module.md +4 -0
  131. package/extensions/channels/acp_channel/server.py +396 -105
  132. package/extensions/channels/phone_channel/__init__.py +1 -0
  133. package/extensions/channels/phone_channel/entry.py +503 -0
  134. package/extensions/channels/phone_channel/module.md +31 -0
  135. package/extensions/channels/phone_channel/server.py +686 -0
  136. package/extensions/event_hub_bench/entry.py +55 -12
  137. package/extensions/event_hub_bench/module.md +27 -27
  138. package/extensions/services/audit/README.md +134 -0
  139. package/extensions/services/audit/collector.py +73 -0
  140. package/extensions/services/audit/entry.py +444 -0
  141. package/extensions/services/audit/module.md +66 -0
  142. package/extensions/services/audit/query_audit.py +111 -0
  143. package/extensions/services/audit/routes/__init__.py +1 -0
  144. package/extensions/services/audit/routes/routes_audit.py +113 -0
  145. package/extensions/services/audit/schemas/__init__.py +5 -0
  146. package/extensions/services/audit/schemas/audit_event.py +92 -0
  147. package/extensions/services/audit/server.py +542 -0
  148. package/extensions/services/audit/storage.py +95 -0
  149. package/extensions/services/auth/entry.py +1054 -0
  150. package/extensions/services/auth/module.md +31 -0
  151. package/extensions/services/auth/token_store.py +185 -0
  152. package/extensions/services/auth/verifiers/evol_account.py +101 -0
  153. package/extensions/services/auth/verifiers/kite_token.py +38 -0
  154. package/extensions/services/auth/verifiers/pairing_code.py +71 -0
  155. package/extensions/services/backup/entry.py +494 -197
  156. package/extensions/services/backup/module.md +4 -2
  157. package/extensions/services/dataclaw/api/__init__.py +0 -0
  158. package/extensions/services/dataclaw/api/admin.py +367 -0
  159. package/extensions/services/dataclaw/api/copyright.py +175 -0
  160. package/extensions/services/dataclaw/api/credits.py +177 -0
  161. package/extensions/services/dataclaw/api/data.py +179 -0
  162. package/extensions/services/dataclaw/api/demands.py +269 -0
  163. package/extensions/services/dataclaw/api/feeds.py +262 -0
  164. package/extensions/services/dataclaw/api/identity.py +505 -0
  165. package/extensions/services/dataclaw/api/notifications.py +104 -0
  166. package/extensions/services/dataclaw/api/reviews.py +138 -0
  167. package/extensions/services/dataclaw/api/search.py +153 -0
  168. package/extensions/services/dataclaw/api/subscriptions.py +157 -0
  169. package/extensions/services/dataclaw/config.json5 +96 -0
  170. package/extensions/services/dataclaw/core/__init__.py +0 -0
  171. package/extensions/services/dataclaw/core/auth.py +95 -0
  172. package/extensions/services/dataclaw/core/config.py +50 -0
  173. package/extensions/services/dataclaw/core/database.py +70 -0
  174. package/extensions/services/dataclaw/entry.py +416 -0
  175. 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
  176. package/extensions/services/dataclaw/migrate.py +283 -0
  177. package/extensions/services/dataclaw/models/__init__.py +0 -0
  178. package/extensions/services/dataclaw/module.md +49 -0
  179. package/extensions/services/dataclaw/requirements.txt +18 -0
  180. package/extensions/services/dataclaw/server.py +759 -0
  181. package/extensions/services/dataclaw/services/__init__.py +0 -0
  182. package/extensions/services/dataclaw/services/agent_service.py +132 -0
  183. package/extensions/services/dataclaw/services/credit_service.py +235 -0
  184. package/extensions/services/dataclaw/services/email_service.py +140 -0
  185. package/extensions/services/dataclaw/services/feed_service.py +259 -0
  186. package/extensions/services/dataclaw/services/notification_service.py +209 -0
  187. package/extensions/services/dataclaw/services/oauth_service.py +275 -0
  188. package/extensions/services/dataclaw/services/pricing.py +102 -0
  189. package/extensions/services/dataclaw/services/quality.py +79 -0
  190. package/extensions/services/dataclaw/services/reputation.py +142 -0
  191. package/extensions/services/dataclaw/services/sms_service.py +174 -0
  192. package/extensions/services/dataclaw/static/css/common.css +853 -0
  193. package/extensions/services/dataclaw/static/css/themes/blue.css +42 -0
  194. package/extensions/services/dataclaw/static/css/themes/dark.css +42 -0
  195. package/extensions/services/dataclaw/static/css/themes/light.css +35 -0
  196. package/extensions/services/dataclaw/static/js/api.js +103 -0
  197. package/extensions/services/dataclaw/static/js/common.js +321 -0
  198. package/extensions/services/dataclaw/static/js/i18n.js +95 -0
  199. package/extensions/services/dataclaw/static/js/pages/admin.js +152 -0
  200. package/extensions/services/dataclaw/static/js/pages/dashboard.js +82 -0
  201. package/extensions/services/dataclaw/static/js/pages/feed-detail.js +180 -0
  202. package/extensions/services/dataclaw/static/js/pages/feed-manage.js +158 -0
  203. package/extensions/services/dataclaw/static/js/theme.js +46 -0
  204. package/extensions/services/dataclaw/static/locales/en-US.json +464 -0
  205. package/extensions/services/dataclaw/static/locales/ja-JP.json +464 -0
  206. package/extensions/services/dataclaw/static/locales/zh-CN.json +464 -0
  207. package/extensions/services/dataclaw/templates/admin/index.html +90 -0
  208. package/extensions/services/dataclaw/templates/base.html +136 -0
  209. package/extensions/services/dataclaw/templates/credits/balance.html +106 -0
  210. package/extensions/services/dataclaw/templates/credits/deposit.html +164 -0
  211. package/extensions/services/dataclaw/templates/credits/history.html +90 -0
  212. package/extensions/services/dataclaw/templates/dashboard.html +52 -0
  213. package/extensions/services/dataclaw/templates/demands/create.html +78 -0
  214. package/extensions/services/dataclaw/templates/demands/detail.html +136 -0
  215. package/extensions/services/dataclaw/templates/demands/list.html +94 -0
  216. package/extensions/services/dataclaw/templates/feeds/create.html +95 -0
  217. package/extensions/services/dataclaw/templates/feeds/detail.html +110 -0
  218. package/extensions/services/dataclaw/templates/feeds/list.html +110 -0
  219. package/extensions/services/dataclaw/templates/feeds/manage.html +88 -0
  220. package/extensions/services/dataclaw/templates/index.html +185 -0
  221. package/extensions/services/dataclaw/templates/login.html +246 -0
  222. package/extensions/services/dataclaw/templates/register.html +164 -0
  223. package/extensions/services/dataclaw/templates/settings/notifications.html +96 -0
  224. package/extensions/services/dataclaw/templates/settings/profile.html +167 -0
  225. package/extensions/services/dataclaw/templates/subscriptions/list.html +64 -0
  226. package/extensions/services/dataclaw/tests/__init__.py +0 -0
  227. package/extensions/services/dataclaw/tests/conftest.py +68 -0
  228. package/extensions/services/dataclaw/tests/integration/__init__.py +0 -0
  229. package/extensions/services/dataclaw/tests/integration/test_workflows.py +239 -0
  230. package/extensions/services/dataclaw/tests/unit/__init__.py +0 -0
  231. package/extensions/services/dataclaw/tests/unit/test_admin.py +70 -0
  232. package/extensions/services/dataclaw/tests/unit/test_copyright.py +63 -0
  233. package/extensions/services/dataclaw/tests/unit/test_credits.py +80 -0
  234. package/extensions/services/dataclaw/tests/unit/test_data.py +98 -0
  235. package/extensions/services/dataclaw/tests/unit/test_demands.py +106 -0
  236. package/extensions/services/dataclaw/tests/unit/test_feeds.py +98 -0
  237. package/extensions/services/dataclaw/tests/unit/test_identity.py +88 -0
  238. package/extensions/services/dataclaw/tests/unit/test_notifications.py +36 -0
  239. package/extensions/services/dataclaw/tests/unit/test_reviews.py +68 -0
  240. package/extensions/services/dataclaw/tests/unit/test_search.py +64 -0
  241. package/extensions/services/dataclaw/tests/unit/test_subscriptions.py +65 -0
  242. package/extensions/services/dataclaw/tests/unit/test_system.py +106 -0
  243. package/extensions/services/dataclaw/utils/__init__.py +0 -0
  244. package/extensions/services/dataclaw/utils/crypto.py +38 -0
  245. package/extensions/services/dataclaw/utils/id_generator.py +52 -0
  246. package/extensions/services/dataclaw/ws/__init__.py +0 -0
  247. package/extensions/services/dataclaw/ws/handler.py +163 -0
  248. 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
  249. 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
  250. package/extensions/services/evol/__init__.py +1 -0
  251. package/extensions/services/evol/async_http.py +551 -0
  252. package/extensions/services/evol/auth_manager.py +602 -443
  253. package/extensions/services/evol/config.json5 +16 -0
  254. package/extensions/services/evol/entry.py +568 -406
  255. package/extensions/services/evol/evol_api.py +969 -173
  256. package/extensions/services/evol/mfa_totp.py +77 -0
  257. package/extensions/services/evol/module.md +150 -32
  258. package/extensions/services/evol/nonce_pool.py +113 -0
  259. package/extensions/services/evol/oauth_manager.py +223 -0
  260. package/extensions/services/evol/pairing.py +3 -2
  261. package/extensions/services/evol/pairing_codes.jsonl +1 -0
  262. package/extensions/services/evol/relay.py +1031 -682
  263. package/extensions/services/evol/relay_config.json5 +85 -67
  264. package/extensions/services/evol/routes/routes_llm.py +231 -0
  265. package/extensions/services/evol/routes/routes_rpc.py +90 -89
  266. package/extensions/services/evol/routes/routes_test.py +11 -4
  267. package/extensions/services/evol/server.py +2426 -875
  268. package/extensions/services/evol/static/assets/CommissionView-Cs_ys6Gm.js +1 -0
  269. package/extensions/services/evol/static/assets/CommissionView-DACet_Oo.css +1 -0
  270. package/extensions/services/evol/static/assets/IframePage-DbO11U9G.js +1 -0
  271. package/extensions/services/evol/static/assets/IframePage-c572lT8i.css +1 -0
  272. package/extensions/services/evol/static/assets/TeamDetailView-DULrGD7k.css +1 -0
  273. package/extensions/services/evol/static/assets/TeamDetailView-gy_MBEqG.js +139 -0
  274. package/extensions/services/evol/static/assets/element-plus-Bd7pZkkM.js +63 -0
  275. package/extensions/services/evol/static/assets/index-CmMONKzG.css +1 -0
  276. package/extensions/services/evol/static/assets/index-D44bBe__.js +2 -0
  277. package/extensions/services/evol/static/assets/vue-vendor-DtF-__I4.js +29 -0
  278. package/extensions/services/evol/static/index.html +16 -781
  279. package/extensions/services/evol/static/logo.png +0 -0
  280. package/extensions/services/evol/stats_manager.py +243 -240
  281. package/extensions/services/evol/web/README.md +89 -0
  282. package/extensions/services/evol/web/build.bat +44 -0
  283. package/extensions/services/evol/web/index.html +13 -0
  284. package/extensions/services/evol/web/package-lock.json +1718 -0
  285. package/extensions/services/evol/web/package.json +26 -0
  286. package/extensions/services/evol/web/public/logo.png +0 -0
  287. package/extensions/services/evol/web/src/App.vue +7 -0
  288. package/extensions/services/evol/web/src/components/layout/AppHeader.vue +202 -0
  289. package/extensions/services/evol/web/src/components/layout/AppLayout.vue +61 -0
  290. package/extensions/services/evol/web/src/components/layout/AppSidebar.vue +115 -0
  291. package/extensions/services/evol/web/src/components/login/LoginPage.vue +271 -0
  292. package/extensions/services/evol/web/src/components/team/AddMemberModal.vue +181 -0
  293. package/extensions/services/evol/web/src/components/team/GroupTreeNode.vue +156 -0
  294. package/extensions/services/evol/web/src/components/team/TeamAlertConfig.vue +221 -0
  295. package/extensions/services/evol/web/src/components/team/TeamBillModal.vue +165 -0
  296. package/extensions/services/evol/web/src/components/team/TeamMembersAndGroups.vue +499 -0
  297. package/extensions/services/evol/web/src/components/team/TeamStatsPanel.vue +907 -0
  298. package/extensions/services/evol/web/src/components/team/TreeNode.vue +331 -0
  299. package/extensions/services/evol/web/src/components/team/stats/StatsExportProgress.vue +44 -0
  300. package/extensions/services/evol/web/src/components/team/stats/StatsHeader.vue +89 -0
  301. package/extensions/services/evol/web/src/components/team/stats/StatsMemberDetail.vue +415 -0
  302. package/extensions/services/evol/web/src/components/team/stats/StatsSummary.vue +42 -0
  303. package/extensions/services/evol/web/src/components/team/stats/helpers.ts +195 -0
  304. package/extensions/services/evol/web/src/components/team/stats/stats.css +741 -0
  305. package/extensions/services/evol/web/src/components/team/stats/useStatsApi.ts +114 -0
  306. package/extensions/services/evol/web/src/components/team/stats/useStatsCharts.ts +242 -0
  307. package/extensions/services/evol/web/src/components/team/stats/useStatsExport.ts +232 -0
  308. package/extensions/services/evol/web/src/composables/useFormatters.ts +42 -0
  309. package/extensions/services/evol/web/src/composables/useTheme.ts +52 -0
  310. package/extensions/services/evol/web/src/env.d.ts +7 -0
  311. package/extensions/services/evol/web/src/i18n/en.ts +361 -0
  312. package/extensions/services/evol/web/src/i18n/index.ts +36 -0
  313. package/extensions/services/evol/web/src/i18n/zh.ts +379 -0
  314. package/extensions/services/evol/web/src/main.ts +21 -0
  315. package/extensions/services/evol/web/src/router/index.ts +81 -0
  316. package/extensions/services/evol/web/src/services/kernel-client.ts +406 -0
  317. package/extensions/services/evol/web/src/stores/auth.ts +189 -0
  318. package/extensions/services/evol/web/src/stores/connection.ts +134 -0
  319. package/extensions/services/evol/web/src/stores/pages.ts +79 -0
  320. package/extensions/services/evol/web/src/styles/base.css +213 -0
  321. package/extensions/services/evol/web/src/styles/variables.css +138 -0
  322. package/extensions/services/evol/web/src/types/rpc.ts +35 -0
  323. package/extensions/services/evol/web/src/types/token.ts +87 -0
  324. package/extensions/services/evol/web/src/views/AccountView.vue +1532 -0
  325. package/extensions/services/evol/web/src/views/AiServiceView.vue +219 -0
  326. package/extensions/services/evol/web/src/views/CommissionView.vue +1220 -0
  327. package/extensions/services/evol/web/src/views/CreditsView.vue +131 -0
  328. package/extensions/services/evol/web/src/views/EndpointView.vue +163 -0
  329. package/extensions/services/evol/web/src/views/IframePage.vue +120 -0
  330. package/extensions/services/evol/web/src/views/TeamDetailView.vue +473 -0
  331. package/extensions/services/evol/web/src/views/TeamView.vue +332 -0
  332. package/extensions/services/evol/web/tsconfig.json +31 -0
  333. package/extensions/services/evol/web/tsconfig.node.json +10 -0
  334. package/extensions/services/evol/web/vite.config.ts +49 -0
  335. package/extensions/services/evolmem/__init__.py +0 -0
  336. package/extensions/services/evolmem/entry.py +387 -0
  337. package/extensions/services/evolmem/hooks/__init__.py +0 -0
  338. package/extensions/services/evolmem/hooks/assistant_stop.py +228 -0
  339. package/extensions/services/evolmem/hooks/common.py +76 -0
  340. package/extensions/services/evolmem/hooks/pre_tool_use.py +56 -0
  341. package/extensions/services/evolmem/hooks/session_end.py +133 -0
  342. package/extensions/services/evolmem/hooks/session_start.py +229 -0
  343. package/extensions/services/evolmem/hooks/user_prompt.py +122 -0
  344. package/extensions/services/evolmem/module.md +48 -0
  345. package/extensions/services/evolmem/prompts/00-server-info.md +28 -0
  346. package/extensions/services/evolmem/prompts/01-behavior.md +46 -0
  347. package/extensions/services/evolmem/prompts/02-summary-format.md +112 -0
  348. package/extensions/services/evolmem/prompts/03-file-query.md +92 -0
  349. package/extensions/services/evolmem/prompts/04-topic-stats.md +11 -0
  350. package/extensions/services/evolmem/prompts/05-recent-topics.md +84 -0
  351. package/extensions/services/evolmem/scripts/__init__.py +0 -0
  352. package/extensions/services/evolmem/scripts/extract_keywords.py +40 -0
  353. package/extensions/services/evolmem/scripts/search_topics.py +91 -0
  354. package/extensions/services/evolmem/server.py +641 -0
  355. package/extensions/services/gateway/entry.py +964 -0
  356. package/extensions/services/gateway/module.md +29 -0
  357. package/extensions/services/gateway/nonce_pool.py +65 -0
  358. package/extensions/services/gateway/relay.py +133 -0
  359. package/extensions/services/gateway/ws_server.py +285 -0
  360. package/extensions/services/kite_console/auth_manager.py +603 -0
  361. package/extensions/services/kite_console/config.json5 +19 -0
  362. package/extensions/services/kite_console/config_loader.py +117 -0
  363. package/extensions/services/kite_console/entry.py +528 -0
  364. package/extensions/services/kite_console/evol_api.py +179 -0
  365. package/extensions/services/kite_console/evol_config.json5 +29 -0
  366. package/extensions/services/kite_console/mfa_totp.py +77 -0
  367. package/extensions/services/kite_console/migrate_tokens.py +122 -0
  368. package/extensions/services/kite_console/module.md +37 -0
  369. package/extensions/services/kite_console/nonce_pool.py +113 -0
  370. package/extensions/services/kite_console/oauth_manager.py +223 -0
  371. package/extensions/services/kite_console/pairing.py +280 -0
  372. package/extensions/services/kite_console/pairing_codes.jsonl +2 -0
  373. package/extensions/services/kite_console/relay.py +1350 -0
  374. package/extensions/services/kite_console/relay_config.json5 +96 -0
  375. package/extensions/services/kite_console/routes/__init__.py +1 -0
  376. package/extensions/services/kite_console/routes/routes_llm.py +231 -0
  377. package/extensions/services/kite_console/routes/routes_proxy.py +115 -0
  378. package/extensions/services/kite_console/routes/routes_rpc.py +89 -0
  379. package/extensions/services/kite_console/routes/routes_test.py +68 -0
  380. package/extensions/services/kite_console/server.py +1742 -0
  381. package/extensions/services/{evol → kite_console}/static/css/style.css +656 -2
  382. package/extensions/services/kite_console/static/index.html +1524 -0
  383. package/extensions/services/{evol → kite_console}/static/js/dialog.js +11 -4
  384. package/extensions/services/kite_console/static/js/evol-app.js +7740 -0
  385. package/extensions/services/{evol/static/js/evol-app.js → kite_console/static/js/evol-app.js.backup} +2777 -1949
  386. package/extensions/services/kite_console/static/js/kernel-client.js +560 -0
  387. package/extensions/services/{evol/static/js/kernel-client.js → kite_console/static/js/kernel-client.js.backup} +41 -3
  388. package/extensions/services/{evol → kite_console}/static/js/registry-tests.js +7 -0
  389. package/extensions/services/kite_console/static/js/tests/ARCHITECTURE.md +67 -0
  390. package/extensions/services/kite_console/static/js/tests/README.md +140 -0
  391. package/extensions/services/kite_console/static/js/tests/index.js +161 -0
  392. package/extensions/services/kite_console/static/js/tests/integration/auth.js +120 -0
  393. package/extensions/services/kite_console/static/js/tests/integration/channel-interaction.js +188 -0
  394. package/extensions/services/kite_console/static/js/tests/integration/elastic-connection.js +115 -0
  395. package/extensions/services/kite_console/static/js/tests/integration/full-workflow.js +43 -0
  396. package/extensions/services/kite_console/static/js/tests/integration/multi-instance.js +304 -0
  397. package/extensions/services/kite_console/static/js/tests/integration/nested-rpc.js +266 -0
  398. package/extensions/services/kite_console/static/js/tests/integration/pingpong.js +25 -0
  399. package/extensions/services/kite_console/static/js/tests/integration/redis.js +227 -0
  400. package/extensions/services/kite_console/static/js/tests/integration/registry-core.js +52 -0
  401. package/extensions/services/kite_console/static/js/tests/integration/remote-deploy.js +85 -0
  402. package/extensions/services/kite_console/static/js/tests/integration/require-init.js +96 -0
  403. package/extensions/services/kite_console/static/js/tests/integration/scaling-control.js +193 -0
  404. package/extensions/services/kite_console/static/js/tests/integration/trace.js +109 -0
  405. package/extensions/services/kite_console/static/js/tests/modules/acp_channel.js +339 -0
  406. package/extensions/services/kite_console/static/js/tests/modules/auth.js +96 -0
  407. package/extensions/services/kite_console/static/js/tests/modules/backup.js +49 -0
  408. package/extensions/services/kite_console/static/js/tests/modules/gateway.js +41 -0
  409. package/extensions/services/kite_console/static/js/tests/modules/kernel.js +90 -0
  410. package/extensions/services/kite_console/static/js/tests/modules/launcher.js +75 -0
  411. package/extensions/services/kite_console/static/js/tests/modules/multi_instance.js +129 -0
  412. package/extensions/services/kite_console/static/js/tests/modules/phone_channel.js +364 -0
  413. package/extensions/services/kite_console/static/js/tests/modules/redis.js +178 -0
  414. package/extensions/services/kite_console/static/js/tests/modules/watchdog.js +60 -0
  415. package/extensions/services/kite_console/static/js/tests/modules/web.js +70 -0
  416. package/extensions/services/kite_console/static/js/tests/test-runner.js +123 -0
  417. package/extensions/services/kite_console/static/js/virtual-list.js +200 -0
  418. package/extensions/services/kite_console/static/test_kernel_client_token.html +352 -0
  419. package/extensions/services/kite_console/stats_manager.py +247 -0
  420. package/extensions/services/logs/README.md +215 -0
  421. package/extensions/services/logs/api_logger.py +37 -0
  422. package/extensions/services/logs/baseline.py +121 -0
  423. package/extensions/services/logs/cleaner.py +76 -0
  424. package/extensions/services/logs/entry.py +449 -0
  425. package/extensions/services/logs/formatter.py +129 -0
  426. package/extensions/services/logs/module.md +38 -0
  427. package/extensions/services/logs/quick_diagnostic.py +128 -0
  428. package/extensions/services/logs/routes/__init__.py +1 -0
  429. package/extensions/services/logs/routes/routes_logs.py +218 -0
  430. package/extensions/services/logs/routes/routes_logs.py.backup +173 -0
  431. package/extensions/services/logs/scanner.py +100 -0
  432. package/extensions/services/logs/searcher.py +263 -0
  433. package/extensions/services/logs/server.py +553 -0
  434. package/extensions/services/logs.zip +0 -0
  435. package/extensions/services/model_service/config.json5 +30 -0
  436. package/extensions/services/model_service/entry.py +620 -171
  437. package/extensions/services/model_service/module.md +11 -2
  438. package/extensions/services/proxy/__init__.py +0 -0
  439. package/extensions/services/proxy/aid_manager.py +419 -0
  440. package/extensions/services/proxy/auth_bridge.py +182 -0
  441. package/extensions/services/proxy/config_store.py +79 -0
  442. package/extensions/services/proxy/entry.py +528 -0
  443. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +2 -2
  444. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +18 -28
  445. package/extensions/services/proxy/evol/presenter/configPresenter.py +80 -1127
  446. package/extensions/services/proxy/evol/presenter/userPresenter.py +71 -477
  447. package/extensions/services/proxy/evol/server/claude_proxy_async.py +11 -7
  448. package/extensions/services/proxy/module.md +151 -0
  449. package/extensions/services/proxy/server.py +952 -271
  450. package/extensions/services/redis/ALIGNMENT_CHECKLIST.md +121 -0
  451. package/extensions/services/redis/ALIGNMENT_STATUS.md +548 -0
  452. package/extensions/services/redis/config.json5 +8 -0
  453. package/extensions/services/redis/entry.py +1509 -0
  454. package/extensions/services/redis/entry.py.backup +405 -0
  455. package/extensions/services/redis/module.md +48 -0
  456. package/extensions/services/redis/redis_builtin.py +332 -0
  457. package/extensions/services/redis/redis_external.py +164 -0
  458. package/extensions/services/testUi/entry.py +446 -0
  459. package/extensions/services/testUi/module.md +18 -0
  460. package/extensions/services/testUi/ui/cards.html +131 -0
  461. package/extensions/services/testUi/ui/index.html +22 -0
  462. package/extensions/services/testUi/ui/particles.html +143 -0
  463. package/extensions/services/watchdog/entry.py +1258 -793
  464. package/extensions/services/watchdog/module.md +2 -0
  465. package/extensions/services/watchdog/monitor.py +465 -87
  466. package/extensions/services/web/auth_manager.py +602 -0
  467. package/extensions/services/web/config.json5 +11 -0
  468. package/extensions/services/web/entry.py +598 -478
  469. package/extensions/services/web/mfa_totp.py +77 -0
  470. package/extensions/services/web/module.md +16 -13
  471. package/extensions/services/web/nonce_pool.py +113 -0
  472. package/extensions/services/web/oauth_manager.py +223 -0
  473. package/extensions/services/web/pairing.py +3 -2
  474. package/extensions/services/web/pairing_codes.jsonl +1 -0
  475. package/extensions/services/web/relay.py +442 -63
  476. package/extensions/services/web/relay_config.json5 +1 -2
  477. package/extensions/services/web/routes/routes_rpc.py +6 -6
  478. package/extensions/services/web/server.py +360 -173
  479. package/extensions/services/web/static/index.html +1752 -1738
  480. package/extensions/services/web/static/js/app.js +32 -0
  481. package/extensions/services/web/static/js/kernel-client.js +48 -9
  482. package/extensions/services/web/vendor/bluetooth/audio.py +1 -1
  483. package/extensions/services/web/vendor/config.py +2 -2
  484. package/extensions/services/web/vendor/storage/identity.py +1 -1
  485. package/kernel/entry.py +77 -23
  486. package/kernel/event_hub.py +1122 -74
  487. package/kernel/module.md +2 -1
  488. package/kernel/registry_store.py +208 -11
  489. package/kernel/rpc_router.py +1400 -491
  490. package/kernel/server.py +1021 -134
  491. package/kite_cli/__init__.py +9 -1
  492. package/kite_cli/builders/__init__.py +4 -0
  493. package/kite_cli/builders/base.py +67 -0
  494. package/kite_cli/builders/custom.py +31 -0
  495. package/kite_cli/builders/detector.py +56 -0
  496. package/kite_cli/builders/go.py +34 -0
  497. package/kite_cli/builders/gradle.py +41 -0
  498. package/kite_cli/builders/maven.py +36 -0
  499. package/kite_cli/builders/npm.py +44 -0
  500. package/kite_cli/builders/python.py +37 -0
  501. package/kite_cli/commands/BUILD_GUIDE.md +109 -0
  502. package/kite_cli/commands/build.py +142 -0
  503. package/kite_cli/commands/check.py +60 -0
  504. package/kite_cli/commands/config.py +156 -0
  505. package/kite_cli/commands/deps.py +58 -0
  506. package/kite_cli/commands/deps_install.py +7 -7
  507. package/kite_cli/commands/disable.py +162 -0
  508. package/kite_cli/commands/enable.py +162 -0
  509. package/kite_cli/commands/export.py +96 -0
  510. package/kite_cli/commands/import_cmd.py +110 -0
  511. package/kite_cli/commands/install.py +50 -23
  512. package/kite_cli/commands/install_skill.py +107 -0
  513. package/kite_cli/commands/list.py +128 -31
  514. package/kite_cli/commands/outdated.py +202 -0
  515. package/kite_cli/commands/search.py +33 -17
  516. package/kite_cli/commands/update.py +115 -2
  517. package/kite_cli/commands/venv_setup.py +6 -6
  518. package/kite_cli/commands/why.py +48 -0
  519. package/kite_cli/core/config_manager.py +145 -0
  520. package/kite_cli/core/downloader.py +32 -2
  521. package/kite_cli/main.py +153 -7
  522. package/kite_cli/utils/colors.py +153 -0
  523. package/kite_cli/utils/dependency_graph.py +209 -0
  524. package/kite_cli/utils/process.py +55 -0
  525. package/kite_cli/utils/progress.py +207 -0
  526. package/kite_cli/utils/table.py +101 -0
  527. package/launcher/count_lines.py +192 -43
  528. package/launcher/entry.py +4543 -2802
  529. package/launcher/logging_setup.py +54 -1
  530. package/launcher/module.md +32 -6
  531. package/launcher/module_scanner.py +93 -20
  532. package/launcher/process_manager.py +355 -76
  533. package/main.py +6 -0
  534. package/package.json +4 -1
  535. package/requirements.txt +41 -38
  536. package/scripts/auto-fix-deps.py +128 -0
  537. package/scripts/env-manager.js +25 -2
  538. package/scripts/final-test.js +78 -0
  539. package/scripts/setup-python-env.js +700 -191
  540. package/scripts/test-alluser.js +48 -0
  541. package/scripts/test-different-version.js +86 -0
  542. package/scripts/test-direct.js +63 -0
  543. package/scripts/test-extract-installer.js +28 -0
  544. package/scripts/test-install-log.js +54 -0
  545. package/scripts/test-installer.js +39 -0
  546. package/scripts/test-integration.js +250 -0
  547. package/scripts/test-real-install.js +210 -0
  548. package/scripts/test-targetdir.js +49 -0
  549. package/scripts/test-venv-real.js +47 -0
  550. package/scripts/test-venv-simple.js +57 -0
  551. package/scripts/test-wait.js +49 -0
  552. package/scripts/test-with-log.js +63 -0
  553. package/extensions/services/evol/config.yaml +0 -149
  554. package/extensions/services/evol/routes/routes_management_ws.py +0 -127
  555. package/extensions/services/evol/static/index_evol.html +0 -14
  556. package/extensions/services/evol/static/js/app.js +0 -6304
  557. package/extensions/services/evol/static/js/auth.js +0 -326
  558. package/extensions/services/evol/static/js/evol-app-fixed.js +0 -50
  559. package/extensions/services/evol/static/js/evol-app.js.bak +0 -1800
  560. package/extensions/services/evol/static/js/kernel-client-example.js +0 -228
  561. package/extensions/services/evol/static/js/main.js +0 -141
  562. package/extensions/services/evol/static/js/stats.js +0 -217
  563. package/extensions/services/evol/static/js/token-manager.js +0 -175
  564. package/extensions/services/proxy/CHANGELOG_20260308.md +0 -258
  565. package/extensions/services/proxy/_fix_prints.py +0 -133
  566. package/extensions/services/proxy/_fix_prints2.py +0 -87
  567. package/extensions/services/proxy/console_auth.py +0 -109
  568. package/extensions/services/proxy/logs/websocket.log +0 -260
  569. package/extensions/services/proxy/main.py +0 -240
  570. package/extensions/services/proxy/requirements.txt +0 -13
  571. package/extensions/services/web/config.yaml +0 -149
  572. /package/extensions/services/{evol → kite_console}/static/pairing.html +0 -0
  573. /package/extensions/services/{evol → kite_console}/static/test_registry.html +0 -0
  574. /package/extensions/services/{evol → kite_console}/static/test_relay.html +0 -0
@@ -1,875 +1,2426 @@
1
- """
2
- Evol HTTP Server
3
- Evol account management with full Kite module management UI.
4
- """
5
-
6
- import asyncio
7
- import json
8
- import logging
9
- import os
10
- import time
11
- import uuid
12
- from datetime import datetime, timezone
13
- from pathlib import Path
14
-
15
- import websockets
16
- from fastapi import FastAPI, WebSocket, Request
17
- from fastapi.staticfiles import StaticFiles
18
- from fastapi.responses import FileResponse, JSONResponse
19
-
20
- from extensions.services.evol.evol_api import EvolAPI
21
- from extensions.services.evol.auth_manager import AuthManager
22
- from extensions.services.evol.stats_manager import StatsManager
23
- from extensions.services.evol.routes.routes_rpc import router as rpc_router, set_evol_server
24
- from extensions.services.evol.routes.routes_management_ws import router as management_ws_router, broadcast_event
25
- from extensions.services.evol.routes.routes_test import router as test_router
26
- from extensions.services.evol.config_loader import load_business_configs
27
- from extensions.services.evol.pairing import PairingManager
28
- from extensions.services.evol.relay import KernelRelay
29
-
30
-
31
- def _fmt_elapsed(t0: float) -> str:
32
- """Format elapsed time since t0."""
33
- d = time.monotonic() - t0
34
- if d < 1:
35
- return f"{d * 1000:.0f}ms"
36
- if d < 10:
37
- return f"{d:.1f}s"
38
- return f"{d:.0f}s"
39
-
40
-
41
- logger = logging.getLogger(__name__)
42
-
43
- # System broadcast events
44
- SYSTEM_BROADCAST_EVENTS = {
45
- "module.ready", "module.registered", "module.started", "module.stopped",
46
- "module.crashed", "module.exiting", "module.offline",
47
- "module.shutdown.ack", "module.shutdown.ready",
48
- "system.ready", "registry.updated",
49
- }
50
-
51
-
52
- class EvolServer:
53
- def __init__(self, token: str, kernel_port: int, host: str, port: int, boot_t0: float):
54
- self.token = token
55
- self.kernel_port = kernel_port
56
- self.host = host
57
- self.port = port
58
- self.boot_t0 = boot_t0
59
- self._ws_task: asyncio.Task | None = None
60
- self._test_task: asyncio.Task | None = None
61
- self._ws: object | None = None
62
- self._shutting_down = False
63
- self._exit_code = 0
64
- self._uvicorn_server = None
65
- self._start_time = time.time()
66
- self._rpc_futures = {}
67
-
68
- # 用户信息缓存(10秒有效期)
69
- self._user_info_cache = {} # {evol_token: {"data": ..., "timestamp": ...}}
70
- self._cache_ttl = 10 # 缓存有效期(秒)
71
-
72
- # Evol business managers
73
- data_dir = os.environ.get("KITE_DATA", os.path.expanduser("~/.kite/data"))
74
- evol_data_dir = os.path.join(data_dir, "evol")
75
- os.makedirs(evol_data_dir, exist_ok=True)
76
-
77
- self.evol_api = EvolAPI()
78
- self.auth_manager = AuthManager(evol_data_dir)
79
- self.stats_manager = StatsManager(evol_data_dir, self.evol_api, self.auth_manager)
80
-
81
- self.app = self._create_app()
82
-
83
- def _create_app(self) -> FastAPI:
84
- app = FastAPI(title="Kite Evol Module", docs_url="/docs", redoc_url=None)
85
- server = self
86
-
87
- @app.on_event("startup")
88
- async def _startup():
89
- # Start stats collection
90
- await server.stats_manager.start()
91
-
92
- # Load business configurations
93
- module_dir = Path(__file__).parent
94
- try:
95
- business_configs = load_business_configs(str(module_dir))
96
- except Exception as e:
97
- logger.error(f"Failed to load business configs: {e}")
98
- business_configs = {}
99
-
100
- # Get relay service config
101
- relay_business = business_configs.get('relay_service')
102
- if relay_business:
103
- try:
104
- relay_config = relay_business['config']
105
-
106
- # Initialize pairing manager
107
- auth_config = relay_config['auth']
108
- pairing_file = module_dir / auth_config['pairing_code_file']
109
- pairing_manager = PairingManager(
110
- pairing_file=str(pairing_file),
111
- code_length=auth_config['pairing_code_length'],
112
- token_expiry=auth_config['token_expiry']
113
- )
114
- app.state.pairing_manager = pairing_manager
115
- logger.info("Pairing manager initialized")
116
-
117
- # Initialize relay service
118
- relay_service = KernelRelay(
119
- kernel_host="127.0.0.1",
120
- kernel_port=server.kernel_port,
121
- kernel_token=server.token,
122
- base_module_id=relay_config['relay']['base_module_id'],
123
- reconnect_timeout=relay_config['relay']['reconnect_timeout'],
124
- permissions=relay_config['permissions'],
125
- pairing_manager=pairing_manager,
126
- evol_server=server
127
- )
128
- app.state.relay_service = relay_service
129
- logger.info("Relay service initialized")
130
- except KeyError as e:
131
- logger.error(f"Missing required config field for relay_service: {e}")
132
- logger.warning("Relay service disabled due to config error")
133
- except Exception as e:
134
- logger.error(f"Failed to initialize relay_service: {e}", exc_info=True)
135
- logger.warning("Relay service disabled due to initialization error")
136
- else:
137
- logger.info("Relay service not configured (no 'relay_service' in businesses)")
138
-
139
- # Start background tasks
140
- if server.kernel_port:
141
- server._ws_task = asyncio.create_task(server._ws_loop())
142
- server._test_task = asyncio.create_task(server._test_event_loop())
143
-
144
- @app.on_event("shutdown")
145
- async def _shutdown():
146
- await server.stats_manager.stop()
147
- if server._ws_task:
148
- server._ws_task.cancel()
149
- if server._test_task:
150
- server._test_task.cancel()
151
- if server._ws:
152
- await server._ws.close()
153
- print("[evol] Shutdown complete")
154
-
155
- # Health and status endpoints
156
- @app.get("/health")
157
- async def health():
158
- return {
159
- "status": "healthy",
160
- "details": {
161
- "kernel_connected": server._ws is not None,
162
- "uptime_seconds": round(time.time() - server._start_time),
163
- },
164
- }
165
-
166
- @app.get("/status")
167
- async def status():
168
- return {
169
- "module": "evol",
170
- "status": "running",
171
- "kernel_connected": server._ws is not None,
172
- "uptime_seconds": round(time.time() - server._start_time),
173
- }
174
-
175
- # Evol API routes
176
- @app.post("/api/send_sms")
177
- async def send_sms(request: Request):
178
- data = await request.json()
179
- phone = data.get("phone", "")
180
- result = await server.evol_api.send_sms(phone)
181
- return JSONResponse(result)
182
-
183
- @app.post("/api/verify_sms")
184
- async def verify_sms(request: Request):
185
- data = await request.json()
186
- phone = data.get("phone", "")
187
- code = data.get("code", "")
188
- device_info = data.get("deviceInfo", {})
189
-
190
- result = await server.evol_api.verify_sms(phone, code)
191
- if not result.get("success"):
192
- return JSONResponse(result)
193
-
194
- evol_data = result["data"]
195
- evol_token = evol_data.get("token", "")
196
- server.auth_manager.save_evol_token(phone, evol_token, evol_data)
197
-
198
- # 生成 Kite Token
199
- kite_token = server.auth_manager.generate_kite_token(device_info)
200
-
201
- # 绑定 Kite Token 到手机号
202
- server.auth_manager.bind_kite_token_to_phone(kite_token, phone)
203
-
204
- return JSONResponse({
205
- "success": True,
206
- "kiteToken": kite_token,
207
- "data": {
208
- "userInfo": evol_data.get("userInfo", {}),
209
- "apiKey": evol_data.get("apiKey", ""),
210
- "credits": evol_data.get("credits", 0)
211
- }
212
- })
213
-
214
- @app.post("/api/get_user_info")
215
- async def get_user_info(request: Request):
216
- data = await request.json()
217
- kite_token = data.get("kiteToken", "")
218
-
219
- if not server.auth_manager.verify_kite_token(kite_token):
220
- return JSONResponse({
221
- "success": False,
222
- "msg": "Kite Token 无效或已过期",
223
- "code": "INVALID_TOKEN"
224
- })
225
-
226
- # 根据 Kite Token 获取绑定的手机号
227
- phone = server.auth_manager.get_phone_by_kite_token(kite_token)
228
- if not phone:
229
- return JSONResponse({
230
- "success": False,
231
- "msg": "未登录 Evol,请先登录",
232
- "code": "NOT_LOGGED_IN"
233
- })
234
-
235
- # 根据手机号获取 Evol Token
236
- evol_record = server.auth_manager.get_evol_token(phone)
237
- if not evol_record:
238
- return JSONResponse({
239
- "success": False,
240
- "msg": "Evol Token 已过期,请重新登录",
241
- "code": "EVOL_TOKEN_EXPIRED"
242
- })
243
-
244
- # 更新 Evol Token 使用时间(超过 1 天才记录)
245
- server.auth_manager.update_evol_token_usage(phone)
246
-
247
- evol_token = evol_record["token"]
248
-
249
- # 检查缓存
250
- now = time.time()
251
- if evol_token in server._user_info_cache:
252
- cached = server._user_info_cache[evol_token]
253
- if now - cached["timestamp"] < server._cache_ttl:
254
- logger.info(f"Using cached user info (age: {now - cached['timestamp']:.1f}s)")
255
- return JSONResponse(cached["data"])
256
-
257
- # 缓存未命中或已过期,从云端获取
258
- result = await server.evol_api.get_user_info(evol_token)
259
- if not result.get("success"):
260
- return JSONResponse(result)
261
-
262
- # 手动触发账户信息采集
263
- await server.stats_manager.collect_manual()
264
-
265
- # 统一返回格式,与 verify_sms 保持一致
266
- user_data = result["data"]
267
- response_data = {
268
- "success": True,
269
- "data": user_data # 返回完整数据
270
- }
271
-
272
- # 更新缓存
273
- server._user_info_cache[evol_token] = {
274
- "data": response_data,
275
- "timestamp": now
276
- }
277
-
278
- return JSONResponse(response_data)
279
-
280
- @app.post("/api/logout")
281
- async def logout(request: Request):
282
- data = await request.json()
283
- kite_token = data.get("kiteToken", "")
284
-
285
- if not server.auth_manager.verify_kite_token(kite_token):
286
- return JSONResponse({"success": False, "msg": "Kite Token 无效"})
287
-
288
- # 获取绑定的手机号
289
- phone = server.auth_manager.get_phone_by_kite_token(kite_token)
290
-
291
- # 吊销 Evol Token(如果已绑定手机号)
292
- if phone:
293
- server.auth_manager.revoke_evol_token(phone)
294
-
295
- # 吊销 Kite Token(解除绑定)
296
- server.auth_manager.revoke_kite_token(kite_token)
297
-
298
- return JSONResponse({"success": True, "msg": "已退出登录"})
299
-
300
- @app.post("/api/get_credits_stats")
301
- async def get_credits_stats(request: Request):
302
- data = await request.json()
303
- kite_token = data.get("kiteToken", "")
304
- period = data.get("period", "day")
305
- date = data.get("date")
306
-
307
- if not server.auth_manager.verify_kite_token(kite_token):
308
- return JSONResponse({
309
- "success": False,
310
- "msg": "Kite Token 无效或已过期",
311
- "code": "INVALID_TOKEN"
312
- })
313
-
314
- result = server.stats_manager.get_stats(period, date)
315
- return JSONResponse(result)
316
-
317
- # Mount module management routes
318
- app.include_router(rpc_router, prefix="/api")
319
- app.include_router(test_router, prefix="/api")
320
- app.include_router(management_ws_router) # /ws/management
321
-
322
- # Relay WebSocket endpoint
323
- @app.websocket("/ws/relay")
324
- async def relay_endpoint(ws: WebSocket):
325
- relay_service = getattr(app.state, 'relay_service', None)
326
- if relay_service:
327
- await relay_service.handle_client(ws)
328
- else:
329
- await ws.close(code=1011, reason="Relay service not initialized")
330
-
331
- # Set evol server reference for RPC forwarding
332
- set_evol_server(server)
333
-
334
- # Serve frontend static files
335
- static_dir = Path(__file__).parent / "static"
336
- if static_dir.exists():
337
- @app.get("/")
338
- async def serve_index():
339
- index_path = static_dir / "index.html"
340
- if index_path.exists():
341
- return FileResponse(index_path)
342
- return {"message": "Kite Evol Module"}
343
-
344
- @app.get("/pairing.html")
345
- async def serve_pairing():
346
- pairing_path = static_dir / "pairing.html"
347
- if pairing_path.exists():
348
- return FileResponse(pairing_path)
349
- return JSONResponse({"error": "Not found"}, status_code=404)
350
-
351
- @app.get("/test_registry.html")
352
- async def serve_test_registry():
353
- test_path = static_dir / "test_registry.html"
354
- if test_path.exists():
355
- return FileResponse(test_path)
356
- return JSONResponse({"error": "Not found"}, status_code=404)
357
-
358
- @app.get("/test_relay.html")
359
- async def serve_test_relay():
360
- test_path = static_dir / "test_relay.html"
361
- if test_path.exists():
362
- return FileResponse(test_path)
363
- return JSONResponse({"error": "Not found"}, status_code=404)
364
-
365
- app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
366
-
367
- @app.get("/js/{file_path:path}")
368
- async def serve_js(file_path: str):
369
- file = static_dir / "js" / file_path
370
- if file.exists() and file.is_file():
371
- return FileResponse(file)
372
- return JSONResponse({"error": "Not found"}, status_code=404)
373
-
374
- @app.get("/css/{file_path:path}")
375
- async def serve_css(file_path: str):
376
- file = static_dir / "css" / file_path
377
- if file.exists() and file.is_file():
378
- return FileResponse(file)
379
- return JSONResponse({"error": "Not found"}, status_code=404)
380
-
381
- return app
382
-
383
- # ── Kernel WebSocket client ──
384
-
385
- async def _ws_loop(self):
386
- retry_delay = 0.3
387
- max_delay = 5.0
388
- max_retries = 10
389
- attempt = 0
390
- while not self._shutting_down:
391
- try:
392
- await self._ws_connect()
393
- retry_delay = 0.3
394
- attempt = 0
395
- except asyncio.CancelledError:
396
- print(f"[evol] WS loop cancelled")
397
- return
398
- except Exception as e:
399
- attempt += 1
400
- if hasattr(e, 'rcvd') and e.rcvd is not None:
401
- code = e.rcvd.code if hasattr(e.rcvd, 'code') else 0
402
- if code in (4001, 4003):
403
- print(f"[evol] Kernel 认证失败 (code {code}),退出")
404
- self._exit_code = 1
405
- self._shutting_down = True
406
- if self._uvicorn_server:
407
- self._uvicorn_server.should_exit = True
408
- return
409
- if attempt >= max_retries:
410
- print(f"[evol] Kernel 重连失败 {max_retries} 次,退出")
411
- self._exit_code = 1
412
- self._shutting_down = True
413
- if self._uvicorn_server:
414
- self._uvicorn_server.should_exit = True
415
- return
416
- if self._shutting_down:
417
- return
418
- print(f"[evol] Kernel connection error: {e}, retrying in {retry_delay:.1f}s ({attempt}/{max_retries})")
419
- self._ws = None
420
- if self._shutting_down:
421
- return
422
- await asyncio.sleep(retry_delay)
423
- retry_delay = min(retry_delay * 2, max_delay)
424
-
425
- async def _ws_receiver(self, ws):
426
- """WebSocket 接收循环(后台任务)"""
427
- try:
428
- async for raw in ws:
429
- try:
430
- msg = json.loads(raw)
431
- except (json.JSONDecodeError, TypeError):
432
- continue
433
-
434
- try:
435
- has_method = "method" in msg
436
- has_id = "id" in msg
437
- has_result_or_error = "result" in msg or "error" in msg
438
-
439
- if has_method and not has_id:
440
- await self._handle_event_notification(msg)
441
- elif has_method and has_id:
442
- asyncio.create_task(self._handle_rpc_request(ws, msg))
443
- elif has_id and has_result_or_error:
444
- rpc_id = msg.get("id")
445
- print(f"[evol] DEBUG: Received RPC response for id={rpc_id}, pending={rpc_id in self._rpc_futures}")
446
- if rpc_id in self._rpc_futures:
447
- self._rpc_futures[rpc_id].set_result(msg)
448
- except Exception as e:
449
- print(f"[evol] 消息处理异常(已忽略): {e}")
450
- except Exception as e:
451
- print(f"[evol] Receive loop exited with exception: {e}")
452
- finally:
453
- print(f"[evol] Receive loop ended")
454
-
455
- async def _ws_connect(self):
456
- url = f"ws://127.0.0.1:{self.kernel_port}/ws?token={self.token}&id=evol"
457
- print(f"[evol] WS connecting to Kernel")
458
- try:
459
- async with websockets.connect(url, open_timeout=5, ping_interval=None, close_timeout=10) as ws:
460
- self._ws = ws
461
- elapsed = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
462
- elapsed_str = f" ({elapsed:.1f}s)" if elapsed else ""
463
- print(f"[evol] Connected to Kernel{elapsed_str}")
464
-
465
- # 启动接收循环(后台任务)
466
- receiver_task = asyncio.create_task(self._ws_receiver(ws))
467
- print(f"[evol] Receiver task started")
468
-
469
- try:
470
- # Subscribe to events
471
- await self._rpc_call("event.subscribe", {
472
- "events": [
473
- "module.started",
474
- "module.stopped",
475
- "module.crashed",
476
- "module.ready",
477
- "module.exiting",
478
- "module.shutdown",
479
- "module.shutdown.ack",
480
- "module.shutdown.ready",
481
- ],
482
- })
483
-
484
- # Register to Kernel
485
- await self._rpc_call("registry.register", {
486
- "module_id": "evol",
487
- "module_type": "service",
488
- "api_endpoint": f"http://127.0.0.1:{self.port}",
489
- "health_endpoint": "/health",
490
- "tools": {
491
- "rpc": {
492
- "module": {
493
- "health": {"method": "health", "description": "健康检查"},
494
- "status": {"method": "status", "description": "状态查询"}
495
- },
496
- "evol": {
497
- "list_tokens": {"method": "list_tokens", "description": "列出所有令牌"},
498
- "list_kite_tokens": {"method": "list_kite_tokens", "description": "列出 Kite 令牌"},
499
- "list_evol_tokens": {"method": "list_evol_tokens", "description": "列出 Evol 令牌"},
500
- "revoke_token": {"method": "revoke_token", "description": "撤销令牌"}
501
- }
502
- }
503
- },
504
- "events_publish": {
505
- "evol": {
506
- "test": {"description": "Test event from evol module"},
507
- "started": {"description": "Evol UI started with access URL"},
508
- }
509
- },
510
- "events_subscribe": [
511
- "module.started",
512
- "module.stopped",
513
- "module.crashed",
514
- "module.ready",
515
- "module.exiting",
516
- "module.shutdown",
517
- "module.shutdown.ack",
518
- "module.shutdown.ready",
519
- ],
520
- })
521
- print(f"[evol] Registered to Kernel{elapsed_str}")
522
-
523
- # Send module.ready
524
- if not self._shutting_down:
525
- startup_time = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
526
- await self._rpc_call("event.publish", {
527
- "event_id": str(uuid.uuid4()),
528
- "event": "module.ready",
529
- "data": {
530
- "module_id": "evol",
531
- "graceful_shutdown": True,
532
- "startup_time": startup_time,
533
- },
534
- })
535
- elapsed_str = _fmt_elapsed(self.boot_t0)
536
- print(f"[evol] module.ready published ({elapsed_str})")
537
-
538
- # Publish evol.started event
539
- display_host = "localhost" if self.host == "0.0.0.0" else self.host
540
- access_url = f"http://{display_host}:{self.port}"
541
- await self._publish_event({
542
- "event": "evol.started",
543
- "data": {
544
- "module_id": "evol",
545
- "url": access_url,
546
- "host": self.host,
547
- "port": self.port,
548
- },
549
- })
550
-
551
- # 等待接收循环结束(连接断开)
552
- await receiver_task
553
- except Exception as e:
554
- # 取消接收任务
555
- receiver_task.cancel()
556
- try:
557
- await receiver_task
558
- except asyncio.CancelledError:
559
- pass
560
- raise
561
- except Exception as e:
562
- print(f"[evol] WebSocket connection error: {e}")
563
- raise
564
- finally:
565
- print(f"[evol] WebSocket connection closed")
566
- self._ws = None
567
-
568
- async def _rpc_call(self, method: str, params: dict = None, timeout: float = 10.0) -> dict:
569
- """Send a JSON-RPC 2.0 request and await the response."""
570
- if not self._ws:
571
- raise RuntimeError("WebSocket not connected")
572
-
573
- rpc_id = str(uuid.uuid4())
574
- msg = {"jsonrpc": "2.0", "id": rpc_id, "method": method}
575
- if params:
576
- msg["params"] = params
577
-
578
- # Create future for response
579
- response_future = asyncio.Future()
580
- self._rpc_futures[rpc_id] = response_future
581
-
582
- # Send request
583
- try:
584
- print(f"[evol] DEBUG: Sending RPC request id={rpc_id}, method={method}")
585
- await self._ws.send(json.dumps(msg))
586
- except Exception as e:
587
- self._rpc_futures.pop(rpc_id, None)
588
- raise RuntimeError(f"Failed to send RPC request: {e}")
589
-
590
- # Wait for response
591
- try:
592
- response = await asyncio.wait_for(response_future, timeout=timeout)
593
- except asyncio.TimeoutError:
594
- self._rpc_futures.pop(rpc_id, None)
595
- raise RuntimeError(f"RPC timeout waiting for {method}")
596
- finally:
597
- self._rpc_futures.pop(rpc_id, None)
598
-
599
- # Check for error
600
- if "error" in response:
601
- error = response["error"]
602
- error_msg = error.get("message", "Unknown error")
603
- error_code = error.get("code", -1)
604
- raise RuntimeError(f"RPC error: {error_msg} (code: {error_code})")
605
-
606
- return response.get("result", {})
607
-
608
- async def _handle_ping_event(self, data: dict):
609
- """Handle system.ping event and reply with system.pong."""
610
- import time
611
- t1 = data.get("ping_time")
612
- t2 = time.time()
613
-
614
- await self._publish_event({
615
- "event": "system.pong",
616
- "data": {
617
- "module_id": "evol",
618
- "ping_time": t1,
619
- "pong_time": t2,
620
- },
621
- })
622
-
623
- async def _handle_event_notification(self, msg: dict):
624
- params = msg.get("params", {})
625
- event_type = params.get("event", "")
626
- data = params.get("data", {})
627
-
628
- # Handle system.ping event
629
- if event_type == "system.ping":
630
- await self._handle_ping_event(data)
631
- return
632
-
633
- print(f"[evol] Event received: {event_type}, data: {data}")
634
-
635
- if event_type == "module.shutdown":
636
- target = data.get("module_id", "")
637
- reason = data.get("reason", "")
638
- if target == "evol" or not target or reason == "launcher_lost":
639
- await self._handle_shutdown()
640
- return
641
-
642
- # Forward module status events to management WebSocket clients
643
- if event_type in (
644
- "module.started", "module.stopped", "module.crashed",
645
- "module.ready", "module.exiting",
646
- "module.shutdown.ack", "module.shutdown.ready",
647
- ):
648
- await broadcast_event(event_type, data)
649
- return
650
-
651
- if event_type in SYSTEM_BROADCAST_EVENTS:
652
- return
653
-
654
- if os.environ.get("KITE_ENV") == "development":
655
- print(f"[evol] Debug: Unhandled event: {event_type}")
656
-
657
- async def _handle_rpc_request(self, ws, msg: dict):
658
- rpc_id = msg.get("id", "")
659
- method = msg.get("method", "")
660
- params = msg.get("params", {})
661
-
662
- if method.startswith("evol."):
663
- method = method[5:]
664
-
665
- handlers = {
666
- "health": lambda p: self._rpc_health(),
667
- "status": lambda p: self._rpc_status(),
668
- "list_tokens": lambda p: self._rpc_list_tokens(),
669
- "list_kite_tokens": lambda p: self._rpc_list_kite_tokens(),
670
- "list_evol_tokens": lambda p: self._rpc_list_evol_tokens(),
671
- "revoke_token": lambda p: self._rpc_revoke_token(p),
672
- "subscribe_events": lambda p: self._rpc_subscribe_events(p),
673
- }
674
- handler = handlers.get(method)
675
- if handler:
676
- try:
677
- result = await handler(params)
678
- await ws.send(json.dumps({"jsonrpc": "2.0", "id": rpc_id, "result": result}))
679
- except Exception as e:
680
- await ws.send(json.dumps({
681
- "jsonrpc": "2.0", "id": rpc_id,
682
- "error": {"code": -32603, "message": str(e)},
683
- }))
684
- else:
685
- await ws.send(json.dumps({
686
- "jsonrpc": "2.0", "id": rpc_id,
687
- "error": {"code": -32601, "message": f"Method not found: {method}"},
688
- }))
689
-
690
- async def _rpc_health(self) -> dict:
691
- return {
692
- "status": "healthy",
693
- "details": {
694
- "uptime_seconds": round(time.time() - self._start_time),
695
- },
696
- }
697
-
698
- async def _rpc_status(self) -> dict:
699
- return {
700
- "module": "evol",
701
- "status": "running",
702
- "uptime_seconds": round(time.time() - self._start_time),
703
- }
704
-
705
- async def _rpc_list_tokens(self) -> dict:
706
- """列出所有 Kite Token(从 AuthManager 读取)"""
707
- latest_tokens = self.auth_manager._get_latest_tokens()
708
- now = time.time()
709
-
710
- tokens = []
711
- for token, info in latest_tokens.items():
712
- # 只返回有效且未过期的 token
713
- if info.get("isValid", True) and now <= info.get("expiresAt", 0):
714
- tokens.append({
715
- "token": token,
716
- "deviceId": info.get("deviceId", "unknown"),
717
- "deviceName": info.get("deviceName", "Unknown Device"),
718
- "phone": info.get("phone"), # 添加绑定的手机号
719
- "createdAt": info.get("createdAt_human", ""),
720
- "lastUsedAt": info.get("lastUsedAt_human", ""),
721
- "expiresAt": info.get("expiresAt_human", ""),
722
- })
723
-
724
- return {"tokens": tokens}
725
-
726
- async def _rpc_list_kite_tokens(self) -> dict:
727
- """列出所有 Kite Token(前端配对令牌)"""
728
- return await self._rpc_list_tokens()
729
-
730
- async def _rpc_list_evol_tokens(self) -> dict:
731
- """列出所有 Evol Token(Evol 云端令牌)"""
732
- evol_records = self.auth_manager.list_all_evol_tokens()
733
-
734
- if not evol_records:
735
- return {"tokens": []}
736
-
737
- tokens = []
738
- for evol_record in evol_records:
739
- # 提取用户信息
740
- user_info = evol_record.get("userInfo", {})
741
- account_info = evol_record.get("accountInfo", {})
742
-
743
- tokens.append({
744
- "token": evol_record.get("token", ""),
745
- "phone": evol_record.get("phone", ""),
746
- "nickName": user_info.get("nickName", ""),
747
- "userName": user_info.get("userName", ""),
748
- "credits": account_info.get("credits", 0),
749
- "creditsLimit": account_info.get("creditsLimit", 0),
750
- "vipType": account_info.get("vipType", 0),
751
- "vipTypeName": account_info.get("vipTypeName", "Unknown"),
752
- "vipExpireTime": account_info.get("vipExpireTime", ""),
753
- "vipRemainingDays": account_info.get("vipRemainingDays", 0),
754
- "obtainedAt": evol_record.get("obtainedAt_human", ""),
755
- "lastUsedAt": evol_record.get("lastUsedAt_human", ""),
756
- "expiresAt": evol_record.get("expiresAt_human", ""),
757
- })
758
-
759
- return {"tokens": tokens}
760
-
761
- async def _rpc_revoke_token(self, params: dict) -> dict:
762
- """吊销 Kite Token(使用 AuthManager)"""
763
- token = params.get("token")
764
- if not token:
765
- raise ValueError("Missing token parameter")
766
-
767
- # 验证 token 是否存在
768
- latest_tokens = self.auth_manager._get_latest_tokens()
769
- if token not in latest_tokens:
770
- raise ValueError("Token not found")
771
-
772
- # 吊销 token
773
- self.auth_manager.revoke_kite_token(token)
774
-
775
- return {"success": True, "message": "Token revoked successfully"}
776
-
777
- async def _rpc_subscribe_events(self, params: dict) -> dict:
778
- """动态订阅事件(通过 Kernel)"""
779
- events = params.get("events", [])
780
- if not events:
781
- raise ValueError("Missing events parameter")
782
-
783
- if not self._ws:
784
- raise RuntimeError("Not connected to Kernel")
785
-
786
- # 调用 Kernel 的 event.subscribe
787
- rpc_id = f"rpc-{uuid.uuid4()}"
788
- await self._ws.send(json.dumps({
789
- "jsonrpc": "2.0",
790
- "id": rpc_id,
791
- "method": "event.subscribe",
792
- "params": {"events": events}
793
- }))
794
-
795
- print(f"[evol] Subscribed to events: {events}")
796
- return {"success": True, "events": events}
797
-
798
- async def _handle_shutdown(self):
799
- print("[evol] Received module.shutdown")
800
- self._shutting_down = True
801
-
802
- await self._publish_event({
803
- "event": "module.shutdown.ack",
804
- "data": {"module_id": "evol"},
805
- })
806
-
807
- await self._publish_event({
808
- "event": "module.exiting",
809
- "data": {
810
- "module_id": "evol",
811
- "type": "passive",
812
- "reason": "shutdown_requested",
813
- "restart": "auto",
814
- "action": "none",
815
- "timeout": 3.0,
816
- "restart_delay": 0.0,
817
- },
818
- })
819
-
820
- if self._test_task:
821
- self._test_task.cancel()
822
-
823
- # Close WebSocket connections
824
- if hasattr(self.app.state, 'relay_service'):
825
- await self.app.state.relay_service.close_all_sessions()
826
-
827
- from extensions.services.evol.routes.routes_management_ws import close_all_clients
828
- await close_all_clients()
829
-
830
- await self._publish_event({
831
- "event": "module.shutdown.ready",
832
- "data": {"module_id": "evol"},
833
- })
834
-
835
- # 等待一小段时间确保事件发送完成
836
- await asyncio.sleep(0.2)
837
-
838
- # 关闭 Kernel WebSocket 连接
839
- if self._ws:
840
- try:
841
- await self._ws.close(code=1000, reason="Graceful shutdown")
842
- print("[evol] Kernel WebSocket closed")
843
- except Exception as e:
844
- print(f"[evol] Failed to close Kernel WebSocket: {e}")
845
-
846
- # 触发 uvicorn 优雅关闭(让 uvicorn 自然退出,不要 sys.exit)
847
- if self._uvicorn_server:
848
- print("[evol] Triggering uvicorn graceful shutdown")
849
- self._uvicorn_server.should_exit = True
850
- else:
851
- print("[evol] Warning: uvicorn_server not set")
852
-
853
- async def _publish_event(self, event: dict):
854
- if not self._ws:
855
- return
856
- try:
857
- await self._rpc_call("event.publish", {
858
- "event_id": str(uuid.uuid4()),
859
- "event": event.get("event", ""),
860
- "data": event.get("data", {}),
861
- })
862
- except Exception as e:
863
- print(f"[evol] ERROR: Failed to publish event {event.get('event')}: {e}")
864
-
865
- async def _test_event_loop(self):
866
- while True:
867
- await asyncio.sleep(10)
868
- await self._publish_event({
869
- "event": "evol.test",
870
- "data": {
871
- "message": "test event from evol",
872
- "timestamp": datetime.now(timezone.utc).isoformat(),
873
- },
874
- })
875
- print("[evol] test event published")
1
+ """
2
+ Evol HTTP Server
3
+ Evol account management with full Kite module management UI.
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ import logging
9
+ import os
10
+ import random
11
+ import time
12
+ import uuid
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+
16
+ import httpx
17
+ import websockets
18
+ from fastapi import FastAPI, WebSocket, Request
19
+ from fastapi.staticfiles import StaticFiles
20
+ from fastapi.responses import FileResponse, JSONResponse
21
+
22
+ from extensions.services.evol.evol_api import EvolAPI
23
+
24
+ # VIP 类型映射: vipType 数字 -> 名称
25
+ VIP_TYPE_NAMES = {0: "Free", 1: "Plus", 2: "Pro", 3: "Max", 4: "Ultra"}
26
+ from extensions.services.evol.auth_manager import AuthManager
27
+ from extensions.services.evol.stats_manager import StatsManager
28
+ from extensions.services.evol.routes.routes_rpc import router as rpc_router, set_evol_server
29
+ from extensions.services.evol.routes.routes_test import router as test_router
30
+ from extensions.services.evol.config_loader import load_business_configs
31
+ from extensions.services.evol.pairing import PairingManager
32
+ from extensions.services.evol.relay import KernelRelay
33
+
34
+
35
+ def _fmt_elapsed(t0: float) -> str:
36
+ """Format elapsed time since t0."""
37
+ d = time.monotonic() - t0
38
+ if d < 1:
39
+ return f"{d * 1000:.0f}ms"
40
+ if d < 10:
41
+ return f"{d:.1f}s"
42
+ return f"{d:.0f}s"
43
+
44
+
45
+ logger = logging.getLogger(__name__)
46
+
47
+ # System broadcast events
48
+ SYSTEM_BROADCAST_EVENTS = {
49
+ "module.ready", "module.registered", "module.started", "module.stopped",
50
+ "module.crashed", "module.exiting", "module.offline",
51
+ "module.shutdown.ack", "module.shutdown.ready",
52
+ "system.ready", "registry.updated",
53
+ "system.instance.started", "system.instance.stopped",
54
+ }
55
+
56
+
57
+ class EvolServer:
58
+ def __init__(self, module_name: str, token: str, kernel_port: int, host: str, port: int, boot_t0: float, max_connections: int = 1,
59
+ gateway_url: str = "", kite_token: str = ""):
60
+ self.module_name = module_name
61
+ self.token = token
62
+ self.kernel_port = kernel_port
63
+ self.host = host
64
+ self.port = port
65
+ self.boot_t0 = boot_t0
66
+ self.max_connections = max_connections
67
+ self.gateway_url = gateway_url
68
+ self.kite_token = kite_token
69
+ self._ws_task: asyncio.Task | None = None
70
+ self._test_task: asyncio.Task | None = None
71
+ self._ws: object | None = None
72
+ self._shutting_down = False
73
+ self._exit_code = 0
74
+ self._auth_failed = False
75
+ self._uvicorn_server = None
76
+ self._start_time = time.time()
77
+ self._pending_rpc = {}
78
+ self._extra_ws: dict = {} # slot → WebSocket
79
+ self._extra_ws_tasks: dict = {} # slot → recv loop Task
80
+ self._has_registered = False
81
+
82
+ # 用户信息缓存(10秒有效期)
83
+ self._user_info_cache = {} # {evol_token: {"data": ..., "timestamp": ...}}
84
+ self._cache_ttl = 10 # 缓存有效期(秒)
85
+
86
+ # Evol business managers
87
+ data_dir = os.environ.get("KITE_DATA", os.path.expanduser("~/.kite/data"))
88
+ evol_data_dir = os.path.join(data_dir, "evol")
89
+ os.makedirs(evol_data_dir, exist_ok=True)
90
+
91
+ self.evol_api = EvolAPI()
92
+ self.auth_manager = AuthManager(evol_data_dir)
93
+ self.stats_manager = StatsManager(evol_data_dir, self.evol_api, self.auth_manager)
94
+
95
+ # 页面注册表(page_id → 页面元数据)
96
+ self._page_registry: dict[str, dict] = {}
97
+ self._reserved_paths = {
98
+ "/", "/login", "/account", "/ai-service", "/credits",
99
+ "/team", "/commission", "/api", "/ws", "/health",
100
+ "/status", "/assets", "/module-ui"
101
+ }
102
+ self.relay = None # relay 实例引用,在 _create_app 中初始化
103
+ self._ws_ready_event = None # WS 连接就绪信号(在 event loop 中初始化)
104
+
105
+ self.app = self._create_app()
106
+
107
+ def _create_app(self) -> FastAPI:
108
+ app = FastAPI(title="Kite Evol Module", docs_url="/docs", redoc_url=None)
109
+ server = self
110
+
111
+ @app.on_event("startup")
112
+ async def _startup():
113
+ # Token already set in entry.py, no need to read from stdin
114
+ if not server.token:
115
+ print("[evol] ERROR: Missing token, cannot connect to Kernel")
116
+ return
117
+
118
+ server._ws_ready_event = asyncio.Event()
119
+
120
+ # Start Kernel WS connection (module.ready depends on this)
121
+ if server.kernel_port:
122
+ server._ws_task = asyncio.create_task(server._ws_loop())
123
+ server._test_task = asyncio.create_task(server._test_event_loop())
124
+
125
+ # Business initialization in background (does not block module.ready)
126
+ asyncio.create_task(_deferred_init())
127
+
128
+ async def _deferred_init():
129
+ """Business initialization that runs after WS connection starts."""
130
+ # 等待 WS 连接就绪(module.ready 已发布),最多等 30s
131
+ if server._ws_ready_event:
132
+ try:
133
+ await asyncio.wait_for(server._ws_ready_event.wait(), timeout=30)
134
+ except asyncio.TimeoutError:
135
+ print("[evol] WARNING: WS ready timeout, continuing deferred init")
136
+
137
+ # 检查本地是否有已登录的 evol_token,有则发布 evol.user_ready
138
+ await server._check_and_publish_user_ready()
139
+
140
+ await server.stats_manager.start()
141
+
142
+ # Load business configurations
143
+ module_dir = Path(__file__).parent
144
+ try:
145
+ business_configs = load_business_configs(str(module_dir))
146
+ except Exception as e:
147
+ logger.error(f"Failed to load business configs: {e}")
148
+ business_configs = {}
149
+
150
+ # Get relay service config
151
+ relay_business = business_configs.get('relay_service')
152
+ if relay_business:
153
+ try:
154
+ relay_config = relay_business['config']
155
+
156
+ # Initialize pairing manager
157
+ auth_config = relay_config['auth']
158
+ pairing_file = module_dir / auth_config['pairing_code_file']
159
+ pairing_manager = PairingManager(
160
+ pairing_file=str(pairing_file),
161
+ code_length=auth_config['pairing_code_length'],
162
+ token_expiry=auth_config['token_expiry']
163
+ )
164
+ app.state.pairing_manager = pairing_manager
165
+ logger.info("Pairing manager initialized")
166
+
167
+ # Initialize relay service
168
+ relay_service = KernelRelay(
169
+ kernel_host="127.0.0.1",
170
+ kernel_port=server.kernel_port,
171
+ kernel_token=server.token,
172
+ base_module_id=relay_config['relay']['base_module_id'],
173
+ reconnect_timeout=relay_config['relay']['reconnect_timeout'],
174
+ permissions=relay_config['permissions'],
175
+ pairing_manager=pairing_manager,
176
+ evol_server=server,
177
+ auth_manager=server.auth_manager # 新增:传递 auth_manager
178
+ )
179
+ app.state.relay_service = relay_service
180
+ server.relay = relay_service # 保存引用供事件处理使用
181
+ logger.info("Relay service initialized")
182
+ except KeyError as e:
183
+ logger.error(f"Missing required config field for relay_service: {e}")
184
+ logger.warning("Relay service disabled due to config error")
185
+ except Exception as e:
186
+ logger.error(f"Failed to initialize relay_service: {e}", exc_info=True)
187
+ logger.warning("Relay service disabled due to initialization error")
188
+ else:
189
+ logger.info("Relay service not configured (no 'relay_service' in businesses)")
190
+
191
+ @app.on_event("shutdown")
192
+ async def _shutdown():
193
+ await server.stats_manager.stop()
194
+ if server._ws_task:
195
+ server._ws_task.cancel()
196
+ if server._test_task:
197
+ server._test_task.cancel()
198
+ if server._ws:
199
+ await server._ws.close()
200
+ print("[evol] Shutdown complete")
201
+
202
+ # Health and status endpoints
203
+ @app.get("/health")
204
+ async def health():
205
+ return {
206
+ "status": "healthy",
207
+ "details": {
208
+ "kernel_connected": server._ws is not None,
209
+ "uptime_seconds": round(time.time() - server._start_time),
210
+ },
211
+ }
212
+
213
+ @app.get("/status")
214
+ async def status():
215
+ return {
216
+ "module": "evol",
217
+ "status": "running",
218
+ "kernel_connected": server._ws is not None,
219
+ "uptime_seconds": round(time.time() - server._start_time),
220
+ }
221
+
222
+ @app.get("/api/system_info")
223
+ async def system_info():
224
+ """获取系统版本和环境信息"""
225
+ # 从环境变量读取版本号和环境类型
226
+ version = os.environ.get("KITE_VERSION", "unknown")
227
+ env = os.environ.get("KITE_ENV", "development")
228
+
229
+ return {
230
+ "success": True,
231
+ "data": {
232
+ "version": version,
233
+ "environment": env
234
+ }
235
+ }
236
+
237
+ # Evol API routes
238
+ @app.post("/api/send_sms")
239
+ async def send_sms(request: Request):
240
+ data = await request.json()
241
+ phone = data.get("phone", "")
242
+ result = await server.evol_api.send_sms(phone)
243
+ return JSONResponse(result)
244
+
245
+ @app.post("/api/verify_sms")
246
+ async def verify_sms(request: Request):
247
+ data = await request.json()
248
+ phone = data.get("phone", "")
249
+ code = data.get("code", "")
250
+ device_info = data.get("deviceInfo", {})
251
+
252
+ result = await server.evol_api.verify_sms(phone, code)
253
+ if not result.get("success"):
254
+ return JSONResponse(result)
255
+
256
+ evol_data = result["data"]
257
+ evol_token = evol_data.get("token", "")
258
+ server.auth_manager.save_evol_token(phone, evol_token, evol_data)
259
+
260
+ # 生成 Kite Token
261
+ kite_token = server.auth_manager.generate_kite_token(device_info)
262
+
263
+ # 绑定 Kite Token 到手机号
264
+ server.auth_manager.bind_kite_token_to_phone(kite_token, phone)
265
+
266
+ # 登录成功,发布 evol.user_ready 通知其他模块
267
+ try:
268
+ await server._publish_user_ready(phone)
269
+ except Exception:
270
+ pass
271
+
272
+ return JSONResponse({
273
+ "success": True,
274
+ "kiteToken": kite_token,
275
+ "data": {
276
+ "userInfo": evol_data.get("userInfo", {}),
277
+ "apiKey": evol_data.get("apiKey", ""),
278
+ "credits": evol_data.get("credits", 0)
279
+ }
280
+ })
281
+
282
+ @app.post("/api/get_user_info")
283
+ async def get_user_info(request: Request):
284
+ data = await request.json()
285
+ kite_token = data.get("kiteToken", "")
286
+
287
+ if not server.auth_manager.verify_kite_token(kite_token):
288
+ return JSONResponse({
289
+ "success": False,
290
+ "msg": "Kite Token 无效或已过期",
291
+ "code": "INVALID_TOKEN"
292
+ })
293
+
294
+ # 根据 Kite Token 获取绑定的手机号
295
+ phone = server.auth_manager.get_phone_by_kite_token(kite_token)
296
+ if not phone:
297
+ return JSONResponse({
298
+ "success": False,
299
+ "msg": "未登录 Evol,请先登录",
300
+ "code": "NOT_LOGGED_IN"
301
+ })
302
+
303
+ # 根据手机号获取 Evol Token
304
+ evol_record = server.auth_manager.get_evol_token(phone)
305
+ if not evol_record:
306
+ return JSONResponse({
307
+ "success": False,
308
+ "msg": "Evol Token 已过期,请重新登录",
309
+ "code": "EVOL_TOKEN_EXPIRED"
310
+ })
311
+
312
+ # 更新 Evol Token 使用时间(超过 1 天才记录)
313
+ server.auth_manager.update_evol_token_usage(phone)
314
+
315
+ evol_token = evol_record["token"]
316
+
317
+ # 检查缓存
318
+ now = time.time()
319
+ if evol_token in server._user_info_cache:
320
+ cached = server._user_info_cache[evol_token]
321
+ if now - cached["timestamp"] < server._cache_ttl:
322
+ logger.info(f"Using cached user info (age: {now - cached['timestamp']:.1f}s)")
323
+ return JSONResponse(cached["data"])
324
+
325
+ # 缓存未命中或已过期,从云端获取
326
+ result = await server.evol_api.get_user_info(evol_token)
327
+ if not result.get("success"):
328
+ return JSONResponse(result)
329
+
330
+ # 手动触发账户信息采集
331
+ await server.stats_manager.collect_manual()
332
+
333
+ # 统一返回格式,与 verify_sms 保持一致
334
+ user_data = result["data"]
335
+ response_data = {
336
+ "success": True,
337
+ "data": user_data # 返回完整数据
338
+ }
339
+
340
+ # 更新缓存
341
+ server._user_info_cache[evol_token] = {
342
+ "data": response_data,
343
+ "timestamp": now
344
+ }
345
+
346
+ return JSONResponse(response_data)
347
+
348
+ @app.post("/api/logout")
349
+ async def logout(request: Request):
350
+ data = await request.json()
351
+ kite_token = data.get("kiteToken", "")
352
+
353
+ if not server.auth_manager.verify_kite_token(kite_token):
354
+ return JSONResponse({"success": False, "msg": "Kite Token 无效"})
355
+
356
+ # 获取绑定的手机号
357
+ phone = server.auth_manager.get_phone_by_kite_token(kite_token)
358
+
359
+ # 吊销 Evol Token(如果已绑定手机号)
360
+ if phone:
361
+ server.auth_manager.revoke_evol_token(phone)
362
+
363
+ # 吊销 Kite Token(解除绑定)
364
+ server.auth_manager.revoke_kite_token(kite_token)
365
+
366
+ # 通知 Proxy 下线
367
+ try:
368
+ await server._publish_event({
369
+ "event": "evol.user_logout",
370
+ "data": {"phone": phone, "reason": "user_logout"},
371
+ })
372
+ except Exception as e:
373
+ print(f"[evol] 发布 user_logout 事件失败: {e}")
374
+
375
+ return JSONResponse({"success": True, "msg": "已退出登录"})
376
+
377
+ @app.post("/api/get_credits_stats")
378
+ async def get_credits_stats(request: Request):
379
+ data = await request.json()
380
+ kite_token = data.get("kiteToken", "")
381
+ period = data.get("period", "day")
382
+ date = data.get("date")
383
+
384
+ if not server.auth_manager.verify_kite_token(kite_token):
385
+ return JSONResponse({
386
+ "success": False,
387
+ "msg": "Kite Token 无效或已过期",
388
+ "code": "INVALID_TOKEN"
389
+ })
390
+
391
+ result = server.stats_manager.get_stats(period, date)
392
+ return JSONResponse(result)
393
+
394
+ # Mount module management routes
395
+ app.include_router(rpc_router, prefix="/api")
396
+ app.include_router(test_router, prefix="/api")
397
+
398
+ # Relay WebSocket endpoint
399
+ @app.websocket("/ws/relay")
400
+ async def relay_endpoint(ws: WebSocket):
401
+ relay_service = getattr(app.state, 'relay_service', None)
402
+ if relay_service:
403
+ await relay_service.handle_client(ws)
404
+ else:
405
+ await ws.close(code=1011, reason="Relay service not initialized")
406
+
407
+ # Set evol server reference for RPC forwarding
408
+ set_evol_server(server)
409
+
410
+ # Serve frontend static files (Vue SPA)
411
+ static_dir = Path(__file__).parent / "static"
412
+ if static_dir.exists():
413
+ # Mount assets directory for Vite build output
414
+ assets_dir = static_dir / "assets"
415
+ if assets_dir.exists():
416
+ app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")
417
+
418
+ # Legacy routes for old test pages
419
+ @app.get("/pairing.html")
420
+ async def serve_pairing():
421
+ pairing_path = static_dir / "pairing.html"
422
+ if pairing_path.exists():
423
+ return FileResponse(pairing_path)
424
+ return JSONResponse({"error": "Not found"}, status_code=404)
425
+
426
+ @app.get("/test_registry.html")
427
+ async def serve_test_registry():
428
+ test_path = static_dir / "test_registry.html"
429
+ if test_path.exists():
430
+ return FileResponse(test_path)
431
+ return JSONResponse({"error": "Not found"}, status_code=404)
432
+
433
+ @app.get("/test_relay.html")
434
+ async def serve_test_relay():
435
+ test_path = static_dir / "test_relay.html"
436
+ if test_path.exists():
437
+ return FileResponse(test_path)
438
+ return JSONResponse({"error": "Not found"}, status_code=404)
439
+
440
+ # SPA fallback - serve index.html for all unmatched routes
441
+ @app.get("/{full_path:path}")
442
+ async def serve_spa(full_path: str):
443
+ # Check if it's a static file request
444
+ file_path = static_dir / full_path
445
+ if file_path.exists() and file_path.is_file():
446
+ return FileResponse(file_path)
447
+
448
+ # Otherwise serve index.html (SPA fallback)
449
+ index_path = static_dir / "index.html"
450
+ if index_path.exists():
451
+ return FileResponse(index_path)
452
+ return JSONResponse({"error": "Not found"}, status_code=404)
453
+
454
+ return app
455
+
456
+ # ── Kernel WebSocket client ──
457
+
458
+ async def _ws_loop(self):
459
+ retry_delay = 0.5
460
+ max_delay = 30.0
461
+ attempt = 0
462
+ cooldown_attempts = 0
463
+
464
+ while not self._shutting_down:
465
+ try:
466
+ await self._ws_connect()
467
+ retry_delay = 0.5
468
+ attempt = 0
469
+ cooldown_attempts = 0
470
+ except asyncio.CancelledError:
471
+ print(f"WS loop cancelled")
472
+ return
473
+ except Exception as e:
474
+ if self._shutting_down:
475
+ return
476
+
477
+ code = self._get_close_code(e)
478
+
479
+ # never: 永不重连
480
+ if code in (4001, 4003, 4004, 1008, 4010):
481
+ print(f"\033[31m致命错误 (code {code}),退出\033[0m")
482
+ self._exit_code = 1
483
+ self._auth_failed = True
484
+ self._shutting_down = True
485
+ if self._uvicorn_server:
486
+ self._uvicorn_server.should_exit = True
487
+ return
488
+
489
+ # cooldown: 速率限制
490
+ if code == 4020:
491
+ cooldown_attempts += 1
492
+ if cooldown_attempts >= 5:
493
+ print(f"\033[31m速率限制重试 5 次,退出\033[0m")
494
+ self._exit_code = 1
495
+ self._shutting_down = True
496
+ if self._uvicorn_server:
497
+ self._uvicorn_server.should_exit = True
498
+ return
499
+ print(f"\033[33m速率限制,10.0s 后重试 ({cooldown_attempts}/5)\033[0m")
500
+ await asyncio.sleep(10.0)
501
+ continue
502
+
503
+ # standard: 指数退避 + jitter
504
+ attempt += 1
505
+ jitter = retry_delay * 0.2 * random.random()
506
+ sleep_time = retry_delay + jitter
507
+ print(f"\033[31mKernel connection error: {e}, retrying in {sleep_time:.1f}s (attempt {attempt})\033[0m")
508
+
509
+ self._ws = None
510
+ if self._shutting_down:
511
+ return
512
+ await asyncio.sleep(sleep_time if 'sleep_time' in locals() else retry_delay)
513
+ retry_delay = min(retry_delay * 2, max_delay)
514
+
515
+ def _get_close_code(self, e: Exception) -> int:
516
+ """从 websockets 异常中提取关闭码"""
517
+ if hasattr(e, 'rcvd') and e.rcvd is not None:
518
+ return getattr(e.rcvd, 'code', 0)
519
+ return 0
520
+
521
+ async def _ws_receiver(self, ws):
522
+ """WebSocket 接收循环(后台任务)"""
523
+ try:
524
+ async for raw in ws:
525
+ try:
526
+ msg = json.loads(raw)
527
+ except (json.JSONDecodeError, TypeError):
528
+ continue
529
+
530
+ try:
531
+ has_method = "method" in msg
532
+ has_id = "id" in msg
533
+ has_result_or_error = "result" in msg or "error" in msg
534
+
535
+ # 检测 system.require_init 事件,触发注册流程
536
+ if has_method and not has_id:
537
+ params = msg.get("params", {})
538
+ event = params.get("event", "")
539
+ if event == "system.require_init":
540
+ asyncio.create_task(self._do_init(ws))
541
+ continue
542
+
543
+ if has_method and not has_id:
544
+ # 事件通知也需要异步处理,避免阻塞接收循环
545
+ asyncio.create_task(self._handle_event_notification(msg))
546
+ elif has_method and has_id:
547
+ asyncio.create_task(self._handle_rpc_request(ws, msg))
548
+ elif has_id and has_result_or_error:
549
+ self._handle_rpc_response(msg)
550
+ except Exception as e:
551
+ print(f"[evol] 消息处理异常(已忽略): {e}")
552
+ except Exception as e:
553
+ print(f"[evol] Receive loop exited with exception: {e}")
554
+ finally:
555
+ print(f"[evol] Receive loop ended")
556
+
557
+ async def _do_init(self, ws):
558
+ """收到 system.require_init 后执行:订阅 + 注册 + module.ready"""
559
+ try:
560
+ reason = "startup" if not self._has_registered else "recovery"
561
+
562
+ # Subscribe to events
563
+ await self._rpc_call(ws, "event.subscribe", {
564
+ "events": [
565
+ "module.started",
566
+ "module.stopped",
567
+ "module.crashed",
568
+ "module.ready",
569
+ "module.exiting",
570
+ "module.shutdown",
571
+ "module.shutdown.ack",
572
+ "module.shutdown.ready",
573
+ "module.offline",
574
+ "ui.page.register",
575
+ "ui.page.deregister",
576
+ ],
577
+ })
578
+
579
+ # Register to Kernel
580
+ await self._rpc_call(ws, "registry.register", {
581
+ "module_id": self.module_name,
582
+ "module_type": "service",
583
+ "base_url": f"http://127.0.0.1:{self.port}",
584
+ "health_path": "/health",
585
+ "tools": {
586
+ "rpc": {
587
+ "module": {
588
+ "health": {"method": "health", "description": "健康检查"},
589
+ "status": {"method": "status", "description": "状态查询"}
590
+ },
591
+ "evol": {
592
+ "list_tokens": {"method": "list_tokens", "description": "列出所有令牌"},
593
+ "list_kite_tokens": {"method": "list_kite_tokens", "description": "列出 Kite 令牌"},
594
+ "list_evol_tokens": {"method": "list_evol_tokens", "description": "列出 Evol 令牌"},
595
+ "revoke_token": {"method": "revoke_token", "description": "撤销 Kite 令牌"},
596
+ "revoke_evol_token": {"method": "revoke_evol_token", "description": "撤销 Evol 令牌"},
597
+ "get_user_aid": {"method": "get_user_aid", "description": "获取用户 AID 基本信息"},
598
+ "get_user_aid_detail": {"method": "get_user_aid_detail", "description": "获取用户 AID 详细信息(含私钥/证书)"},
599
+ "submit_user_aid": {"method": "submit_user_aid", "description": "提交用户 AID 信息到服务器"},
600
+ "get_api_keys": {"method": "get_api_keys", "description": "获取用户的 API Key 列表"},
601
+ "send_sms": {"method": "send_sms", "description": "发送短信验证码"},
602
+ "verify_sms": {"method": "verify_sms", "description": "验证短信并登录"},
603
+ "has_evol_token": {"method": "has_evol_token", "description": "检查是否有可用的 evol_token"}
604
+ }
605
+ }
606
+ },
607
+ "events_publish": {
608
+ self.module_name: {
609
+ "test": {"description": "Test event from evol module"},
610
+ "started": {"description": "Evol UI started with access URL"},
611
+ "user_ready": {"description": "用户登录状态可用(有 evol_token)"},
612
+ "user_logout": {"description": "用户退出登录"},
613
+ }
614
+ },
615
+ "events_subscribe": [
616
+ "module.started",
617
+ "module.stopped",
618
+ "module.crashed",
619
+ "module.ready",
620
+ "module.exiting",
621
+ "module.shutdown",
622
+ "module.shutdown.ack",
623
+ "module.shutdown.ready",
624
+ "module.offline",
625
+ "ui.page.register",
626
+ "ui.page.deregister",
627
+ ],
628
+ })
629
+ print(f"[evol] Registered to Kernel")
630
+
631
+ # Send module.ready
632
+ if not self._shutting_down:
633
+ startup_time = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
634
+ await self._publish_event(ws, "module.ready", {
635
+ "module_id": self.module_name,
636
+ "graceful_shutdown": True,
637
+ "startup_time": startup_time,
638
+ "reason": reason,
639
+ })
640
+ elapsed_str = _fmt_elapsed(self.boot_t0)
641
+ print(f"[evol] module.ready published (reason={reason}, {elapsed_str})")
642
+
643
+ # Publish evol.started event
644
+ display_host = "localhost" if self.host == "0.0.0.0" else self.host
645
+ access_url = f"http://{display_host}:{self.port}"
646
+ await self._publish_event(ws, "evol.started", {
647
+ "module_id": self.module_name,
648
+ "url": access_url,
649
+ "host": self.host,
650
+ "port": self.port,
651
+ })
652
+
653
+ # 通知 _deferred_init:WS 已就绪,可以发布业务事件
654
+ if self._ws_ready_event:
655
+ self._ws_ready_event.set()
656
+ self._has_registered = True
657
+ except Exception as e:
658
+ print(f"[evol] _do_init failed: {e}")
659
+ self._has_registered = False
660
+
661
+ async def _ws_connect(self):
662
+ url = f"ws://127.0.0.1:{self.kernel_port}/ws?id={self.module_name}"
663
+ print(f"[evol] WS connecting to Kernel")
664
+ try:
665
+ async with websockets.connect(url, open_timeout=5, ping_interval=None, close_timeout=10) as ws:
666
+ # Send auth message first
667
+ auth_req = {
668
+ "jsonrpc": "2.0",
669
+ "id": "auth",
670
+ "method": "auth",
671
+ "params": {"token": self.token}
672
+ }
673
+ await ws.send(json.dumps(auth_req))
674
+
675
+ # Wait for auth response
676
+ auth_resp_raw = await asyncio.wait_for(ws.recv(), timeout=5)
677
+ auth_resp = json.loads(auth_resp_raw)
678
+ if "error" in auth_resp:
679
+ raise Exception(f"Auth failed: {auth_resp['error']}")
680
+
681
+ self._ws = ws
682
+ elapsed = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
683
+ elapsed_str = f" ({elapsed:.1f}s)" if elapsed else ""
684
+ print(f"[evol] Connected to Kernel{elapsed_str}")
685
+
686
+ # 启动接收循环(后台任务)
687
+ receiver_task = asyncio.create_task(self._ws_receiver(ws))
688
+ print(f"[evol] Receiver task started")
689
+
690
+ try:
691
+ # subscribe/register/module.ready 由 _do_init 在收到 system.require_init 后执行
692
+
693
+ # 等待接收循环结束(连接断开)
694
+ await receiver_task
695
+ except Exception as e:
696
+ # 取消接收任务
697
+ receiver_task.cancel()
698
+ try:
699
+ await receiver_task
700
+ except asyncio.CancelledError:
701
+ pass
702
+ raise
703
+ except Exception as e:
704
+ print(f"[evol] WebSocket connection error: {e}")
705
+ raise
706
+ finally:
707
+ print(f"[evol] WebSocket connection closed")
708
+ self._ws = None
709
+ # 清理所有等待中的 RPC future
710
+ for fut in self._pending_rpc.values():
711
+ if not fut.done():
712
+ fut.set_exception(ConnectionError("WebSocket disconnected"))
713
+ self._pending_rpc.clear()
714
+
715
+ async def _rpc_call(self, ws, method: str, params: dict = None,
716
+ wait_response: bool = True, timeout: float = 3.0) -> dict:
717
+ """JSON-RPC 2.0 request。默认等待响应。"""
718
+ rpc_id = str(uuid.uuid4())
719
+ msg = {"jsonrpc": "2.0", "id": rpc_id, "method": method}
720
+ if params:
721
+ msg["params"] = params
722
+ if not wait_response:
723
+ await ws.send(json.dumps(msg))
724
+ return {}
725
+ future = asyncio.get_event_loop().create_future()
726
+ self._pending_rpc[rpc_id] = future
727
+ await ws.send(json.dumps(msg))
728
+ try:
729
+ return await asyncio.wait_for(future, timeout=timeout)
730
+ except asyncio.TimeoutError:
731
+ self._pending_rpc.pop(rpc_id, None)
732
+ return {"error": {"code": -32000, "message": f"RPC timeout: {method} ({timeout}s)"}}
733
+
734
+ def _handle_rpc_response(self, msg: dict):
735
+ rpc_id = msg.get("id")
736
+ future = self._pending_rpc.pop(rpc_id, None)
737
+ if future and not future.done():
738
+ future.set_result(msg)
739
+
740
+ async def _handle_ping_event(self, data: dict):
741
+ """Handle system.ping event and reply with system.pong."""
742
+ import time
743
+ t1 = data.get("ping_time")
744
+ t2 = time.time()
745
+
746
+ await self._publish_event(self._ws, "system.pong", {
747
+ "module_id": self.module_name,
748
+ "ping_time": t1,
749
+ "pong_time": t2,
750
+ })
751
+
752
+ async def _handle_event_notification(self, msg: dict):
753
+ params = msg.get("params", {})
754
+ event_type = params.get("event", "")
755
+ data = params.get("data", {})
756
+ source = params.get("source", "")
757
+
758
+ # Handle system.ping event
759
+ if event_type == "system.ping":
760
+ await self._handle_ping_event(data)
761
+ return
762
+
763
+ # 弹性连接 offer/release
764
+ if event_type == "system.connection.offer":
765
+ asyncio.create_task(self._handle_connection_offer(data))
766
+ return
767
+ if event_type == "system.connection.release":
768
+ asyncio.create_task(self._handle_connection_release(data))
769
+ return
770
+
771
+ # Handle UI page registration
772
+ if event_type == "ui.page.register":
773
+ await self._handle_page_register(source, data)
774
+ return
775
+
776
+ if event_type == "ui.page.deregister":
777
+ await self._handle_page_deregister(data)
778
+ return
779
+
780
+ # Handle module offline - cleanup pages
781
+ if event_type == "module.offline":
782
+ module_id = data.get("module_id", "")
783
+ if module_id:
784
+ await self._cleanup_module_pages(module_id)
785
+ return
786
+
787
+ print(f"[evol] Event received: {event_type}, data: {data}")
788
+
789
+ if event_type == "module.shutdown":
790
+ target = data.get("module_id", "")
791
+ reason = data.get("reason", "")
792
+ if target == "evol" or not target or reason == "launcher_lost":
793
+ await self._handle_shutdown()
794
+ return
795
+
796
+ # Module status events are now handled by Kernel event system
797
+ # Clients subscribe via /ws/relay
798
+ if event_type in (
799
+ "module.started", "module.stopped", "module.crashed",
800
+ "module.ready", "module.exiting",
801
+ "module.shutdown.ack", "module.shutdown.ready",
802
+ ):
803
+ return
804
+
805
+ if event_type in SYSTEM_BROADCAST_EVENTS:
806
+ return
807
+
808
+ if os.environ.get("KITE_ENV") == "development":
809
+ print(f"[evol] Debug: Unhandled event: {event_type}")
810
+
811
+ async def _handle_rpc_request(self, ws, msg: dict):
812
+ rpc_id = msg.get("id", "")
813
+ method = msg.get("method", "")
814
+ params = msg.get("params", {})
815
+
816
+ if method.startswith("evol."):
817
+ method = method[5:]
818
+
819
+ handlers = {
820
+ "health": lambda p: self._rpc_health(),
821
+ "status": lambda p: self._rpc_status(),
822
+ "list_tokens": lambda p: self._rpc_list_tokens(),
823
+ "list_kite_tokens": lambda p: self._rpc_list_kite_tokens(),
824
+ "list_evol_tokens": lambda p: self._rpc_list_evol_tokens(),
825
+ "revoke_token": lambda p: self._rpc_revoke_token(p),
826
+ "revoke_evol_token": lambda p: self._rpc_revoke_evol_token(p),
827
+ "subscribe_events": lambda p: self._rpc_subscribe_events(p),
828
+ "get_user_info": lambda p: self._rpc_get_user_info(p),
829
+ "get_credits_stats": lambda p: self._rpc_get_credits_stats(p),
830
+ "logout": lambda p: self._rpc_logout(p),
831
+ "get_user_aid": lambda p: self._rpc_get_user_aid(p),
832
+ "get_user_aid_detail": lambda p: self._rpc_get_user_aid_detail(p),
833
+ "submit_user_aid": lambda p: self._rpc_submit_user_aid(p),
834
+ "get_api_keys": lambda p: self._rpc_get_api_keys(p),
835
+ "send_sms": lambda p: self._rpc_send_sms(p),
836
+ "verify_sms": lambda p: self._rpc_verify_sms(p),
837
+ "has_evol_token": lambda p: self._rpc_has_evol_token(),
838
+ "switch_team": lambda p: self._rpc_switch_team(p),
839
+ # 积分模块
840
+ "get_credits_change_records": lambda p: self._rpc_get_credits_change_records(p),
841
+ "get_credits_package_list": lambda p: self._rpc_get_credits_package_list(p),
842
+ "get_user_hourly_trend": lambda p: self._rpc_get_user_hourly_trend(p),
843
+ "get_user_daily_trend": lambda p: self._rpc_get_user_daily_trend(p),
844
+ "get_user_summary": lambda p: self._rpc_get_user_summary(p),
845
+ "get_credits_list": lambda p: self._rpc_get_credits_list(p),
846
+ # VIP 订阅模块
847
+ "get_vip_packages": lambda p: self._rpc_get_vip_packages(p),
848
+ "create_vip_order": lambda p: self._rpc_create_vip_order(p),
849
+ "get_order_list": lambda p: self._rpc_get_order_list(p),
850
+ "cancel_order": lambda p: self._rpc_cancel_order(p),
851
+ # 发票模块
852
+ "get_invoice_list": lambda p: self._rpc_get_invoice_list(p),
853
+ "apply_invoice": lambda p: self._rpc_apply_invoice(p),
854
+ # API Key 模块
855
+ "get_apikey_list": lambda p: self._rpc_get_apikey_list(p),
856
+ "create_apikey": lambda p: self._rpc_create_apikey(p),
857
+ "update_apikey": lambda p: self._rpc_update_apikey(p),
858
+ "delete_apikey": lambda p: self._rpc_delete_apikey(p),
859
+ # 团队模块
860
+ "get_team_members": lambda p: self._rpc_get_team_members(p),
861
+ "team.create": lambda p: self._rpc_team_create(p),
862
+ "team.get_detail": lambda p: self._rpc_team_get_detail(p),
863
+ "team.delete": lambda p: self._rpc_team_delete(p),
864
+ "team.quit": lambda p: self._rpc_team_quit(p),
865
+ "team.get_account": lambda p: self._rpc_team_get_account(p),
866
+ "team.search_user": lambda p: self._rpc_team_search_user(p),
867
+ "team.add_member": lambda p: self._rpc_team_add_member(p),
868
+ "team.batch_add_members": lambda p: self._rpc_team_batch_add_members(p),
869
+ "team.update_member": lambda p: self._rpc_team_update_member(p),
870
+ "team.remove_member": lambda p: self._rpc_team_remove_member(p),
871
+ "team.set_member_admin": lambda p: self._rpc_team_set_member_admin(p),
872
+ "team.remove_member_admin": lambda p: self._rpc_team_remove_member_admin(p),
873
+ "team.set_member_credits_limit": lambda p: self._rpc_team_set_member_credits_limit(p),
874
+ "team.batch_set_credits_limit": lambda p: self._rpc_team_batch_set_credits_limit(p),
875
+ "team.get_member_quota_info": lambda p: self._rpc_team_get_member_quota_info(p),
876
+ "team.reset_member_quota": lambda p: self._rpc_team_reset_member_quota(p),
877
+ "team.convert_member_credits": lambda p: self._rpc_team_convert_member_credits(p),
878
+ "team.update_share_creator_credits": lambda p: self._rpc_team_update_share_creator_credits(p),
879
+ "team.update_analyze_member_data": lambda p: self._rpc_team_update_analyze_member_data(p),
880
+ "team.get_credits_stats": lambda p: self._rpc_team_get_credits_stats(p),
881
+ "team.get_members_ranking": lambda p: self._rpc_team_get_members_ranking(p),
882
+ "team.get_trend": lambda p: self._rpc_team_get_trend(p),
883
+ "team.get_today_stats": lambda p: self._rpc_team_get_today_stats(p),
884
+ "team.get_today_members_stats": lambda p: self._rpc_team_get_today_members_stats(p),
885
+ "team.get_hourly_stats": lambda p: self._rpc_team_get_hourly_stats(p),
886
+ "team.get_weekly_stats": lambda p: self._rpc_team_get_weekly_stats(p),
887
+ "team.get_monthly_stats": lambda p: self._rpc_team_get_monthly_stats(p),
888
+ "team.get_credits_logs": lambda p: self._rpc_team_get_credits_logs(p),
889
+ "team.get_user_today_models": lambda p: self._rpc_team_get_user_today_models(p),
890
+ "team.statistics_history_30days": lambda p: self._rpc_team_statistics_history_30days(p),
891
+ "team.get_today_group_stats": lambda p: self._rpc_team_get_today_group_stats(p),
892
+ "team.get_history_group_stats": lambda p: self._rpc_team_get_history_group_stats(p),
893
+ "team.update_models": lambda p: self._rpc_team_update_models(p),
894
+ "team.get_member_model_settings": lambda p: self._rpc_team_get_member_model_settings(p),
895
+ "team.set_member_model_settings": lambda p: self._rpc_team_set_member_model_settings(p),
896
+ "team.create_group": lambda p: self._rpc_team_create_group(p),
897
+ "team.get_group_tree": lambda p: self._rpc_team_get_group_tree(p),
898
+ "team.get_group_detail": lambda p: self._rpc_team_get_group_detail(p),
899
+ "team.update_group": lambda p: self._rpc_team_update_group(p),
900
+ "team.delete_group": lambda p: self._rpc_team_delete_group(p),
901
+ "team.move_group": lambda p: self._rpc_team_move_group(p),
902
+ "team.add_members_to_group": lambda p: self._rpc_team_add_members_to_group(p),
903
+ "team.remove_members_from_group": lambda p: self._rpc_team_remove_members_from_group(p),
904
+ "team.get_group_members": lambda p: self._rpc_team_get_group_members(p),
905
+ "team.get_ungrouped_members": lambda p: self._rpc_team_get_ungrouped_members(p),
906
+ "team.get_my_group_scope": lambda p: self._rpc_team_get_my_group_scope(p),
907
+ "team.set_group_admin": lambda p: self._rpc_team_set_group_admin(p),
908
+ "team.remove_group_admin": lambda p: self._rpc_team_remove_group_admin(p),
909
+ "team.get_group_admins": lambda p: self._rpc_team_get_group_admins(p),
910
+ "team.get_bill_range": lambda p: self._rpc_team_get_bill_range(p),
911
+ "team.get_insight_reports": lambda p: self._rpc_team_get_insight_reports(p),
912
+ "team.get_insight_report_detail": lambda p: self._rpc_team_get_insight_report_detail(p),
913
+ "team.update_insight_config": lambda p: self._rpc_team_update_insight_config(p),
914
+ "team.generate_member_insight_report": lambda p: self._rpc_team_generate_member_insight_report(p),
915
+ "team.get_alert_config": lambda p: self._rpc_team_get_alert_config(p),
916
+ "team.update_alert_config": lambda p: self._rpc_team_update_alert_config(p),
917
+ "team.test_alert": lambda p: self._rpc_team_test_alert(p),
918
+ "team.reset_alert_status": lambda p: self._rpc_team_reset_alert_status(p),
919
+ "team.get_vip_packages": lambda p: self._rpc_team_get_vip_packages(p),
920
+ # 邀请分润模块
921
+ "commission.get_activities": lambda p: self._rpc_commission_get_activities(p),
922
+ "commission.join": lambda p: self._rpc_commission_join(p),
923
+ "commission.quit": lambda p: self._rpc_commission_quit(p),
924
+ "commission.get_participation": lambda p: self._rpc_commission_get_participation(p),
925
+ "commission.get_account": lambda p: self._rpc_commission_get_account(p),
926
+ "commission.get_summary": lambda p: self._rpc_commission_get_summary(p),
927
+ "commission.get_records": lambda p: self._rpc_commission_get_records(p),
928
+ "commission.transfer": lambda p: self._rpc_commission_transfer(p),
929
+ "commission.withdraw": lambda p: self._rpc_commission_withdraw(p),
930
+ "commission.get_withdrawals": lambda p: self._rpc_commission_get_withdrawals(p),
931
+ }
932
+ handler = handlers.get(method)
933
+ if handler:
934
+ try:
935
+ result = await handler(params)
936
+ await ws.send(json.dumps({"jsonrpc": "2.0", "id": rpc_id, "result": result}))
937
+ except Exception as e:
938
+ await ws.send(json.dumps({
939
+ "jsonrpc": "2.0", "id": rpc_id,
940
+ "error": {"code": -32603, "message": str(e)},
941
+ }))
942
+ else:
943
+ await ws.send(json.dumps({
944
+ "jsonrpc": "2.0", "id": rpc_id,
945
+ "error": {"code": -32601, "message": f"Method not found: {method}"},
946
+ }))
947
+
948
+ async def _rpc_health(self) -> dict:
949
+ return {
950
+ "status": "healthy",
951
+ "details": {
952
+ "uptime_seconds": round(time.time() - self._start_time),
953
+ },
954
+ }
955
+
956
+ async def _rpc_status(self) -> dict:
957
+ return {
958
+ "module": "evol",
959
+ "status": "running",
960
+ "uptime_seconds": round(time.time() - self._start_time),
961
+ }
962
+
963
+ async def _rpc_list_tokens(self) -> dict:
964
+ """列出所有 Kite Token(从 AuthManager 读取)"""
965
+ latest_tokens = self.auth_manager._get_latest_tokens()
966
+ now = time.time()
967
+
968
+ tokens = []
969
+ for token, info in latest_tokens.items():
970
+ # 只返回有效且未过期的 token
971
+ if info.get("isValid", True) and now <= info.get("expiresAt", 0):
972
+ tokens.append({
973
+ "token": token,
974
+ "deviceId": info.get("deviceId", "unknown"),
975
+ "deviceName": info.get("deviceName", "Unknown Device"),
976
+ "phone": info.get("phone"), # 添加绑定的手机号
977
+ "createdAt": info.get("createdAt_human", ""),
978
+ "lastUsedAt": info.get("lastUsedAt_human", ""),
979
+ "expiresAt": info.get("expiresAt_human", ""),
980
+ })
981
+
982
+ return {"tokens": tokens}
983
+
984
+ async def _rpc_list_kite_tokens(self) -> dict:
985
+ """列出所有 Kite Token(前端配对令牌)"""
986
+ return await self._rpc_list_tokens()
987
+
988
+ async def _rpc_has_evol_token(self) -> dict:
989
+ """检查是否有可用的 evol_token(轻量级探测,供 proxy 主动查询)"""
990
+ evol_records = self.auth_manager.list_all_evol_tokens()
991
+ has_token = bool(evol_records)
992
+ phone = evol_records[0].get("phone", "") if evol_records else ""
993
+ masked_phone = f"{phone[:3]}****" if len(phone) >= 3 else phone
994
+ return {"has_token": has_token, "phone": masked_phone}
995
+
996
+ async def _rpc_list_evol_tokens(self) -> dict:
997
+ """列出所有 Evol Token(Evol 云端令牌)"""
998
+ evol_records = self.auth_manager.list_all_evol_tokens()
999
+
1000
+ if not evol_records:
1001
+ return {"tokens": []}
1002
+
1003
+ tokens = []
1004
+ for evol_record in evol_records:
1005
+ # 提取用户信息
1006
+ user_info = evol_record.get("userInfo", {})
1007
+ account_info = evol_record.get("accountInfo", {})
1008
+
1009
+ tokens.append({
1010
+ "token": evol_record.get("token", ""),
1011
+ "phone": evol_record.get("phone", ""),
1012
+ "nickName": user_info.get("nickName", ""),
1013
+ "userName": user_info.get("userName", ""),
1014
+ "credits": account_info.get("credits", 0),
1015
+ "creditsLimit": account_info.get("creditsLimit", 0),
1016
+ "vipType": account_info.get("vipType", 0),
1017
+ "vipTypeName": account_info.get("vipTypeName") or VIP_TYPE_NAMES.get(account_info.get("vipType", 0), "Unknown"),
1018
+ "vipExpireTime": account_info.get("vipExpireTime", ""),
1019
+ "vipRemainingDays": account_info.get("vipRemainingDays", 0),
1020
+ "obtainedAt": evol_record.get("obtainedAt_human", ""),
1021
+ "lastUsedAt": evol_record.get("lastUsedAt_human", ""),
1022
+ "expiresAt": evol_record.get("expiresAt_human", ""),
1023
+ })
1024
+
1025
+ return {"tokens": tokens}
1026
+
1027
+ async def _rpc_revoke_token(self, params: dict) -> dict:
1028
+ """吊销 Kite Token(使用 AuthManager)"""
1029
+ token = params.get("token")
1030
+ if not token:
1031
+ raise ValueError("Missing token parameter")
1032
+
1033
+ # 验证 token 是否存在
1034
+ latest_tokens = self.auth_manager._get_latest_tokens()
1035
+ if token not in latest_tokens:
1036
+ raise ValueError("Token not found")
1037
+
1038
+ # 吊销 token
1039
+ self.auth_manager.revoke_kite_token(token)
1040
+
1041
+ return {"success": True, "message": "Token revoked successfully"}
1042
+
1043
+ async def _rpc_revoke_evol_token(self, params: dict) -> dict:
1044
+ """吊销 Evol Token(使用 AuthManager)"""
1045
+ token = params.get("token")
1046
+ if not token:
1047
+ raise ValueError("Missing token parameter")
1048
+
1049
+ # 吊销 token
1050
+ self.auth_manager.revoke_evol_token_by_token(token)
1051
+
1052
+ return {"success": True, "message": "Evol Token revoked successfully"}
1053
+
1054
+ async def _rpc_get_user_info(self, params: dict) -> dict:
1055
+ """获取用户信息(RPC 版本)"""
1056
+ kite_token = params.get("kiteToken", "")
1057
+
1058
+ if not self.auth_manager.verify_kite_token(kite_token):
1059
+ return {
1060
+ "success": False,
1061
+ "msg": "Kite Token 无效或已过期",
1062
+ "code": "INVALID_TOKEN"
1063
+ }
1064
+
1065
+ # 根据 Kite Token 获取绑定的手机号
1066
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1067
+ if not phone:
1068
+ return {
1069
+ "success": False,
1070
+ "msg": "未登录 Evol,请先登录",
1071
+ "code": "NOT_LOGGED_IN"
1072
+ }
1073
+
1074
+ # 根据手机号获取 Evol Token
1075
+ evol_record = self.auth_manager.get_evol_token(phone)
1076
+ if not evol_record:
1077
+ return {
1078
+ "success": False,
1079
+ "msg": "Evol Token 已过期,请重新登录",
1080
+ "code": "EVOL_TOKEN_EXPIRED"
1081
+ }
1082
+
1083
+ # 更新 Evol Token 使用时间
1084
+ self.auth_manager.update_evol_token_usage(phone)
1085
+
1086
+ evol_token = evol_record["token"]
1087
+
1088
+ # 检查缓存
1089
+ now = time.time()
1090
+ if evol_token in self._user_info_cache:
1091
+ cached = self._user_info_cache[evol_token]
1092
+ if now - cached["timestamp"] < self._cache_ttl:
1093
+ logger.info(f"Using cached user info (age: {now - cached['timestamp']:.1f}s)")
1094
+ return cached["data"]
1095
+
1096
+ # 缓存未命中或已过期,从云端获取
1097
+ result = await self.evol_api.get_user_info(evol_token)
1098
+ if not result.get("success"):
1099
+ return result
1100
+
1101
+ # 手动触发账户信息采集
1102
+ await self.stats_manager.collect_manual()
1103
+
1104
+ # 统一返回格式
1105
+ user_data = result["data"]
1106
+
1107
+ # 补全 vipTypeName:如果远端 API 没有返回或返回了无效值,根据 vipType 映射
1108
+ account_info = user_data.get("accountInfo", {})
1109
+ if account_info:
1110
+ vip_type_name = account_info.get("vipTypeName")
1111
+ if not vip_type_name or vip_type_name == "Unknown":
1112
+ vip_type = account_info.get("vipType", 0)
1113
+ account_info["vipTypeName"] = VIP_TYPE_NAMES.get(vip_type, "Unknown")
1114
+
1115
+ response_data = {
1116
+ "success": True,
1117
+ "data": user_data
1118
+ }
1119
+
1120
+ # 更新缓存
1121
+ self._user_info_cache[evol_token] = {
1122
+ "data": response_data,
1123
+ "timestamp": now
1124
+ }
1125
+
1126
+ return response_data
1127
+
1128
+ async def _rpc_get_credits_stats(self, params: dict) -> dict:
1129
+ """获取积分统计(RPC 版本)"""
1130
+ kite_token = params.get("kiteToken", "")
1131
+ period = params.get("period", "day")
1132
+ date = params.get("date")
1133
+
1134
+ if not self.auth_manager.verify_kite_token(kite_token):
1135
+ return {
1136
+ "success": False,
1137
+ "msg": "Kite Token 无效或已过期",
1138
+ "code": "INVALID_TOKEN"
1139
+ }
1140
+
1141
+ result = self.stats_manager.get_stats(period, date)
1142
+ return result
1143
+
1144
+ async def _rpc_logout(self, params: dict) -> dict:
1145
+ """退出登录(RPC 版本)"""
1146
+ kite_token = params.get("kiteToken", "")
1147
+
1148
+ if not self.auth_manager.verify_kite_token(kite_token):
1149
+ return {"success": False, "msg": "Kite Token 无效"}
1150
+
1151
+ # 获取绑定的手机号
1152
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1153
+
1154
+ # 吊销 Evol Token(如果已绑定手机号)
1155
+ if phone:
1156
+ self.auth_manager.revoke_evol_token(phone)
1157
+
1158
+ # 吊销 Kite Token(解除绑定)
1159
+ self.auth_manager.revoke_kite_token(kite_token)
1160
+
1161
+ # 通知 Proxy 下线
1162
+ try:
1163
+ await self._publish_event(self._ws, "evol.user_logout", {
1164
+ "phone": phone, "reason": "user_logout",
1165
+ })
1166
+ except Exception as e:
1167
+ print(f"[evol] 发布 user_logout 事件失败: {e}")
1168
+
1169
+ return {"success": True, "msg": "已退出登录"}
1170
+
1171
+ async def _rpc_get_user_aid(self, params: dict) -> dict:
1172
+ """获取用户 AID 基本信息
1173
+
1174
+ Args:
1175
+ params: {"evol_token": "..."}
1176
+
1177
+ Returns:
1178
+ {"success": True, "data": {"aid": "...", ...}}
1179
+ """
1180
+ import aiohttp
1181
+
1182
+ evol_token = params.get("evol_token")
1183
+ if not evol_token:
1184
+ return {"success": False, "msg": "缺少 evol_token"}
1185
+
1186
+ try:
1187
+ url = f"{self.evol_api.base_url}/system/user/aid"
1188
+ headers = {
1189
+ "Authorization": f"Bearer {evol_token}",
1190
+ "Accept": "application/json",
1191
+ }
1192
+
1193
+ async with aiohttp.ClientSession() as session:
1194
+ async with session.get(url, headers=headers, ssl=False, timeout=aiohttp.ClientTimeout(total=10)) as resp:
1195
+ if resp.status != 200:
1196
+ text = await resp.text()
1197
+ return {"success": False, "msg": f"HTTP {resp.status} - /system/user/aid: {text[:200]}"}
1198
+ try:
1199
+ data = await resp.json()
1200
+ except Exception:
1201
+ return {"success": False, "msg": f"HTTP {resp.status} - /system/user/aid: 响应非 JSON"}
1202
+
1203
+ if data.get("code") == 200:
1204
+ return {"success": True, "data": data.get("data")}
1205
+ else:
1206
+ return {"success": False, "msg": data.get("msg", "未知错误")}
1207
+
1208
+ except Exception as e:
1209
+ return {"success": False, "msg": f"请求失败: {e}"}
1210
+
1211
+ async def _rpc_get_user_aid_detail(self, params: dict) -> dict:
1212
+ """获取用户 AID 详细信息(含私钥/证书)
1213
+
1214
+ Args:
1215
+ params: {"evol_token": "...", "_caller_id": "proxy"}
1216
+
1217
+ Returns:
1218
+ {"success": True, "data": {"aid": "...", "privateKey": "...", "pemStr": "...", ...}}
1219
+ """
1220
+ # 敏感接口:仅允许 proxy 模块调用
1221
+ caller_id = params.get("_caller_id", "")
1222
+ if caller_id != "proxy":
1223
+ return {"success": False, "msg": f"权限不足: {caller_id or '未知模块'} 无权调用此方法"}
1224
+
1225
+ import aiohttp
1226
+
1227
+ evol_token = params.get("evol_token")
1228
+ if not evol_token:
1229
+ return {"success": False, "msg": "缺少 evol_token"}
1230
+
1231
+ try:
1232
+ url = f"{self.evol_api.base_url}/system/user/aid/detail"
1233
+ headers = {
1234
+ "Authorization": f"Bearer {evol_token}",
1235
+ "Accept": "application/json",
1236
+ }
1237
+
1238
+ async with aiohttp.ClientSession() as session:
1239
+ async with session.get(url, headers=headers, ssl=False, timeout=aiohttp.ClientTimeout(total=10)) as resp:
1240
+ if resp.status != 200:
1241
+ text = await resp.text()
1242
+ return {"success": False, "msg": f"HTTP {resp.status} - /system/user/aid/detail: {text[:200]}"}
1243
+ try:
1244
+ data = await resp.json()
1245
+ except Exception:
1246
+ return {"success": False, "msg": f"HTTP {resp.status} - /system/user/aid/detail: 响应非 JSON"}
1247
+
1248
+ if data.get("code") == 200:
1249
+ return {"success": True, "data": data.get("data")}
1250
+ else:
1251
+ return {"success": False, "msg": data.get("msg", "未知错误")}
1252
+
1253
+ except Exception as e:
1254
+ return {"success": False, "msg": f"请求失败: {e}"}
1255
+
1256
+ async def _rpc_submit_user_aid(self, params: dict) -> dict:
1257
+ """提交用户 AID 信息到服务器
1258
+
1259
+ Args:
1260
+ params: {
1261
+ "evol_token": "...",
1262
+ "aid": "...",
1263
+ "privateKey": "...",
1264
+ "pemStr": "...",
1265
+ "_caller_id": "proxy"
1266
+ }
1267
+
1268
+ Returns:
1269
+ {"success": True}
1270
+ """
1271
+ # 敏感接口:仅允许 proxy 模块调用
1272
+ caller_id = params.get("_caller_id", "")
1273
+ if caller_id != "proxy":
1274
+ return {"success": False, "msg": f"权限不足: {caller_id or '未知模块'} 无权调用此方法"}
1275
+
1276
+ import aiohttp
1277
+
1278
+ evol_token = params.get("evol_token")
1279
+ aid_data = {
1280
+ "aid": params.get("aid"),
1281
+ "privateKey": params.get("privateKey"),
1282
+ "pemStr": params.get("pemStr"),
1283
+ }
1284
+
1285
+ if not evol_token or not aid_data["aid"]:
1286
+ return {"success": False, "msg": "缺少必要参数"}
1287
+
1288
+ try:
1289
+ url = f"{self.evol_api.base_url}/system/user/aid"
1290
+ headers = {
1291
+ "Authorization": f"Bearer {evol_token}",
1292
+ "Content-Type": "application/json",
1293
+ }
1294
+
1295
+ async with aiohttp.ClientSession() as session:
1296
+ async with session.post(url, json=aid_data, headers=headers, ssl=False, timeout=aiohttp.ClientTimeout(total=10)) as resp:
1297
+ if resp.status != 200:
1298
+ text = await resp.text()
1299
+ return {"success": False, "msg": f"HTTP {resp.status} - /system/user/aid (POST): {text[:200]}"}
1300
+ try:
1301
+ data = await resp.json()
1302
+ except Exception:
1303
+ return {"success": False, "msg": f"HTTP {resp.status} - /system/user/aid (POST): 响应非 JSON"}
1304
+
1305
+ if data.get("code") == 200:
1306
+ return {"success": True}
1307
+ else:
1308
+ return {"success": False, "msg": data.get("msg", "未知错误")}
1309
+
1310
+ except Exception as e:
1311
+ return {"success": False, "msg": f"请求失败: {e}"}
1312
+
1313
+ async def _rpc_get_api_keys(self, params: dict) -> dict:
1314
+ """获取用户的 API Key 列表
1315
+
1316
+ Args:
1317
+ params: {"kiteToken": "..."} 或 {"evol_token": "..."}
1318
+
1319
+ Returns:
1320
+ {"success": True, "data": [...]}
1321
+ """
1322
+ import aiohttp
1323
+
1324
+ # 优先通过 kiteToken 查 evol_token
1325
+ evol_token = params.get("evol_token")
1326
+ kite_token = params.get("kiteToken", "")
1327
+ if not evol_token and kite_token:
1328
+ if not self.auth_manager.verify_kite_token(kite_token):
1329
+ return {"success": False, "msg": "Token无效"}
1330
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1331
+ if not phone:
1332
+ return {"success": False, "msg": "未登录"}
1333
+ evol_record = self.auth_manager.get_evol_token(phone)
1334
+ if not evol_record:
1335
+ return {"success": False, "msg": "Token已过期"}
1336
+ evol_token = evol_record["token"]
1337
+
1338
+ if not evol_token:
1339
+ return {"success": False, "msg": "缺少 evol_token 或 kiteToken"}
1340
+
1341
+ try:
1342
+ url = f"{self.evol_api.base_url}/api/ai-key/list"
1343
+ headers = {
1344
+ "Authorization": f"Bearer {evol_token}",
1345
+ "Accept": "application/json",
1346
+ }
1347
+
1348
+ async with aiohttp.ClientSession() as session:
1349
+ async with session.get(url, headers=headers, ssl=False, timeout=aiohttp.ClientTimeout(total=10)) as resp:
1350
+ if resp.status != 200:
1351
+ text = await resp.text()
1352
+ return {"success": False, "msg": f"HTTP {resp.status} - /api/ai-key/list: {text[:200]}"}
1353
+ try:
1354
+ data = await resp.json()
1355
+ except Exception:
1356
+ return {"success": False, "msg": f"HTTP {resp.status} - /api/ai-key/list: 响应非 JSON"}
1357
+
1358
+ if data.get("code") == 200:
1359
+ return {"success": True, "data": data.get("data", [])}
1360
+ else:
1361
+ return {"success": False, "msg": data.get("msg", "未知错误")}
1362
+
1363
+ except Exception as e:
1364
+ return {"success": False, "msg": f"请求失败: {e}"}
1365
+
1366
+ async def _rpc_send_sms(self, params: dict) -> dict:
1367
+ """发送短信验证码"""
1368
+ phone = params.get("phone", "")
1369
+ if not phone:
1370
+ return {"success": False, "msg": "缺少手机号"}
1371
+ result = await self.evol_api.send_sms(phone)
1372
+ return result
1373
+
1374
+ async def _rpc_verify_sms(self, params: dict) -> dict:
1375
+ """验证短信并登录"""
1376
+ phone = params.get("phone", "")
1377
+ code = params.get("code", "")
1378
+ device_info = params.get("deviceInfo", {})
1379
+
1380
+ if not phone or not code:
1381
+ return {"success": False, "msg": "缺少手机号或验证码"}
1382
+
1383
+ result = await self.evol_api.verify_sms(phone, code)
1384
+ if not result.get("success"):
1385
+ return result
1386
+
1387
+ evol_data = result["data"]
1388
+ evol_token = evol_data.get("token", "")
1389
+ self.auth_manager.save_evol_token(phone, evol_token, evol_data)
1390
+
1391
+ kite_token = self.auth_manager.generate_kite_token(device_info)
1392
+ self.auth_manager.bind_kite_token_to_phone(kite_token, phone)
1393
+
1394
+ # 登录成功,发布 evol.user_ready 通知其他模块
1395
+ try:
1396
+ await self._publish_user_ready(phone)
1397
+ except Exception:
1398
+ pass
1399
+
1400
+ return {
1401
+ "success": True,
1402
+ "kiteToken": kite_token,
1403
+ "data": {
1404
+ "userInfo": evol_data.get("userInfo", {}),
1405
+ "apiKey": evol_data.get("apiKey", ""),
1406
+ "credits": evol_data.get("credits", 0)
1407
+ }
1408
+ }
1409
+
1410
+ async def _rpc_switch_team(self, params: dict) -> dict:
1411
+ """切换团队"""
1412
+ logger.info(f"[switch_team] Called with params: team_id={params.get('team_id')}")
1413
+ kite_token = params.get("kiteToken", "")
1414
+ team_id = params.get("team_id")
1415
+
1416
+ if not self.auth_manager.verify_kite_token(kite_token):
1417
+ return {
1418
+ "status": "error",
1419
+ "error": "Kite Token 无效或已过期",
1420
+ "error_code": 401
1421
+ }
1422
+
1423
+ # 根据 Kite Token 获取绑定的手机号
1424
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1425
+ if not phone:
1426
+ return {
1427
+ "status": "error",
1428
+ "error": "用户未登录,请先登录",
1429
+ "error_code": 401
1430
+ }
1431
+
1432
+ # 根据手机号获取 Evol Token
1433
+ evol_record = self.auth_manager.get_evol_token(phone)
1434
+ if not evol_record:
1435
+ return {
1436
+ "status": "error",
1437
+ "error": "Token已失效,请重新登录",
1438
+ "need_relogin": True,
1439
+ "error_code": 401
1440
+ }
1441
+
1442
+ evol_token = evol_record["token"]
1443
+
1444
+ # 调用 Evol API 切换团队
1445
+ result = await self.evol_api.switch_team(evol_token, team_id)
1446
+
1447
+ # 清除用户信息缓存,强制下次重新获取
1448
+ if evol_token in self._user_info_cache:
1449
+ del self._user_info_cache[evol_token]
1450
+
1451
+ return result
1452
+
1453
+ # ==================== 积分模块 ====================
1454
+
1455
+ async def _rpc_get_credits_change_records(self, params: dict) -> dict:
1456
+ """获取积分变动记录"""
1457
+ kite_token = params.get("kiteToken", "")
1458
+ page = params.get("page", 1)
1459
+ page_size = params.get("pageSize", 20)
1460
+ change_type = params.get("changeType")
1461
+
1462
+ if not self.auth_manager.verify_kite_token(kite_token):
1463
+ return {"success": False, "msg": "Token无效"}
1464
+
1465
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1466
+ if not phone:
1467
+ return {"success": False, "msg": "未登录"}
1468
+
1469
+ evol_record = self.auth_manager.get_evol_token(phone)
1470
+ if not evol_record:
1471
+ return {"success": False, "msg": "Token已过期"}
1472
+
1473
+ return await self.evol_api.get_credits_change_records(evol_record["token"], page, page_size, change_type)
1474
+
1475
+ async def _rpc_get_credits_package_list(self, params: dict) -> dict:
1476
+ """获取积分包列表"""
1477
+ kite_token = params.get("kiteToken", "")
1478
+
1479
+ if not self.auth_manager.verify_kite_token(kite_token):
1480
+ return {"success": False, "msg": "Token无效"}
1481
+
1482
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1483
+ if not phone:
1484
+ return {"success": False, "msg": "未登录"}
1485
+
1486
+ evol_record = self.auth_manager.get_evol_token(phone)
1487
+ if not evol_record:
1488
+ return {"success": False, "msg": "Token已过期"}
1489
+
1490
+ return await self.evol_api.get_credits_package_list(evol_record["token"])
1491
+
1492
+ async def _rpc_get_user_hourly_trend(self, params: dict) -> dict:
1493
+ """获取个人24小时消耗趋势"""
1494
+ kite_token = params.get("kiteToken", "")
1495
+ date = params.get("date", "")
1496
+
1497
+ if not self.auth_manager.verify_kite_token(kite_token):
1498
+ return {"success": False, "msg": "Token无效"}
1499
+
1500
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1501
+ if not phone:
1502
+ return {"success": False, "msg": "未登录"}
1503
+
1504
+ evol_record = self.auth_manager.get_evol_token(phone)
1505
+ if not evol_record:
1506
+ return {"success": False, "msg": "Token已过期"}
1507
+
1508
+ return await self.evol_api.get_user_hourly_trend(evol_record["token"], date)
1509
+
1510
+ async def _rpc_get_user_daily_trend(self, params: dict) -> dict:
1511
+ """获取个人日消耗趋势"""
1512
+ kite_token = params.get("kiteToken", "")
1513
+ start_date = params.get("startDate", "")
1514
+ end_date = params.get("endDate", "")
1515
+
1516
+ if not self.auth_manager.verify_kite_token(kite_token):
1517
+ return {"success": False, "msg": "Token无效"}
1518
+
1519
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1520
+ if not phone:
1521
+ return {"success": False, "msg": "未登录"}
1522
+
1523
+ evol_record = self.auth_manager.get_evol_token(phone)
1524
+ if not evol_record:
1525
+ return {"success": False, "msg": "Token已过期"}
1526
+
1527
+ return await self.evol_api.get_user_daily_trend(evol_record["token"], start_date, end_date)
1528
+
1529
+ async def _rpc_get_user_summary(self, params: dict) -> dict:
1530
+ """获取个人时间段汇总"""
1531
+ kite_token = params.get("kiteToken", "")
1532
+ start_date = params.get("startDate", "")
1533
+ end_date = params.get("endDate", "")
1534
+
1535
+ if not self.auth_manager.verify_kite_token(kite_token):
1536
+ return {"success": False, "msg": "Token无效"}
1537
+
1538
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1539
+ if not phone:
1540
+ return {"success": False, "msg": "未登录"}
1541
+
1542
+ evol_record = self.auth_manager.get_evol_token(phone)
1543
+ if not evol_record:
1544
+ return {"success": False, "msg": "Token已过期"}
1545
+
1546
+ return await self.evol_api.get_user_summary(evol_record["token"], start_date, end_date)
1547
+
1548
+ async def _rpc_get_credits_list(self, params: dict) -> dict:
1549
+ """获取积分变动列表(分页)"""
1550
+ kite_token = params.get("kiteToken", "")
1551
+ page_num = params.get("pageNum", 1)
1552
+ page_size = params.get("pageSize", 20)
1553
+ start_time = params.get("startTime")
1554
+ end_time = params.get("endTime")
1555
+
1556
+ if not self.auth_manager.verify_kite_token(kite_token):
1557
+ return {"success": False, "msg": "Token无效"}
1558
+
1559
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1560
+ if not phone:
1561
+ return {"success": False, "msg": "未登录"}
1562
+
1563
+ evol_record = self.auth_manager.get_evol_token(phone)
1564
+ if not evol_record:
1565
+ return {"success": False, "msg": "Token已过期"}
1566
+
1567
+ return await self.evol_api.get_credits_list(evol_record["token"], page_num, page_size, start_time, end_time)
1568
+
1569
+ # ==================== VIP 订阅模块 ====================
1570
+
1571
+ async def _rpc_get_vip_packages(self, params: dict) -> dict:
1572
+ """获取 VIP 套餐列表"""
1573
+ kite_token = params.get("kiteToken", "")
1574
+
1575
+ if not self.auth_manager.verify_kite_token(kite_token):
1576
+ return {"success": False, "msg": "Token无效"}
1577
+
1578
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1579
+ if not phone:
1580
+ return {"success": False, "msg": "未登录"}
1581
+
1582
+ evol_record = self.auth_manager.get_evol_token(phone)
1583
+ if not evol_record:
1584
+ return {"success": False, "msg": "Token已过期"}
1585
+
1586
+ return await self.evol_api.get_vip_packages(evol_record["token"])
1587
+
1588
+ async def _rpc_create_vip_order(self, params: dict) -> dict:
1589
+ """创建 VIP 订单"""
1590
+ kite_token = params.get("kiteToken", "")
1591
+ package_id = params.get("packageId")
1592
+ payment_method = params.get("paymentMethod", "alipay")
1593
+
1594
+ if not self.auth_manager.verify_kite_token(kite_token):
1595
+ return {"success": False, "msg": "Token无效"}
1596
+
1597
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1598
+ if not phone:
1599
+ return {"success": False, "msg": "未登录"}
1600
+
1601
+ evol_record = self.auth_manager.get_evol_token(phone)
1602
+ if not evol_record:
1603
+ return {"success": False, "msg": "Token已过期"}
1604
+
1605
+ return await self.evol_api.create_vip_order(evol_record["token"], package_id, payment_method)
1606
+
1607
+ async def _rpc_get_order_list(self, params: dict) -> dict:
1608
+ """获取订单列表"""
1609
+ kite_token = params.get("kiteToken", "")
1610
+ page = params.get("page", 1)
1611
+ page_size = params.get("pageSize", 20)
1612
+ order_type = params.get("orderType")
1613
+
1614
+ if not self.auth_manager.verify_kite_token(kite_token):
1615
+ return {"success": False, "msg": "Token无效"}
1616
+
1617
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1618
+ if not phone:
1619
+ return {"success": False, "msg": "未登录"}
1620
+
1621
+ evol_record = self.auth_manager.get_evol_token(phone)
1622
+ if not evol_record:
1623
+ return {"success": False, "msg": "Token已过期"}
1624
+
1625
+ return await self.evol_api.get_order_list(evol_record["token"], page, page_size, order_type)
1626
+
1627
+ async def _rpc_cancel_order(self, params: dict) -> dict:
1628
+ """取消订单"""
1629
+ kite_token = params.get("kiteToken", "")
1630
+ order_id = params.get("orderId")
1631
+
1632
+ if not self.auth_manager.verify_kite_token(kite_token):
1633
+ return {"success": False, "msg": "Token无效"}
1634
+
1635
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1636
+ if not phone:
1637
+ return {"success": False, "msg": "未登录"}
1638
+
1639
+ evol_record = self.auth_manager.get_evol_token(phone)
1640
+ if not evol_record:
1641
+ return {"success": False, "msg": "Token已过期"}
1642
+
1643
+ return await self.evol_api.cancel_order(evol_record["token"], order_id)
1644
+
1645
+ # ==================== 发票模块 ====================
1646
+
1647
+ async def _rpc_get_invoice_list(self, params: dict) -> dict:
1648
+ """获取发票列表"""
1649
+ kite_token = params.get("kiteToken", "")
1650
+ page = params.get("page", 1)
1651
+ page_size = params.get("pageSize", 20)
1652
+
1653
+ if not self.auth_manager.verify_kite_token(kite_token):
1654
+ return {"success": False, "msg": "Token无效"}
1655
+
1656
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1657
+ if not phone:
1658
+ return {"success": False, "msg": "未登录"}
1659
+
1660
+ evol_record = self.auth_manager.get_evol_token(phone)
1661
+ if not evol_record:
1662
+ return {"success": False, "msg": "Token已过期"}
1663
+
1664
+ return await self.evol_api.get_invoice_list(evol_record["token"], page, page_size)
1665
+
1666
+ async def _rpc_apply_invoice(self, params: dict) -> dict:
1667
+ """申请发票"""
1668
+ kite_token = params.get("kiteToken", "")
1669
+ order_id = params.get("orderId")
1670
+ invoice_type = params.get("invoiceType")
1671
+ title_type = params.get("titleType")
1672
+ title = params.get("title")
1673
+ tax_number = params.get("taxNumber", "")
1674
+ email = params.get("email", "")
1675
+
1676
+ if not self.auth_manager.verify_kite_token(kite_token):
1677
+ return {"success": False, "msg": "Token无效"}
1678
+
1679
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1680
+ if not phone:
1681
+ return {"success": False, "msg": "未登录"}
1682
+
1683
+ evol_record = self.auth_manager.get_evol_token(phone)
1684
+ if not evol_record:
1685
+ return {"success": False, "msg": "Token已过期"}
1686
+
1687
+ return await self.evol_api.apply_invoice(evol_record["token"], order_id, invoice_type, title_type, title, tax_number, email)
1688
+
1689
+ # ==================== API Key 模块 ====================
1690
+
1691
+ async def _rpc_get_apikey_list(self, params: dict) -> dict:
1692
+ """获取 API Key 列表"""
1693
+ kite_token = params.get("kiteToken", "")
1694
+
1695
+ if not self.auth_manager.verify_kite_token(kite_token):
1696
+ return {"success": False, "msg": "Token无效"}
1697
+
1698
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1699
+ if not phone:
1700
+ return {"success": False, "msg": "未登录"}
1701
+
1702
+ evol_record = self.auth_manager.get_evol_token(phone)
1703
+ if not evol_record:
1704
+ return {"success": False, "msg": "Token已过期"}
1705
+
1706
+ return await self.evol_api.get_apikey_list(evol_record["token"])
1707
+
1708
+ async def _rpc_create_apikey(self, params: dict) -> dict:
1709
+ """创建 API Key"""
1710
+ kite_token = params.get("kiteToken", "")
1711
+ name = params.get("name")
1712
+ remark = params.get("remark", "")
1713
+
1714
+ if not self.auth_manager.verify_kite_token(kite_token):
1715
+ return {"success": False, "msg": "Token无效"}
1716
+
1717
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1718
+ if not phone:
1719
+ return {"success": False, "msg": "未登录"}
1720
+
1721
+ evol_record = self.auth_manager.get_evol_token(phone)
1722
+ if not evol_record:
1723
+ return {"success": False, "msg": "Token已过期"}
1724
+
1725
+ return await self.evol_api.create_apikey(evol_record["token"], name, remark)
1726
+
1727
+ async def _rpc_update_apikey(self, params: dict) -> dict:
1728
+ """更新 API Key"""
1729
+ kite_token = params.get("kiteToken", "")
1730
+ key_id = params.get("keyId")
1731
+ name = params.get("name", "")
1732
+ remark = params.get("remark", "")
1733
+ status = params.get("status")
1734
+
1735
+ if not self.auth_manager.verify_kite_token(kite_token):
1736
+ return {"success": False, "msg": "Token无效"}
1737
+
1738
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1739
+ if not phone:
1740
+ return {"success": False, "msg": "未登录"}
1741
+
1742
+ evol_record = self.auth_manager.get_evol_token(phone)
1743
+ if not evol_record:
1744
+ return {"success": False, "msg": "Token已过期"}
1745
+
1746
+ return await self.evol_api.update_apikey(evol_record["token"], key_id, name, remark, status)
1747
+
1748
+ async def _rpc_delete_apikey(self, params: dict) -> dict:
1749
+ """删除 API Key"""
1750
+ kite_token = params.get("kiteToken", "")
1751
+ key_id = params.get("keyId")
1752
+
1753
+ if not self.auth_manager.verify_kite_token(kite_token):
1754
+ return {"success": False, "msg": "Token无效"}
1755
+
1756
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1757
+ if not phone:
1758
+ return {"success": False, "msg": "未登录"}
1759
+
1760
+ evol_record = self.auth_manager.get_evol_token(phone)
1761
+ if not evol_record:
1762
+ return {"success": False, "msg": "Token已过期"}
1763
+
1764
+ return await self.evol_api.delete_apikey(evol_record["token"], key_id)
1765
+
1766
+ # ==================== 团队模块 ====================
1767
+
1768
+ async def _rpc_get_team_members(self, params: dict) -> dict:
1769
+ """获取团队成员列表"""
1770
+ kite_token = params.get("kiteToken", "")
1771
+ team_id = params.get("teamId")
1772
+ page = params.get("page", 1)
1773
+ page_size = params.get("pageSize", 20)
1774
+
1775
+ if not self.auth_manager.verify_kite_token(kite_token):
1776
+ return {"success": False, "msg": "Token无效"}
1777
+
1778
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1779
+ if not phone:
1780
+ return {"success": False, "msg": "未登录"}
1781
+
1782
+ evol_record = self.auth_manager.get_evol_token(phone)
1783
+ if not evol_record:
1784
+ return {"success": False, "msg": "Token已过期"}
1785
+
1786
+ return await self.evol_api.get_team_members(evol_record["token"], team_id, page, page_size)
1787
+
1788
+ async def _ensure_evol_token(self, params: dict) -> str:
1789
+ """从 kiteToken 解析出 evol_token,失败则抛异常"""
1790
+ kite_token = params.get("kiteToken", "")
1791
+ if not self.auth_manager.verify_kite_token(kite_token):
1792
+ raise ValueError("Token无效")
1793
+ phone = self.auth_manager.get_phone_by_kite_token(kite_token)
1794
+ if not phone:
1795
+ raise ValueError("未登录")
1796
+ evol_record = self.auth_manager.get_evol_token(phone)
1797
+ if not evol_record:
1798
+ raise ValueError("Token已过期")
1799
+ return evol_record["token"]
1800
+
1801
+ # ---- 团队基础 RPC ----
1802
+
1803
+ async def _rpc_team_create(self, params: dict) -> dict:
1804
+ token = await self._ensure_evol_token(params)
1805
+ return await self.evol_api.create_team(token, params.get("name", ""))
1806
+
1807
+ async def _rpc_team_get_detail(self, params: dict) -> dict:
1808
+ token = await self._ensure_evol_token(params)
1809
+ return await self.evol_api.get_team_detail(token, params["teamId"])
1810
+
1811
+ async def _rpc_team_delete(self, params: dict) -> dict:
1812
+ token = await self._ensure_evol_token(params)
1813
+ return await self.evol_api.delete_team(token, params["teamId"])
1814
+
1815
+ async def _rpc_team_quit(self, params: dict) -> dict:
1816
+ token = await self._ensure_evol_token(params)
1817
+ return await self.evol_api.quit_team(token, params["teamId"])
1818
+
1819
+ async def _rpc_team_get_account(self, params: dict) -> dict:
1820
+ token = await self._ensure_evol_token(params)
1821
+ return await self.evol_api.get_team_account(token, params["teamId"])
1822
+
1823
+ async def _rpc_team_search_user(self, params: dict) -> dict:
1824
+ token = await self._ensure_evol_token(params)
1825
+ return await self.evol_api.search_user(token, params.get("phonenumber", ""), params["teamId"])
1826
+
1827
+ # ---- 成员管理 RPC ----
1828
+
1829
+ async def _rpc_team_add_member(self, params: dict) -> dict:
1830
+ token = await self._ensure_evol_token(params)
1831
+ return await self.evol_api.add_team_member(token, params["teamId"], params.get("data", {}))
1832
+
1833
+ async def _rpc_team_batch_add_members(self, params: dict) -> dict:
1834
+ token = await self._ensure_evol_token(params)
1835
+ return await self.evol_api.batch_add_members(token, params["teamId"], params.get("data", {}))
1836
+
1837
+ async def _rpc_team_update_member(self, params: dict) -> dict:
1838
+ token = await self._ensure_evol_token(params)
1839
+ return await self.evol_api.update_member(token, params["teamId"], params["memberId"], params.get("data", {}))
1840
+
1841
+ async def _rpc_team_remove_member(self, params: dict) -> dict:
1842
+ token = await self._ensure_evol_token(params)
1843
+ return await self.evol_api.remove_member(token, params["teamId"], params["memberId"])
1844
+
1845
+ async def _rpc_team_set_member_admin(self, params: dict) -> dict:
1846
+ token = await self._ensure_evol_token(params)
1847
+ return await self.evol_api.set_member_as_admin(token, params["teamId"], params["memberId"])
1848
+
1849
+ async def _rpc_team_remove_member_admin(self, params: dict) -> dict:
1850
+ token = await self._ensure_evol_token(params)
1851
+ return await self.evol_api.remove_member_admin(token, params["teamId"], params["memberId"])
1852
+
1853
+ async def _rpc_team_set_member_credits_limit(self, params: dict) -> dict:
1854
+ token = await self._ensure_evol_token(params)
1855
+ return await self.evol_api.set_member_credits_limit(token, params["teamId"], params["memberId"], params.get("data", {}))
1856
+
1857
+ async def _rpc_team_batch_set_credits_limit(self, params: dict) -> dict:
1858
+ token = await self._ensure_evol_token(params)
1859
+ return await self.evol_api.batch_set_credits_limit(token, params["teamId"], params.get("data", {}))
1860
+
1861
+ async def _rpc_team_get_member_quota_info(self, params: dict) -> dict:
1862
+ token = await self._ensure_evol_token(params)
1863
+ return await self.evol_api.get_member_quota_info(token, params["teamId"], params["memberId"])
1864
+
1865
+ async def _rpc_team_reset_member_quota(self, params: dict) -> dict:
1866
+ token = await self._ensure_evol_token(params)
1867
+ return await self.evol_api.reset_member_quota(token, params["teamId"], params["memberId"])
1868
+
1869
+ async def _rpc_team_convert_member_credits(self, params: dict) -> dict:
1870
+ token = await self._ensure_evol_token(params)
1871
+ return await self.evol_api.convert_member_credits(token, params["teamId"], params.get("data", {}))
1872
+
1873
+ # ---- 团队设置 RPC ----
1874
+
1875
+ async def _rpc_team_update_share_creator_credits(self, params: dict) -> dict:
1876
+ token = await self._ensure_evol_token(params)
1877
+ return await self.evol_api.update_share_creator_credits(token, params["teamId"], params.get("data", {}))
1878
+
1879
+ async def _rpc_team_update_analyze_member_data(self, params: dict) -> dict:
1880
+ token = await self._ensure_evol_token(params)
1881
+ return await self.evol_api.update_analyze_member_data(token, params["teamId"], params.get("data", {}))
1882
+
1883
+ # ---- 积分统计 RPC ----
1884
+
1885
+ async def _rpc_team_get_credits_stats(self, params: dict) -> dict:
1886
+ token = await self._ensure_evol_token(params)
1887
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1888
+ return await self.evol_api.get_team_credits_stats(token, params["teamId"], **kw)
1889
+
1890
+ async def _rpc_team_get_members_ranking(self, params: dict) -> dict:
1891
+ token = await self._ensure_evol_token(params)
1892
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1893
+ return await self.evol_api.get_team_members_ranking(token, params["teamId"], **kw)
1894
+
1895
+ async def _rpc_team_get_trend(self, params: dict) -> dict:
1896
+ token = await self._ensure_evol_token(params)
1897
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1898
+ return await self.evol_api.get_team_trend(token, params["teamId"], **kw)
1899
+
1900
+ async def _rpc_team_get_today_stats(self, params: dict) -> dict:
1901
+ token = await self._ensure_evol_token(params)
1902
+ return await self.evol_api.get_today_team_stats(token, params["teamId"])
1903
+
1904
+ async def _rpc_team_get_today_members_stats(self, params: dict) -> dict:
1905
+ token = await self._ensure_evol_token(params)
1906
+ return await self.evol_api.get_today_members_stats(token, params["teamId"])
1907
+
1908
+ async def _rpc_team_get_hourly_stats(self, params: dict) -> dict:
1909
+ token = await self._ensure_evol_token(params)
1910
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1911
+ return await self.evol_api.get_team_hourly_stats(token, params["teamId"], **kw)
1912
+
1913
+ async def _rpc_team_get_weekly_stats(self, params: dict) -> dict:
1914
+ token = await self._ensure_evol_token(params)
1915
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1916
+ return await self.evol_api.get_team_weekly_stats(token, params["teamId"], **kw)
1917
+
1918
+ async def _rpc_team_get_monthly_stats(self, params: dict) -> dict:
1919
+ token = await self._ensure_evol_token(params)
1920
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1921
+ return await self.evol_api.get_team_monthly_stats(token, params["teamId"], **kw)
1922
+
1923
+ async def _rpc_team_get_credits_logs(self, params: dict) -> dict:
1924
+ token = await self._ensure_evol_token(params)
1925
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1926
+ return await self.evol_api.get_team_credits_logs(token, params["teamId"], **kw)
1927
+
1928
+ async def _rpc_team_get_user_today_models(self, params: dict) -> dict:
1929
+ token = await self._ensure_evol_token(params)
1930
+ return await self.evol_api.get_user_today_models(token, params["teamId"], params["userId"])
1931
+
1932
+ async def _rpc_team_statistics_history_30days(self, params: dict) -> dict:
1933
+ token = await self._ensure_evol_token(params)
1934
+ return await self.evol_api.statistics_history_30days(token, params["teamId"], params.get("data"))
1935
+
1936
+ # ---- 分组统计 RPC ----
1937
+
1938
+ async def _rpc_team_get_today_group_stats(self, params: dict) -> dict:
1939
+ token = await self._ensure_evol_token(params)
1940
+ return await self.evol_api.get_today_group_stats(token, params["teamId"])
1941
+
1942
+ async def _rpc_team_get_history_group_stats(self, params: dict) -> dict:
1943
+ token = await self._ensure_evol_token(params)
1944
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
1945
+ return await self.evol_api.get_history_group_stats(token, params["teamId"], **kw)
1946
+
1947
+ # ---- 模型管理 RPC ----
1948
+
1949
+ async def _rpc_team_update_models(self, params: dict) -> dict:
1950
+ token = await self._ensure_evol_token(params)
1951
+ return await self.evol_api.update_team_models(token, params["teamId"], params.get("data", {}))
1952
+
1953
+ async def _rpc_team_get_member_model_settings(self, params: dict) -> dict:
1954
+ token = await self._ensure_evol_token(params)
1955
+ return await self.evol_api.get_member_model_settings(token, params["teamId"], params["memberId"])
1956
+
1957
+ async def _rpc_team_set_member_model_settings(self, params: dict) -> dict:
1958
+ token = await self._ensure_evol_token(params)
1959
+ return await self.evol_api.set_member_model_settings(token, params["teamId"], params["memberId"], params.get("data", {}))
1960
+
1961
+ # ---- 分组管理 RPC ----
1962
+
1963
+ async def _rpc_team_create_group(self, params: dict) -> dict:
1964
+ token = await self._ensure_evol_token(params)
1965
+ return await self.evol_api.create_group(token, params["teamId"], params.get("data", {}))
1966
+
1967
+ async def _rpc_team_get_group_tree(self, params: dict) -> dict:
1968
+ token = await self._ensure_evol_token(params)
1969
+ return await self.evol_api.get_group_tree(token, params["teamId"])
1970
+
1971
+ async def _rpc_team_get_group_detail(self, params: dict) -> dict:
1972
+ token = await self._ensure_evol_token(params)
1973
+ return await self.evol_api.get_group_detail(token, params["teamId"], params["groupId"])
1974
+
1975
+ async def _rpc_team_update_group(self, params: dict) -> dict:
1976
+ token = await self._ensure_evol_token(params)
1977
+ return await self.evol_api.update_group(token, params["teamId"], params["groupId"], params.get("data", {}))
1978
+
1979
+ async def _rpc_team_delete_group(self, params: dict) -> dict:
1980
+ token = await self._ensure_evol_token(params)
1981
+ return await self.evol_api.delete_group(token, params["teamId"], params["groupId"])
1982
+
1983
+ async def _rpc_team_move_group(self, params: dict) -> dict:
1984
+ token = await self._ensure_evol_token(params)
1985
+ return await self.evol_api.move_group(token, params["teamId"], params["groupId"], params.get("data", {}))
1986
+
1987
+ async def _rpc_team_add_members_to_group(self, params: dict) -> dict:
1988
+ token = await self._ensure_evol_token(params)
1989
+ return await self.evol_api.add_members_to_group(token, params["teamId"], params["groupId"], params.get("data", {}))
1990
+
1991
+ async def _rpc_team_remove_members_from_group(self, params: dict) -> dict:
1992
+ token = await self._ensure_evol_token(params)
1993
+ return await self.evol_api.remove_members_from_group(token, params["teamId"], params["groupId"], params.get("data", {}))
1994
+
1995
+ async def _rpc_team_get_group_members(self, params: dict) -> dict:
1996
+ token = await self._ensure_evol_token(params)
1997
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId", "groupId") and v is not None}
1998
+ return await self.evol_api.get_group_members(token, params["teamId"], params["groupId"], **kw)
1999
+
2000
+ async def _rpc_team_get_ungrouped_members(self, params: dict) -> dict:
2001
+ token = await self._ensure_evol_token(params)
2002
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
2003
+ return await self.evol_api.get_ungrouped_members(token, params["teamId"], **kw)
2004
+
2005
+ async def _rpc_team_get_my_group_scope(self, params: dict) -> dict:
2006
+ token = await self._ensure_evol_token(params)
2007
+ return await self.evol_api.get_my_group_scope(token, params["teamId"])
2008
+
2009
+ # ---- 分组管理员 RPC ----
2010
+
2011
+ async def _rpc_team_set_group_admin(self, params: dict) -> dict:
2012
+ token = await self._ensure_evol_token(params)
2013
+ return await self.evol_api.set_group_admin(token, params["teamId"], params["groupId"], params.get("data", {}))
2014
+
2015
+ async def _rpc_team_remove_group_admin(self, params: dict) -> dict:
2016
+ token = await self._ensure_evol_token(params)
2017
+ return await self.evol_api.remove_group_admin(token, params["teamId"], params["groupId"], params.get("data", {}))
2018
+
2019
+ async def _rpc_team_get_group_admins(self, params: dict) -> dict:
2020
+ token = await self._ensure_evol_token(params)
2021
+ return await self.evol_api.get_group_admins(token, params["teamId"], params["groupId"])
2022
+
2023
+ # ---- 账单 RPC ----
2024
+
2025
+ async def _rpc_team_get_bill_range(self, params: dict) -> dict:
2026
+ token = await self._ensure_evol_token(params)
2027
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
2028
+ return await self.evol_api.get_team_bill_range(token, params["teamId"], **kw)
2029
+
2030
+ # ---- 洞察报告 RPC ----
2031
+
2032
+ async def _rpc_team_get_insight_reports(self, params: dict) -> dict:
2033
+ token = await self._ensure_evol_token(params)
2034
+ kw = {k: v for k, v in params.items() if k not in ("kiteToken", "teamId") and v is not None}
2035
+ return await self.evol_api.get_insight_reports(token, params["teamId"], **kw)
2036
+
2037
+ async def _rpc_team_get_insight_report_detail(self, params: dict) -> dict:
2038
+ token = await self._ensure_evol_token(params)
2039
+ return await self.evol_api.get_insight_report_detail(token, params["teamId"], params["reportId"])
2040
+
2041
+ async def _rpc_team_update_insight_config(self, params: dict) -> dict:
2042
+ token = await self._ensure_evol_token(params)
2043
+ return await self.evol_api.update_insight_config(token, params["teamId"], params.get("data", {}))
2044
+
2045
+ async def _rpc_team_generate_member_insight_report(self, params: dict) -> dict:
2046
+ token = await self._ensure_evol_token(params)
2047
+ return await self.evol_api.generate_member_insight_report(token, params["teamId"], params["userId"], params.get("date"))
2048
+
2049
+ # ---- 预警 RPC ----
2050
+
2051
+ async def _rpc_team_get_alert_config(self, params: dict) -> dict:
2052
+ token = await self._ensure_evol_token(params)
2053
+ return await self.evol_api.get_alert_config(token, params["teamId"])
2054
+
2055
+ async def _rpc_team_update_alert_config(self, params: dict) -> dict:
2056
+ token = await self._ensure_evol_token(params)
2057
+ return await self.evol_api.update_alert_config(token, params["teamId"], params.get("data", {}))
2058
+
2059
+ async def _rpc_team_test_alert(self, params: dict) -> dict:
2060
+ token = await self._ensure_evol_token(params)
2061
+ return await self.evol_api.test_alert(token, params["teamId"])
2062
+
2063
+ async def _rpc_team_reset_alert_status(self, params: dict) -> dict:
2064
+ token = await self._ensure_evol_token(params)
2065
+ return await self.evol_api.reset_alert_status(token, params["teamId"])
2066
+
2067
+ # ---- VIP 套餐(团队版) ----
2068
+
2069
+ async def _rpc_team_get_vip_packages(self, params: dict) -> dict:
2070
+ token = await self._ensure_evol_token(params)
2071
+ url = f"{self.evol_api.base_url}/api/vip/packages?targetType=1"
2072
+ headers = {"Authorization": f"Bearer {token}"}
2073
+ try:
2074
+ async with httpx.AsyncClient(timeout=self.evol_api.timeout) as client:
2075
+ response = await client.get(url, headers=headers)
2076
+ result = response.json()
2077
+ return {"success": result.get("code") == 200, "data": result.get("data"), "msg": result.get("msg")}
2078
+ except Exception as e:
2079
+ return {"success": False, "msg": f"网络错误: {str(e)}"}
2080
+
2081
+ # ==================== 邀请分润 RPC ====================
2082
+
2083
+ async def _rpc_commission_get_activities(self, params: dict) -> dict:
2084
+ token = await self._ensure_evol_token(params)
2085
+ return await self.evol_api.get_commission_activities(token)
2086
+
2087
+ async def _rpc_commission_join(self, params: dict) -> dict:
2088
+ token = await self._ensure_evol_token(params)
2089
+ return await self.evol_api.join_commission_activity(
2090
+ token, params.get("activityId"), params.get("inviteCode"))
2091
+
2092
+ async def _rpc_commission_quit(self, params: dict) -> dict:
2093
+ token = await self._ensure_evol_token(params)
2094
+ return await self.evol_api.quit_commission_activity(token)
2095
+
2096
+ async def _rpc_commission_get_participation(self, params: dict) -> dict:
2097
+ token = await self._ensure_evol_token(params)
2098
+ return await self.evol_api.get_commission_participation(token)
2099
+
2100
+ async def _rpc_commission_get_account(self, params: dict) -> dict:
2101
+ token = await self._ensure_evol_token(params)
2102
+ return await self.evol_api.get_commission_account(token)
2103
+
2104
+ async def _rpc_commission_get_summary(self, params: dict) -> dict:
2105
+ token = await self._ensure_evol_token(params)
2106
+ return await self.evol_api.get_commission_summary(token)
2107
+
2108
+ async def _rpc_commission_get_records(self, params: dict) -> dict:
2109
+ token = await self._ensure_evol_token(params)
2110
+ return await self.evol_api.get_commission_records(
2111
+ token, params.get("startDate"), params.get("endDate"))
2112
+
2113
+ async def _rpc_commission_transfer(self, params: dict) -> dict:
2114
+ token = await self._ensure_evol_token(params)
2115
+ return await self.evol_api.transfer_commission(token, params.get("creditsAmount", 0))
2116
+
2117
+ async def _rpc_commission_withdraw(self, params: dict) -> dict:
2118
+ token = await self._ensure_evol_token(params)
2119
+ return await self.evol_api.withdraw_commission(token, params.get("creditsAmount", 0))
2120
+
2121
+ async def _rpc_commission_get_withdrawals(self, params: dict) -> dict:
2122
+ token = await self._ensure_evol_token(params)
2123
+ return await self.evol_api.get_commission_withdrawals(
2124
+ token, params.get("startDate"), params.get("endDate"))
2125
+
2126
+ async def _rpc_subscribe_events(self, params: dict) -> dict:
2127
+ """动态订阅事件(通过 Kernel)"""
2128
+ events = params.get("events", [])
2129
+ if not events:
2130
+ raise ValueError("Missing events parameter")
2131
+
2132
+ if not self._ws:
2133
+ raise RuntimeError("Not connected to Kernel")
2134
+
2135
+ # 调用 Kernel 的 event.subscribe(fire-and-forget)
2136
+ await self._rpc_call(self._ws, "event.subscribe", {"events": events},
2137
+ wait_response=False)
2138
+
2139
+ print(f"[evol] Subscribed to events: {events}")
2140
+ return {"success": True, "events": events}
2141
+
2142
+ async def _handle_connection_offer(self, data):
2143
+ """处理 Kernel 下发的 slot token,建立附加连接。"""
2144
+ slots = data.get("slots", {})
2145
+ for slot_str, info in slots.items():
2146
+ slot = int(slot_str)
2147
+ token = info.get("token", "")
2148
+ if not token or slot in self._extra_ws:
2149
+ continue
2150
+ asyncio.create_task(self._connect_slot(slot, token))
2151
+
2152
+ async def _connect_slot(self, slot, token):
2153
+ """建立单个 slot 附加连接。"""
2154
+ ws_url = f"ws://127.0.0.1:{self.kernel_port}/ws"
2155
+ try:
2156
+ ws = await websockets.connect(ws_url, open_timeout=5, ping_interval=None, close_timeout=5)
2157
+ auth_req = {"jsonrpc": "2.0", "id": f"auth-slot-{slot}", "method": "auth", "params": {"token": token}}
2158
+ await ws.send(json.dumps(auth_req))
2159
+ resp = json.loads(await asyncio.wait_for(ws.recv(), timeout=5))
2160
+ if "error" in resp:
2161
+ await ws.close()
2162
+ return
2163
+ self._extra_ws[slot] = ws
2164
+ self._extra_ws_tasks[slot] = asyncio.create_task(self._slot_recv_loop(slot, ws))
2165
+ print(f"[evol] Slot {slot} connected")
2166
+ except Exception as e:
2167
+ print(f"[evol] Slot {slot} connect failed: {e}")
2168
+
2169
+ async def _slot_recv_loop(self, slot, ws):
2170
+ """附加 slot 接收循环:与主连接平等处理所有消息。"""
2171
+ try:
2172
+ async for raw in ws:
2173
+ try:
2174
+ msg = json.loads(raw)
2175
+ except (json.JSONDecodeError, TypeError):
2176
+ continue
2177
+
2178
+ try:
2179
+ has_method = "method" in msg
2180
+ has_id = "id" in msg
2181
+ has_result_or_error = "result" in msg or "error" in msg
2182
+
2183
+ if has_method and not has_id:
2184
+ asyncio.create_task(self._handle_event_notification(msg))
2185
+ elif has_method and has_id:
2186
+ asyncio.create_task(self._handle_rpc_request(ws, msg))
2187
+ elif has_id and has_result_or_error:
2188
+ self._handle_rpc_response(msg)
2189
+ except Exception as e:
2190
+ print(f"[evol] Slot {slot} 消息处理异常(已忽略): {e}")
2191
+ except Exception as e:
2192
+ print(f"[evol] Slot {slot} recv loop exited: {e}")
2193
+ finally:
2194
+ self._extra_ws.pop(slot, None)
2195
+ self._extra_ws_tasks.pop(slot, None)
2196
+
2197
+ async def _handle_connection_release(self, data):
2198
+ """Kernel 请求释放 slot,优雅关闭。"""
2199
+ for slot in data.get("slots", []):
2200
+ ws = self._extra_ws.pop(slot, None)
2201
+ task = self._extra_ws_tasks.pop(slot, None)
2202
+ if ws:
2203
+ try:
2204
+ await ws.close(code=1000, reason="release")
2205
+ except Exception:
2206
+ pass
2207
+ if task:
2208
+ task.cancel()
2209
+
2210
+ async def _handle_shutdown(self):
2211
+ print("[evol] Received module.shutdown")
2212
+ self._shutting_down = True
2213
+
2214
+ # 使用短超时发送 shutdown 事件,避免阻塞
2215
+ try:
2216
+ await self._publish_event(self._ws, "module.shutdown.ack", {
2217
+ "module_id": self.module_name,
2218
+ })
2219
+ except Exception as e:
2220
+ print(f"[evol] Failed to send shutdown.ack: {e}")
2221
+
2222
+ try:
2223
+ await self._publish_event(self._ws, "module.exiting", {
2224
+ "module_id": self.module_name,
2225
+ "type": "passive",
2226
+ "reason": "shutdown_requested",
2227
+ "restart": "auto",
2228
+ "action": "none",
2229
+ "timeout": 3.0,
2230
+ "restart_delay": 0.0,
2231
+ })
2232
+ except Exception as e:
2233
+ print(f"[evol] Failed to send module.exiting: {e}")
2234
+
2235
+ # Send shutdown.ready(必须在关闭连接之前发送)
2236
+ try:
2237
+ await self._publish_event(self._ws, "module.shutdown.ready", {
2238
+ "module_id": self.module_name,
2239
+ })
2240
+ except Exception as e:
2241
+ print(f"[evol] Failed to send shutdown.ready: {e}")
2242
+
2243
+ # 等待 Kernel 处理 shutdown.ready
2244
+ await asyncio.sleep(0.01)
2245
+
2246
+ if self._test_task:
2247
+ self._test_task.cancel()
2248
+
2249
+ # Close WebSocket connections
2250
+ if hasattr(self.app.state, 'relay_service'):
2251
+ try:
2252
+ await asyncio.wait_for(
2253
+ self.app.state.relay_service.close_all_sessions(),
2254
+ timeout=1.0
2255
+ )
2256
+ except asyncio.TimeoutError:
2257
+ print("[evol] Relay service close timeout")
2258
+ except Exception as e:
2259
+ print(f"[evol] Failed to close relay sessions: {e}")
2260
+
2261
+ # 关闭所有附加连接
2262
+ for _s, _w in list(self._extra_ws.items()):
2263
+ try:
2264
+ await _w.close(code=1000, reason="shutdown")
2265
+ except Exception:
2266
+ pass
2267
+ for _t in self._extra_ws_tasks.values():
2268
+ _t.cancel()
2269
+ self._extra_ws.clear()
2270
+ self._extra_ws_tasks.clear()
2271
+
2272
+ # 关闭 Kernel WebSocket 连接
2273
+ if self._ws:
2274
+ try:
2275
+ await self._ws.close(code=1000, reason="Graceful shutdown")
2276
+ print("[evol] Kernel WebSocket closed")
2277
+ except Exception as e:
2278
+ print(f"[evol] Failed to close Kernel WebSocket: {e}")
2279
+
2280
+ # 触发 uvicorn 优雅关闭(让 uvicorn 自然退出,不要 sys.exit)
2281
+ if self._uvicorn_server:
2282
+ print("[evol] Triggering uvicorn graceful shutdown")
2283
+ self._uvicorn_server.should_exit = True
2284
+ else:
2285
+ print("[evol] Warning: uvicorn_server not set")
2286
+
2287
+ async def _publish_event(self, ws, event: str, data: dict = None):
2288
+ """Publish event via event.publish RPC (fire-and-forget)."""
2289
+ await self._rpc_call(ws, "event.publish", {
2290
+ "event_id": str(uuid.uuid4()),
2291
+ "event": event,
2292
+ "data": data or {},
2293
+ }, wait_response=False)
2294
+
2295
+ async def _check_and_publish_user_ready(self):
2296
+ """检查本地是否有已登录的 evol_token,有则发布 evol.user_ready"""
2297
+ try:
2298
+ evol_records = self.auth_manager.list_all_evol_tokens()
2299
+ if evol_records:
2300
+ phone = evol_records[0].get("phone", "")
2301
+ print(f"[evol] 检测到本地已登录 token (phone: {phone[:3]}****),发布 user_ready")
2302
+ await self._publish_user_ready(phone)
2303
+ except Exception as e:
2304
+ print(f"[evol] 检查本地 token 失败: {e}")
2305
+
2306
+ async def _publish_user_ready(self, phone: str = ""):
2307
+ """发布 evol.user_ready 事件"""
2308
+ masked_phone = f"{phone[:3]}****" if len(phone) >= 3 else phone
2309
+ await self._publish_event(self._ws, "evol.user_ready", {
2310
+ "module_id": self.module_name,
2311
+ "has_token": True,
2312
+ "phone": masked_phone,
2313
+ })
2314
+
2315
+ async def _test_event_loop(self):
2316
+ try:
2317
+ while True:
2318
+ await asyncio.sleep(10)
2319
+ await self._publish_event(self._ws, "evol.test", {
2320
+ "message": "test event from evol",
2321
+ "timestamp": datetime.now(timezone.utc).isoformat(),
2322
+ })
2323
+ except Exception:
2324
+ pass
2325
+
2326
+ async def _handle_page_register(self, source: str, data: dict):
2327
+ """处理页面注册事件"""
2328
+ page_id = data.get("page_id")
2329
+ path = data.get("path")
2330
+ page_type = data.get("type")
2331
+
2332
+ if not page_id or not path or not page_type:
2333
+ print(f"[evol] Invalid page registration from {source}: missing required fields")
2334
+ return
2335
+
2336
+ # 检查路径冲突
2337
+ if path in self._reserved_paths:
2338
+ print(f"[evol] Page registration rejected: path {path} is reserved")
2339
+ return
2340
+
2341
+ # 检查是否与已注册页面冲突
2342
+ for pid, page in self._page_registry.items():
2343
+ if pid != page_id and page.get("path") == path:
2344
+ print(f"[evol] Page registration rejected: path {path} already used by {pid}")
2345
+ return
2346
+
2347
+ # 存入注册表
2348
+ page_entry = {**data, "module_id": source}
2349
+ self._page_registry[page_id] = page_entry
2350
+ print(f"[evol] Page registered: {page_id} from {source}")
2351
+
2352
+ # 动态挂载静态文件(hosted 类型)
2353
+ if page_type == "hosted":
2354
+ await self._mount_module_ui(source)
2355
+
2356
+ # 推送给前端(通过 relay)
2357
+ if hasattr(self, 'relay') and self.relay:
2358
+ await self.relay.broadcast_to_clients({
2359
+ "jsonrpc": "2.0",
2360
+ "method": "event",
2361
+ "params": {
2362
+ "event": "ui.page.register",
2363
+ "source": source,
2364
+ "data": page_entry
2365
+ }
2366
+ })
2367
+
2368
+ async def _handle_page_deregister(self, data: dict):
2369
+ """处理页面反注册事件"""
2370
+ page_id = data.get("page_id")
2371
+ if not page_id:
2372
+ return
2373
+
2374
+ page = self._page_registry.pop(page_id, None)
2375
+ if page:
2376
+ print(f"[evol] Page deregistered: {page_id}")
2377
+ # 推送给前端
2378
+ if hasattr(self, 'relay') and self.relay:
2379
+ await self.relay.broadcast_to_clients({
2380
+ "jsonrpc": "2.0",
2381
+ "method": "event",
2382
+ "params": {
2383
+ "event": "ui.page.deregister",
2384
+ "data": {"page_id": page_id}
2385
+ }
2386
+ })
2387
+
2388
+ async def _cleanup_module_pages(self, module_id: str):
2389
+ """清理模块的所有页面"""
2390
+ pages_to_remove = [
2391
+ page_id for page_id, page in self._page_registry.items()
2392
+ if page.get("module_id") == module_id
2393
+ ]
2394
+
2395
+ for page_id in pages_to_remove:
2396
+ await self._handle_page_deregister({"page_id": page_id})
2397
+
2398
+ async def _mount_module_ui(self, module_name: str):
2399
+ """动态挂载模块的 UI 静态文件"""
2400
+ # 查找模块的 ui 目录
2401
+ possible_paths = [
2402
+ Path(f"extensions/services/{module_name}/ui"),
2403
+ Path(os.environ.get("KITE_INSTANCE_DIR", "")) / module_name / "ui"
2404
+ ]
2405
+
2406
+ ui_dir = None
2407
+ for p in possible_paths:
2408
+ if p.exists() and p.is_dir():
2409
+ ui_dir = p
2410
+ break
2411
+
2412
+ if not ui_dir:
2413
+ print(f"[evol] Warning: UI directory not found for module {module_name}")
2414
+ return
2415
+
2416
+ # 动态挂载
2417
+ mount_path = f"/module-ui/{module_name}"
2418
+ try:
2419
+ self.app.mount(
2420
+ mount_path,
2421
+ StaticFiles(directory=str(ui_dir), html=True),
2422
+ name=f"module-ui-{module_name}"
2423
+ )
2424
+ print(f"[evol] Mounted UI for {module_name} at {mount_path}")
2425
+ except Exception as e:
2426
+ print(f"[evol] Failed to mount UI for {module_name}: {e}")