@agentunion/kite 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/cli.js +38 -4
- package/core/env_checker.py +96 -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 +291 -105
- 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 +396 -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 +494 -197
- 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 -443
- package/extensions/services/evol/config.json5 +16 -0
- package/extensions/services/evol/entry.py +568 -406
- package/extensions/services/evol/evol_api.py +969 -173
- package/extensions/services/evol/mfa_totp.py +77 -0
- package/extensions/services/evol/module.md +150 -32
- package/extensions/services/evol/nonce_pool.py +113 -0
- package/extensions/services/evol/oauth_manager.py +223 -0
- package/extensions/services/evol/pairing.py +3 -2
- package/extensions/services/evol/pairing_codes.jsonl +1 -0
- package/extensions/services/evol/relay.py +1031 -682
- package/extensions/services/evol/relay_config.json5 +85 -67
- package/extensions/services/evol/routes/routes_llm.py +231 -0
- package/extensions/services/evol/routes/routes_rpc.py +90 -89
- package/extensions/services/evol/routes/routes_test.py +11 -4
- package/extensions/services/evol/server.py +2426 -875
- 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 -781
- package/extensions/services/evol/static/logo.png +0 -0
- package/extensions/services/evol/stats_manager.py +243 -240
- 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/{evol → kite_console}/static/css/style.css +656 -2
- package/extensions/services/kite_console/static/index.html +1524 -0
- package/extensions/services/{evol → kite_console}/static/js/dialog.js +11 -4
- package/extensions/services/kite_console/static/js/evol-app.js +7740 -0
- package/extensions/services/{evol/static/js/evol-app.js → kite_console/static/js/evol-app.js.backup} +2777 -1949
- package/extensions/services/kite_console/static/js/kernel-client.js +560 -0
- package/extensions/services/{evol/static/js/kernel-client.js → kite_console/static/js/kernel-client.js.backup} +41 -3
- package/extensions/services/{evol → kite_console}/static/js/registry-tests.js +7 -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/test_kernel_client_token.html +352 -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 +620 -171
- package/extensions/services/model_service/module.md +11 -2
- package/extensions/services/proxy/__init__.py +0 -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/presenter/agentIdPresenter.py +2 -2
- package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +18 -28
- package/extensions/services/proxy/evol/presenter/configPresenter.py +80 -1127
- package/extensions/services/proxy/evol/presenter/userPresenter.py +71 -477
- package/extensions/services/proxy/evol/server/claude_proxy_async.py +11 -7
- package/extensions/services/proxy/module.md +151 -0
- package/extensions/services/proxy/server.py +952 -271
- 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 -793
- package/extensions/services/watchdog/module.md +2 -0
- package/extensions/services/watchdog/monitor.py +465 -87
- 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 +16 -13
- 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 +360 -173
- 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/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 +2 -1
- package/kernel/registry_store.py +208 -11
- package/kernel/rpc_router.py +1400 -491
- package/kernel/server.py +1021 -134
- 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 +7 -7
- package/kite_cli/commands/disable.py +162 -0
- package/kite_cli/commands/enable.py +162 -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/search.py +33 -17
- package/kite_cli/commands/update.py +115 -2
- package/kite_cli/commands/venv_setup.py +6 -6
- 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 +151 -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 -2802
- package/launcher/logging_setup.py +54 -1
- package/launcher/module.md +32 -6
- package/launcher/module_scanner.py +93 -20
- package/launcher/process_manager.py +355 -76
- package/main.py +6 -0
- package/package.json +4 -1
- package/requirements.txt +41 -38
- package/scripts/auto-fix-deps.py +128 -0
- package/scripts/env-manager.js +25 -2
- package/scripts/final-test.js +78 -0
- package/scripts/setup-python-env.js +700 -191
- 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/evol/config.yaml +0 -149
- package/extensions/services/evol/routes/routes_management_ws.py +0 -127
- package/extensions/services/evol/static/index_evol.html +0 -14
- package/extensions/services/evol/static/js/app.js +0 -6304
- package/extensions/services/evol/static/js/auth.js +0 -326
- package/extensions/services/evol/static/js/evol-app-fixed.js +0 -50
- package/extensions/services/evol/static/js/evol-app.js.bak +0 -1800
- package/extensions/services/evol/static/js/kernel-client-example.js +0 -228
- package/extensions/services/evol/static/js/main.js +0 -141
- package/extensions/services/evol/static/js/stats.js +0 -217
- package/extensions/services/evol/static/js/token-manager.js +0 -175
- package/extensions/services/proxy/CHANGELOG_20260308.md +0 -258
- package/extensions/services/proxy/_fix_prints.py +0 -133
- package/extensions/services/proxy/_fix_prints2.py +0 -87
- package/extensions/services/proxy/console_auth.py +0 -109
- package/extensions/services/proxy/logs/websocket.log +0 -260
- package/extensions/services/proxy/main.py +0 -240
- package/extensions/services/proxy/requirements.txt +0 -13
- package/extensions/services/web/config.yaml +0 -149
- /package/extensions/services/{evol → kite_console}/static/pairing.html +0 -0
- /package/extensions/services/{evol → kite_console}/static/test_registry.html +0 -0
- /package/extensions/services/{evol → kite_console}/static/test_relay.html +0 -0
|
@@ -14,6 +14,7 @@ Kernel WebSocket 中转服务
|
|
|
14
14
|
|
|
15
15
|
import asyncio
|
|
16
16
|
import json
|
|
17
|
+
import os
|
|
17
18
|
import secrets
|
|
18
19
|
import time
|
|
19
20
|
import uuid
|
|
@@ -23,6 +24,9 @@ from typing import Optional
|
|
|
23
24
|
import websockets
|
|
24
25
|
from fastapi import WebSocket, WebSocketDisconnect
|
|
25
26
|
|
|
27
|
+
from extensions.services.kite_console.nonce_pool import NoncePool
|
|
28
|
+
from extensions.services.kite_console.mfa_totp import TOTPVerifier
|
|
29
|
+
|
|
26
30
|
|
|
27
31
|
class SessionInfo:
|
|
28
32
|
"""前端会话信息"""
|
|
@@ -35,6 +39,7 @@ class SessionInfo:
|
|
|
35
39
|
client_ws: WebSocket,
|
|
36
40
|
frontend_token: str,
|
|
37
41
|
role: str,
|
|
42
|
+
kernel_token: str,
|
|
38
43
|
):
|
|
39
44
|
self.session_token = session_token
|
|
40
45
|
self.module_id = module_id
|
|
@@ -42,6 +47,7 @@ class SessionInfo:
|
|
|
42
47
|
self.client_ws: Optional[WebSocket] = client_ws
|
|
43
48
|
self.frontend_token = frontend_token
|
|
44
49
|
self.role = role
|
|
50
|
+
self.kernel_token = kernel_token
|
|
45
51
|
self.created_at = time.time()
|
|
46
52
|
self.last_active = time.time()
|
|
47
53
|
self.status = "active" # active | waiting_reconnect
|
|
@@ -59,7 +65,9 @@ class KernelRelay:
|
|
|
59
65
|
reconnect_timeout: int,
|
|
60
66
|
permissions: dict,
|
|
61
67
|
pairing_manager,
|
|
62
|
-
web_server, #
|
|
68
|
+
web_server, # web server 实例,用于发送事件
|
|
69
|
+
auth_manager=None, # 新增:AuthManager 实例,用于 Kite Token 认证
|
|
70
|
+
oauth_manager=None, # 新增:OAuthManager 实例,用于 OAuth 认证
|
|
63
71
|
):
|
|
64
72
|
"""
|
|
65
73
|
初始化中转服务。
|
|
@@ -73,6 +81,8 @@ class KernelRelay:
|
|
|
73
81
|
permissions: 权限配置(角色 → RPC 白名单)
|
|
74
82
|
pairing_manager: 配对管理器实例
|
|
75
83
|
web_server: web server 实例
|
|
84
|
+
auth_manager: AuthManager 实例(可选,用于 Kite Token 认证)
|
|
85
|
+
oauth_manager: OAuthManager 实例(可选,用于 OAuth auth_ticket 认证)
|
|
76
86
|
"""
|
|
77
87
|
self.kernel_host = kernel_host
|
|
78
88
|
self.kernel_port = kernel_port
|
|
@@ -82,6 +92,16 @@ class KernelRelay:
|
|
|
82
92
|
self.permissions = permissions
|
|
83
93
|
self.pairing_manager = pairing_manager
|
|
84
94
|
self.web_server = web_server
|
|
95
|
+
self.auth_manager = auth_manager
|
|
96
|
+
self.oauth_manager = oauth_manager
|
|
97
|
+
|
|
98
|
+
# Nonce 池(challenge-response 防重放)
|
|
99
|
+
self.nonce_pool = NoncePool(max_size=10000, ttl=600)
|
|
100
|
+
|
|
101
|
+
# TLS configuration
|
|
102
|
+
# 默认值根据环境变量:开发环境允许 WS,生产环境强制 WSS
|
|
103
|
+
env = os.environ.get("KITE_ENV", "development").lower()
|
|
104
|
+
self.require_tls = os.environ.get("KITE_REQUIRE_TLS", "true" if env == "production" else "false").lower() == "true"
|
|
85
105
|
|
|
86
106
|
# session_token → SessionInfo
|
|
87
107
|
self.sessions: dict[str, SessionInfo] = {}
|
|
@@ -89,6 +109,16 @@ class KernelRelay:
|
|
|
89
109
|
# 待重连的 session(超时清理)
|
|
90
110
|
self.reconnect_timers: dict[str, asyncio.Task] = {}
|
|
91
111
|
|
|
112
|
+
# 速率限制:(ip, device_id) → [timestamp, timestamp, ...]
|
|
113
|
+
self._rate_limits: dict[tuple[str, str], list[float]] = {}
|
|
114
|
+
self._rate_limit_max = 20 # 10 秒内最多 20 次
|
|
115
|
+
self._rate_limit_window = 10.0 # 秒
|
|
116
|
+
|
|
117
|
+
# MFA/TOTP 验证器
|
|
118
|
+
self._totp = TOTPVerifier()
|
|
119
|
+
# MFA 密钥存储:token → totp_secret(由外部配置注入)
|
|
120
|
+
self._mfa_secrets: dict[str, str] = {}
|
|
121
|
+
|
|
92
122
|
async def handle_client(self, client_ws: WebSocket):
|
|
93
123
|
"""
|
|
94
124
|
处理客户端连接。
|
|
@@ -100,9 +130,28 @@ class KernelRelay:
|
|
|
100
130
|
4. 创建或恢复 Kernel 连接
|
|
101
131
|
5. 双向转发消息
|
|
102
132
|
"""
|
|
133
|
+
# Check TLS requirement before accepting
|
|
134
|
+
if self.require_tls:
|
|
135
|
+
# Check if connection is secure (WSS)
|
|
136
|
+
if client_ws.url.scheme != "wss":
|
|
137
|
+
await client_ws.close(code=1008, reason="TLS required")
|
|
138
|
+
print(f"[relay] Rejected non-TLS connection from {client_ws.client.host if client_ws.client else 'unknown'}")
|
|
139
|
+
return
|
|
140
|
+
|
|
103
141
|
await client_ws.accept()
|
|
104
142
|
|
|
105
143
|
try:
|
|
144
|
+
# 速率限制检查
|
|
145
|
+
client_ip = client_ws.client.host if client_ws.client else "unknown"
|
|
146
|
+
if not self._check_rate_limit(client_ip, "unknown"):
|
|
147
|
+
await client_ws.send_json({
|
|
148
|
+
"type": "error",
|
|
149
|
+
"message": "Rate limit exceeded"
|
|
150
|
+
})
|
|
151
|
+
await client_ws.close(code=4020, reason="Rate limit exceeded")
|
|
152
|
+
await self._audit_log("auth.rate_limited", {"ip": client_ip})
|
|
153
|
+
return
|
|
154
|
+
|
|
106
155
|
# 接收认证/配对/请求配对码消息
|
|
107
156
|
raw = await client_ws.receive_text()
|
|
108
157
|
msg = json.loads(raw)
|
|
@@ -111,6 +160,9 @@ class KernelRelay:
|
|
|
111
160
|
if msg_type == "request_code":
|
|
112
161
|
# 请求配对码
|
|
113
162
|
await self._handle_request_code(client_ws, msg)
|
|
163
|
+
elif msg_type == "challenge":
|
|
164
|
+
# Challenge-response 握手(第一步:客户端请求 challenge)
|
|
165
|
+
await self._handle_challenge(client_ws, msg)
|
|
114
166
|
elif msg_type == "pair":
|
|
115
167
|
# 配对流程
|
|
116
168
|
await self._handle_pair(client_ws, msg)
|
|
@@ -120,7 +172,7 @@ class KernelRelay:
|
|
|
120
172
|
else:
|
|
121
173
|
await client_ws.send_json({
|
|
122
174
|
"type": "error",
|
|
123
|
-
"message": "Invalid message type, expected 'request_code', 'pair' or 'auth'"
|
|
175
|
+
"message": "Invalid message type, expected 'challenge', 'request_code', 'pair' or 'auth'"
|
|
124
176
|
})
|
|
125
177
|
await client_ws.close(code=4000, reason="Invalid message type")
|
|
126
178
|
|
|
@@ -133,6 +185,95 @@ class KernelRelay:
|
|
|
133
185
|
except Exception:
|
|
134
186
|
pass
|
|
135
187
|
|
|
188
|
+
async def _handle_challenge(self, client_ws: WebSocket, msg: dict):
|
|
189
|
+
"""处理 challenge 请求(握手第一步)"""
|
|
190
|
+
client_ip = client_ws.client.host if client_ws.client else "unknown"
|
|
191
|
+
nonce = self.nonce_pool.generate(metadata={"client_ip": client_ip})
|
|
192
|
+
|
|
193
|
+
await client_ws.send_json({
|
|
194
|
+
"type": "challenge",
|
|
195
|
+
"nonce": nonce,
|
|
196
|
+
"protocol_version": "1.0",
|
|
197
|
+
"auth_methods": ["pairing_code", "kite_token", "oauth"],
|
|
198
|
+
"expires_in": self.nonce_pool.ttl,
|
|
199
|
+
"server_time": time.time(), # 客户端可据此校准时钟偏移
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
# 等待客户端的 connect 消息(带 nonce)
|
|
203
|
+
try:
|
|
204
|
+
raw = await asyncio.wait_for(client_ws.receive_text(), timeout=30)
|
|
205
|
+
connect_msg = json.loads(raw)
|
|
206
|
+
|
|
207
|
+
if connect_msg.get("type") != "connect":
|
|
208
|
+
await client_ws.send_json({
|
|
209
|
+
"type": "error",
|
|
210
|
+
"message": "Expected 'connect' message after challenge"
|
|
211
|
+
})
|
|
212
|
+
await client_ws.close(code=4000, reason="Protocol error")
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
# 验证 nonce
|
|
216
|
+
returned_nonce = connect_msg.get("nonce")
|
|
217
|
+
if not returned_nonce:
|
|
218
|
+
await client_ws.send_json({
|
|
219
|
+
"type": "error",
|
|
220
|
+
"message": "Missing nonce in connect message"
|
|
221
|
+
})
|
|
222
|
+
await client_ws.close(code=4000, reason="Missing nonce")
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
nonce_meta = self.nonce_pool.verify(returned_nonce)
|
|
226
|
+
if nonce_meta is None:
|
|
227
|
+
await client_ws.send_json({
|
|
228
|
+
"type": "error",
|
|
229
|
+
"message": "Invalid or expired nonce"
|
|
230
|
+
})
|
|
231
|
+
await client_ws.close(code=4001, reason="Invalid nonce")
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
# Nonce 验证通过,根据 auth_method 分发
|
|
235
|
+
auth_method = connect_msg.get("auth_method", "")
|
|
236
|
+
|
|
237
|
+
if auth_method == "pairing_code":
|
|
238
|
+
code = connect_msg.get("code")
|
|
239
|
+
if not code:
|
|
240
|
+
await client_ws.send_json({"type": "error", "message": "Missing pairing code"})
|
|
241
|
+
await client_ws.close(code=4000, reason="Missing code")
|
|
242
|
+
return
|
|
243
|
+
await self._handle_pair(client_ws, {"code": code})
|
|
244
|
+
|
|
245
|
+
elif auth_method == "kite_token":
|
|
246
|
+
token = connect_msg.get("token")
|
|
247
|
+
session_token = connect_msg.get("session_token")
|
|
248
|
+
if not token or not session_token:
|
|
249
|
+
await client_ws.send_json({"type": "error", "message": "Missing token or session_token"})
|
|
250
|
+
await client_ws.close(code=4000, reason="Missing credentials")
|
|
251
|
+
return
|
|
252
|
+
await self._handle_auth(client_ws, {"token": token, "session_token": session_token})
|
|
253
|
+
|
|
254
|
+
elif auth_method == "oauth":
|
|
255
|
+
auth_ticket = connect_msg.get("auth_ticket")
|
|
256
|
+
if not auth_ticket:
|
|
257
|
+
await client_ws.send_json({"type": "error", "message": "Missing auth_ticket"})
|
|
258
|
+
await client_ws.close(code=4000, reason="Missing auth_ticket")
|
|
259
|
+
return
|
|
260
|
+
await self._handle_auth(client_ws, {
|
|
261
|
+
"method": "oauth",
|
|
262
|
+
"auth_ticket": auth_ticket,
|
|
263
|
+
"session_token": connect_msg.get("session_token", "")
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
else:
|
|
267
|
+
await client_ws.send_json({
|
|
268
|
+
"type": "error",
|
|
269
|
+
"message": f"Unsupported auth_method: {auth_method}"
|
|
270
|
+
})
|
|
271
|
+
await client_ws.close(code=4000, reason="Unsupported auth method")
|
|
272
|
+
|
|
273
|
+
except asyncio.TimeoutError:
|
|
274
|
+
await client_ws.send_json({"type": "error", "message": "Connect timeout"})
|
|
275
|
+
await client_ws.close(code=4000, reason="Timeout")
|
|
276
|
+
|
|
136
277
|
async def _handle_request_code(self, client_ws: WebSocket, msg: dict):
|
|
137
278
|
"""处理请求配对码"""
|
|
138
279
|
# 生成配对码
|
|
@@ -141,15 +282,17 @@ class KernelRelay:
|
|
|
141
282
|
# 发送事件给 Launcher(通过 Kernel)
|
|
142
283
|
if self.web_server and self.web_server._ws:
|
|
143
284
|
try:
|
|
144
|
-
await self.web_server._publish_event(
|
|
145
|
-
|
|
146
|
-
"
|
|
285
|
+
await self.web_server._publish_event(
|
|
286
|
+
self.web_server._ws,
|
|
287
|
+
"pairing.status",
|
|
288
|
+
{
|
|
147
289
|
"step": "code_generated",
|
|
148
290
|
"success": True,
|
|
149
291
|
"code": code,
|
|
150
|
-
"expires_in": 300
|
|
292
|
+
"expires_in": 300,
|
|
293
|
+
"module_id": "evol"
|
|
151
294
|
}
|
|
152
|
-
|
|
295
|
+
)
|
|
153
296
|
except Exception as e:
|
|
154
297
|
print(f"[relay] Failed to publish pairing event: {e}")
|
|
155
298
|
|
|
@@ -179,14 +322,15 @@ class KernelRelay:
|
|
|
179
322
|
# 发送配对失败事件
|
|
180
323
|
if self.web_server and self.web_server._ws:
|
|
181
324
|
try:
|
|
182
|
-
await self.web_server._publish_event(
|
|
183
|
-
|
|
184
|
-
"
|
|
325
|
+
await self.web_server._publish_event(
|
|
326
|
+
self.web_server._ws,
|
|
327
|
+
"pairing.status",
|
|
328
|
+
{
|
|
185
329
|
"step": "completed",
|
|
186
330
|
"success": False,
|
|
187
331
|
"reason": "Invalid pairing code"
|
|
188
332
|
}
|
|
189
|
-
|
|
333
|
+
)
|
|
190
334
|
except Exception as e:
|
|
191
335
|
print(f"[relay] Failed to publish pairing failed event: {e}")
|
|
192
336
|
|
|
@@ -195,6 +339,7 @@ class KernelRelay:
|
|
|
195
339
|
"message": "Invalid pairing code"
|
|
196
340
|
})
|
|
197
341
|
await client_ws.close(code=4001, reason="Invalid pairing code")
|
|
342
|
+
await self._audit_log("auth.pair_failed", {"reason": "invalid_code"})
|
|
198
343
|
return
|
|
199
344
|
|
|
200
345
|
# 生成 session_token
|
|
@@ -213,6 +358,55 @@ class KernelRelay:
|
|
|
213
358
|
"""处理认证请求(已有 token)"""
|
|
214
359
|
frontend_token = msg.get("token")
|
|
215
360
|
session_token = msg.get("session_token")
|
|
361
|
+
auth_method = msg.get("method", "") # 可选:oauth
|
|
362
|
+
|
|
363
|
+
# OAuth auth_ticket 认证
|
|
364
|
+
if auth_method == "oauth":
|
|
365
|
+
auth_ticket = msg.get("auth_ticket")
|
|
366
|
+
if not auth_ticket:
|
|
367
|
+
await client_ws.send_json({
|
|
368
|
+
"type": "error",
|
|
369
|
+
"message": "Missing auth_ticket for OAuth auth"
|
|
370
|
+
})
|
|
371
|
+
await client_ws.close(code=4000, reason="Missing auth_ticket")
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
if not self.oauth_manager:
|
|
375
|
+
await client_ws.send_json({
|
|
376
|
+
"type": "error",
|
|
377
|
+
"message": "OAuth not configured"
|
|
378
|
+
})
|
|
379
|
+
await client_ws.close(code=4000, reason="OAuth not configured")
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
ticket_info = self.oauth_manager.verify_auth_ticket(auth_ticket)
|
|
383
|
+
if not ticket_info:
|
|
384
|
+
await client_ws.send_json({
|
|
385
|
+
"type": "error",
|
|
386
|
+
"message": "Invalid or expired auth_ticket"
|
|
387
|
+
})
|
|
388
|
+
await client_ws.close(code=4001, reason="Invalid auth_ticket")
|
|
389
|
+
await self._audit_log("auth.failed", {"reason": "invalid_auth_ticket", "auth_type": "oauth"})
|
|
390
|
+
return
|
|
391
|
+
print(f"[Relay] Authenticated via OAuth ({ticket_info['provider']}), user={ticket_info['user_info'].get('name', 'unknown')}")
|
|
392
|
+
await self._audit_log("auth.login", {
|
|
393
|
+
"auth_type": "oauth",
|
|
394
|
+
"provider": ticket_info["provider"],
|
|
395
|
+
"user": ticket_info["user_info"].get("name", "unknown"),
|
|
396
|
+
"role": role,
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
# 生成 session_token
|
|
400
|
+
if not session_token:
|
|
401
|
+
session_token = "sess_" + secrets.token_urlsafe(6)[:6]
|
|
402
|
+
|
|
403
|
+
await self._create_new_connection(
|
|
404
|
+
client_ws,
|
|
405
|
+
session_token,
|
|
406
|
+
f"oauth:{ticket_info['provider']}:{ticket_info['user_info'].get('id', '')}",
|
|
407
|
+
role
|
|
408
|
+
)
|
|
409
|
+
return
|
|
216
410
|
|
|
217
411
|
if not frontend_token or not session_token:
|
|
218
412
|
await client_ws.send_json({
|
|
@@ -222,16 +416,47 @@ class KernelRelay:
|
|
|
222
416
|
await client_ws.close(code=4000, reason="Missing credentials")
|
|
223
417
|
return
|
|
224
418
|
|
|
225
|
-
#
|
|
419
|
+
# 1. 尝试配对码认证
|
|
226
420
|
token_info = self.pairing_manager.verify_token(frontend_token)
|
|
227
|
-
if
|
|
421
|
+
if token_info:
|
|
422
|
+
role = token_info["role"]
|
|
423
|
+
auth_type = "pairing"
|
|
424
|
+
print(f"[Relay] Authenticated via pairing code, role={role}")
|
|
425
|
+
|
|
426
|
+
# 2. 尝试 Kite Token 认证
|
|
427
|
+
elif self.auth_manager and self.auth_manager.verify_kite_token(frontend_token):
|
|
428
|
+
role = "admin" # Kite Token 用户默认为 admin
|
|
429
|
+
auth_type = "kite_token"
|
|
430
|
+
print(f"[Relay] Authenticated via Kite Token, role={role}")
|
|
431
|
+
# 构造 token_info 格式以保持兼容
|
|
432
|
+
token_info = {"role": role}
|
|
433
|
+
|
|
434
|
+
# 3. 认证失败
|
|
435
|
+
else:
|
|
228
436
|
await client_ws.send_json({
|
|
229
437
|
"type": "error",
|
|
230
438
|
"message": "Invalid or expired token"
|
|
231
439
|
})
|
|
232
440
|
await client_ws.close(code=4001, reason="Invalid token")
|
|
441
|
+
await self._audit_log("auth.failed", {"reason": "invalid_token", "session_token": session_token})
|
|
233
442
|
return
|
|
234
443
|
|
|
444
|
+
# 审计:认证成功
|
|
445
|
+
await self._audit_log("auth.login", {"auth_type": auth_type, "role": role, "session_token": session_token})
|
|
446
|
+
|
|
447
|
+
# MFA 检查(可选:如果该 token 配置了 MFA 密钥)
|
|
448
|
+
mfa_secret = self._mfa_secrets.get(frontend_token)
|
|
449
|
+
if mfa_secret:
|
|
450
|
+
mfa_code = msg.get("mfa_code", "")
|
|
451
|
+
if not mfa_code or not self._totp.verify(mfa_secret, mfa_code):
|
|
452
|
+
await client_ws.send_json({
|
|
453
|
+
"type": "error",
|
|
454
|
+
"message": "MFA verification failed"
|
|
455
|
+
})
|
|
456
|
+
await client_ws.close(code=4004, reason="MFA failed")
|
|
457
|
+
await self._audit_log("auth.mfa_failed", {"session_token": session_token})
|
|
458
|
+
return
|
|
459
|
+
|
|
235
460
|
# 检查是否是重连
|
|
236
461
|
if session_token in self.sessions:
|
|
237
462
|
await self._handle_reconnect(client_ws, session_token, token_info)
|
|
@@ -240,7 +465,7 @@ class KernelRelay:
|
|
|
240
465
|
client_ws,
|
|
241
466
|
session_token,
|
|
242
467
|
frontend_token,
|
|
243
|
-
|
|
468
|
+
role
|
|
244
469
|
)
|
|
245
470
|
|
|
246
471
|
async def _create_new_connection(
|
|
@@ -257,8 +482,11 @@ class KernelRelay:
|
|
|
257
482
|
module_id = f"{self.base_module_id}-{suffix}"
|
|
258
483
|
|
|
259
484
|
try:
|
|
485
|
+
# 向 Launcher 申请 kernel_token
|
|
486
|
+
kernel_token = await self._request_kernel_token(module_id)
|
|
487
|
+
|
|
260
488
|
# 连接 Kernel
|
|
261
|
-
kernel_ws = await self._connect_kernel(module_id)
|
|
489
|
+
kernel_ws = await self._connect_kernel(module_id, kernel_token)
|
|
262
490
|
|
|
263
491
|
# 创建 session
|
|
264
492
|
session = SessionInfo(
|
|
@@ -268,6 +496,7 @@ class KernelRelay:
|
|
|
268
496
|
client_ws=client_ws,
|
|
269
497
|
frontend_token=frontend_token,
|
|
270
498
|
role=role,
|
|
499
|
+
kernel_token=kernel_token,
|
|
271
500
|
)
|
|
272
501
|
self.sessions[session_token] = session
|
|
273
502
|
|
|
@@ -285,15 +514,16 @@ class KernelRelay:
|
|
|
285
514
|
# 如果是配对,发送配对成功事件给 Launcher
|
|
286
515
|
if is_pairing and self.web_server and self.web_server._ws:
|
|
287
516
|
try:
|
|
288
|
-
await self.web_server._publish_event(
|
|
289
|
-
|
|
290
|
-
"
|
|
517
|
+
await self.web_server._publish_event(
|
|
518
|
+
self.web_server._ws,
|
|
519
|
+
"pairing.status",
|
|
520
|
+
{
|
|
291
521
|
"step": "completed",
|
|
292
522
|
"success": True,
|
|
293
523
|
"module_id": module_id,
|
|
294
524
|
"role": role
|
|
295
525
|
}
|
|
296
|
-
|
|
526
|
+
)
|
|
297
527
|
except Exception as e:
|
|
298
528
|
print(f"[relay] Failed to publish pairing success event: {e}")
|
|
299
529
|
|
|
@@ -301,12 +531,39 @@ class KernelRelay:
|
|
|
301
531
|
await self._relay_messages(session)
|
|
302
532
|
|
|
303
533
|
except Exception as e:
|
|
304
|
-
|
|
534
|
+
error_msg = str(e)
|
|
535
|
+
|
|
536
|
+
# 判断错误类型
|
|
537
|
+
is_fatal = self._is_fatal_error(error_msg)
|
|
538
|
+
error_code = 1011 # Internal Error (default)
|
|
539
|
+
|
|
540
|
+
if is_fatal:
|
|
541
|
+
# 永久性错误(权限、配置错误)
|
|
542
|
+
error_code = 1008 # Policy Violation
|
|
543
|
+
print(f"\033[31m[relay] 致命错误: {error_msg}\033[0m")
|
|
544
|
+
if "relay_modules whitelist" in error_msg:
|
|
545
|
+
print(f"\033[31m[relay] 请在 launcher/module.md 的 relay.modules 中添加本模块名\033[0m")
|
|
546
|
+
else:
|
|
547
|
+
print(f"[relay] Failed to create connection: {error_msg}")
|
|
548
|
+
|
|
305
549
|
await client_ws.send_json({
|
|
306
550
|
"type": "error",
|
|
307
|
-
"message": f"Failed to connect to Kernel: {
|
|
551
|
+
"message": f"Failed to connect to Kernel: {error_msg}",
|
|
552
|
+
"fatal": is_fatal, # 告知前端是否应该重试
|
|
553
|
+
"code": error_code
|
|
308
554
|
})
|
|
309
|
-
await client_ws.close(code=
|
|
555
|
+
await client_ws.close(code=error_code, reason="Kernel connection failed")
|
|
556
|
+
|
|
557
|
+
def _is_fatal_error(self, error_msg: str) -> bool:
|
|
558
|
+
"""判断是否为永久性错误(不应重试)"""
|
|
559
|
+
fatal_keywords = [
|
|
560
|
+
"Permission denied",
|
|
561
|
+
"not in relay_modules whitelist",
|
|
562
|
+
"module_id must start with",
|
|
563
|
+
"Invalid module_id",
|
|
564
|
+
"Token limit reached",
|
|
565
|
+
]
|
|
566
|
+
return any(keyword in error_msg for keyword in fatal_keywords)
|
|
310
567
|
|
|
311
568
|
async def _handle_reconnect(
|
|
312
569
|
self,
|
|
@@ -339,33 +596,63 @@ class KernelRelay:
|
|
|
339
596
|
# 继续双向转发
|
|
340
597
|
await self._relay_messages(session)
|
|
341
598
|
|
|
342
|
-
async def
|
|
599
|
+
async def _request_kernel_token(self, module_id: str) -> str:
|
|
600
|
+
"""向 Launcher 申请 kernel_token"""
|
|
601
|
+
if not self.web_server or not self.web_server._ws:
|
|
602
|
+
raise RuntimeError("evol_server not connected to Kernel")
|
|
603
|
+
|
|
604
|
+
print(f"[relay] Requesting kernel_token for {module_id} from Launcher")
|
|
605
|
+
|
|
606
|
+
try:
|
|
607
|
+
resp = await self.web_server._rpc_call(
|
|
608
|
+
self.web_server._ws,
|
|
609
|
+
"launcher.request_client_token",
|
|
610
|
+
{"module_id": module_id}
|
|
611
|
+
)
|
|
612
|
+
# _rpc_call 返回完整 JSON-RPC 响应,需要解包 result 层
|
|
613
|
+
inner = resp.get("result", resp)
|
|
614
|
+
token = inner.get("token")
|
|
615
|
+
if not token:
|
|
616
|
+
raise RuntimeError(f"Launcher returned no token: {result}")
|
|
617
|
+
|
|
618
|
+
print(f"[relay] Received kernel_token for {module_id}")
|
|
619
|
+
return token
|
|
620
|
+
|
|
621
|
+
except Exception as e:
|
|
622
|
+
print(f"[relay] Failed to request kernel_token: {e}")
|
|
623
|
+
raise
|
|
624
|
+
|
|
625
|
+
async def _connect_kernel(self, module_id: str, kernel_token: str):
|
|
343
626
|
"""连接到 Kernel"""
|
|
344
|
-
url = f"ws://{self.kernel_host}:{self.kernel_port}/ws?
|
|
627
|
+
url = f"ws://{self.kernel_host}:{self.kernel_port}/ws?id={module_id}"
|
|
628
|
+
print(f"[relay] DEBUG: Connecting to Kernel with module_id={module_id}")
|
|
345
629
|
kernel_ws = await websockets.connect(
|
|
346
630
|
url,
|
|
347
631
|
open_timeout=5,
|
|
348
|
-
ping_interval=
|
|
349
|
-
ping_timeout=20,
|
|
632
|
+
ping_interval=None,
|
|
350
633
|
close_timeout=10
|
|
351
634
|
)
|
|
635
|
+
print(f"[relay] DEBUG: Connected to Kernel")
|
|
352
636
|
|
|
353
|
-
#
|
|
637
|
+
# 1. 先发送认证请求(Kernel 要求第一条消息必须是 auth)
|
|
638
|
+
auth_id = str(uuid.uuid4())
|
|
354
639
|
await self._send_to_kernel(kernel_ws, {
|
|
355
640
|
"jsonrpc": "2.0",
|
|
356
|
-
"id":
|
|
357
|
-
"method": "
|
|
641
|
+
"id": auth_id,
|
|
642
|
+
"method": "auth",
|
|
358
643
|
"params": {
|
|
359
|
-
"
|
|
360
|
-
"module.started",
|
|
361
|
-
"module.stopped",
|
|
362
|
-
"module.crashed",
|
|
363
|
-
"module.ready",
|
|
364
|
-
]
|
|
644
|
+
"token": kernel_token
|
|
365
645
|
}
|
|
366
646
|
})
|
|
367
647
|
|
|
368
|
-
#
|
|
648
|
+
# 等待认证响应
|
|
649
|
+
auth_response = await asyncio.wait_for(kernel_ws.recv(), timeout=5.0)
|
|
650
|
+
auth_msg = json.loads(auth_response)
|
|
651
|
+
if "error" in auth_msg:
|
|
652
|
+
raise RuntimeError(f"Kernel auth failed: {auth_msg['error']}")
|
|
653
|
+
print(f"[relay] DEBUG: Kernel auth success")
|
|
654
|
+
|
|
655
|
+
# 2. 注册模块(不预订阅事件,由前端自己决定)
|
|
369
656
|
await self._send_to_kernel(kernel_ws, {
|
|
370
657
|
"jsonrpc": "2.0",
|
|
371
658
|
"id": str(uuid.uuid4()),
|
|
@@ -373,12 +660,6 @@ class KernelRelay:
|
|
|
373
660
|
"params": {
|
|
374
661
|
"module_id": module_id,
|
|
375
662
|
"module_type": "web_client",
|
|
376
|
-
"events_subscribe": [
|
|
377
|
-
"module.started",
|
|
378
|
-
"module.stopped",
|
|
379
|
-
"module.crashed",
|
|
380
|
-
"module.ready",
|
|
381
|
-
]
|
|
382
663
|
}
|
|
383
664
|
})
|
|
384
665
|
|
|
@@ -440,10 +721,13 @@ class KernelRelay:
|
|
|
440
721
|
|
|
441
722
|
async def _forward_client_to_kernel(self, session: SessionInfo):
|
|
442
723
|
"""转发客户端消息到 Kernel(带权限检查)"""
|
|
443
|
-
|
|
444
|
-
|
|
724
|
+
try:
|
|
725
|
+
while True:
|
|
726
|
+
raw = await session.client_ws.receive_text()
|
|
445
727
|
msg = json.loads(raw)
|
|
446
728
|
|
|
729
|
+
# print(f"[relay] Client → Kernel: {msg.get('method', msg.get('type', 'unknown'))}")
|
|
730
|
+
|
|
447
731
|
# 处理心跳 ping
|
|
448
732
|
if msg.get("type") == "ping":
|
|
449
733
|
await session.client_ws.send_json({"type": "pong"})
|
|
@@ -474,13 +758,35 @@ class KernelRelay:
|
|
|
474
758
|
await self._handle_web_rpc(session, msg)
|
|
475
759
|
continue
|
|
476
760
|
|
|
477
|
-
# 转发到 Kernel
|
|
761
|
+
# 转发到 Kernel(包括 evol.* 调用)
|
|
762
|
+
# print(f"[relay] Forwarding to Kernel: {method}")
|
|
478
763
|
await self._send_to_kernel(session.kernel_ws, msg)
|
|
764
|
+
except (WebSocketDisconnect, AttributeError, asyncio.CancelledError,
|
|
765
|
+
websockets.exceptions.ConnectionClosedOK, websockets.exceptions.ConnectionClosedError):
|
|
766
|
+
pass # 客户端断开或 Kernel 连接关闭,正常退出
|
|
479
767
|
|
|
480
768
|
async def _forward_kernel_to_client(self, session: SessionInfo):
|
|
481
769
|
"""转发 Kernel 消息到客户端"""
|
|
482
|
-
|
|
770
|
+
try:
|
|
771
|
+
async for raw in session.kernel_ws:
|
|
772
|
+
# 解析消息内容用于日志
|
|
773
|
+
# try:
|
|
774
|
+
# msg = json.loads(raw)
|
|
775
|
+
# if "method" in msg:
|
|
776
|
+
# print(f"[relay] Kernel → Client: {msg['method']} ({len(raw)} bytes)")
|
|
777
|
+
# elif "result" in msg:
|
|
778
|
+
# print(f"[relay] Kernel → Client: response id={msg.get('id')} ({len(raw)} bytes)")
|
|
779
|
+
# elif "error" in msg:
|
|
780
|
+
# print(f"[relay] Kernel → Client: error id={msg.get('id')} ({len(raw)} bytes)")
|
|
781
|
+
# else:
|
|
782
|
+
# print(f"[relay] Kernel → Client: {len(raw)} bytes")
|
|
783
|
+
# except:
|
|
784
|
+
# print(f"[relay] Kernel → Client: {len(raw)} bytes")
|
|
483
785
|
await session.client_ws.send_text(raw)
|
|
786
|
+
except (websockets.exceptions.ConnectionClosedOK, websockets.exceptions.ConnectionClosedError):
|
|
787
|
+
pass # Kernel 连接关闭,正常退出
|
|
788
|
+
except Exception:
|
|
789
|
+
pass # client_ws 断开或被置 None,正常退出
|
|
484
790
|
|
|
485
791
|
def _check_permission(self, role: str, msg: dict) -> bool:
|
|
486
792
|
"""检查 RPC 权限"""
|
|
@@ -526,9 +832,10 @@ class KernelRelay:
|
|
|
526
832
|
async def _graceful_shutdown(self, session: SessionInfo):
|
|
527
833
|
"""代表前端执行优雅退出"""
|
|
528
834
|
print(f"[relay] Graceful shutdown: {session.module_id}")
|
|
835
|
+
await self._audit_log("auth.logout", {"module_id": session.module_id, "reason": "reconnect_timeout"})
|
|
529
836
|
|
|
530
837
|
try:
|
|
531
|
-
# 发送 module.exiting
|
|
838
|
+
# 发送 module.exiting 事件(带 token_revoked 标记)
|
|
532
839
|
await self._send_to_kernel(session.kernel_ws, {
|
|
533
840
|
"jsonrpc": "2.0",
|
|
534
841
|
"id": str(uuid.uuid4()),
|
|
@@ -538,7 +845,8 @@ class KernelRelay:
|
|
|
538
845
|
"event": "module.exiting",
|
|
539
846
|
"data": {
|
|
540
847
|
"module_id": session.module_id,
|
|
541
|
-
"action": "none"
|
|
848
|
+
"action": "none",
|
|
849
|
+
"token_revoked": True
|
|
542
850
|
}
|
|
543
851
|
}
|
|
544
852
|
})
|
|
@@ -587,7 +895,7 @@ class KernelRelay:
|
|
|
587
895
|
method = method[4:]
|
|
588
896
|
|
|
589
897
|
try:
|
|
590
|
-
# 调用
|
|
898
|
+
# 调用 evol_server 的 RPC 处理器
|
|
591
899
|
if method == "list_tokens":
|
|
592
900
|
print(f"[relay] Calling list_tokens")
|
|
593
901
|
result = await self.web_server._rpc_list_tokens()
|
|
@@ -600,23 +908,25 @@ class KernelRelay:
|
|
|
600
908
|
print(f"[relay] RPC success: {method}, result keys: {list(result.keys()) if isinstance(result, dict) else type(result)}")
|
|
601
909
|
|
|
602
910
|
# 返回结果
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
911
|
+
if session.client_ws:
|
|
912
|
+
await session.client_ws.send_json({
|
|
913
|
+
"jsonrpc": "2.0",
|
|
914
|
+
"id": rpc_id,
|
|
915
|
+
"result": result
|
|
916
|
+
})
|
|
608
917
|
except Exception as e:
|
|
609
918
|
# 红色高亮显示 RPC 错误
|
|
610
919
|
print(f"\033[91m[relay] ✗ RPC 错误: {method}, 错误: {e}\033[0m")
|
|
611
|
-
#
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
"
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
920
|
+
# 返回错误(仅当连接存在时)
|
|
921
|
+
if session.client_ws:
|
|
922
|
+
await session.client_ws.send_json({
|
|
923
|
+
"jsonrpc": "2.0",
|
|
924
|
+
"id": rpc_id,
|
|
925
|
+
"error": {
|
|
926
|
+
"code": -32603,
|
|
927
|
+
"message": str(e)
|
|
928
|
+
}
|
|
929
|
+
})
|
|
620
930
|
|
|
621
931
|
async def close_all_sessions(self):
|
|
622
932
|
"""优雅关闭所有会话(用于 shutdown)"""
|
|
@@ -641,3 +951,72 @@ class KernelRelay:
|
|
|
641
951
|
|
|
642
952
|
self.sessions.clear()
|
|
643
953
|
print(f"[relay] All sessions closed")
|
|
954
|
+
|
|
955
|
+
# ── 审计日志 ──
|
|
956
|
+
|
|
957
|
+
async def _audit_log(self, event: str, data: dict):
|
|
958
|
+
"""
|
|
959
|
+
发布 auth.* 审计事件到 Kernel。
|
|
960
|
+
|
|
961
|
+
Args:
|
|
962
|
+
event: 事件名(如 auth.login, auth.logout, auth.failed)
|
|
963
|
+
data: 事件数据
|
|
964
|
+
"""
|
|
965
|
+
if not self.web_server or not self.web_server._ws:
|
|
966
|
+
return
|
|
967
|
+
try:
|
|
968
|
+
await self.web_server._publish_event(
|
|
969
|
+
self.web_server._ws,
|
|
970
|
+
event,
|
|
971
|
+
{
|
|
972
|
+
**data,
|
|
973
|
+
"timestamp": time.time(),
|
|
974
|
+
"source": "relay",
|
|
975
|
+
}
|
|
976
|
+
)
|
|
977
|
+
except Exception as e:
|
|
978
|
+
print(f"[relay] Audit log failed: {e}")
|
|
979
|
+
|
|
980
|
+
# ── 速率限制 ──
|
|
981
|
+
|
|
982
|
+
def _check_rate_limit(self, ip: str, device_id: str) -> bool:
|
|
983
|
+
"""
|
|
984
|
+
检查 (IP, device_id) 是否超出速率限制。
|
|
985
|
+
|
|
986
|
+
Returns:
|
|
987
|
+
True 表示允许,False 表示被限制
|
|
988
|
+
"""
|
|
989
|
+
key = (ip, device_id)
|
|
990
|
+
now = time.time()
|
|
991
|
+
cutoff = now - self._rate_limit_window
|
|
992
|
+
|
|
993
|
+
# 获取或创建时间戳列表
|
|
994
|
+
timestamps = self._rate_limits.get(key, [])
|
|
995
|
+
|
|
996
|
+
# 清理过期的时间戳
|
|
997
|
+
timestamps = [t for t in timestamps if t > cutoff]
|
|
998
|
+
|
|
999
|
+
# 检查是否超限
|
|
1000
|
+
if len(timestamps) >= self._rate_limit_max:
|
|
1001
|
+
self._rate_limits[key] = timestamps
|
|
1002
|
+
return False
|
|
1003
|
+
|
|
1004
|
+
# 记录本次请求
|
|
1005
|
+
timestamps.append(now)
|
|
1006
|
+
self._rate_limits[key] = timestamps
|
|
1007
|
+
|
|
1008
|
+
# 定期清理长期不活跃的 key(避免内存泄漏)
|
|
1009
|
+
if len(self._rate_limits) > 1000:
|
|
1010
|
+
self._cleanup_rate_limits(now)
|
|
1011
|
+
|
|
1012
|
+
return True
|
|
1013
|
+
|
|
1014
|
+
def _cleanup_rate_limits(self, now: float):
|
|
1015
|
+
"""清理不活跃的速率限制条目"""
|
|
1016
|
+
cutoff = now - self._rate_limit_window * 10 # 10 个窗口期未活跃则清理
|
|
1017
|
+
expired = [
|
|
1018
|
+
k for k, ts_list in self._rate_limits.items()
|
|
1019
|
+
if not ts_list or ts_list[-1] < cutoff
|
|
1020
|
+
]
|
|
1021
|
+
for k in expired:
|
|
1022
|
+
del self._rate_limits[k]
|