@agentunion/kite 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (718) hide show
  1. package/.claude/skills/kite/checklists/feature-checklist.md +496 -0
  2. package/.claude/skills/kite/references/event-patterns.md +180 -0
  3. package/.claude/skills/kite/references/health-check.md +202 -0
  4. package/.claude/skills/kite/references/http-service.md +199 -0
  5. package/.claude/skills/kite/references/module-md-spec.md +172 -0
  6. package/.claude/skills/kite/references/multi-connection.md +147 -0
  7. package/.claude/skills/kite/references/rpc-patterns.md +199 -0
  8. package/.claude/skills/kite/references/shutdown-sequence.md +146 -0
  9. package/.claude/skills/kite/references/stdin-protocol.md +147 -0
  10. package/.claude/skills/kite/references/test-center-integration.md +178 -0
  11. package/.claude/skills/kite/references/ws-lifecycle.md +301 -0
  12. package/.claude/skills/kite/skill.md +272 -0
  13. package/.claude/skills/kite/templates/go/README.md +20 -0
  14. package/.claude/skills/kite/templates/node/entry.js +134 -0
  15. package/.claude/skills/kite/templates/node/module.md +16 -0
  16. package/.claude/skills/kite/templates/node/server.js +351 -0
  17. package/.claude/skills/kite/templates/node/server_http.js +90 -0
  18. package/.claude/skills/kite/templates/python/entry.py +425 -0
  19. package/.claude/skills/kite/templates/python/module.md +26 -0
  20. package/.claude/skills/kite/templates/python/server.py +447 -0
  21. package/.claude/skills/kite/templates/python/server_http.py +433 -0
  22. package/CHANGELOG.md +102 -0
  23. package/cli.js +78 -5
  24. package/core/dependency_checker.py +250 -0
  25. package/core/env_checker.py +586 -0
  26. package/dependencies_lock.json +128 -0
  27. package/docs/05-/347/237/255/344/277/241/350/256/244/350/257/201/344/270/216/347/224/250/346/210/267/344/277/241/346/201/257/346/216/245/345/217/243/346/226/207/346/241/243.md +507 -0
  28. package/docs/ACP/345/215/217/350/256/256/345/205/274/345/256/271/346/226/271/346/241/210.md +138 -0
  29. package/docs/CI/344/270/216AI/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/346/226/271/346/241/210.md +75 -0
  30. package/docs/CLI/345/274/200/345/217/221/350/256/241/345/210/222.md +595 -0
  31. package/docs/ClaudeCode/350/277/234/347/250/213/345/215/217/344/275/234/347/263/273/347/273/237-/346/212/200/346/234/257/350/257/204/344/274/260.md +535 -0
  32. package/docs/ClaudeCode/350/277/234/347/250/213/345/215/217/344/275/234/347/263/273/347/273/237/350/256/276/350/256/241.md +631 -0
  33. package/docs/Evol-App/344/275/277/347/224/250KernelClient/346/224/271/351/200/240/345/256/214/346/210/220.md +342 -0
  34. package/docs/Evol/346/216/247/345/210/266/345/217/260/346/217/222/344/273/266/345/214/226/346/236/266/346/236/204/346/246/202/350/246/201.md +604 -0
  35. package/docs/Evol/346/216/247/345/210/266/345/217/260/346/217/222/344/273/266/345/214/226/346/236/266/346/236/204/350/256/276/350/256/241.md +1708 -0
  36. package/docs/Evol/346/250/241/345/235/227/350/256/276/350/256/241/346/226/271/346/241/210.md +1154 -0
  37. package/docs/Evol/351/241/265/351/235/242/346/217/222/344/273/266/345/214/226-Evol/346/250/241/345/235/227/345/256/236/346/226/275/346/214/207/345/215/227.md +403 -0
  38. package/docs/Evol/351/241/265/351/235/242/346/217/222/344/273/266/345/214/226-/345/244/226/351/203/250/346/250/241/345/235/227/346/216/245/345/205/245/346/214/207/345/215/227.md +468 -0
  39. package/docs/HTTP-RPC/350/277/201/347/247/273/345/210/260WebSocket/350/256/241/345/210/222.md +318 -0
  40. package/docs/INDEX.md +388 -0
  41. package/docs/KITE_DOCS_GUIDE.md +33 -0
  42. package/docs/Kernel-Client-Kite-Token/346/224/257/346/214/201/345/256/236/346/226/275/345/256/214/346/210/220.md +330 -0
  43. package/docs/Kernel/344/270/273/345/212/250Ping/346/234/272/345/210/266-/346/255/243/347/241/256/345/256/236/347/216/260.md +235 -0
  44. package/docs/Kernel/344/270/273/345/212/250Ping/346/234/272/345/210/266/345/256/236/346/226/275/346/200/273/347/273/223.md +204 -0
  45. package/docs/Kite/345/256/211/350/243/205/351/227/256/351/242/230/350/247/243/345/206/263/346/226/271/346/241/210.md +362 -0
  46. package/docs/Kite/346/216/247/345/210/266/345/217/260/346/217/222/344/273/266/345/214/226/346/236/266/346/236/204/350/256/276/350/256/241-/347/273/210/346/236/201/347/233/256/346/240/207.md +721 -0
  47. package/docs/Kite/346/216/247/345/210/266/345/217/260/347/273/237/344/270/200WebSocket/346/224/271/351/200/240/346/226/271/346/241/210.md +821 -0
  48. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/01-/346/241/206/346/236/266/345/256/232/344/275/215.md +12 -0
  49. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/02-/346/240/270/345/277/203/346/246/202/345/277/265.md +341 -0
  50. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/03-/347/263/273/347/273/237/346/236/266/346/236/204.md +257 -0
  51. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/04-/346/250/241/345/235/227/350/247/204/350/214/203.md +263 -0
  52. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/05-/346/240/270/345/277/203/346/265/201/347/250/213-/346/226/260/347/211/210.md +267 -0
  53. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/05-/346/240/270/345/277/203/346/265/201/347/250/213.md +149 -0
  54. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/06-/347/233/256/345/275/225/347/273/223/346/236/204.md +231 -0
  55. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/07-/346/225/260/346/215/256/346/250/241/345/236/213.md +68 -0
  56. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/08-/346/211/251/345/261/225/346/200/247.md +34 -0
  57. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/09-/344/270/216/345/205/267/344/275/223/345/272/224/347/224/250/347/232/204/345/205/263/347/263/273.md +22 -0
  58. package/docs/Kite/346/241/206/346/236/266/350/256/276/350/256/241/README.md +46 -0
  59. package/docs/Kite/347/263/273/347/273/237/345/220/257/345/212/250/346/265/201/347/250/213.md +567 -0
  60. package/docs/Launcher/345/220/257/345/212/250/345/231/250/346/226/207/346/241/243.md +745 -0
  61. package/docs/Polyglot/350/277/220/350/241/214/346/227/266/344/270/216Clawdbot/345/205/274/345/256/271/346/200/247/350/256/276/350/256/241.md +321 -0
  62. package/docs/Redis/344/270/216/346/250/241/345/235/227/345/244/232/345/256/236/344/276/213/346/226/271/346/241/210.md +438 -0
  63. package/docs/Relay-Kite-Token/350/256/244/350/257/201/345/256/236/346/226/275/345/256/214/346/210/220.md +178 -0
  64. package/docs/Relay-Token/346/235/203/351/231/220/351/205/215/347/275/256/351/252/214/350/257/201.md +113 -0
  65. package/docs/Watchdog/345/201/245/345/272/267/346/243/200/346/237/245/344/270/216WebSocket-Ping/346/234/272/345/210/266/345/210/206/346/236/220.md +367 -0
  66. package/docs/Watchdog/350/265/204/346/272/220/347/233/221/346/216/247/347/255/226/347/225/245.md +92 -0
  67. package/docs/WebSocket/346/216/245/346/224/266/345/276/252/347/216/257/346/255/273/351/224/201/351/230/262/350/214/203/350/247/204/350/214/203.md +357 -0
  68. package/docs/WebSocket/350/277/236/346/216/245/351/237/247/346/200/247/344/270/216/351/207/215/350/277/236/346/234/272/345/210/266/345/256/214/346/225/264/346/226/271/346/241/210.md +531 -0
  69. package/docs/WebSocket/350/277/236/346/216/245/351/237/247/346/200/247/346/226/271/346/241/210.md +169 -0
  70. package/docs/WebSocket/351/207/215/350/277/236/346/234/272/345/210/266/346/265/213/350/257/225/346/212/245/345/221/212.md +169 -0
  71. package/docs/WebSocket/351/207/215/350/277/236/351/200/200/351/201/277/346/234/272/345/210/266/346/226/271/346/241/210.md +394 -0
  72. package/docs/Web/346/250/241/345/235/227/344/270/216Evol/346/250/241/345/235/227/351/207/215/346/236/204/345/210/206/346/236/220.md +521 -0
  73. package/docs/audit-api-guide.md +68 -0
  74. package/docs/audit-module-design.md +315 -0
  75. package/docs/audit-module-implementation-summary.md +149 -0
  76. package/docs/llm-context-design.md +52 -0
  77. package/docs/llm-test-enhancement-plan.md +970 -0
  78. package/docs/logs-api-guide.md +42 -0
  79. package/docs/npm/345/214/205Python/347/216/257/345/242/203/347/256/241/347/220/206/346/226/271/346/241/210.md +302 -0
  80. package/docs/npm/345/217/221/345/270/203/344/270/216CLI/344/275/277/347/224/250/346/214/207/345/215/227.md +245 -0
  81. package/docs/stdio/344/270/216/347/253/257/345/217/243/345/217/221/347/216/260/351/207/215/346/236/204.md +480 -0
  82. package/docs/web/346/250/241/345/235/227/344/270/255/350/275/254/346/234/215/345/212/241/350/256/276/350/256/241/346/226/271/346/241/210.md +449 -0
  83. package/docs//344/272/213/344/273/266/345/244/204/347/220/206/346/234/272/345/210/266.md +388 -0
  84. package/docs//344/272/213/344/273/266/345/244/204/347/220/206/350/247/204/350/214/203.md +113 -0
  85. package/docs//344/272/213/344/273/266/350/256/242/351/230/205/351/200/232/351/205/215/347/254/246/350/247/204/350/214/203.md +256 -0
  86. package/docs//344/272/213/344/273/266/351/230/237/345/210/227/345/274/271/346/200/247/347/256/241/347/220/206.md +449 -0
  87. package/docs//344/272/244/344/272/222/345/274/217/347/273/210/347/253/257/346/216/247/345/210/266/346/226/271/346/241/210.md +301 -0
  88. package/docs//344/273/243/347/220/206/345/220/257/345/212/250/345/231/250/344/270/216/345/256/271/345/231/250/345/214/226.md +140 -0
  89. package/docs//344/273/243/347/240/201/347/273/237/350/256/241/345/267/245/345/205/267/344/275/277/347/224/250/350/257/264/346/230/216.md +217 -0
  90. package/docs//344/274/230/351/233/205/351/200/200/345/207/272/350/247/204/350/214/203.md +362 -0
  91. package/docs//344/276/235/350/265/226/347/256/241/347/220/206/350/257/264/346/230/216.md +141 -0
  92. package/docs//344/277/256/345/244/215/346/235/203/351/231/220/351/227/256/351/242/230-evol-RPC/346/235/203/351/231/220.md +268 -0
  93. package/docs//345/210/240/351/231/244kernel-client-example/345/256/214/346/210/220.md +309 -0
  94. package/docs//345/210/240/351/231/244ws-management/345/256/214/346/210/220.md +418 -0
  95. package/docs//345/220/257/345/212/250/344/274/230/345/214/226/346/226/271/346/241/210.md +522 -0
  96. package/docs//345/220/257/345/212/250/344/276/235/350/265/226/344/270/216/346/216/222/345/272/217.md +105 -0
  97. package/docs//345/256/211/350/243/205/350/204/232/346/234/254/345/274/200/345/217/221/346/226/207/346/241/243.md +643 -0
  98. package/docs//345/256/214/346/225/264/345/220/257/345/212/250/346/265/201/347/250/213/350/256/276/350/256/241.md +452 -0
  99. package/docs//345/256/236/347/216/260/350/247/204/345/210/222.md +195 -0
  100. package/docs//345/277/203/350/267/263/346/234/272/345/210/266/351/207/215/346/236/204/346/200/273/347/273/223.md +166 -0
  101. package/docs//346/217/241/346/211/213/350/256/244/350/257/201/346/226/271/346/241/210-/345/256/211/345/205/250/345/256/241/346/237/245.md +176 -0
  102. package/docs//346/217/241/346/211/213/350/256/244/350/257/201/346/226/271/346/241/210.md +908 -0
  103. package/docs//346/226/207/346/241/243/346/233/264/346/226/260/346/270/205/345/215/225.md +83 -0
  104. package/docs//346/227/245/345/277/227/344/270/216/345/274/202/345/270/270/345/244/204/347/220/206/350/247/204/350/214/203.md +829 -0
  105. package/docs//346/227/245/345/277/227/350/260/203/350/257/225/345/256/236/346/210/230/346/214/207/345/215/227.md +25 -0
  106. package/docs//346/236/266/346/236/204/345/200/237/351/211/264/346/214/207/345/215/227.md +977 -0
  107. package/docs//346/236/266/346/236/204/346/224/271/351/200/240-/345/256/214/346/210/220/346/200/273/347/273/223.md +440 -0
  108. package/docs//346/236/266/346/236/204/347/216/260/347/212/266/344/270/216/347/273/210/346/236/201/347/233/256/346/240/207/345/257/271/346/257/224/345/210/206/346/236/220.md +508 -0
  109. package/docs//346/250/241/345/235/227/345/244/232/350/277/236/346/216/245/346/216/247/345/210/266/347/255/226/347/225/245.md +220 -0
  110. package/docs//346/250/241/345/235/227/345/256/211/350/243/205/346/234/272/345/210/266/350/256/276/350/256/241.md +500 -0
  111. package/docs//346/250/241/345/235/227/345/274/200/345/217/221/346/214/207/345/215/227.md +1824 -0
  112. package/docs//346/250/241/345/235/227/347/203/255/346/233/264/346/226/260.md +89 -0
  113. package/docs//346/250/241/345/235/227/350/277/234/347/250/213/351/203/250/347/275/262/345/274/200/345/217/221/350/247/204/350/214/203.md +460 -0
  114. package/docs//346/250/241/345/235/227/351/200/200/345/207/272/346/234/272/345/210/266/345/256/214/346/225/264/346/226/271/346/241/210.md +303 -0
  115. package/docs//346/250/241/345/235/227/351/205/215/347/275/256/345/212/240/350/275/275/344/270/216/347/203/255/351/207/215/350/275/275/350/247/204/350/214/203.md +369 -0
  116. package/docs//346/265/213/350/257/225/344/270/255/345/277/203/346/267/273/345/212/240/346/250/241/345/235/227/346/265/213/350/257/225/346/214/207/345/215/227.md +147 -0
  117. package/docs//347/211/210/346/234/254/351/224/201/345/256/232/347/216/257/345/242/203/347/256/241/347/220/206/346/226/271/346/241/210.md +331 -0
  118. package/docs//347/216/257/345/242/203/345/217/230/351/207/217/344/270/216/350/277/220/350/241/214/346/227/266/347/233/256/345/275/225/350/256/276/350/256/241.md +499 -0
  119. package/docs//347/216/257/345/242/203/347/256/241/347/220/206/345/256/214/346/225/264/346/226/271/346/241/210.md +334 -0
  120. package/docs//350/231/232/346/213/237/346/250/241/345/235/227/344/270/255/350/275/254/346/234/215/345/212/241/345/256/214/346/225/264/350/256/276/350/256/241.md +1496 -0
  121. package/docs//350/231/232/346/213/237/347/216/257/345/242/203/345/267/245/344/275/234/345/216/237/347/220/206.md +163 -0
  122. package/docs//350/256/241/345/210/222/347/256/241/347/220/206/345/231/250/344/275/277/347/224/250/346/214/207/345/215/227.md +196 -0
  123. package/docs//350/256/244/350/257/201/346/250/241/345/235/227/344/270/216Gateway/350/256/276/350/256/241/346/226/271/346/241/210.md +765 -0
  124. package/docs//350/277/234/347/250/213/346/250/241/345/235/227/350/256/276/350/256/241-/346/227/247/347/211/210.md +1117 -0
  125. package/docs//350/277/234/347/250/213/346/250/241/345/235/227/350/256/276/350/256/241.md +451 -0
  126. package/docs//351/207/215/346/236/204/346/234/272/345/210/266/346/270/205/345/215/225.md +192 -0
  127. package/docs//351/223/276/350/267/257/350/277/275/350/270/252/346/226/271/346/241/210.md +242 -0
  128. package/docs//351/231/215/347/272/247/347/255/226/347/225/245/350/256/276/350/256/241/346/226/271/346/241/210.md +618 -0
  129. package/extensions/agents/assistant/entry.py +113 -14
  130. package/extensions/agents/assistant/module.md +27 -22
  131. package/extensions/agents/assistant/server.py +308 -106
  132. package/extensions/channels/acp_channel/entry.py +114 -16
  133. package/extensions/channels/acp_channel/module.md +4 -0
  134. package/extensions/channels/acp_channel/server.py +412 -105
  135. package/extensions/channels/phone_channel/__init__.py +1 -0
  136. package/extensions/channels/phone_channel/entry.py +503 -0
  137. package/extensions/channels/phone_channel/module.md +31 -0
  138. package/extensions/channels/phone_channel/server.py +686 -0
  139. package/extensions/event_hub_bench/entry.py +55 -12
  140. package/extensions/event_hub_bench/module.md +27 -27
  141. package/extensions/services/audit/README.md +134 -0
  142. package/extensions/services/audit/collector.py +73 -0
  143. package/extensions/services/audit/entry.py +444 -0
  144. package/extensions/services/audit/module.md +66 -0
  145. package/extensions/services/audit/query_audit.py +111 -0
  146. package/extensions/services/audit/routes/__init__.py +1 -0
  147. package/extensions/services/audit/routes/routes_audit.py +113 -0
  148. package/extensions/services/audit/schemas/__init__.py +5 -0
  149. package/extensions/services/audit/schemas/audit_event.py +92 -0
  150. package/extensions/services/audit/server.py +542 -0
  151. package/extensions/services/audit/storage.py +95 -0
  152. package/extensions/services/auth/entry.py +1054 -0
  153. package/extensions/services/auth/module.md +31 -0
  154. package/extensions/services/auth/token_store.py +185 -0
  155. package/extensions/services/auth/verifiers/evol_account.py +101 -0
  156. package/extensions/services/auth/verifiers/kite_token.py +38 -0
  157. package/extensions/services/auth/verifiers/pairing_code.py +71 -0
  158. package/extensions/services/backup/entry.py +505 -201
  159. package/extensions/services/backup/module.md +4 -2
  160. package/extensions/services/dataclaw/api/__init__.py +0 -0
  161. package/extensions/services/dataclaw/api/admin.py +367 -0
  162. package/extensions/services/dataclaw/api/copyright.py +175 -0
  163. package/extensions/services/dataclaw/api/credits.py +177 -0
  164. package/extensions/services/dataclaw/api/data.py +179 -0
  165. package/extensions/services/dataclaw/api/demands.py +269 -0
  166. package/extensions/services/dataclaw/api/feeds.py +262 -0
  167. package/extensions/services/dataclaw/api/identity.py +505 -0
  168. package/extensions/services/dataclaw/api/notifications.py +104 -0
  169. package/extensions/services/dataclaw/api/reviews.py +138 -0
  170. package/extensions/services/dataclaw/api/search.py +153 -0
  171. package/extensions/services/dataclaw/api/subscriptions.py +157 -0
  172. package/extensions/services/dataclaw/config.json5 +96 -0
  173. package/extensions/services/dataclaw/core/__init__.py +0 -0
  174. package/extensions/services/dataclaw/core/auth.py +95 -0
  175. package/extensions/services/dataclaw/core/config.py +50 -0
  176. package/extensions/services/dataclaw/core/database.py +70 -0
  177. package/extensions/services/dataclaw/entry.py +416 -0
  178. package/extensions/services/dataclaw/gofeed/351/241/271/347/233/256/346/211/200/346/234/211/346/235/203/350/275/254/347/247/273/346/265/201/347/250/213/350/257/264/346/230/216.md +309 -0
  179. package/extensions/services/dataclaw/migrate.py +283 -0
  180. package/extensions/services/dataclaw/models/__init__.py +0 -0
  181. package/extensions/services/dataclaw/module.md +49 -0
  182. package/extensions/services/dataclaw/requirements.txt +18 -0
  183. package/extensions/services/dataclaw/server.py +759 -0
  184. package/extensions/services/dataclaw/services/__init__.py +0 -0
  185. package/extensions/services/dataclaw/services/agent_service.py +132 -0
  186. package/extensions/services/dataclaw/services/credit_service.py +235 -0
  187. package/extensions/services/dataclaw/services/email_service.py +140 -0
  188. package/extensions/services/dataclaw/services/feed_service.py +259 -0
  189. package/extensions/services/dataclaw/services/notification_service.py +209 -0
  190. package/extensions/services/dataclaw/services/oauth_service.py +275 -0
  191. package/extensions/services/dataclaw/services/pricing.py +102 -0
  192. package/extensions/services/dataclaw/services/quality.py +79 -0
  193. package/extensions/services/dataclaw/services/reputation.py +142 -0
  194. package/extensions/services/dataclaw/services/sms_service.py +174 -0
  195. package/extensions/services/dataclaw/static/css/common.css +853 -0
  196. package/extensions/services/dataclaw/static/css/themes/blue.css +42 -0
  197. package/extensions/services/dataclaw/static/css/themes/dark.css +42 -0
  198. package/extensions/services/dataclaw/static/css/themes/light.css +35 -0
  199. package/extensions/services/dataclaw/static/js/api.js +103 -0
  200. package/extensions/services/dataclaw/static/js/common.js +321 -0
  201. package/extensions/services/dataclaw/static/js/i18n.js +95 -0
  202. package/extensions/services/dataclaw/static/js/pages/admin.js +152 -0
  203. package/extensions/services/dataclaw/static/js/pages/dashboard.js +82 -0
  204. package/extensions/services/dataclaw/static/js/pages/feed-detail.js +180 -0
  205. package/extensions/services/dataclaw/static/js/pages/feed-manage.js +158 -0
  206. package/extensions/services/dataclaw/static/js/theme.js +46 -0
  207. package/extensions/services/dataclaw/static/locales/en-US.json +464 -0
  208. package/extensions/services/dataclaw/static/locales/ja-JP.json +464 -0
  209. package/extensions/services/dataclaw/static/locales/zh-CN.json +464 -0
  210. package/extensions/services/dataclaw/templates/admin/index.html +90 -0
  211. package/extensions/services/dataclaw/templates/base.html +136 -0
  212. package/extensions/services/dataclaw/templates/credits/balance.html +106 -0
  213. package/extensions/services/dataclaw/templates/credits/deposit.html +164 -0
  214. package/extensions/services/dataclaw/templates/credits/history.html +90 -0
  215. package/extensions/services/dataclaw/templates/dashboard.html +52 -0
  216. package/extensions/services/dataclaw/templates/demands/create.html +78 -0
  217. package/extensions/services/dataclaw/templates/demands/detail.html +136 -0
  218. package/extensions/services/dataclaw/templates/demands/list.html +94 -0
  219. package/extensions/services/dataclaw/templates/feeds/create.html +95 -0
  220. package/extensions/services/dataclaw/templates/feeds/detail.html +110 -0
  221. package/extensions/services/dataclaw/templates/feeds/list.html +110 -0
  222. package/extensions/services/dataclaw/templates/feeds/manage.html +88 -0
  223. package/extensions/services/dataclaw/templates/index.html +185 -0
  224. package/extensions/services/dataclaw/templates/login.html +246 -0
  225. package/extensions/services/dataclaw/templates/register.html +164 -0
  226. package/extensions/services/dataclaw/templates/settings/notifications.html +96 -0
  227. package/extensions/services/dataclaw/templates/settings/profile.html +167 -0
  228. package/extensions/services/dataclaw/templates/subscriptions/list.html +64 -0
  229. package/extensions/services/dataclaw/tests/__init__.py +0 -0
  230. package/extensions/services/dataclaw/tests/conftest.py +68 -0
  231. package/extensions/services/dataclaw/tests/integration/__init__.py +0 -0
  232. package/extensions/services/dataclaw/tests/integration/test_workflows.py +239 -0
  233. package/extensions/services/dataclaw/tests/unit/__init__.py +0 -0
  234. package/extensions/services/dataclaw/tests/unit/test_admin.py +70 -0
  235. package/extensions/services/dataclaw/tests/unit/test_copyright.py +63 -0
  236. package/extensions/services/dataclaw/tests/unit/test_credits.py +80 -0
  237. package/extensions/services/dataclaw/tests/unit/test_data.py +98 -0
  238. package/extensions/services/dataclaw/tests/unit/test_demands.py +106 -0
  239. package/extensions/services/dataclaw/tests/unit/test_feeds.py +98 -0
  240. package/extensions/services/dataclaw/tests/unit/test_identity.py +88 -0
  241. package/extensions/services/dataclaw/tests/unit/test_notifications.py +36 -0
  242. package/extensions/services/dataclaw/tests/unit/test_reviews.py +68 -0
  243. package/extensions/services/dataclaw/tests/unit/test_search.py +64 -0
  244. package/extensions/services/dataclaw/tests/unit/test_subscriptions.py +65 -0
  245. package/extensions/services/dataclaw/tests/unit/test_system.py +106 -0
  246. package/extensions/services/dataclaw/utils/__init__.py +0 -0
  247. package/extensions/services/dataclaw/utils/crypto.py +38 -0
  248. package/extensions/services/dataclaw/utils/id_generator.py +52 -0
  249. package/extensions/services/dataclaw/ws/__init__.py +0 -0
  250. package/extensions/services/dataclaw/ws/handler.py +163 -0
  251. package/extensions/services/dataclaw//345/215/217/350/256/2561-/351/241/271/347/233/256/346/235/241/344/273/266/346/216/210/346/235/203/344/270/216/350/202/241/346/235/203/345/257/271/344/273/267/345/215/217/350/256/256.md +243 -0
  252. package/extensions/services/dataclaw//345/215/217/350/256/2562-/351/241/271/347/233/256/350/264/255/344/271/260/346/235/203/344/270/216/345/244/226/345/214/205/345/247/224/346/211/230/345/274/200/345/217/221/345/215/217/350/256/256.md +434 -0
  253. package/extensions/services/evol/__init__.py +1 -0
  254. package/extensions/services/evol/async_http.py +551 -0
  255. package/extensions/services/evol/auth_manager.py +602 -0
  256. package/extensions/services/evol/config.json5 +16 -0
  257. package/extensions/services/evol/config_loader.py +117 -0
  258. package/extensions/services/evol/entry.py +568 -0
  259. package/extensions/services/evol/evol_api.py +969 -0
  260. package/extensions/services/evol/evol_config.json5 +29 -0
  261. package/extensions/services/evol/mfa_totp.py +77 -0
  262. package/extensions/services/evol/migrate_tokens.py +122 -0
  263. package/extensions/services/evol/module.md +150 -0
  264. package/extensions/services/evol/nonce_pool.py +113 -0
  265. package/extensions/services/evol/oauth_manager.py +223 -0
  266. package/extensions/services/evol/pairing.py +251 -0
  267. package/extensions/services/evol/pairing_codes.jsonl +2 -0
  268. package/extensions/services/evol/relay.py +1031 -0
  269. package/extensions/services/evol/relay_config.json5 +85 -0
  270. package/extensions/services/evol/routes/__init__.py +1 -0
  271. package/extensions/services/evol/routes/routes_llm.py +231 -0
  272. package/extensions/services/evol/routes/routes_rpc.py +90 -0
  273. package/extensions/services/evol/routes/routes_test.py +68 -0
  274. package/extensions/services/evol/server.py +2426 -0
  275. package/extensions/services/evol/static/assets/CommissionView-Cs_ys6Gm.js +1 -0
  276. package/extensions/services/evol/static/assets/CommissionView-DACet_Oo.css +1 -0
  277. package/extensions/services/evol/static/assets/IframePage-DbO11U9G.js +1 -0
  278. package/extensions/services/evol/static/assets/IframePage-c572lT8i.css +1 -0
  279. package/extensions/services/evol/static/assets/TeamDetailView-DULrGD7k.css +1 -0
  280. package/extensions/services/evol/static/assets/TeamDetailView-gy_MBEqG.js +139 -0
  281. package/extensions/services/evol/static/assets/element-plus-Bd7pZkkM.js +63 -0
  282. package/extensions/services/evol/static/assets/index-CmMONKzG.css +1 -0
  283. package/extensions/services/evol/static/assets/index-D44bBe__.js +2 -0
  284. package/extensions/services/evol/static/assets/vue-vendor-DtF-__I4.js +29 -0
  285. package/extensions/services/evol/static/index.html +16 -0
  286. package/extensions/services/evol/static/logo.png +0 -0
  287. package/extensions/services/evol/stats_manager.py +243 -0
  288. package/extensions/services/evol/web/README.md +89 -0
  289. package/extensions/services/evol/web/build.bat +44 -0
  290. package/extensions/services/evol/web/index.html +13 -0
  291. package/extensions/services/evol/web/package-lock.json +1718 -0
  292. package/extensions/services/evol/web/package.json +26 -0
  293. package/extensions/services/evol/web/public/logo.png +0 -0
  294. package/extensions/services/evol/web/src/App.vue +7 -0
  295. package/extensions/services/evol/web/src/components/layout/AppHeader.vue +202 -0
  296. package/extensions/services/evol/web/src/components/layout/AppLayout.vue +61 -0
  297. package/extensions/services/evol/web/src/components/layout/AppSidebar.vue +115 -0
  298. package/extensions/services/evol/web/src/components/login/LoginPage.vue +271 -0
  299. package/extensions/services/evol/web/src/components/team/AddMemberModal.vue +181 -0
  300. package/extensions/services/evol/web/src/components/team/GroupTreeNode.vue +156 -0
  301. package/extensions/services/evol/web/src/components/team/TeamAlertConfig.vue +221 -0
  302. package/extensions/services/evol/web/src/components/team/TeamBillModal.vue +165 -0
  303. package/extensions/services/evol/web/src/components/team/TeamMembersAndGroups.vue +499 -0
  304. package/extensions/services/evol/web/src/components/team/TeamStatsPanel.vue +907 -0
  305. package/extensions/services/evol/web/src/components/team/TreeNode.vue +331 -0
  306. package/extensions/services/evol/web/src/components/team/stats/StatsExportProgress.vue +44 -0
  307. package/extensions/services/evol/web/src/components/team/stats/StatsHeader.vue +89 -0
  308. package/extensions/services/evol/web/src/components/team/stats/StatsMemberDetail.vue +415 -0
  309. package/extensions/services/evol/web/src/components/team/stats/StatsSummary.vue +42 -0
  310. package/extensions/services/evol/web/src/components/team/stats/helpers.ts +195 -0
  311. package/extensions/services/evol/web/src/components/team/stats/stats.css +741 -0
  312. package/extensions/services/evol/web/src/components/team/stats/useStatsApi.ts +114 -0
  313. package/extensions/services/evol/web/src/components/team/stats/useStatsCharts.ts +242 -0
  314. package/extensions/services/evol/web/src/components/team/stats/useStatsExport.ts +232 -0
  315. package/extensions/services/evol/web/src/composables/useFormatters.ts +42 -0
  316. package/extensions/services/evol/web/src/composables/useTheme.ts +52 -0
  317. package/extensions/services/evol/web/src/env.d.ts +7 -0
  318. package/extensions/services/evol/web/src/i18n/en.ts +361 -0
  319. package/extensions/services/evol/web/src/i18n/index.ts +36 -0
  320. package/extensions/services/evol/web/src/i18n/zh.ts +379 -0
  321. package/extensions/services/evol/web/src/main.ts +21 -0
  322. package/extensions/services/evol/web/src/router/index.ts +81 -0
  323. package/extensions/services/evol/web/src/services/kernel-client.ts +406 -0
  324. package/extensions/services/evol/web/src/stores/auth.ts +189 -0
  325. package/extensions/services/evol/web/src/stores/connection.ts +134 -0
  326. package/extensions/services/evol/web/src/stores/pages.ts +79 -0
  327. package/extensions/services/evol/web/src/styles/base.css +213 -0
  328. package/extensions/services/evol/web/src/styles/variables.css +138 -0
  329. package/extensions/services/evol/web/src/types/rpc.ts +35 -0
  330. package/extensions/services/evol/web/src/types/token.ts +87 -0
  331. package/extensions/services/evol/web/src/views/AccountView.vue +1532 -0
  332. package/extensions/services/evol/web/src/views/AiServiceView.vue +219 -0
  333. package/extensions/services/evol/web/src/views/CommissionView.vue +1220 -0
  334. package/extensions/services/evol/web/src/views/CreditsView.vue +131 -0
  335. package/extensions/services/evol/web/src/views/EndpointView.vue +163 -0
  336. package/extensions/services/evol/web/src/views/IframePage.vue +120 -0
  337. package/extensions/services/evol/web/src/views/TeamDetailView.vue +473 -0
  338. package/extensions/services/evol/web/src/views/TeamView.vue +332 -0
  339. package/extensions/services/evol/web/tsconfig.json +31 -0
  340. package/extensions/services/evol/web/tsconfig.node.json +10 -0
  341. package/extensions/services/evol/web/vite.config.ts +49 -0
  342. package/extensions/services/evolmem/__init__.py +0 -0
  343. package/extensions/services/evolmem/entry.py +387 -0
  344. package/extensions/services/evolmem/hooks/__init__.py +0 -0
  345. package/extensions/services/evolmem/hooks/assistant_stop.py +228 -0
  346. package/extensions/services/evolmem/hooks/common.py +76 -0
  347. package/extensions/services/evolmem/hooks/pre_tool_use.py +56 -0
  348. package/extensions/services/evolmem/hooks/session_end.py +133 -0
  349. package/extensions/services/evolmem/hooks/session_start.py +229 -0
  350. package/extensions/services/evolmem/hooks/user_prompt.py +122 -0
  351. package/extensions/services/evolmem/module.md +48 -0
  352. package/extensions/services/evolmem/prompts/00-server-info.md +28 -0
  353. package/extensions/services/evolmem/prompts/01-behavior.md +46 -0
  354. package/extensions/services/evolmem/prompts/02-summary-format.md +112 -0
  355. package/extensions/services/evolmem/prompts/03-file-query.md +92 -0
  356. package/extensions/services/evolmem/prompts/04-topic-stats.md +11 -0
  357. package/extensions/services/evolmem/prompts/05-recent-topics.md +84 -0
  358. package/extensions/services/evolmem/scripts/__init__.py +0 -0
  359. package/extensions/services/evolmem/scripts/extract_keywords.py +40 -0
  360. package/extensions/services/evolmem/scripts/search_topics.py +91 -0
  361. package/extensions/services/evolmem/server.py +641 -0
  362. package/extensions/services/gateway/entry.py +964 -0
  363. package/extensions/services/gateway/module.md +29 -0
  364. package/extensions/services/gateway/nonce_pool.py +65 -0
  365. package/extensions/services/gateway/relay.py +133 -0
  366. package/extensions/services/gateway/ws_server.py +285 -0
  367. package/extensions/services/kite_console/auth_manager.py +603 -0
  368. package/extensions/services/kite_console/config.json5 +19 -0
  369. package/extensions/services/kite_console/config_loader.py +117 -0
  370. package/extensions/services/kite_console/entry.py +528 -0
  371. package/extensions/services/kite_console/evol_api.py +179 -0
  372. package/extensions/services/kite_console/evol_config.json5 +29 -0
  373. package/extensions/services/kite_console/mfa_totp.py +77 -0
  374. package/extensions/services/kite_console/migrate_tokens.py +122 -0
  375. package/extensions/services/kite_console/module.md +37 -0
  376. package/extensions/services/kite_console/nonce_pool.py +113 -0
  377. package/extensions/services/kite_console/oauth_manager.py +223 -0
  378. package/extensions/services/kite_console/pairing.py +280 -0
  379. package/extensions/services/kite_console/pairing_codes.jsonl +2 -0
  380. package/extensions/services/kite_console/relay.py +1350 -0
  381. package/extensions/services/kite_console/relay_config.json5 +96 -0
  382. package/extensions/services/kite_console/routes/__init__.py +1 -0
  383. package/extensions/services/kite_console/routes/routes_llm.py +231 -0
  384. package/extensions/services/kite_console/routes/routes_proxy.py +115 -0
  385. package/extensions/services/kite_console/routes/routes_rpc.py +89 -0
  386. package/extensions/services/kite_console/routes/routes_test.py +68 -0
  387. package/extensions/services/kite_console/server.py +1742 -0
  388. package/extensions/services/kite_console/static/css/style.css +1854 -0
  389. package/extensions/services/kite_console/static/index.html +1524 -0
  390. package/extensions/services/kite_console/static/js/dialog.js +292 -0
  391. package/extensions/services/kite_console/static/js/evol-app.js +7740 -0
  392. package/extensions/services/kite_console/static/js/evol-app.js.backup +2777 -0
  393. package/extensions/services/kite_console/static/js/kernel-client.js +560 -0
  394. package/extensions/services/kite_console/static/js/kernel-client.js.backup +434 -0
  395. package/extensions/services/kite_console/static/js/registry-tests.js +592 -0
  396. package/extensions/services/kite_console/static/js/tests/ARCHITECTURE.md +67 -0
  397. package/extensions/services/kite_console/static/js/tests/README.md +140 -0
  398. package/extensions/services/kite_console/static/js/tests/index.js +161 -0
  399. package/extensions/services/kite_console/static/js/tests/integration/auth.js +120 -0
  400. package/extensions/services/kite_console/static/js/tests/integration/channel-interaction.js +188 -0
  401. package/extensions/services/kite_console/static/js/tests/integration/elastic-connection.js +115 -0
  402. package/extensions/services/kite_console/static/js/tests/integration/full-workflow.js +43 -0
  403. package/extensions/services/kite_console/static/js/tests/integration/multi-instance.js +304 -0
  404. package/extensions/services/kite_console/static/js/tests/integration/nested-rpc.js +266 -0
  405. package/extensions/services/kite_console/static/js/tests/integration/pingpong.js +25 -0
  406. package/extensions/services/kite_console/static/js/tests/integration/redis.js +227 -0
  407. package/extensions/services/kite_console/static/js/tests/integration/registry-core.js +52 -0
  408. package/extensions/services/kite_console/static/js/tests/integration/remote-deploy.js +85 -0
  409. package/extensions/services/kite_console/static/js/tests/integration/require-init.js +96 -0
  410. package/extensions/services/kite_console/static/js/tests/integration/scaling-control.js +193 -0
  411. package/extensions/services/kite_console/static/js/tests/integration/trace.js +109 -0
  412. package/extensions/services/kite_console/static/js/tests/modules/acp_channel.js +339 -0
  413. package/extensions/services/kite_console/static/js/tests/modules/auth.js +96 -0
  414. package/extensions/services/kite_console/static/js/tests/modules/backup.js +49 -0
  415. package/extensions/services/kite_console/static/js/tests/modules/gateway.js +41 -0
  416. package/extensions/services/kite_console/static/js/tests/modules/kernel.js +90 -0
  417. package/extensions/services/kite_console/static/js/tests/modules/launcher.js +75 -0
  418. package/extensions/services/kite_console/static/js/tests/modules/multi_instance.js +129 -0
  419. package/extensions/services/kite_console/static/js/tests/modules/phone_channel.js +364 -0
  420. package/extensions/services/kite_console/static/js/tests/modules/redis.js +178 -0
  421. package/extensions/services/kite_console/static/js/tests/modules/watchdog.js +60 -0
  422. package/extensions/services/kite_console/static/js/tests/modules/web.js +70 -0
  423. package/extensions/services/kite_console/static/js/tests/test-runner.js +123 -0
  424. package/extensions/services/kite_console/static/js/virtual-list.js +200 -0
  425. package/extensions/services/kite_console/static/pairing.html +248 -0
  426. package/extensions/services/kite_console/static/test_kernel_client_token.html +352 -0
  427. package/extensions/services/kite_console/static/test_registry.html +262 -0
  428. package/extensions/services/kite_console/static/test_relay.html +462 -0
  429. package/extensions/services/kite_console/stats_manager.py +247 -0
  430. package/extensions/services/logs/README.md +215 -0
  431. package/extensions/services/logs/api_logger.py +37 -0
  432. package/extensions/services/logs/baseline.py +121 -0
  433. package/extensions/services/logs/cleaner.py +76 -0
  434. package/extensions/services/logs/entry.py +449 -0
  435. package/extensions/services/logs/formatter.py +129 -0
  436. package/extensions/services/logs/module.md +38 -0
  437. package/extensions/services/logs/quick_diagnostic.py +128 -0
  438. package/extensions/services/logs/routes/__init__.py +1 -0
  439. package/extensions/services/logs/routes/routes_logs.py +218 -0
  440. package/extensions/services/logs/routes/routes_logs.py.backup +173 -0
  441. package/extensions/services/logs/scanner.py +100 -0
  442. package/extensions/services/logs/searcher.py +263 -0
  443. package/extensions/services/logs/server.py +553 -0
  444. package/extensions/services/logs.zip +0 -0
  445. package/extensions/services/model_service/config.json5 +30 -0
  446. package/extensions/services/model_service/entry.py +633 -162
  447. package/extensions/services/model_service/module.md +11 -2
  448. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  449. package/extensions/services/proxy/__init__.py +0 -0
  450. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  451. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  452. package/extensions/services/proxy/agentcp/README.md +260 -0
  453. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  454. package/extensions/services/proxy/agentcp/agent.py +4 -0
  455. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  456. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  457. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  458. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  459. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  460. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  461. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  462. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  463. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  464. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  465. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  466. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  467. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  468. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  469. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  470. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  471. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  472. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  473. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  474. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  475. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  476. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  477. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  478. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  479. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  480. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  481. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  482. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  483. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  484. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  485. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  486. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  487. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  488. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  489. package/extensions/services/proxy/agentcp/message.py +149 -0
  490. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  491. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  492. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  493. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  494. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  495. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  496. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  497. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  498. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  499. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  500. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  501. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  502. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  503. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  504. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  505. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  506. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  507. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  508. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  509. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  510. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  511. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  512. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  513. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  514. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  515. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  516. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  517. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  518. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  519. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  520. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  521. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  522. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  523. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  524. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  525. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  526. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  527. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  528. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  529. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  530. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  531. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  532. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  533. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  534. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  535. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  536. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  537. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  538. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  539. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  540. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  541. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  542. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  543. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  544. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  545. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  546. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  547. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  548. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  549. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  550. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  551. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  552. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  553. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  554. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  555. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  556. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  557. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  558. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  559. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  560. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  561. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  562. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  563. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  564. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  565. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  566. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  567. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  568. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  569. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  570. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  571. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  572. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  573. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  574. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  575. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  576. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  577. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  578. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  579. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  580. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  581. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  582. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  583. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  584. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  585. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  586. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  587. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  588. package/extensions/services/proxy/aid_manager.py +419 -0
  589. package/extensions/services/proxy/auth_bridge.py +182 -0
  590. package/extensions/services/proxy/config_store.py +79 -0
  591. package/extensions/services/proxy/entry.py +528 -0
  592. package/extensions/services/proxy/evol/__init__.py +1 -0
  593. package/extensions/services/proxy/evol/config.py +37 -0
  594. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  595. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  596. package/extensions/services/proxy/evol/log.py +28 -0
  597. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  598. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  599. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +96 -0
  600. package/extensions/services/proxy/evol/presenter/configPresenter.py +234 -0
  601. package/extensions/services/proxy/evol/presenter/userPresenter.py +71 -0
  602. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  603. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3434 -0
  604. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  605. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  606. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  607. package/extensions/services/proxy/evol/version.py +24 -0
  608. package/extensions/services/proxy/module.md +151 -0
  609. package/extensions/services/proxy/server.py +952 -0
  610. package/extensions/services/redis/ALIGNMENT_CHECKLIST.md +121 -0
  611. package/extensions/services/redis/ALIGNMENT_STATUS.md +548 -0
  612. package/extensions/services/redis/config.json5 +8 -0
  613. package/extensions/services/redis/entry.py +1509 -0
  614. package/extensions/services/redis/entry.py.backup +405 -0
  615. package/extensions/services/redis/module.md +48 -0
  616. package/extensions/services/redis/redis_builtin.py +332 -0
  617. package/extensions/services/redis/redis_external.py +164 -0
  618. package/extensions/services/testUi/entry.py +446 -0
  619. package/extensions/services/testUi/module.md +18 -0
  620. package/extensions/services/testUi/ui/cards.html +131 -0
  621. package/extensions/services/testUi/ui/index.html +22 -0
  622. package/extensions/services/testUi/ui/particles.html +143 -0
  623. package/extensions/services/watchdog/entry.py +1258 -767
  624. package/extensions/services/watchdog/module.md +3 -0
  625. package/extensions/services/watchdog/monitor.py +483 -75
  626. package/extensions/services/web/auth_manager.py +602 -0
  627. package/extensions/services/web/config.json5 +11 -0
  628. package/extensions/services/web/entry.py +598 -478
  629. package/extensions/services/web/mfa_totp.py +77 -0
  630. package/extensions/services/web/module.md +17 -14
  631. package/extensions/services/web/nonce_pool.py +113 -0
  632. package/extensions/services/web/oauth_manager.py +223 -0
  633. package/extensions/services/web/pairing.py +3 -2
  634. package/extensions/services/web/pairing_codes.jsonl +1 -0
  635. package/extensions/services/web/relay.py +442 -63
  636. package/extensions/services/web/relay_config.json5 +1 -2
  637. package/extensions/services/web/routes/routes_rpc.py +6 -6
  638. package/extensions/services/web/server.py +380 -181
  639. package/extensions/services/web/static/index.html +1752 -1738
  640. package/extensions/services/web/static/js/app.js +32 -0
  641. package/extensions/services/web/static/js/kernel-client.js +48 -9
  642. package/extensions/services/web/static/js/token-manager.js +10 -10
  643. package/extensions/services/web/vendor/bluetooth/audio.py +1 -1
  644. package/extensions/services/web/vendor/config.py +2 -2
  645. package/extensions/services/web/vendor/storage/identity.py +1 -1
  646. package/kernel/entry.py +77 -23
  647. package/kernel/event_hub.py +1122 -74
  648. package/kernel/module.md +26 -1
  649. package/kernel/registry_store.py +209 -36
  650. package/kernel/rpc_router.py +1400 -465
  651. package/kernel/server.py +1084 -108
  652. package/kite_cli/builders/__init__.py +4 -0
  653. package/kite_cli/builders/base.py +67 -0
  654. package/kite_cli/builders/custom.py +31 -0
  655. package/kite_cli/builders/detector.py +56 -0
  656. package/kite_cli/builders/go.py +34 -0
  657. package/kite_cli/builders/gradle.py +41 -0
  658. package/kite_cli/builders/maven.py +36 -0
  659. package/kite_cli/builders/npm.py +44 -0
  660. package/kite_cli/builders/python.py +37 -0
  661. package/kite_cli/commands/BUILD_GUIDE.md +109 -0
  662. package/kite_cli/commands/build.py +142 -0
  663. package/kite_cli/commands/check.py +60 -0
  664. package/kite_cli/commands/config.py +156 -0
  665. package/kite_cli/commands/deps.py +58 -0
  666. package/kite_cli/commands/deps_install.py +67 -0
  667. package/kite_cli/commands/disable.py +162 -0
  668. package/kite_cli/commands/enable.py +162 -0
  669. package/kite_cli/commands/env_check.py +45 -0
  670. package/kite_cli/commands/export.py +96 -0
  671. package/kite_cli/commands/import_cmd.py +110 -0
  672. package/kite_cli/commands/install.py +50 -23
  673. package/kite_cli/commands/install_skill.py +107 -0
  674. package/kite_cli/commands/list.py +128 -31
  675. package/kite_cli/commands/outdated.py +202 -0
  676. package/kite_cli/commands/prepare.py +49 -0
  677. package/kite_cli/commands/search.py +33 -17
  678. package/kite_cli/commands/update.py +115 -2
  679. package/kite_cli/commands/venv_setup.py +56 -0
  680. package/kite_cli/commands/why.py +48 -0
  681. package/kite_cli/core/config_manager.py +145 -0
  682. package/kite_cli/core/downloader.py +32 -2
  683. package/kite_cli/main.py +179 -5
  684. package/kite_cli/utils/colors.py +153 -0
  685. package/kite_cli/utils/dependency_graph.py +209 -0
  686. package/kite_cli/utils/process.py +55 -0
  687. package/kite_cli/utils/progress.py +207 -0
  688. package/kite_cli/utils/table.py +101 -0
  689. package/launcher/count_lines.py +192 -43
  690. package/launcher/entry.py +4543 -2517
  691. package/launcher/logging_setup.py +54 -1
  692. package/launcher/module.md +37 -2
  693. package/launcher/module_scanner.py +103 -20
  694. package/launcher/process_manager.py +355 -76
  695. package/main.py +10 -1
  696. package/package.json +11 -1
  697. package/python_version.json +4 -0
  698. package/requirements.txt +41 -0
  699. package/scripts/auto-fix-deps.py +128 -0
  700. package/scripts/env-manager.js +351 -0
  701. package/scripts/final-test.js +78 -0
  702. package/scripts/python-env.js +79 -0
  703. package/scripts/scan_dependencies.py +461 -0
  704. package/scripts/setup-python-env.js +700 -0
  705. package/scripts/test-alluser.js +48 -0
  706. package/scripts/test-different-version.js +86 -0
  707. package/scripts/test-direct.js +63 -0
  708. package/scripts/test-extract-installer.js +28 -0
  709. package/scripts/test-install-log.js +54 -0
  710. package/scripts/test-installer.js +39 -0
  711. package/scripts/test-integration.js +250 -0
  712. package/scripts/test-real-install.js +210 -0
  713. package/scripts/test-targetdir.js +49 -0
  714. package/scripts/test-venv-real.js +47 -0
  715. package/scripts/test-venv-simple.js +57 -0
  716. package/scripts/test-wait.js +49 -0
  717. package/scripts/test-with-log.js +63 -0
  718. package/extensions/services/web/config.yaml +0 -149
