@agentunion/kite 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (573) 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/builders/__init__.py +4 -0
  492. package/kite_cli/builders/base.py +67 -0
  493. package/kite_cli/builders/custom.py +31 -0
  494. package/kite_cli/builders/detector.py +56 -0
  495. package/kite_cli/builders/go.py +34 -0
  496. package/kite_cli/builders/gradle.py +41 -0
  497. package/kite_cli/builders/maven.py +36 -0
  498. package/kite_cli/builders/npm.py +44 -0
  499. package/kite_cli/builders/python.py +37 -0
  500. package/kite_cli/commands/BUILD_GUIDE.md +109 -0
  501. package/kite_cli/commands/build.py +142 -0
  502. package/kite_cli/commands/check.py +60 -0
  503. package/kite_cli/commands/config.py +156 -0
  504. package/kite_cli/commands/deps.py +58 -0
  505. package/kite_cli/commands/deps_install.py +7 -7
  506. package/kite_cli/commands/disable.py +162 -0
  507. package/kite_cli/commands/enable.py +162 -0
  508. package/kite_cli/commands/export.py +96 -0
  509. package/kite_cli/commands/import_cmd.py +110 -0
  510. package/kite_cli/commands/install.py +50 -23
  511. package/kite_cli/commands/install_skill.py +107 -0
  512. package/kite_cli/commands/list.py +128 -31
  513. package/kite_cli/commands/outdated.py +202 -0
  514. package/kite_cli/commands/search.py +33 -17
  515. package/kite_cli/commands/update.py +115 -2
  516. package/kite_cli/commands/venv_setup.py +6 -6
  517. package/kite_cli/commands/why.py +48 -0
  518. package/kite_cli/core/config_manager.py +145 -0
  519. package/kite_cli/core/downloader.py +32 -2
  520. package/kite_cli/main.py +151 -5
  521. package/kite_cli/utils/colors.py +153 -0
  522. package/kite_cli/utils/dependency_graph.py +209 -0
  523. package/kite_cli/utils/process.py +55 -0
  524. package/kite_cli/utils/progress.py +207 -0
  525. package/kite_cli/utils/table.py +101 -0
  526. package/launcher/count_lines.py +192 -43
  527. package/launcher/entry.py +4543 -2802
  528. package/launcher/logging_setup.py +54 -1
  529. package/launcher/module.md +32 -6
  530. package/launcher/module_scanner.py +93 -20
  531. package/launcher/process_manager.py +355 -76
  532. package/main.py +6 -0
  533. package/package.json +4 -1
  534. package/requirements.txt +41 -38
  535. package/scripts/auto-fix-deps.py +128 -0
  536. package/scripts/env-manager.js +25 -2
  537. package/scripts/final-test.js +78 -0
  538. package/scripts/setup-python-env.js +700 -191
  539. package/scripts/test-alluser.js +48 -0
  540. package/scripts/test-different-version.js +86 -0
  541. package/scripts/test-direct.js +63 -0
  542. package/scripts/test-extract-installer.js +28 -0
  543. package/scripts/test-install-log.js +54 -0
  544. package/scripts/test-installer.js +39 -0
  545. package/scripts/test-integration.js +250 -0
  546. package/scripts/test-real-install.js +210 -0
  547. package/scripts/test-targetdir.js +49 -0
  548. package/scripts/test-venv-real.js +47 -0
  549. package/scripts/test-venv-simple.js +57 -0
  550. package/scripts/test-wait.js +49 -0
  551. package/scripts/test-with-log.js +63 -0
  552. package/extensions/services/evol/config.yaml +0 -149
  553. package/extensions/services/evol/routes/routes_management_ws.py +0 -127
  554. package/extensions/services/evol/static/index_evol.html +0 -14
  555. package/extensions/services/evol/static/js/app.js +0 -6304
  556. package/extensions/services/evol/static/js/auth.js +0 -326
  557. package/extensions/services/evol/static/js/evol-app-fixed.js +0 -50
  558. package/extensions/services/evol/static/js/evol-app.js.bak +0 -1800
  559. package/extensions/services/evol/static/js/kernel-client-example.js +0 -228
  560. package/extensions/services/evol/static/js/main.js +0 -141
  561. package/extensions/services/evol/static/js/stats.js +0 -217
  562. package/extensions/services/evol/static/js/token-manager.js +0 -175
  563. package/extensions/services/proxy/CHANGELOG_20260308.md +0 -258
  564. package/extensions/services/proxy/_fix_prints.py +0 -133
  565. package/extensions/services/proxy/_fix_prints2.py +0 -87
  566. package/extensions/services/proxy/console_auth.py +0 -109
  567. package/extensions/services/proxy/logs/websocket.log +0 -260
  568. package/extensions/services/proxy/main.py +0 -240
  569. package/extensions/services/proxy/requirements.txt +0 -13
  570. package/extensions/services/web/config.yaml +0 -149
  571. /package/extensions/services/{evol → kite_console}/static/pairing.html +0 -0
  572. /package/extensions/services/{evol → kite_console}/static/test_registry.html +0 -0
  573. /package/extensions/services/{evol → kite_console}/static/test_relay.html +0 -0
