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