@@ -18,7 +18,9 @@ SYSTEM_BROADCAST_EVENTS = {
18
18
  "module.ready", "module.registered", "module.started", "module.stopped",
19
19
  "module.crashed", "module.exiting", "module.offline",
20
20
  "module.shutdown.ack", "module.shutdown.ready",
21
+ "module.degraded", "module.recovered",
21
22
  "system.ready", "registry.updated",
23
+ "system.instance.started", "system.instance.stopped",
22
24
  }
23
25
 
24
26
  # Module health states
@@ -35,10 +37,10 @@ CRITICAL = "critical"
35
37
  class ModuleStatus:
36
38
  """Track health and resource state for a single module."""
37
39
 
38
- def __init__(self, module_id: str, api_endpoint: str, health_endpoint: str, pid: int = None):
40
+ def __init__(self, module_id: str, base_url: str, health_path: str, pid: int = None):
39
41
  self.module_id = module_id
40
- self.api_endpoint = api_endpoint
41
- self.health_endpoint = health_endpoint
42
+ self.base_url = base_url
43
+ self.health_path = health_path
42
44
  self.pid = pid
43
45
  # Health check state
44
46
  self.state: str = UNKNOWN
@@ -55,6 +57,8 @@ class ModuleStatus:
55
57
  self.memory_samples: list[float] = [] # last 5 memory_rss samples