@@ -1,271 +1,952 @@
1
- """
2
- server.py - 精简版 FastAPI 服务器
3
-
4
- 仅提供4个代理路由 + 健康检查:
5
- - /claude-proxy/{path} - Claude代理(仅 v1/messages 路径且 model 包含 "claude")
6
- - /codex-proxy/{path} - Codex代理(不做 model 过滤)
7
- - /gemini-proxy/{path} - Gemini代理(不做 model 过滤)
8
- - /openclaw-proxy/{path} - OpenClaw代理(OpenAI兼容接口)
9
- - /health - 健康检查
10
- """
11
-
12
- import asyncio
13
- import json
14
- import logging
15
- import threading
16
- import time
17
- import uuid
18
-
19
- import uvicorn
20
- from fastapi import FastAPI, Request, HTTPException
21
- from fastapi.middleware.cors import CORSMiddleware
22
- from fastapi.responses import JSONResponse, Response
23
-
24
-
25
- # ==================== 并发管理器 ====================
26
-
27
- class ConcurrencyLimitManager:
28
- """管理代理接口的总并发数,带自动清理超时请求"""
29
-
30
- _instance = None
31
- _lock = threading.Lock()
32
-
33
- def __new__(cls):
34
- if cls._instance is None:
35
- with cls._lock:
36
- if cls._instance is None:
37
- cls._instance = super().__new__(cls)
38
- cls._instance._initialized = False
39
- return cls._instance
40
-
41
- def __init__(self):
42
- if self._initialized:
43
- return
44
- self._initialized = True
45
- self._active_requests = {}
46
- self._count_lock = threading.Lock()
47
- self._timeout_seconds = 300
48
- self._limit = 5
49
- print("[ConcurrencyLimitManager] initialized")
50
-
51
- def acquire(self) -> tuple:
52
- with self._count_lock:
53
- self._cleanup_timeout_requests()
54
- request_id = f"req_{int(time.time() * 1000)}_{id(threading.current_thread())}"
55
- self._active_requests[request_id] = time.time()
56
- return True, "", request_id
57
-
58
- def release(self, request_id: str = None):
59
- with self._count_lock:
60
- if request_id and request_id in self._active_requests:
61
- del self._active_requests[request_id]
62
- elif not request_id and self._active_requests:
63
- oldest_id = min(self._active_requests, key=self._active_requests.get)
64
- del self._active_requests[oldest_id]
65
-
66
- def _cleanup_timeout_requests(self):
67
- current_time = time.time()
68
- timeout_ids = [
69
- rid for rid, t in self._active_requests.items()
70
- if current_time - t > self._timeout_seconds
71
- ]
72
- for rid in timeout_ids:
73
- del self._active_requests[rid]
74
-
75
- def get_current_count(self) -> int:
76
- with self._count_lock:
77
- return len(self._active_requests)
78
-
79
-
80
- concurrency_manager = ConcurrencyLimitManager()
81
-
82
-
83
- # ==================== FastAPI 应用 ====================
84
-
85
- app = FastAPI(title="Evol Sample Backend", version="1.0.0")
86
-
87
- app.add_middleware(
88
- CORSMiddleware,
89
- allow_origins=["*"],
90
- allow_credentials=True,
91
- allow_methods=["*"],
92
- allow_headers=["*"],
93
- )
94
-
95
-
96
- # ==================== 健康检查 ====================
97
-
98
- @app.get("/health")
99
- async def health_check():
100
- from evol.server.claude_proxy_async import get_current_agent_id
101
- agent_id = get_current_agent_id()
102
- return {
103
- "status": "ok",
104
- "agent_id": agent_id.id if agent_id else None,
105
- "is_online": agent_id.is_online_success if agent_id else False,
106
- "active_requests": concurrency_manager.get_current_count()
107
- }
108
-
109
-
110
- # ==================== Claude Proxy 路由 ====================
111
-
112
- @app.api_route("/claude-proxy/{full_path:path}",
113
- methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
114
- async def claude_proxy_route(request: Request, full_path: str):
115
- """Claude代理路由 - 仅放行 v1/messages 路径且 model 包含 'claude'"""
116
- acquired, error_msg, request_id = concurrency_manager.acquire()
117
- if not acquired:
118
- return Response(content=error_msg.encode("utf-8"), status_code=429)
119
-
120
- if "v1/messages" not in full_path:
121
- concurrency_manager.release(request_id)
122
- raise HTTPException(status_code=200, detail="OK")
123
-
124
- try:
125
- request_body = await request.body()
126
- bodyjson = await asyncio.to_thread(json.loads, request_body.decode('utf-8'))
127
- model = bodyjson.get('model', '')
128
- if "claude" not in model:
129
- raise HTTPException(status_code=400, detail="Unsupported model")
130
-
131
- from evol.server.claude_proxy_async import proxy_claude_request
132
- response = await proxy_claude_request(request)
133
- return response
134
-
135
- except asyncio.CancelledError:
136
- logging.getLogger("evol_server").warning("claude-proxy request cancelled (client disconnected?)")
137
- return Response(content=b"Client Closed Request", status_code=499)
138
- except Exception as e:
139
- if isinstance(e, HTTPException):
140
- return Response(content=str(e.detail).encode("utf-8"), status_code=e.status_code)
141
- return Response(content=str(e).encode("utf-8"), status_code=503)
142
- finally:
143
- concurrency_manager.release(request_id)
144
-
145
-
146
- # ==================== Codex Proxy 路由 ====================
147
-
148
- @app.api_route("/codex-proxy/{full_path:path}",
149
- methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
150
- async def codex_proxy_route(request: Request, full_path: str):
151
- """Codex代理路由 - 不做 model 过滤"""
152
- acquired, error_msg, request_id = concurrency_manager.acquire()
153
- if not acquired:
154
- return Response(content=error_msg.encode("utf-8"), status_code=429)
155
-
156
- try:
157
- from evol.server.claude_proxy_async import proxy_claude_request
158
- response = await proxy_claude_request(request)
159
- return response
160
-
161
- except asyncio.CancelledError:
162
- logging.getLogger("evol_server").warning("codex-proxy request cancelled (client disconnected?)")
163
- return Response(content=b"Client Closed Request", status_code=499)
164
- except Exception as e:
165
- if isinstance(e, HTTPException):
166
- return Response(content=str(e.detail).encode("utf-8"), status_code=e.status_code)
167
- return Response(content=str(e).encode("utf-8"), status_code=503)
168
- finally:
169
- concurrency_manager.release(request_id)
170
-
171
-
172
- # ==================== Gemini Proxy 路由 ====================
173
-
174
- @app.api_route("/gemini-proxy/{full_path:path}",
175
- methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
176
- async def gemini_proxy_route(request: Request, full_path: str):
177
- """Gemini代理路由 - 不做 model 过滤"""
178
- acquired, error_msg, request_id = concurrency_manager.acquire()
179
- if not acquired:
180
- return Response(content=error_msg.encode("utf-8"), status_code=429)
181
-
182
- try:
183
- from evol.server.claude_proxy_async import proxy_claude_request
184
- response = await proxy_claude_request(request)
185
- return response
186
-
187
- except asyncio.CancelledError:
188
- logging.getLogger("evol_server").warning("gemini-proxy request cancelled (client disconnected?)")
189
- return Response(content=b"Client Closed Request", status_code=499)
190
- except Exception as e:
191
- if isinstance(e, HTTPException):
192
- return Response(content=str(e.detail).encode("utf-8"), status_code=e.status_code)
193
- return Response(content=str(e).encode("utf-8"), status_code=503)
194
- finally:
195
- concurrency_manager.release(request_id)
196
-
197
-
198
- # ==================== OpenClaw Proxy 路由 ====================
199
-
200
- @app.api_route("/openclaw-proxy/{full_path:path}",
201
- methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
202
- async def openclaw_proxy_route(request: Request, full_path: str):
203
- """OpenClaw代理路由 - OpenAI兼容接口"""
204
- acquired, error_msg, request_id = concurrency_manager.acquire()
205
- if not acquired:
206
- from evol.server.openclaw_proxy import openai_error_response
207
- return openai_error_response(error_msg, error_type="rate_limit_exceeded", status_code=429)
208
-
209
- try:
210
- if full_path == "v1/models":
211
- from evol.server.openclaw_proxy import get_models_list
212
- return JSONResponse(get_models_list())
213
- elif full_path == "v1/chat/completions":
214
- from evol.server.openclaw_proxy import proxy_openclaw_request
215
- response = await proxy_openclaw_request(request)
216
- return response
217
- else:
218
- from evol.server.openclaw_proxy import openai_error_response
219
- return openai_error_response(
220
- f"Path not found: /{full_path}. Supported: /v1/models, /v1/chat/completions.",
221
- error_type="invalid_request_error",
222
- status_code=404
223
- )
224
-
225
- except asyncio.CancelledError:
226
- logging.getLogger("evol_server").warning("openclaw-proxy request cancelled (client disconnected?)")
227
- return Response(content=b"Client Closed Request", status_code=499)
228
- except Exception as e:
229
- from evol.server.openclaw_proxy import openai_error_response
230
- return openai_error_response(f"Internal server error: {str(e)}", error_type="api_error", status_code=500)
231
- finally:
232
- concurrency_manager.release(request_id)
233
-
234
-
235
- # ==================== 启动服务器 ====================
236
-
237
- # 全局变量存储实际端口
238
- _actual_port = None
239
-
240
- def get_actual_port():
241
- """获取服务器实际使用的端口"""
242
- return _actual_port
243
-
244
- async def run_server(port: int = 0):
245
- """启动 FastAPI 服务器
246
-
247
- Args:
248
- port: 端口号,0 表示系统自动分配
249
- """
250
- global _actual_port
251
-
252
- # 如果端口为 0,先获取一个可用端口
253
- if port == 0:
254
- import socket
255
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
256
- sock.bind(('', 0))
257
- port = sock.getsockname()[1]
258
- sock.close()
259
-
260
- _actual_port = port
261
-
262
- config = uvicorn.Config(
263
- app,
264
- host="0.0.0.0",
265
- port=port,
266
- log_level="info",
267
- access_log=True,
268
- )
269
- server = uvicorn.Server(config)
270
- server.install_signal_handlers = False # 禁止 uvicorn 安装信号处理器,防止外部信号导致服务器关闭
271
- await server.serve()
1
+ """
2
+ ProxyServer - Kite 模块服务器
3
+
4
+ FastAPI 4 路由 + Kernel WS 客户端 + RPC 注册/处理 + 事件发布订阅。
5
+ 整合 auth_bridge、aid_manager、config_store,提供完整的代理生命周期管理。
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import logging
11
+ import os
12
+ import threading
13
+ import time
14
+ import uuid
15
+
16
+ import websockets
17
+ from fastapi import FastAPI, Request, HTTPException
18
+ from fastapi.middleware.cors import CORSMiddleware
19
+ from fastapi.responses import JSONResponse, Response
20
+
21
+ from .auth_bridge import AuthBridge
22
+ from .aid_manager import AIDManager
23
+ from .config_store import ConfigStore
24
+
25
+
26
+ # ==================== 并发管理器 ====================
27
+
28
+ class ConcurrencyLimitManager:
29
+ """管理代理接口的总并发数,带自动清理超时请求"""
30
+
31
+ _instance = None
32
+ _lock = threading.Lock()
33
+
34
+ def __new__(cls):
35
+ if cls._instance is None:
36
+ with cls._lock:
37
+ if cls._instance is None:
38
+ cls._instance = super().__new__(cls)
39
+ cls._instance._initialized = False
40
+ return cls._instance
41
+
42
+ def __init__(self):
43
+ if self._initialized:
44
+ return
45
+ self._initialized = True
46
+ self._active_requests = {}
47
+ self._count_lock = threading.Lock()
48
+ self._timeout_seconds = 300
49
+ print("[ConcurrencyLimitManager] initialized")
50
+
51
+ def acquire(self) -> tuple:
52
+ with self._count_lock:
53
+ self._cleanup_timeout_requests()
54
+ request_id = f"req_{int(time.time() * 1000)}_{id(threading.current_thread())}"
55
+ self._active_requests[request_id] = time.time()
56
+ return True, "", request_id
57
+
58
+ def release(self, request_id: str = None):
59
+ with self._count_lock:
60
+ if request_id and request_id in self._active_requests:
61
+ del self._active_requests[request_id]
62
+ elif not request_id and self._active_requests:
63
+ oldest_id = min(self._active_requests, key=self._active_requests.get)
64
+ del self._active_requests[oldest_id]
65
+
66
+ def _cleanup_timeout_requests(self):
67
+ current_time = time.time()
68
+ timeout_ids = [
69
+ rid for rid, t in self._active_requests.items()
70
+ if current_time - t > self._timeout_seconds
71
+ ]
72
+ for rid in timeout_ids:
73
+ del self._active_requests[rid]
74
+
75
+ def get_current_count(self) -> int:
76
+ with self._count_lock:
77
+ return len(self._active_requests)
78
+
79
+
80
+ concurrency_manager = ConcurrencyLimitManager()
81
+
82
+
83
+ # ==================== ProxyServer ====================
84
+
85
+ class ProxyServer:
86
+ def __init__(self, token: str, kernel_port: int, host: str, port: int, boot_t0: float, max_connections: int = 1,
87
+ gateway_url: str = "", kite_token: str = ""):
88
+ self.token = token
89
+ self.kernel_port = kernel_port
90
+ self.host = host
91
+ self.port = port
92
+ self.boot_t0 = boot_t0
93
+ self.max_connections = max_connections
94
+ self.gateway_url = gateway_url
95
+ self.kite_token = kite_token
96
+ self._ws_task: asyncio.Task | None = None
97
+ self._ws = None
98
+ self._shutting_down = False
99
+ self._exit_code = 0
100
+ self._auth_failed = False
101
+ self._uvicorn_server = None
102
+ self._start_time = time.time()
103
+ self._pending_rpc = {}
104
+ self._extra_ws: dict = {} # slot → WebSocket
105
+ self._extra_ws_tasks: dict = {} # slot → recv loop Task
106
+ self._has_registered = False
107
+
108
+ # 组件初始化
109
+ self.auth_bridge = AuthBridge()
110
+ self.config_store = ConfigStore()
111
+ self.aid_manager = AIDManager(self.auth_bridge, self.config_store)
112
+
113
+ # 注入 userPresenter / configPresenter
114
+ self._inject_presenters()
115
+
116
+ # AID 上线回调
117
+ self.aid_manager.set_on_aid_online(self._on_aid_online)
118
+
119
+ self.app = self._create_app()
120
+
121
+ def _inject_presenters(self):
122
+ """将 auth_bridge / aid_manager 注入到 presenter 层"""
123
+ try:
124
+ from .evol.presenter.userPresenter import userPresenter
125
+ from .evol.presenter.configPresenter import configPresenter
126
+ from .evol.presenter.apikeyPresenter import apikeyPresenter
127
+
128
+ userPresenter.set_aid_manager(self.aid_manager)
129
+ userPresenter.set_auth_bridge(self.auth_bridge)
130
+ apikeyPresenter.set_auth_bridge(self.auth_bridge)
131
+
132
+ # 同步注入 token(如果缓存中有)
133
+ cached = self.auth_bridge.get_cached_token()
134
+ if cached:
135
+ configPresenter.set_evol_token(cached)
136
+ except Exception as e:
137
+ print(f"[proxy] 注入 presenter 失败: {e}")
138
+
139
+ def _on_aid_online(self, aid):
140
+ """AID 上线回调 - 注册消息处理器"""
141
+ try:
142
+ from .evol.presenter.agentIdPresenter import agentIdPresenter
143
+ agentIdPresenter.evol_agentId_online(aid)
144
+ print(f"[proxy] AID 消息处理器已注册: {aid.id}")
145
+
146
+ # 发布事件(异步)
147
+ if self._ws:
148
+ asyncio.ensure_future(self._publish_event(
149
+ self._ws, "proxy.aid_online",
150
+ {"module_id": "proxy", "agent_id": aid.id},
151
+ ))
152
+ except Exception as e:
153
+ print(f"[proxy] AID 上线处理失败: {e}")
154
+
155
+ def _create_app(self) -> FastAPI:
156
+ app = FastAPI(title="Kite Proxy Module", docs_url=None, redoc_url=None)
157
+ server = self
158
+
159
+ app.add_middleware(
160
+ CORSMiddleware,
161
+ allow_origins=["*"],
162
+ allow_credentials=True,
163
+ allow_methods=["*"],
164
+ allow_headers=["*"],
165
+ )
166
+
167
+ @app.on_event("startup")
168
+ async def _startup():
169
+ if server.kernel_port:
170
+ server._ws_task = asyncio.create_task(server._ws_loop())
171
+
172
+ @app.on_event("shutdown")
173
+ async def _shutdown():
174
+ if server._ws_task:
175
+ server._ws_task.cancel()
176
+ if server._ws:
177
+ try:
178
+ await server._ws.close()
179
+ except Exception:
180
+ pass
181
+ print("[proxy] FastAPI shutdown complete")
182
+
183
+ # ── 健康检查 ──
184
+
185
+ @app.get("/health")
186
+ async def health_check():
187
+ agent_id = server.aid_manager.agentId
188
+ return {
189
+ "status": "ok",
190
+ "agent_id": agent_id.id if agent_id else None,
191
+ "is_online": agent_id.is_online_success if agent_id else False,
192
+ "active_requests": concurrency_manager.get_current_count(),
193
+ }
194
+
195
+ @app.get("/status")
196
+ async def status():
197
+ agent_id = server.aid_manager.agentId
198
+ return {
199
+ "module": "proxy",
200
+ "status": "running",
201
+ "kernel_connected": server._ws is not None,
202
+ "agent_id": agent_id.id if agent_id else None,
203
+ "is_online": agent_id.is_online_success if agent_id else False,
204
+ "uptime_seconds": round(time.time() - server._start_time),
205
+ "active_requests": concurrency_manager.get_current_count(),
206
+ }
207
+
208
+ # ── Claude Proxy 路由 ──
209
+
210
+ @app.api_route("/claude-proxy/{full_path:path}",
211
+ methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
212
+ async def claude_proxy_route(request: Request, full_path: str):
213
+ acquired, error_msg, request_id = concurrency_manager.acquire()
214
+ if not acquired:
215
+ return Response(content=error_msg.encode("utf-8"), status_code=429)
216
+
217
+ if "v1/messages" not in full_path:
218
+ concurrency_manager.release(request_id)
219
+ raise HTTPException(status_code=200, detail="OK")
220
+
221
+ try:
222
+ request_body = await request.body()
223
+ bodyjson = await asyncio.to_thread(json.loads, request_body.decode("utf-8"))
224
+ model = bodyjson.get("model", "")
225
+ if "claude" not in model:
226
+ raise HTTPException(status_code=400, detail="Unsupported model")
227
+
228
+ from .evol.server.claude_proxy_async import proxy_claude_request
229
+ response = await proxy_claude_request(request)
230
+ return response
231
+
232
+ except asyncio.CancelledError:
233
+ return Response(content=b"Client Closed Request", status_code=499)
234
+ except HTTPException:
235
+ raise
236
+ except Exception as e:
237
+ return Response(content=str(e).encode("utf-8"), status_code=503)
238
+ finally:
239
+ concurrency_manager.release(request_id)
240
+
241
+ # ── Codex Proxy 路由 ──
242
+
243
+ @app.api_route("/codex-proxy/{full_path:path}",
244
+ methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
245
+ async def codex_proxy_route(request: Request, full_path: str):
246
+ acquired, error_msg, request_id = concurrency_manager.acquire()
247
+ if not acquired:
248
+ return Response(content=error_msg.encode("utf-8"), status_code=429)
249
+
250
+ try:
251
+ from .evol.server.claude_proxy_async import proxy_claude_request
252
+ response = await proxy_claude_request(request)
253
+ return response
254
+
255
+ except asyncio.CancelledError:
256
+ return Response(content=b"Client Closed Request", status_code=499)
257
+ except Exception as e:
258
+ if isinstance(e, HTTPException):
259
+ return Response(content=str(e.detail).encode("utf-8"), status_code=e.status_code)
260
+ return Response(content=str(e).encode("utf-8"), status_code=503)
261
+ finally:
262
+ concurrency_manager.release(request_id)
263
+
264
+ # ── Gemini Proxy 路由 ──
265
+
266
+ @app.api_route("/gemini-proxy/{full_path:path}",
267
+ methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
268
+ async def gemini_proxy_route(request: Request, full_path: str):
269
+ acquired, error_msg, request_id = concurrency_manager.acquire()
270
+ if not acquired:
271
+ return Response(content=error_msg.encode("utf-8"), status_code=429)
272
+
273
+ try:
274
+ from .evol.server.claude_proxy_async import proxy_claude_request
275
+ response = await proxy_claude_request(request)
276
+ return response
277
+
278
+ except asyncio.CancelledError:
279
+ return Response(content=b"Client Closed Request", status_code=499)
280
+ except Exception as e:
281
+ if isinstance(e, HTTPException):
282
+ return Response(content=str(e.detail).encode("utf-8"), status_code=e.status_code)
283
+ return Response(content=str(e).encode("utf-8"), status_code=503)
284
+ finally:
285
+ concurrency_manager.release(request_id)
286
+
287
+ # ── OpenClaw Proxy 路由 ──
288
+
289
+ @app.api_route("/openclaw-proxy/{full_path:path}",
290
+ methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
291
+ async def openclaw_proxy_route(request: Request, full_path: str):
292
+ acquired, error_msg, request_id = concurrency_manager.acquire()
293
+ if not acquired:
294
+ from .evol.server.openclaw_proxy import openai_error_response
295
+ return openai_error_response(error_msg, error_type="rate_limit_exceeded", status_code=429)
296
+
297
+ try:
298
+ if full_path == "v1/models":
299
+ from .evol.server.openclaw_proxy import get_models_list
300
+ return JSONResponse(get_models_list())
301
+ elif full_path == "v1/chat/completions":
302
+ from .evol.server.openclaw_proxy import proxy_openclaw_request
303
+ response = await proxy_openclaw_request(request)
304
+ return response
305
+ else:
306
+ from .evol.server.openclaw_proxy import openai_error_response
307
+ return openai_error_response(
308
+ f"Path not found: /{full_path}. Supported: /v1/models, /v1/chat/completions.",
309
+ error_type="invalid_request_error",
310
+ status_code=404,
311
+ )
312
+
313
+ except asyncio.CancelledError:
314
+ return Response(content=b"Client Closed Request", status_code=499)
315
+ except Exception as e:
316
+ from .evol.server.openclaw_proxy import openai_error_response
317
+ return openai_error_response(f"Internal server error: {str(e)}", error_type="api_error", status_code=500)
318
+ finally:
319
+ concurrency_manager.release(request_id)
320
+
321
+ return app
322
+
323
+ # ── system.require_init 处理 ──
324
+
325
+ async def _do_init(self, ws):
326
+ """收到 system.require_init 后执行:订阅 + 注册 + module.ready"""
327
+ reason = "startup" if not self._has_registered else "recovery"
328
+ print(f"[proxy] Received system.require_init (reason={reason})")
329
+
330
+ try:
331
+ # 注入 RPC 调用到 auth_bridge(包装为旧签名兼容)
332
+ async def _rpc_caller_for_bridge(method, params=None, timeout=3.0):
333
+ return await self._rpc_call(self._ws, method, params, timeout=timeout)
334
+ self.auth_bridge.set_rpc_caller(_rpc_caller_for_bridge)
335
+
336
+ # 订阅事件
337
+ await self._rpc_call(ws, "event.subscribe", {
338
+ "events": [
339
+ "module.shutdown",
340
+ "module.ready",
341
+ "evol.user_ready",
342
+ "evol.user_logout",
343
+ ],
344
+ })
345
+
346
+ # 注册到 Kernel
347
+ elapsed = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
348
+ elapsed_str = f" ({elapsed:.1f}s)" if elapsed else ""
349
+ await self._rpc_call(ws, "registry.register", {
350
+ "module_id": "proxy",
351
+ "module_type": "service",
352
+ "api_endpoint": f"http://127.0.0.1:{self.port}",
353
+ "health_endpoint": "/health",
354
+ "tools": {
355
+ "rpc": {
356
+ "module": {
357
+ "health": {"method": "health", "description": "健康检查"},
358
+ "status": {"method": "status", "description": "状态查询"},
359
+ },
360
+ "proxy": {
361
+ "get_endpoints": {"method": "get_endpoints", "description": "获取代理端点"},
362
+ "get_aid_status": {"method": "get_aid_status", "description": "AID 状态"},
363
+ "reconnect_aid": {"method": "reconnect_aid", "description": "重连 AID"},
364
+ },
365
+ },
366
+ },
367
+ "events_publish": {
368
+ "proxy": {
369
+ "started": {"description": "Proxy 模块启动"},
370
+ "aid_online": {"description": "AID 上线"},
371
+ "aid_offline": {"description": "AID 离线"},
372
+ },
373
+ },
374
+ "events_subscribe": [
375
+ "module.shutdown",
376
+ "module.ready",
377
+ "evol.user_ready",
378
+ "evol.user_logout",
379
+ ],
380
+ })
381
+ print(f"[proxy] Registered to Kernel{elapsed_str}")
382
+
383
+ # 发布 module.ready
384
+ if not self._shutting_down:
385
+ startup_time = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
386
+ await self._publish_event(ws, "module.ready", {
387
+ "module_id": "proxy",
388
+ "graceful_shutdown": True,
389
+ "startup_time": startup_time,
390
+ "reason": reason,
391
+ })
392
+ print(f"[proxy] module.ready published{elapsed_str}")
393
+
394
+ # 发布 proxy.started 事件
395
+ display_host = "localhost" if self.host == "0.0.0.0" else self.host
396
+ await self._publish_event(ws, "proxy.started", {
397
+ "module_id": "proxy",
398
+ "url": f"http://{display_host}:{self.port}",
399
+ "endpoints": {
400
+ "claude": f"/claude-proxy/",
401
+ "codex": f"/codex-proxy/",
402
+ "gemini": f"/gemini-proxy/",
403
+ "openclaw": f"/openclaw-proxy/",
404
+ },
405
+ })
406
+
407
+ self._has_registered = True
408
+
409
+ # 尝试初始化 AID(如果 evol 已就绪)
410
+ asyncio.create_task(self._try_init_aid())
411
+
412
+ except Exception as e:
413
+ print(f"[proxy] _do_init failed: {e}")
414
+
415
+ # ── Kernel WebSocket 客户端 ──
416
+
417
+ async def _ws_loop(self):
418
+ retry_delay = 0.3
419
+ max_delay = 5.0
420
+ max_retries = 10
421
+ attempt = 0
422
+ while not self._shutting_down:
423
+ try:
424
+ await self._ws_connect()
425
+ retry_delay = 0.3
426
+ attempt = 0
427
+ except asyncio.CancelledError:
428
+ print("[proxy] WS loop cancelled")
429
+ return
430
+ except Exception as e:
431
+ attempt += 1
432
+ if hasattr(e, "rcvd") and e.rcvd is not None:
433
+ code = e.rcvd.code if hasattr(e.rcvd, "code") else 0
434
+ if code in (4001, 4003):
435
+ print(f"[proxy] Kernel 认证失败 (code {code}),退出")
436
+ self._exit_code = 1
437
+ self._auth_failed = True
438
+ self._shutting_down = True
439
+ if self._uvicorn_server:
440
+ self._uvicorn_server.should_exit = True
441
+ return
442
+ if attempt >= max_retries:
443
+ print(f"[proxy] Kernel 重连失败 {max_retries} 次,退出")
444
+ self._exit_code = 1
445
+ self._shutting_down = True
446
+ if self._uvicorn_server:
447
+ self._uvicorn_server.should_exit = True
448
+ return
449
+ if self._shutting_down:
450
+ return
451
+ print(f"[proxy] Kernel connection error: {e}, retrying in {retry_delay:.1f}s ({attempt}/{max_retries})")
452
+ self._ws = None
453
+ if self._shutting_down:
454
+ return
455
+ await asyncio.sleep(retry_delay)
456
+ retry_delay = min(retry_delay * 2, max_delay)
457
+
458
+ async def _ws_connect(self):
459
+ url = f"ws://127.0.0.1:{self.kernel_port}/ws?id=proxy"
460
+ print("[proxy] WS connecting to Kernel")
461
+ async with websockets.connect(url, open_timeout=5, ping_interval=None, close_timeout=10) as ws:
462
+ self._ws = ws
463
+ elapsed = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
464
+ elapsed_str = f" ({elapsed:.1f}s)" if elapsed else ""
465
+ print(f"[proxy] Connected to Kernel{elapsed_str}")
466
+
467
+ # 认证握手
468
+ auth_req = {
469
+ "jsonrpc": "2.0",
470
+ "id": "auth",
471
+ "method": "auth",
472
+ "params": {"token": self.token}
473
+ }
474
+ await ws.send(json.dumps(auth_req))
475
+ auth_resp_raw = await asyncio.wait_for(ws.recv(), timeout=5)
476
+ auth_resp = json.loads(auth_resp_raw)
477
+ if "error" in auth_resp:
478
+ raise Exception(f"Auth failed: {auth_resp['error']}")
479
+
480
+ # 启动接收循环(等待 system.require_init 触发 _do_init)
481
+ receiver_task = asyncio.create_task(self._ws_receiver(ws))
482
+
483
+ try:
484
+ # 等待接收循环结束
485
+ await receiver_task
486
+ except Exception as e:
487
+ receiver_task.cancel()
488
+ try:
489
+ await receiver_task
490
+ except asyncio.CancelledError:
491
+ pass
492
+ raise
493
+
494
+ async def _ws_receiver(self, ws):
495
+ """WebSocket 接收循环"""
496
+ try:
497
+ async for raw in ws:
498
+ try:
499
+ msg = json.loads(raw)
500
+ except (json.JSONDecodeError, TypeError):
501
+ continue
502
+
503
+ try:
504
+ has_method = "method" in msg
505
+ has_id = "id" in msg
506
+ has_result_or_error = "result" in msg or "error" in msg
507
+
508
+ if has_method and not has_id:
509
+ # 检测 system.require_init 事件
510
+ params = msg.get("params", {})
511
+ event_type = params.get("event", "")
512
+ if event_type == "system.require_init":
513
+ asyncio.create_task(self._do_init(ws))
514
+ continue
515
+ asyncio.create_task(self._handle_event_notification(msg))
516
+ elif has_method and has_id:
517
+ asyncio.create_task(self._handle_rpc_request(ws, msg))
518
+ elif has_id and has_result_or_error:
519
+ self._handle_rpc_response(msg)
520
+ except Exception as e:
521
+ print(f"[proxy] 消息处理异常: {e}")
522
+ except Exception as e:
523
+ print(f"[proxy] Receive loop exited: {e}")
524
+ finally:
525
+ # 断连清理:取消所有等待中的 RPC future
526
+ for rpc_id, future in list(self._pending_rpc.items()):
527
+ if not future.done():
528
+ future.set_exception(ConnectionError("WebSocket disconnected"))
529
+ self._pending_rpc.clear()
530
+
531
+ async def _rpc_call(self, ws, method: str, params: dict = None,
532
+ wait_response: bool = True, timeout: float = 3.0) -> dict:
533
+ """JSON-RPC 2.0 request。默认等待响应。"""
534
+ rpc_id = str(uuid.uuid4())
535
+ msg = {"jsonrpc": "2.0", "id": rpc_id, "method": method}
536
+ if params:
537
+ msg["params"] = params
538
+ if not wait_response:
539
+ await ws.send(json.dumps(msg))
540
+ return {}
541
+ future = asyncio.get_event_loop().create_future()
542
+ self._pending_rpc[rpc_id] = future
543
+ await ws.send(json.dumps(msg))
544
+ try:
545
+ return await asyncio.wait_for(future, timeout=timeout)
546
+ except asyncio.TimeoutError:
547
+ self._pending_rpc.pop(rpc_id, None)
548
+ return {"error": {"code": -32000, "message": f"RPC timeout: {method} ({timeout}s)"}}
549
+
550
+ # ── 事件处理 ──
551
+
552
+ async def _handle_event_notification(self, msg: dict):
553
+ params = msg.get("params", {})
554
+ event_type = params.get("event", "")
555
+ data = params.get("data", {})
556
+
557
+ if event_type == "system.ping":
558
+ await self._handle_ping(data)
559
+ return
560
+
561
+ # 弹性连接 offer/release
562
+ if event_type == "system.connection.offer":
563
+ asyncio.create_task(self._handle_connection_offer(data))
564
+ return
565
+ if event_type == "system.connection.release":
566
+ asyncio.create_task(self._handle_connection_release(data))
567
+ return
568
+
569
+ if event_type == "module.shutdown":
570
+ target = data.get("module_id", "")
571
+ reason = data.get("reason", "")
572
+ if target == "proxy" or not target or reason == "launcher_lost":
573
+ await self._handle_shutdown()
574
+ return
575
+
576
+ if event_type == "module.ready":
577
+ module_id = data.get("module_id", "")
578
+ if module_id == "evol":
579
+ print("[proxy] 检测到 evol 模块就绪(框架层)")
580
+ self.auth_bridge.set_evol_ready(True)
581
+ # 不再在此触发 AID 初始化,等待 evol.user_ready 业务事件
582
+ return
583
+
584
+ if event_type == "evol.user_ready":
585
+ phone = data.get("phone", "")
586
+ print(f"[proxy] 收到 evol.user_ready 事件 (phone: {phone})")
587
+ self.auth_bridge.set_evol_ready(True)
588
+ asyncio.create_task(self._try_init_aid())
589
+ return
590
+
591
+ if event_type == "evol.user_logout":
592
+ print(f"[proxy] 收到用户登出事件,开始清理...")
593
+ asyncio.create_task(self._handle_user_logout(data))
594
+ return
595
+
596
+ async def _handle_user_logout(self, data: dict):
597
+ """处理用户登出:清缓存 → 清 Session → AID 下线"""
598
+ try:
599
+ # 1. 清除 AuthBridge token 缓存
600
+ self.auth_bridge.invalidate()
601
+
602
+ # 2. 清除 AsyncSessionManager 所有会话缓存
603
+ from .evol.server.claude_proxy_async import _clear_async_session_manager_cache
604
+ _clear_async_session_manager_cache()
605
+
606
+ # 3. AID 下线
607
+ await self.aid_manager.shutdown()
608
+
609
+ # 4. 发布 AID 离线事件
610
+ if self._ws:
611
+ await self._publish_event(self._ws, "proxy.aid_offline", {
612
+ "module_id": "proxy", "reason": "user_logout",
613
+ })
614
+
615
+ print("[proxy] 用户登出清理完成,AID 已下线")
616
+ except Exception as e:
617
+ print(f"[proxy] 用户登出清理异常: {e}")
618
+
619
+ async def _handle_ping(self, data: dict):
620
+ if not self._ws:
621
+ return
622
+ await self._publish_event(self._ws, "system.pong", {
623
+ "module_id": "proxy",
624
+ "ping_time": data.get("ping_time"),
625
+ "pong_time": time.time(),
626
+ })
627
+
628
+ async def _handle_rpc_request(self, ws, msg: dict):
629
+ rpc_id = msg.get("id", "")
630
+ method = msg.get("method", "")
631
+ params = msg.get("params", {})
632
+
633
+ if method.startswith("proxy."):
634
+ method = method[6:]
635
+
636
+ handlers = {
637
+ "health": lambda p: self._rpc_health(),
638
+ "status": lambda p: self._rpc_status(),
639
+ "get_endpoints": lambda p: self._rpc_get_endpoints(),
640
+ "get_aid_status": lambda p: self._rpc_get_aid_status(),
641
+ "reconnect_aid": lambda p: self._rpc_reconnect_aid(),
642
+ }
643
+
644
+ handler = handlers.get(method)
645
+ if handler:
646
+ try:
647
+ result = await handler(params)
648
+ await ws.send(json.dumps({"jsonrpc": "2.0", "id": rpc_id, "result": result}))
649
+ except Exception as e:
650
+ await ws.send(json.dumps({
651
+ "jsonrpc": "2.0", "id": rpc_id,
652
+ "error": {"code": -32603, "message": str(e)},
653
+ }))
654
+ else:
655
+ await ws.send(json.dumps({
656
+ "jsonrpc": "2.0", "id": rpc_id,
657
+ "error": {"code": -32601, "message": f"Method not found: {method}"},
658
+ }))
659
+
660
+ # ── RPC 处理器 ──
661
+
662
+ async def _rpc_health(self) -> dict:
663
+ agent_id = self.aid_manager.agentId
664
+ return {
665
+ "status": "healthy",
666
+ "agent_id": agent_id.id if agent_id else None,
667
+ "is_online": agent_id.is_online_success if agent_id else False,
668
+ "active_requests": concurrency_manager.get_current_count(),
669
+ }
670
+
671
+ async def _rpc_status(self) -> dict:
672
+ agent_id = self.aid_manager.agentId
673
+ return {
674
+ "module": "proxy",
675
+ "status": "running",
676
+ "agent_id": agent_id.id if agent_id else None,
677
+ "is_online": agent_id.is_online_success if agent_id else False,
678
+ "uptime_seconds": round(time.time() - self._start_time),
679
+ "active_requests": concurrency_manager.get_current_count(),
680
+ }
681
+
682
+ async def _rpc_get_endpoints(self) -> dict:
683
+ display_host = "localhost" if self.host == "0.0.0.0" else self.host
684
+ base = f"http://{display_host}:{self.port}"
685
+ return {
686
+ "endpoints": {
687
+ "claude": f"{base}/claude-proxy/",
688
+ "codex": f"{base}/codex-proxy/",
689
+ "gemini": f"{base}/gemini-proxy/",
690
+ "openclaw": f"{base}/openclaw-proxy/",
691
+ },
692
+ }
693
+
694
+ async def _rpc_get_aid_status(self) -> dict:
695
+ agent_id = self.aid_manager.agentId
696
+ return {
697
+ "agent_id": agent_id.id if agent_id else None,
698
+ "is_online": agent_id.is_online_success if agent_id else False,
699
+ "evol_ready": self.auth_bridge._evol_ready,
700
+ "has_token": self.auth_bridge.is_logged_in(),
701
+ }
702
+
703
+ async def _rpc_reconnect_aid(self) -> dict:
704
+ result = await self.aid_manager.force_rebuild(bypass_cooldown=True)
705
+ return result
706
+
707
+ # ── AID 初始化 ──
708
+
709
+ async def _try_init_aid(self, retries: int = 0, max_retries: int = 3, retry_interval: float = 5.0):
710
+ """尝试初始化 AID(evol 就绪后执行)
711
+
712
+ Args:
713
+ retries: 当前重试次数
714
+ max_retries: 最大重试次数
715
+ retry_interval: 重试间隔(秒)
716
+ """
717
+ # 防重入:已有 AID 且在线则跳过
718
+ if self.aid_manager.agentId is not None and self.aid_manager.agentId.is_online_success:
719
+ return
720
+
721
+ # 防并发:首次调用时检查
722
+ if retries == 0 and self.aid_manager._aid_initializing:
723
+ return
724
+ if retries == 0:
725
+ self.aid_manager._aid_initializing = True
726
+
727
+ if not self.auth_bridge._evol_ready:
728
+ # evol 尚未就绪时,主动探测 evol 模块状态
729
+ if retries == 0:
730
+ await self._probe_evol_ready()
731
+ if not self.auth_bridge._evol_ready:
732
+ self.aid_manager._aid_initializing = False
733
+ return
734
+
735
+ try:
736
+ # 先获取 token 并注入
737
+ evol_token = await self.auth_bridge.get_evol_token()
738
+ if evol_token:
739
+ from .evol.presenter.configPresenter import configPresenter
740
+ configPresenter.set_evol_token(evol_token)
741
+ print(f"[proxy] evol_token 已注入 configPresenter")
742
+
743
+ # 初始化 AID
744
+ success = await self.aid_manager.initialize()
745
+ if success:
746
+ # 同步 agentId 到 userPresenter
747
+ from .evol.presenter.userPresenter import userPresenter
748
+ userPresenter.agentId = self.aid_manager.agentId
749
+ print(f"[proxy] AID 初始化成功")
750
+ else:
751
+ print("[proxy] AID 初始化失败")
752
+ else:
753
+ # token 获取失败,有限重试
754
+ if retries < max_retries:
755
+ print(f"[proxy] 无法获取 evol_token,{retry_interval}s 后重试 ({retries + 1}/{max_retries})")
756
+ await asyncio.sleep(retry_interval)
757
+ await self._try_init_aid(retries=retries + 1, max_retries=max_retries, retry_interval=retry_interval)
758
+ else:
759
+ print("[proxy] 无法获取 evol_token,已达最大重试次数,AID 初始化延迟")
760
+ except Exception as e:
761
+ print(f"[proxy] AID 初始化异常: {e}")
762
+ finally:
763
+ self.aid_manager._aid_initializing = False
764
+
765
+ async def _probe_evol_ready(self):
766
+ """主动探测 evol 是否有可用 token(防止错过 evol.user_ready 事件)"""
767
+ if not self._ws:
768
+ return
769
+ try:
770
+ # 先探测 evol 框架是否就绪
771
+ result = await self._rpc_call(self._ws, "evol.health", timeout=3.0)
772
+ if result.get("status") != "healthy":
773
+ return
774
+ self.auth_bridge.set_evol_ready(True)
775
+
776
+ # 再探测是否有已登录的 token
777
+ token_result = await self._rpc_call(self._ws, "evol.has_evol_token", timeout=3.0)
778
+ if token_result.get("has_token"):
779
+ phone = token_result.get("phone", "")
780
+ print(f"[proxy] 主动探测发现 evol 已有可用 token (phone: {phone})")
781
+ asyncio.create_task(self._try_init_aid())
782
+ except Exception:
783
+ pass
784
+
785
+ # ── RPC 响应处理 ──
786
+
787
+ def _handle_rpc_response(self, msg: dict):
788
+ rpc_id = msg.get("id")
789
+ future = self._pending_rpc.pop(rpc_id, None)
790
+ if future and not future.done():
791
+ future.set_result(msg)
792
+
793
+ # ── 事件发布 ──
794
+
795
+ async def _publish_event(self, ws, event: str, data: dict = None):
796
+ """Publish event via event.publish RPC (fire-and-forget)."""
797
+ await self._rpc_call(ws, "event.publish", {
798
+ "event_id": str(uuid.uuid4()),
799
+ "event": event,
800
+ "data": data or {},
801
+ }, wait_response=False)
802
+
803
+ # ── 弹性多连接 ──
804
+
805
+ async def _handle_connection_offer(self, data):
806
+ """处理 Kernel 下发的 slot token,建立附加连接。"""
807
+ slots = data.get("slots", {})
808
+ for slot_str, info in slots.items():
809
+ slot = int(slot_str)
810
+ token = info.get("token", "")
811
+ if not token or slot in self._extra_ws:
812
+ continue
813
+ asyncio.create_task(self._connect_slot(slot, token))
814
+
815
+ async def _connect_slot(self, slot, token):
816
+ """建立单个 slot 附加连接。"""
817
+ ws_url = f"ws://127.0.0.1:{self.kernel_port}/ws"
818
+ try:
819
+ ws = await websockets.connect(ws_url, open_timeout=5, ping_interval=None, close_timeout=5)
820
+ auth_req = {"jsonrpc": "2.0", "id": f"auth-slot-{slot}", "method": "auth", "params": {"token": token}}
821
+ await ws.send(json.dumps(auth_req))
822
+ resp = json.loads(await asyncio.wait_for(ws.recv(), timeout=5))
823
+ if "error" in resp:
824
+ await ws.close()
825
+ return
826
+ self._extra_ws[slot] = ws
827
+ self._extra_ws_tasks[slot] = asyncio.create_task(self._slot_recv_loop(slot, ws))
828
+ print(f"[proxy] Slot {slot} connected")
829
+ except Exception as e:
830
+ print(f"[proxy] Slot {slot} connect failed: {e}")
831
+
832
+ async def _slot_recv_loop(self, slot, ws):
833
+ """附加 slot 接收循环:与主连接平等处理所有消息。"""
834
+ try:
835
+ async for raw in ws:
836
+ try:
837
+ msg = json.loads(raw)
838
+ except (json.JSONDecodeError, TypeError):
839
+ continue
840
+
841
+ try:
842
+ has_method = "method" in msg
843
+ has_id = "id" in msg
844
+ has_result_or_error = "result" in msg or "error" in msg
845
+
846
+ if has_method and not has_id:
847
+ asyncio.create_task(self._handle_event_notification(msg))
848
+ elif has_method and has_id:
849
+ asyncio.create_task(self._handle_rpc_request(ws, msg))
850
+ elif has_id and has_result_or_error:
851
+ self._handle_rpc_response(msg)
852
+ except Exception as e:
853
+ print(f"[proxy] Slot {slot} 消息处理异常: {e}")
854
+ except Exception as e:
855
+ print(f"[proxy] Slot {slot} recv loop exited: {e}")
856
+ finally:
857
+ self._extra_ws.pop(slot, None)
858
+ self._extra_ws_tasks.pop(slot, None)
859
+
860
+ async def _handle_connection_release(self, data):
861
+ """Kernel 请求释放 slot,优雅关闭。"""
862
+ for slot in data.get("slots", []):
863
+ ws = self._extra_ws.pop(slot, None)
864
+ task = self._extra_ws_tasks.pop(slot, None)
865
+ if ws:
866
+ try:
867
+ await ws.close(code=1000, reason="release")
868
+ except Exception:
869
+ pass
870
+ if task:
871
+ task.cancel()
872
+
873
+ # ── 优雅关闭 ──
874
+
875
+ async def _handle_shutdown(self):
876
+ print("[proxy] Received module.shutdown")
877
+ self._shutting_down = True
878
+
879
+ ws = self._ws
880
+ if not ws:
881
+ return
882
+
883
+ # ack
884
+ try:
885
+ await self._publish_event(ws, "module.shutdown.ack", {
886
+ "module_id": "proxy",
887
+ })
888
+ except Exception:
889
+ pass
890
+
891
+ # exiting
892
+ try:
893
+ await self._publish_event(ws, "module.exiting", {
894
+ "module_id": "proxy",
895
+ "type": "passive",
896
+ "reason": "shutdown_requested",
897
+ "restart": "auto",
898
+ "action": "none",
899
+ "timeout": 5.0,
900
+ "restart_delay": 0.0,
901
+ })
902
+ except Exception:
903
+ pass
904
+
905
+ # AID 下线
906
+ try:
907
+ await self.aid_manager.shutdown()
908
+ except Exception as e:
909
+ print(f"[proxy] AID 关闭失败: {e}")
910
+
911
+ # AID 离线事件
912
+ try:
913
+ await self._publish_event(ws, "proxy.aid_offline", {
914
+ "module_id": "proxy",
915
+ })
916
+ except Exception:
917
+ pass
918
+
919
+ # ready(必须在关闭连接之前发送)
920
+ try:
921
+ await self._publish_event(ws, "module.shutdown.ready", {
922
+ "module_id": "proxy",
923
+ })
924
+ except Exception:
925
+ pass
926
+
927
+ # 等待 Kernel 处理 shutdown.ready
928
+ await asyncio.sleep(0.01)
929
+
930
+ # 关闭所有附加连接
931
+ for _s, _w in list(self._extra_ws.items()):
932
+ try:
933
+ await _w.close(code=1000, reason="shutdown")
934
+ except Exception:
935
+ pass
936
+ for _t in self._extra_ws_tasks.values():
937
+ _t.cancel()
938
+ self._extra_ws.clear()
939
+ self._extra_ws_tasks.clear()
940
+
941
+ # 关闭 Kernel WS
942
+ if self._ws:
943
+ try:
944
+ await self._ws.close(code=1000, reason="Graceful shutdown")
945
+ print("[proxy] Kernel WebSocket closed")
946
+ except Exception:
947
+ pass
948
+
949
+ # 触发 uvicorn 退出
950
+ if self._uvicorn_server:
951
+ print("[proxy] Triggering uvicorn shutdown")
952
+ self._uvicorn_server.should_exit = True