56
58
  self.recovery_since: float = 0 # when recovery observation started
57
59
  self.last_metrics: dict = {}
60
+ # Startup metrics
61
+ self.startup_time: float = 0 # module startup time in seconds (from module.ready event)
58
62
 
59
63
 
60
64
  class HealthMonitor:
@@ -63,9 +67,13 @@ class HealthMonitor:
63
67
  # Thresholds
64
68
  HEALTH_TIMEOUT = 5 # HTTP timeout per health check
65
69
  FAIL_THRESHOLD = 3 # consecutive failures before restart
66
- MAX_RESTARTS = 3 # max restarts before giving up on a module
70
+ MAX_STARTUP_RETRIES = 5 # 启动失败(未到 ready)最多重试 5
67
71
  ALERT_AFTER_RESTARTS = 2 # publish alert after this many restarts
68
72
 
73
+ # Crash restart policy
74
+ MAX_RESTART_DELAY = 600 # 最大重启延迟:10分钟
75
+ STABLE_RUN_TIME = 600 # 稳定运行时间:10分钟(超过此时间才重置崩溃计数)
76
+
69
77
  # Resource thresholds
70
78
  MEMORY_WARNING = 70 # memory_percent > 70% → warning
71
79
  MEMORY_CRITICAL = 85 # memory_percent > 85% → critical
@@ -82,9 +90,12 @@ class HealthMonitor:
82
90
  self.kernel_port = kernel_port
83
91
  self.publish_event = publish_event # async callable(event_dict)
84
92
  self.rpc_call = None # set by entry.py: async callable(method, params)
93
+ self.request_exit = None # set by entry.py: callable(exit_code) — 请求进程退出
85
94
  self.modules: dict[str, ModuleStatus] = {}
86
95
  self._running = False
87
96
  self._psutil = None # lazy import
97
+ self._dirty = False # 模块上下线标记,定时器到期时检查
98
+ self._first_check_done = False # 首次检查是否完成
88
99
 
89
100
  # Restart decision state (module.exiting / module.stopped / module.ready)
90
101
  self._exit_intents: dict[str, str] = {} # module_id -> action from module.exiting
@@ -94,7 +105,11 @@ class HealthMonitor:
94
105
  self._system_shutting_down = False
95
106
  self._system_ready = False
96
107
  self._system_ready_event = asyncio.Event()
108
+ self._monitor_enabled = False # 监控开关,由 Launcher 通过 RPC 控制
97
109
  self._crash_counts: dict[str, int] = {} # module_id -> consecutive crash count
110
+ self._restart_tasks: dict[str, asyncio.Task] = {} # module_id -> pending restart task
111
+ self._module_ready_times: dict[str, float] = {} # module_id -> last ready timestamp
112
+ self._module_ever_ready: dict[str, bool] = {} # module_id -> has ever been ready
98
113
 
99
114
  # Launcher loss tracking
100
115
  self._launcher_offline = False
@@ -102,6 +117,23 @@ class HealthMonitor:
102
117
  self._launcher_restart_requested = False # True if launcher requested restart
103
118
  self._launcher_startup_info = None # Startup info from launcher (python, argv, cwd, env)
104
119
 
120
+ # 降级状态跟踪:module_id -> {level, reason, degraded_at, ...}
121
+ self._degraded_modules: dict[str, dict] = {}
122
+ # 超长降级检测间隔(秒)
123
+ self._degraded_timeout = 300 # 5 分钟
124
+
125
+ # 健康事件持久化
126
+ self._health_events_dir = ""
127
+ self._health_events_path = ""
128
+ self._health_events_max = 10000 # 单文件最大条数
129
+ instance_dir = os.environ.get("KITE_INSTANCE_DIR", "")
130
+ if instance_dir:
131
+ self._health_events_dir = os.path.join(instance_dir, "health")
132
+ os.makedirs(self._health_events_dir, exist_ok=True)
133
+ self._health_events_path = os.path.join(self._health_events_dir, "events.jsonl")
134
+ # RPC 调用 Kernel 的回调(由 entry.py 设置)
135
+ self.rpc_call_kernel = None # async callable(method, params) → response
136
+
105
137
  # ── Module discovery ──
106
138
 
107
139
  async def discover_modules(self):
@@ -121,41 +153,53 @@ class HealthMonitor:
121
153
  print(f"[watchdog] Launcher RPC failed: {e}")
122
154
  return
123
155
 
124
- # Step 2: Get health endpoints from Registry via RPC
125
- health_map = {} # name -> {api_endpoint, health_endpoint}
156
+ # Step 2: Get health info from Registry via RPC
157
+ health_map = {} # name -> {base_url, health_path}
126
158
  try:
127
- resp = await self.rpc_call("registry.lookup", {"field": "health_endpoint"})
159
+ # base_url:有 base_url 的模块才有 HTTP 服务
160
+ resp = await self.rpc_call("registry.lookup", {"field": "base_url"})
128
161
  result = resp.get("result", {})
129
162
  for entry in result.get("results", []):
130
163
  mid = entry.get("module", "")
131
164
  if mid in monitored:
132
165
  health_map[mid] = {
133
- "api_endpoint": entry.get("api_endpoint", ""),
134
- "health_endpoint": entry.get("value", "/health"),
166
+ "base_url": entry.get("value", ""),
167
+ "health_path": "/health",
135
168
  }
136
169
  except Exception:
137
170
  pass
138
171
 
172
+ try:
173
+ # 查 health_path:覆盖默认值
174
+ resp = await self.rpc_call("registry.lookup", {"field": "health_path"})
175
+ result = resp.get("result", {})
176
+ for entry in result.get("results", []):
177
+ mid = entry.get("module", "")
178
+ if mid in health_map:
179
+ health_map[mid]["health_path"] = entry.get("value", "/health")
180
+ except Exception:
181
+ pass
182
+
139
183
  # Step 3: Sync module list
140
184
  seen = set()
141
185
  for mid, pid in monitored.items():
142
186
  seen.add(mid)
143
187
  h = health_map.get(mid, {})
144
- ep = h.get("api_endpoint", "")
145
- hp = h.get("health_endpoint", "/health")
188
+ ep = h.get("base_url", "")
189
+ hp = h.get("health_path", "/health")
146
190
  if mid not in self.modules:
147
191
  self.modules[mid] = ModuleStatus(
148
192
  module_id=mid,
149
- api_endpoint=ep,
150
- health_endpoint=hp,
193
+ base_url=ep,
194
+ health_path=hp,
151
195
  pid=pid,
152
196
  )
153
197
  else:
154
198
  self.modules[mid].pid = pid
155
199
  # Refresh endpoints once Registry has them
156
200
  if ep:
157
- self.modules[mid].api_endpoint = ep
158
- self.modules[mid].health_endpoint = hp
201
+ self.modules[mid].base_url = ep
202
+ self.modules[mid].health_path = hp
159
203
 
160
204
  for mid in list(self.modules):
161
205
  if mid not in seen:
@@ -165,7 +209,7 @@ class HealthMonitor:
165
209
 
166
210
  async def _check_one(self, status: ModuleStatus):
167
211
  """Check a single module's health via RPC."""
168
- if not status.api_endpoint:
212
+ if not status.base_url:
169
213
  return # Not yet registered in Registry, will be picked up on next discover
170
214
 
171
215
  status.last_check = time.time()
@@ -217,9 +261,10 @@ class HealthMonitor:
217
261
  "error": status.last_error,
218
262
  })
219
263
 
220
- # Restart if threshold reached
264
+ # Restart if threshold reached (but NOT if module is degraded — degraded ≠ crashed)
221
265
  if (status.fail_count >= self.FAIL_THRESHOLD
222
- and status.restarted_count < self.MAX_RESTARTS):
266
+ and status.restarted_count < self.MAX_RESTARTS
267
+ and not self.is_degraded(status.module_id)):
223
268
  await self._restart_module(status)
224
269
 
225
270
  # ── Restart via Launcher API ──
@@ -238,6 +283,10 @@ class HealthMonitor:
238
283
  status.restarted_count += 1
239
284
  status.fail_count = 0
240
285
  print(f"[watchdog] {mid} restart requested")
286
+ await self._persist_health_event("watchdog.module.restarted", {
287
+ "module": mid, "restarted_count": status.restarted_count,
288
+ "reason": "resource_critical" if status.resource_state == CRITICAL else "restart",
289
+ })
241
290
  if status.restarted_count >= self.ALERT_AFTER_RESTARTS:
242
291
  await self._publish("watchdog.alert", {
243
292
  "module_id": mid,
@@ -251,10 +300,8 @@ class HealthMonitor:
251
300
 
252
301
  # ── Resource monitoring ──
253
302
 
254
- def _collect_metrics(self, status: ModuleStatus) -> dict | None:
255
- """Collect CPU/memory metrics for a module via psutil."""
256
- if not status.pid:
257
- return None
303
+ def _collect_metrics_sync(self, pid: int) -> dict | None:
304
+ """Synchronous metrics collection runs in thread pool to avoid blocking event loop."""
258
305
  if not self._psutil:
259
306
  try:
260
307
  import psutil
@@ -262,11 +309,11 @@ class HealthMonitor:
262
309
  except ImportError:
263
310
  return None
264
311
  try:
265
- proc = self._psutil.Process(status.pid)
312
+ proc = self._psutil.Process(pid)
266
313
  return {
267
314
  "memory_rss": round(proc.memory_info().rss / 1024 / 1024, 1),
268
315
  "memory_percent": round(proc.memory_percent(), 1),
269
- "cpu_percent": round(proc.cpu_percent(interval=0.1), 1),
316
+ "cpu_percent": round(proc.cpu_percent(interval=0), 1),
270
317
  "thread_count": proc.num_threads(),
271
318
  }
272
319
  except Exception:
@@ -274,7 +321,10 @@ class HealthMonitor:
274
321
 
275
322
  async def _check_resources(self, status: ModuleStatus):
276
323
  """Collect metrics and evaluate resource state."""
277
- metrics = self._collect_metrics(status)
324
+ if not status.pid:
325
+ return
326
+ loop = asyncio.get_event_loop()
327
+ metrics = await loop.run_in_executor(None, self._collect_metrics_sync, status.pid)
278
328
  if not metrics:
279
329
  return
280
330
  status.last_metrics = metrics
@@ -331,6 +381,11 @@ class HealthMonitor:
331
381
  await self._publish("watchdog.module.resource_warning", {
332
382
  "module_id": status.module_id, "metrics": metrics,
333
383
  })
384
+ # 资源告警 → 代发降级(level: slow)
385
+ await self._report_degraded_to_kernel(
386
+ status.module_id, "slow", "resource_pressure",
387
+ detail=f"CPU {metrics.get('cpu_percent', 0)}%, MEM {metrics.get('memory_percent', 0)}%"
388
+ )
334
389
  else: # NORMAL
335
390
  status.resource_warning_count = 0
336
391
  status.resource_critical_count = 0
@@ -343,6 +398,11 @@ class HealthMonitor:
343
398
  await self._publish("watchdog.module.resource_recovered", {
344
399
  "module_id": status.module_id,
345
400
  })
401
+ # 资源恢复 → 代发恢复(仅当是 Watchdog 代发的降级时)
402
+ if self.is_degraded(status.module_id):
403
+ deg_info = self._degraded_modules.get(status.module_id, {})
404
+ if deg_info.get("reason") == "resource_pressure":
405
+ await self._report_recovered_to_kernel(status.module_id)
346
406
 
347
407
  async def _resource_restart(self, status: ModuleStatus):
348
408
  """Restart module due to resource critical timeout."""
@@ -383,7 +443,8 @@ class HealthMonitor:
383
443
  if event_type == "system.ready":
384
444
  print("[watchdog] Received system.ready")
385
445
  self._system_ready = True
386
- self._system_ready_event.set()
446
+ await self._persist_health_event("system.ready", {})
447
+ # 不再自动启动监控,等待 Launcher 通过 RPC 调用 start_monitor
387
448
  return
388
449
 
389
450
  # module.offline — track launcher state
@@ -409,18 +470,27 @@ class HealthMonitor:
409
470
  return
410
471
 
411
472
  if not module_id or module_id == "watchdog":
473
+ # Handle registry.updated (no module_id)
474
+ if event_type == "registry.updated":
475
+ self._dirty = True # 标记,等定时器到期时重新获取
412
476
  return
413
477
 
414
478
  if event_type == "module.started":
415
479
  print(f"[watchdog] Received module.started: {module_id}")
416
480
  self._crash_counts.pop(module_id, None)
417
- # Don't await discover_modules() here - it blocks event processing!
418
- # Schedule it as a background task instead
419
- asyncio.create_task(self.discover_modules())
481
+ await self._persist_health_event("module.started", {"module": module_id})
482
+ self._dirty = True # 标记,等定时器到期时获取新模块信息
420
483
 
421
484
  elif event_type == "module.stopped":
422
485
  print(f"[watchdog] Received module.stopped: {module_id}")
423
- self.modules.pop(module_id, None)
486
+ self.modules.pop(module_id, None) # 立即从监控列表移除
487
+ self._degraded_modules.pop(module_id, None) # 清除降级状态
488
+ await self._persist_health_event("module.stopped", {
489
+ "module": module_id,
490
+ "exit_code": data.get("exit_code"),
491
+ "stop_type": data.get("stop_type"),
492
+ })
493
+ self._dirty = True # 标记,等定时器到期时同步清单
424
494
  await self._handle_module_stopped(module_id, data)
425
495
 
426
496
  elif event_type == "module.exiting":
@@ -441,13 +511,40 @@ class HealthMonitor:
441
511
 
442
512
  elif event_type == "module.ready":
443
513
  graceful = bool(data.get("graceful_shutdown"))
444
- print(f"[watchdog] Received module.ready: {module_id}, graceful_shutdown={graceful}")
514
+ startup_time = data.get("startup_time", 0)
515
+ print(f"[watchdog] Received module.ready: {module_id}, graceful_shutdown={graceful}, startup_time={startup_time:.3f}s")
445
516
  self._graceful_modules[module_id] = graceful
517
+ # Save startup time to module status
518
+ if module_id in self.modules:
519
+ self.modules[module_id].startup_time = startup_time
446
520
  # Reset launcher loss tracking when launcher reconnects
447
521
  if module_id == "launcher":
448
522
  self._launcher_offline = False
449
523
  self._launcher_had_exiting = False
450
524
 
525
+ # 记录模块 ready 时间和状态
526
+ self._module_ready_times[module_id] = time.time()
527
+ self._module_ever_ready[module_id] = True
528
+
529
+ # 取消 pending 的重启任务(如果有)
530
+ if module_id in self._restart_tasks:
531
+ task = self._restart_tasks.pop(module_id)
532
+ if not task.done():
533
+ task.cancel()
534
+
535
+ # 检查是否稳定运行超过 STABLE_RUN_TIME,如果是则重置崩溃计数
536
+ # 注意:这里不立即重置,而是在下次崩溃时检查运行时长
537
+
538
+ elif event_type == "module.degraded":
539
+ await self._handle_module_degraded(module_id, data)
540
+
541
+ elif event_type == "module.recovered":
542
+ await self._handle_module_recovered(module_id, data)
543
+
544
+ elif event_type in ("system.instance.started", "system.instance.stopped"):
545
+ # 实例扩缩容事件:标记需要重新发现,下次检查周期自动更新监控列表
546
+ self.mark_dirty()
547
+
451
548
  # Layer 2: 忽略系统广播事件
452
549
  elif event_type in SYSTEM_BROADCAST_EVENTS:
453
550
  pass
@@ -460,15 +557,23 @@ class HealthMonitor:
460
557
  """Restart decision engine — called when module.stopped is received.
461
558
 
462
559
  Priority:
560
+ 0. Monitor disabled → no restart (Launcher controls module lifecycle)
463
561
  1. System shutting down → no restart
464
562
  2. stop_type == "graceful_stop" → Launcher主动停止,不重启
465
- 3. stop_type == "process_exit" + has exit_intent 按 intent 处理
466
- 4. stop_type == "process_exit" + no intent崩溃,重启
563
+ 3. Launcher stopped 事件中标记 restart=False + exit_type=graceful不重启(兜底)
564
+ 4. stop_type == "process_exit" + has exit_intent按 intent 处理
565
+ 5. stop_type == "process_exit" + no intent → 崩溃,重启
467
566
  """
468
567
  # Sync graceful_shutdown from Launcher (covers missed module.ready)
469
568
  if "graceful_shutdown" in data:
470
569
  self._graceful_modules[module_id] = bool(data["graceful_shutdown"])
471
570
 
571
+ # 监控未开启时,不做任何重启决策(启动阶段由 Launcher 管理)
572
+ if not self._monitor_enabled:
573
+ print(f"[watchdog] {module_id} stopped, but monitoring is disabled — skipping restart")
574
+ self._exit_intents.pop(module_id, None)
575
+ return
576
+
472
577
  if self._system_shutting_down:
473
578
  print(f"[watchdog] {module_id} stopped during shutdown, skipping restart")
474
579
  return
@@ -484,6 +589,13 @@ class HealthMonitor:
484
589
  self._exit_intents.pop(module_id, None) # 清理可能残留的intent
485
590
  return
486
591
 
592
+ # 兜底:检查 Launcher 在 module.stopped 事件中的决策(防止 module.exiting 因竞态丢失)
593
+ if data.get("restart") is False and data.get("exit_type") == "graceful":
594
+ reason = data.get("reason", "unknown")
595
+ print(f"[watchdog] {module_id} exited gracefully (reason={reason}), no restart (Launcher decision)")
596
+ self._exit_intents.pop(module_id, None)
597
+ return
598
+
487
599
  # stop_type == "process_exit": 进程自行退出,需要判断是否崩溃
488
600
  intent = self._exit_intents.pop(module_id, None)
489
601
  if intent is not None:
@@ -505,19 +617,77 @@ class HealthMonitor:
505
617
  return
506
618
 
507
619
  # No exit intent → treat as crash
508
- self._crash_counts[module_id] = self._crash_counts.get(module_id, 0) + 1
509
- count = self._crash_counts[module_id]
510
- exit_code = data.get("exit_code", -1)
620
+ # 如果已有 pending 的重启任务,取消它(避免重复重启)
621
+ if module_id in self._restart_tasks:
622
+ existing_task = self._restart_tasks[module_id]
623
+ if not existing_task.done():
624
+ print(f"[watchdog] {module_id} already has a pending restart, skipping duplicate")
625
+ return
626
+
627
+ # 检查模块是否曾经 ready 过
628
+ ever_ready = self._module_ever_ready.get(module_id, False)
511
629
 
512
- if count <= self.MAX_RESTARTS:
513
- print(f"[watchdog] {module_id} crashed (exit_code={exit_code}), restarting ({count}/{self.MAX_RESTARTS})")
514
- await self._restart_module_by_id(module_id, reason="crash")
630
+ if ever_ready:
631
+ # 运行时崩溃:检查是否稳定运行超过 STABLE_RUN_TIME
632
+ last_ready_time = self._module_ready_times.get(module_id, 0)
633
+ run_time = time.time() - last_ready_time
634
+
635
+ if run_time >= self.STABLE_RUN_TIME:
636
+ # 稳定运行超过 10 分钟,重置崩溃计数
637
+ print(f"[watchdog] {module_id} ran stably for {run_time:.0f}s, resetting crash count")
638
+ self._crash_counts[module_id] = 0
639
+
640
+ # 运行时崩溃:无限重试,使用指数退避
641
+ self._crash_counts[module_id] = self._crash_counts.get(module_id, 0) + 1
642
+ count = self._crash_counts[module_id]
643
+ exit_code = data.get("exit_code", -1)
644
+
645
+ # 指数退避:1秒、2秒、4秒、8秒...最大10分钟
646
+ delay = min(2 ** (count - 1), self.MAX_RESTART_DELAY)
647
+ print(f"\033[94m[watchdog] {module_id} crashed (exit_code={exit_code}), will restart in {delay}s (crash #{count}, runtime crash)\033[0m")
648
+
649
+ # 创建延迟重启任务
650
+ restart_task = asyncio.create_task(self._delayed_restart(module_id, delay, "runtime_crash", count))
651
+ self._restart_tasks[module_id] = restart_task
515
652
  else:
516
- print(f"[watchdog] {module_id} crashed {count} times, giving up")
517
- await self._publish("watchdog.alert", {
518
- "module_id": module_id,
519
- "message": f"{module_id} exceeded {self.MAX_RESTARTS} crash restarts",
520
- })
653
+ # 启动失败(未到 ready):最多重试 5
654
+ self._crash_counts[module_id] = self._crash_counts.get(module_id, 0) + 1
655
+ count = self._crash_counts[module_id]
656
+ exit_code = data.get("exit_code", -1)
657
+
658
+ if count <= self.MAX_STARTUP_RETRIES:
659
+ # 指数退避:1秒、2秒、4秒、8秒、16秒
660
+ delay = 2 ** (count - 1)
661
+ print(f"\033[94m[watchdog] {module_id} failed to start (exit_code={exit_code}), will retry in {delay}s ({count}/{self.MAX_STARTUP_RETRIES})\033[0m")
662
+
663
+ # 创建延迟重启任务
664
+ restart_task = asyncio.create_task(self._delayed_restart(module_id, delay, "startup_failure", count))
665
+ self._restart_tasks[module_id] = restart_task
666
+ else:
667
+ print(f"\033[94m[watchdog] {module_id} failed to start {count} times, giving up\033[0m")
668
+ await self._publish("watchdog.alert", {
669
+ "module_id": module_id,
670
+ "message": f"{module_id} failed to start after {self.MAX_STARTUP_RETRIES} retries",
671
+ })
672
+
673
+ async def _delayed_restart(self, module_id: str, delay: float, crash_type: str, attempt: int):
674
+ """延迟重启模块(指数退避)
675
+
676
+ Args:
677
+ module_id: 模块名
678
+ delay: 延迟时间(秒)
679
+ crash_type: 崩溃类型 (startup_failure, runtime_crash)
680
+ attempt: 重启尝试次数
681
+ """
682
+ try:
683
+ await asyncio.sleep(delay)
684
+ # 构造详细的 reason,包含崩溃类型和尝试次数
685
+ reason = f"watchdog_{crash_type}_attempt_{attempt}"
686
+ await self._restart_module_by_id(module_id, reason=reason)
687
+ except asyncio.CancelledError:
688
+ print(f"[watchdog] {module_id} restart cancelled")
689
+ finally:
690
+ self._restart_tasks.pop(module_id, None)
521
691
 
522
692
 
523
693
  async def _quick_check_launcher_exit(self):
@@ -532,13 +702,16 @@ class HealthMonitor:
532
702
  print(f"[watchdog] Launcher 已退出(检测 {(i+1)*0.2:.1f}s),启动新实例")
533
703
  self._start_new_instance()
534
704
  print("[watchdog] 新实例已启动,watchdog 退出")
535
- sys.exit(0)
705
+ if self.request_exit:
706
+ self.request_exit(0)
707
+ return
536
708
 
537
709
  # 5s 后仍未退出,强制重启
538
710
  print("[watchdog] Launcher 5s 内未退出,强制重启")
539
711
  self._start_new_instance()
540
712
  print("[watchdog] 新实例已启动,watchdog 退出")
541
- sys.exit(0)
713
+ if self.request_exit:
714
+ self.request_exit(0)
542
715
 
543
716
  async def _handle_launcher_lost(self):
544
717
  """Handle launcher_lost: decide whether to start a new Kite instance.
@@ -548,12 +721,12 @@ class HealthMonitor:
548
721
  """
549
722
  if self._launcher_had_exiting:
550
723
  print("[watchdog] Launcher had sent module.exiting before loss → normal exit, following suit")
551
- sys.exit(0)
552
724
  else:
553
725
  print("[watchdog] Launcher lost without module.exiting → crash detected, starting new instance")
554
726
  self._start_new_instance()
555
727
  print("[watchdog] New instance started, exiting")
556
- sys.exit(0)
728
+ if self.request_exit:
729
+ self.request_exit(0)
557
730
 
558
731
  def _start_new_instance(self):
559
732
  """Start a new Kite instance using saved startup info from launcher."""
@@ -659,6 +832,150 @@ class HealthMonitor:
659
832
  except Exception as e:
660
833
  print(f"[watchdog] Failed to start new instance: {e}")
661
834
 
835
+ # ── 降级状态管理 ──
836
+
837
+ async def _handle_module_degraded(self, module_id: str, data: dict):
838
+ """处理 module.degraded 事件:记录降级状态,降级模块不触发重启。"""
839
+ level = data.get("level", "unknown")
840
+ reason = data.get("reason", "unknown")
841
+ print(f"[watchdog] Module degraded: {module_id} level={level} reason={reason}")
842
+ self._degraded_modules[module_id] = {
843
+ "level": level,
844
+ "reason": reason,
845
+ "affected": data.get("affected", []),
846
+ "degraded_at": time.time(),
847
+ }
848
+ # 持久化健康事件
849
+ await self._persist_health_event("module.degraded", {
850
+ "module": module_id, "level": level, "reason": reason,
851
+ "detail": data.get("detail"),
852
+ })
853
+
854
+ async def _handle_module_recovered(self, module_id: str, data: dict):
855
+ """处理 module.recovered 事件:清除降级状态。"""
856
+ prev = self._degraded_modules.pop(module_id, None)
857
+ duration_ms = data.get("duration_ms")
858
+ print(f"[watchdog] Module recovered: {module_id} (duration={duration_ms}ms)")
859
+ await self._persist_health_event("module.recovered", {
860
+ "module": module_id,
861
+ "previous_level": data.get("previous_level"),
862
+ "duration_ms": duration_ms,
863
+ })
864
+
865
+ def is_degraded(self, module_id: str) -> bool:
866
+ """检查模块是否处于降级状态。"""
867
+ return module_id in self._degraded_modules
868
+
869
+ async def _report_degraded_to_kernel(self, module_id: str, level: str, reason: str, detail: str = None):
870
+ """通过 Kernel RPC 代发降级事件(资源类降级)。"""
871
+ if not self.rpc_call_kernel:
872
+ return
873
+ try:
874
+ params = {
875
+ "module_id": module_id,
876
+ "level": level,
877
+ "reason": reason,
878
+ "affected": [],
879
+ }
880
+ if detail:
881
+ params["detail"] = detail
882
+ await self.rpc_call_kernel("kernel.report_degraded", params)
883
+ except Exception as e:
884
+ print(f"[watchdog] Failed to report degraded for {module_id}: {e}")
885
+
886
+ async def _report_recovered_to_kernel(self, module_id: str):
887
+ """通过 Kernel RPC 代发恢复事件(资源类恢复)。"""
888
+ if not self.rpc_call_kernel:
889
+ return
890
+ try:
891
+ await self.rpc_call_kernel("kernel.report_recovered", {"module_id": module_id})
892
+ except Exception as e:
893
+ print(f"[watchdog] Failed to report recovered for {module_id}: {e}")
894
+
895
+ async def _check_degraded_timeout(self):
896
+ """检查超长降级(超过 5 分钟),发布告警事件。"""
897
+ now = time.time()
898
+ for mid, info in list(self._degraded_modules.items()):
899
+ degraded_at = info.get("degraded_at", 0)
900
+ if degraded_at and (now - degraded_at) >= self._degraded_timeout:
901
+ duration = int((now - degraded_at) * 1000)
902
+ # 只告警一次:标记已告警
903
+ if not info.get("_timeout_alerted"):
904
+ info["_timeout_alerted"] = True
905
+ print(f"[watchdog] Module {mid} degraded for {duration}ms (>{self._degraded_timeout}s), alerting")
906
+ await self._publish("watchdog.module.degraded_timeout", {
907
+ "module_id": mid,
908
+ "level": info.get("level"),
909
+ "reason": info.get("reason"),
910
+ "duration_ms": duration,
911
+ })
912
+ await self._persist_health_event("watchdog.module.degraded_timeout", {
913
+ "module": mid, "duration_ms": duration,
914
+ })
915
+
916
+ # ── 健康事件持久化 ──
917
+
918
+ async def _persist_health_event(self, event: str, data: dict):
919
+ """将健康事件追加到 events.jsonl,按天归档。"""
920
+ if not self._health_events_dir:
921
+ return
922
+ record = {
923
+ "ts": datetime.now(timezone.utc).isoformat(),
924
+ "event": event,
925
+ **data,
926
+ }
927
+ line = json.dumps(record, ensure_ascii=False) + "\n"
928
+ try:
929
+ # 写入当前文件
930
+ with open(self._health_events_path, "a", encoding="utf-8") as f:
931
+ f.write(line)
932
+ # 写入按天归档
933
+ today = datetime.now().strftime("%Y-%m-%d")
934
+ month_dir = os.path.join(self._health_events_dir, today[:7])
935
+ os.makedirs(month_dir, exist_ok=True)
936
+ daily_path = os.path.join(month_dir, f"{today}.jsonl")
937
+ with open(daily_path, "a", encoding="utf-8") as f:
938
+ f.write(line)
939
+ except Exception as e:
940
+ print(f"[watchdog] Failed to persist health event: {e}")
941
+
942
+ def get_health_events(self, limit: int = 50, since: str = None) -> dict:
943
+ """查询健康事件记录。
944
+
945
+ Args:
946
+ limit: 最多返回条数(默认 50,最大 500)
947
+ since: ISO 8601 时间戳,只返回此时间之后的事件
948
+
949
+ Returns:
950
+ {"events": [...], "total": int, "has_more": bool}
951
+ """
952
+ limit = min(max(1, limit), 500)
953
+ if not self._health_events_path or not os.path.exists(self._health_events_path):
954
+ return {"events": [], "total": 0, "has_more": False}
955
+
956
+ events = []
957
+ try:
958
+ with open(self._health_events_path, "r", encoding="utf-8") as f:
959
+ for line in f:
960
+ line = line.strip()
961
+ if not line:
962
+ continue
963
+ try:
964
+ record = json.loads(line)
965
+ if since and record.get("ts", "") <= since:
966
+ continue
967
+ events.append(record)
968
+ except json.JSONDecodeError:
969
+ continue
970
+ except Exception:
971
+ pass
972
+
973
+ total = len(events)
974
+ # 返回最新的 limit 条(倒序)
975
+ events = events[-limit:]
976
+ events.reverse()
977
+ return {"events": events, "total": total, "has_more": total > limit}
978
+
662
979
  async def _restart_module_by_id(self, module_id: str, reason: str = "restart"):
663
980
  """Restart a module via Launcher RPC by module_id."""
664
981
  print(f"[watchdog] Requesting restart for {module_id} (reason={reason})")
@@ -675,16 +992,51 @@ class HealthMonitor:
675
992
  except Exception as e:
676
993
  print(f"[watchdog] {module_id} restart error: {e}")
677
994
 
995
+ # ── Monitor control (called by Launcher via RPC) ──
996
+
997
+ def start_monitor(self) -> dict:
998
+ """由 Launcher 调用,开启监控(模块重启职责移交给 Watchdog)"""
999
+ if self._monitor_enabled:
1000
+ return {"status": "already_enabled"}
1001
+ self._monitor_enabled = True
1002
+ # 同时设置 system.ready,唤醒健康检查循环
1003
+ self._system_ready = True
1004
+ self._system_ready_event.set()
1005
+ print("[watchdog] 监控已开启(由 Launcher 控制)")
1006
+ return {"status": "enabled"}
1007
+
1008
+ def stop_monitor(self) -> dict:
1009
+ """由 Launcher 调用,关闭监控(模块重启职责收回 Launcher)"""
1010
+ if not self._monitor_enabled:
1011
+ return {"status": "already_disabled"}
1012
+ self._monitor_enabled = False
1013
+ # 取消所有 pending 的重启任务
1014
+ for module_id, task in list(self._restart_tasks.items()):
1015
+ if not task.done():
1016
+ task.cancel()
1017
+ print(f"[watchdog] 取消 {module_id} 的 pending 重启任务")
1018
+ self._restart_tasks.clear()
1019
+ print("[watchdog] 监控已关闭(由 Launcher 控制)")
1020
+ return {"status": "disabled"}
1021
+
678
1022
  # ── Main loop ──
679
1023
 
680
1024
  async def run(self):
681
- """Main monitoring loop with dynamic intervals per resource state.
682
- Waits for system.ready before starting health checks."""
1025
+ """Main monitoring loop with grouped timers per resource state.
1026
+
1027
+ 机制:
1028
+ - system.ready + start_monitor 后等 15 秒再开始首次检查
1029
+ - 首次检查时查注册中心获取健康接口清单
1030
+ - 3 个状态组(NORMAL/WARNING/CRITICAL)各自独立周期
1031
+ - 单循环维护 3 个 next_check 时间戳,每次 sleep 到最近到期的组
1032
+ - 模块状态变化时自动迁移到对应组
1033
+ - dirty 标记:事件驱动标记 + 定时器驱动获取,防止抖动
1034
+ """
683
1035
  self._running = True
684
- print("[watchdog] Monitor started, waiting for system.ready...")
1036
+ print("[watchdog] Monitor started, waiting for Launcher to enable monitoring...")
685
1037
 
686
- # Wait for system.ready (or manual start via API later)
687
- while self._running and not self._system_ready:
1038
+ # Wait for Launcher to call start_monitor() via RPC
1039
+ while self._running and not self._monitor_enabled:
688
1040
  try:
689
1041
  await asyncio.wait_for(self._system_ready_event.wait(), timeout=1.0)
690
1042
  except asyncio.TimeoutError:
@@ -692,28 +1044,82 @@ class HealthMonitor:
692
1044
 
693
1045
  if not self._running:
694
1046
  return
695
- print("[watchdog] system.ready received, starting health checks")
1047
+ print("[watchdog] Monitoring enabled, first check in 15s...")
1048
+
1049
+ # 等 15 秒,给模块充分时间完成注册
1050
+ for _ in range(15):
1051
+ if not self._running:
1052
+ return
1053
+ await asyncio.sleep(1)
1054
+
1055
+ # 首次发现
1056
+ await self.discover_modules()
1057
+ self._first_check_done = True
1058
+ self._dirty = False
1059
+ print(f"[watchdog] Initial discovery done, monitoring {len(self.modules)} modules")
1060
+
1061
+ # 分组定时器:3 个 next_check 时间戳
1062
+ now = asyncio.get_event_loop().time()
1063
+ next_check = {
1064
+ NORMAL: now, # 立即开始首次检查
1065
+ WARNING: now,
1066
+ CRITICAL: now,
1067
+ }
1068
+ # 上次 discover 时间(定期兜底重发现,5 分钟)
1069
+ last_discovery = now
1070
+ discovery_interval = 300.0
696
1071
 
697
1072
  while self._running:
698
- # Re-discover every cycle to pick up newly started/stopped modules
699
- await self.discover_modules()
700
-
701
- if self.modules:
702
- tasks = []
703
- for s in self.modules.values():
704
- tasks.append(self._check_one(s))
705
- tasks.append(self._check_resources(s))
706
- await asyncio.gather(*tasks, return_exceptions=True)
707
-
708
- # Sleep = shortest interval needed by any module
709
- interval = self._min_interval()
710
- await asyncio.sleep(interval)
711
-
712
- def _min_interval(self) -> float:
713
- """Return the shortest check interval needed across all modules."""
714
- if not self.modules:
715
- return self.INTERVALS[NORMAL]
716
- return min(self.INTERVALS.get(s.resource_state, 15) for s in self.modules.values())
1073
+ now = asyncio.get_event_loop().time()
1074
+
1075
+ # 找到最近到期的时间戳
1076
+ nearest = min(next_check.values())
1077
+ wait = max(0, nearest - now)
1078
+ if wait > 0:
1079
+ await asyncio.sleep(wait)
1080
+ if not self._running:
1081
+ return
1082
+ now = asyncio.get_event_loop().time()
1083
+
1084
+ # dirty 检查:任意定时器到期时,如果有 dirty 标记就重新获取清单
1085
+ if self._dirty:
1086
+ await self.discover_modules()
1087
+ self._dirty = False
1088
+ last_discovery = now
1089
+
1090
+ # 定期兜底重发现(5 分钟)
1091
+ if now - last_discovery >= discovery_interval:
1092
+ await self.discover_modules()
1093
+ self._dirty = False
1094
+ last_discovery = now
1095
+
1096
+ # 按模块当前 resource_state 分组
1097
+ groups: dict[str, list[ModuleStatus]] = {NORMAL: [], WARNING: [], CRITICAL: []}
1098
+ for s in self.modules.values():
1099
+ groups.get(s.resource_state, groups[NORMAL]).append(s)
1100
+
1101
+ # 检查到期的组
1102
+ for level in (CRITICAL, WARNING, NORMAL):
1103
+ if now >= next_check[level] and groups[level]:
1104
+ tasks = []
1105
+ for s in groups[level]:
1106
+ tasks.append(self._check_one(s))
1107
+ tasks.append(self._check_resources(s))
1108
+ await asyncio.gather(*tasks, return_exceptions=True)
1109
+ # 更新该组的下次检查时间
1110
+ next_check[level] = now + self.INTERVALS[level]
1111
+
1112
+ # 没有模块的组也要推进时间戳,防止空组一直触发
1113
+ for level in (CRITICAL, WARNING, NORMAL):
1114
+ if not groups[level] and now >= next_check[level]:
1115
+ next_check[level] = now + self.INTERVALS[level]
1116
+
1117
+ # 超长降级检测
1118
+ await self._check_degraded_timeout()
1119
+
1120
+ def mark_dirty(self):
1121
+ """外部标记需要重新获取模块清单(如 module.offline 事件)。"""
1122
+ self._dirty = True
717
1123
 
718
1124
  def stop(self):
719
1125
  self._running = False
@@ -723,7 +1129,8 @@ class HealthMonitor:
723
1129
  def get_status(self) -> dict:
724
1130
  """Return current health and resource status of all monitored modules."""
725
1131
  return {
726
- mid: {
1132
+ "_monitor_enabled": self._monitor_enabled,
1133
+ **{mid: {
727
1134
  "state": s.state,
728
1135
  "fail_count": s.fail_count,
729
1136
  "restarted_count": s.restarted_count,
@@ -732,6 +1139,7 @@ class HealthMonitor:
732
1139
  "last_error": s.last_error,
733
1140
  "resource_state": s.resource_state,
734
1141
  "metrics": s.last_metrics,
1142
+ "startup_time": s.startup_time,
735
1143
  }
736
1144
  for mid, s in self.modules.items()
737
- }
1145
+ }}