@agentunion/kite 1.4.0 → 1.5.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/CHANGELOG.md +102 -0
- package/cli.js +44 -5
- package/core/dependency_checker.py +250 -0
- package/core/env_checker.py +490 -0
- package/dependencies_lock.json +128 -0
- package/extensions/agents/assistant/server.py +33 -17
- package/extensions/channels/acp_channel/server.py +33 -17
- package/extensions/services/backup/entry.py +23 -16
- package/extensions/services/evol/auth_manager.py +443 -0
- package/extensions/services/evol/config.yaml +149 -0
- package/extensions/services/evol/config_loader.py +117 -0
- package/extensions/services/evol/entry.py +406 -0
- package/extensions/services/evol/evol_api.py +173 -0
- package/extensions/services/evol/evol_config.json5 +29 -0
- package/extensions/services/evol/migrate_tokens.py +122 -0
- package/extensions/services/evol/module.md +32 -0
- package/extensions/services/evol/pairing.py +250 -0
- package/extensions/services/evol/pairing_codes.jsonl +1 -0
- package/extensions/services/evol/relay.py +682 -0
- package/extensions/services/evol/relay_config.json5 +67 -0
- package/extensions/services/evol/routes/__init__.py +1 -0
- package/extensions/services/evol/routes/routes_management_ws.py +127 -0
- package/extensions/services/evol/routes/routes_rpc.py +89 -0
- package/extensions/services/evol/routes/routes_test.py +61 -0
- package/extensions/services/evol/server.py +875 -0
- package/extensions/services/evol/static/css/style.css +1200 -0
- package/extensions/services/evol/static/index.html +781 -0
- package/extensions/services/evol/static/index_evol.html +14 -0
- package/extensions/services/evol/static/js/app.js +6304 -0
- package/extensions/services/evol/static/js/auth.js +326 -0
- package/extensions/services/evol/static/js/dialog.js +285 -0
- package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
- package/extensions/services/evol/static/js/evol-app.js +1949 -0
- package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
- package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
- package/extensions/services/evol/static/js/kernel-client.js +396 -0
- package/extensions/services/evol/static/js/main.js +141 -0
- package/extensions/services/evol/static/js/registry-tests.js +585 -0
- package/extensions/services/evol/static/js/stats.js +217 -0
- package/extensions/services/evol/static/js/token-manager.js +175 -0
- package/extensions/services/evol/static/pairing.html +248 -0
- package/extensions/services/evol/static/test_registry.html +262 -0
- package/extensions/services/evol/static/test_relay.html +462 -0
- package/extensions/services/evol/stats_manager.py +240 -0
- package/extensions/services/model_service/entry.py +23 -1
- package/extensions/services/proxy/.claude/settings.local.json +13 -0
- package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
- package/extensions/services/proxy/_fix_prints.py +133 -0
- package/extensions/services/proxy/_fix_prints2.py +87 -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/console_auth.py +109 -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 +106 -0
- package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
- package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
- package/extensions/services/proxy/evol/server/__init__.py +1 -0
- package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -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/logs/websocket.log +260 -0
- package/extensions/services/proxy/main.py +240 -0
- package/extensions/services/proxy/requirements.txt +13 -0
- package/extensions/services/proxy/server.py +271 -0
- package/extensions/services/watchdog/entry.py +42 -16
- package/extensions/services/watchdog/module.md +1 -0
- package/extensions/services/watchdog/monitor.py +34 -4
- package/extensions/services/web/module.md +1 -1
- package/extensions/services/web/server.py +30 -18
- package/extensions/services/web/static/js/token-manager.js +10 -10
- package/kernel/entry.py +1 -1
- package/kernel/module.md +25 -1
- package/kernel/registry_store.py +2 -26
- package/kernel/rpc_router.py +36 -10
- package/kernel/server.py +106 -17
- package/kite_cli/commands/deps_install.py +67 -0
- package/kite_cli/commands/env_check.py +45 -0
- package/kite_cli/commands/prepare.py +49 -0
- package/kite_cli/commands/venv_setup.py +56 -0
- package/kite_cli/main.py +29 -1
- package/launcher/entry.py +306 -21
- package/launcher/module.md +9 -0
- package/launcher/module_scanner.py +11 -1
- package/main.py +4 -1
- package/package.json +8 -1
- package/python_version.json +4 -0
- package/requirements.txt +38 -0
- package/scripts/env-manager.js +328 -0
- package/scripts/python-env.js +79 -0
- package/scripts/scan_dependencies.py +461 -0
- package/scripts/setup-python-env.js +191 -0
package/kernel/module.md
CHANGED
|
@@ -9,6 +9,7 @@ version: 1.0.1
|
|
|
9
9
|
advertise_ip: 127.0.0.1
|
|
10
10
|
monitor: true
|
|
11
11
|
preferred_port: 0
|
|
12
|
+
display_order: 100
|
|
12
13
|
---
|
|
13
14
|
# Kernel
|
|
14
15
|
|
|
@@ -16,10 +17,11 @@ Unified infrastructure module that merges Registry (service discovery) and Event
|
|
|
16
17
|
|
|
17
18
|
## Responsibilities
|
|
18
19
|
|
|
19
|
-
- **Service Registry**: Module registration,
|
|
20
|
+
- **Service Registry**: Module registration, glob lookup, dot-path queries
|
|
20
21
|
- **Event Routing**: NATS-style wildcard subscriptions, per-subscriber queues, 1h dedup
|
|
21
22
|
- **RPC Routing**: JSON-RPC 2.0 dispatch for builtin methods + cross-module forwarding
|
|
22
23
|
- **Token Verification**: In-memory token→module_id resolution (no cross-process HTTP)
|
|
24
|
+
- **Ping/Pong Monitoring**: Active ping to all connected modules, track response latencies
|
|
23
25
|
|
|
24
26
|
## Protocol
|
|
25
27
|
|
|
@@ -34,3 +36,25 @@ Three frame types on the wire:
|
|
|
34
36
|
|
|
35
37
|
- `GET /health` — combined registry + event hub health
|
|
36
38
|
- `GET /stats` — connections, subscriptions, counters
|
|
39
|
+
|
|
40
|
+
## RPC Methods
|
|
41
|
+
|
|
42
|
+
### Registry Methods
|
|
43
|
+
- `registry.register` — Register a module
|
|
44
|
+
- `registry.deregister` — Deregister a module
|
|
45
|
+
- `registry.lookup` — Lookup modules by field/value
|
|
46
|
+
- `registry.get` — Get module info by dot-path
|
|
47
|
+
- `registry.verify` — Verify a token
|
|
48
|
+
|
|
49
|
+
### Event Methods
|
|
50
|
+
- `event.publish` — Publish an event
|
|
51
|
+
- `event.subscribe` — Subscribe to events
|
|
52
|
+
- `event.unsubscribe` — Unsubscribe from events
|
|
53
|
+
|
|
54
|
+
### Kernel Methods
|
|
55
|
+
- `kernel.ping` — Ping (used for latency measurement)
|
|
56
|
+
- `kernel.stats` — Get kernel statistics
|
|
57
|
+
- `kernel.health` — Get kernel health status
|
|
58
|
+
- `kernel.latencies` — Get ping/pong latencies for all modules
|
|
59
|
+
- `kernel.generate_tokens` — Generate tokens for modules (Launcher only)
|
|
60
|
+
- `kernel.register_tokens` — Register token mapping (Launcher only)
|
package/kernel/registry_store.py
CHANGED
|
@@ -20,9 +20,6 @@ class RegistryStore:
|
|
|
20
20
|
self.launcher_token = launcher_token
|
|
21
21
|
self.token_map: dict[str, str] = {} # module_id -> token
|
|
22
22
|
self.modules: dict[str, dict] = {} # module_id -> registration payload
|
|
23
|
-
self.heartbeats: dict[str, float] = {} # module_id -> last heartbeat timestamp
|
|
24
|
-
self.ttl = 60 # seconds before marking offline
|
|
25
|
-
self.heartbeat_interval = 30
|
|
26
23
|
self.is_debug = os.environ.get("KITE_DEBUG") == "1"
|
|
27
24
|
self.last_update_time = time.time() # Global registry update timestamp
|
|
28
25
|
|
|
@@ -59,7 +56,7 @@ class RegistryStore:
|
|
|
59
56
|
|
|
60
57
|
def register_module(self, data: dict) -> dict:
|
|
61
58
|
"""Register or update a module. Idempotent — same module_id overwrites.
|
|
62
|
-
Returns dict with
|
|
59
|
+
Returns dict with changed flag on success, raises exception on failure."""
|
|
63
60
|
# Validate required fields
|
|
64
61
|
missing = [f for f in self._REQUIRED_FIELDS if not data.get(f)]
|
|
65
62
|
if missing:
|
|
@@ -111,30 +108,20 @@ class RegistryStore:
|
|
|
111
108
|
record["status"] = "registered" # State machine: connected → registered (via register RPC)
|
|
112
109
|
record["registered_at"] = time.time()
|
|
113
110
|
self.modules[mid] = record
|
|
114
|
-
self.heartbeats[mid] = time.time()
|
|
115
111
|
|
|
116
112
|
# Update global timestamp if content changed
|
|
117
113
|
if changed:
|
|
118
114
|
self.last_update_time = time.time()
|
|
119
115
|
|
|
120
|
-
return {"
|
|
116
|
+
return {"changed": changed}
|
|
121
117
|
|
|
122
118
|
def deregister_module(self, module_id: str) -> dict:
|
|
123
119
|
"""Remove a module record immediately. Returns empty dict."""
|
|
124
120
|
self.modules.pop(module_id, None)
|
|
125
|
-
self.heartbeats.pop(module_id, None)
|
|
126
121
|
# Update global timestamp
|
|
127
122
|
self.last_update_time = time.time()
|
|
128
123
|
return {}
|
|
129
124
|
|
|
130
|
-
def heartbeat(self, module_id: str) -> dict:
|
|
131
|
-
"""Renew heartbeat for a module. Returns empty dict on success, raises exception on failure."""
|
|
132
|
-
if module_id not in self.modules:
|
|
133
|
-
raise KeyError(f"Module '{module_id}' not registered")
|
|
134
|
-
self.heartbeats[module_id] = time.time()
|
|
135
|
-
# Don't change status — heartbeat just keeps alive, doesn't upgrade state
|
|
136
|
-
return {}
|
|
137
|
-
|
|
138
125
|
def set_connected(self, module_id: str):
|
|
139
126
|
"""Mark a module as connected (WS established, not yet registered).
|
|
140
127
|
Only upgrades from offline; doesn't downgrade from registered/ready."""
|
|
@@ -157,17 +144,6 @@ class RegistryStore:
|
|
|
157
144
|
mod = self.modules.get(module_id)
|
|
158
145
|
return mod is not None and mod.get("status") == "ready"
|
|
159
146
|
|
|
160
|
-
def check_ttl(self) -> list[str]:
|
|
161
|
-
"""Mark modules as offline if heartbeat expired. Returns list of newly-offline module_ids."""
|
|
162
|
-
now = time.time()
|
|
163
|
-
expired = []
|
|
164
|
-
for mid, last in list(self.heartbeats.items()):
|
|
165
|
-
if mid in self.modules and now - last > self.ttl:
|
|
166
|
-
if self.modules[mid].get("status") not in ("offline",):
|
|
167
|
-
self.modules[mid]["status"] = "offline"
|
|
168
|
-
expired.append(mid)
|
|
169
|
-
return expired
|
|
170
|
-
|
|
171
147
|
# ── Get by dot-path ──
|
|
172
148
|
|
|
173
149
|
def get_by_path(self, path: str) -> Any | None:
|
package/kernel/rpc_router.py
CHANGED
|
@@ -83,7 +83,6 @@ class RpcRouter:
|
|
|
83
83
|
self.methods: dict[str, callable] = {
|
|
84
84
|
"registry.register": self._registry_register,
|
|
85
85
|
"registry.deregister": self._registry_deregister,
|
|
86
|
-
"registry.heartbeat": self._registry_heartbeat,
|
|
87
86
|
"registry.lookup": self._registry_lookup,
|
|
88
87
|
"registry.get": self._registry_get,
|
|
89
88
|
"registry.verify": self._registry_verify,
|
|
@@ -93,6 +92,7 @@ class RpcRouter:
|
|
|
93
92
|
"kernel.ping": self._kernel_ping,
|
|
94
93
|
"kernel.stats": self._kernel_stats,
|
|
95
94
|
"kernel.health": self._kernel_health,
|
|
95
|
+
"kernel.latencies": self._kernel_latencies,
|
|
96
96
|
"kernel.generate_tokens": self._kernel_generate_tokens,
|
|
97
97
|
"kernel.register_tokens": self._kernel_register_tokens,
|
|
98
98
|
}
|
|
@@ -218,6 +218,11 @@ class RpcRouter:
|
|
|
218
218
|
# Strip target prefix from method for the forwarded request
|
|
219
219
|
actual_method = method[len(target) + 1:] # e.g. "watchdog.get_status" -> "get_status"
|
|
220
220
|
|
|
221
|
+
# Inject caller_id into params for permission checking
|
|
222
|
+
if not isinstance(params, dict):
|
|
223
|
+
params = {}
|
|
224
|
+
params["_caller_id"] = caller_id
|
|
225
|
+
|
|
221
226
|
# Record pending forward
|
|
222
227
|
loop = asyncio.get_event_loop()
|
|
223
228
|
pending = PendingForward(
|
|
@@ -324,15 +329,6 @@ class RpcRouter:
|
|
|
324
329
|
|
|
325
330
|
return {}
|
|
326
331
|
|
|
327
|
-
async def _registry_heartbeat(self, caller_id: str, params: dict) -> dict:
|
|
328
|
-
mid = params.get("module_id")
|
|
329
|
-
if not mid:
|
|
330
|
-
raise ValueError("module_id required")
|
|
331
|
-
if caller_id != "launcher" and caller_id != mid:
|
|
332
|
-
raise PermissionError(f"Module '{caller_id}' cannot heartbeat for '{mid}'")
|
|
333
|
-
|
|
334
|
-
return self.registry.heartbeat(mid)
|
|
335
|
-
|
|
336
332
|
async def _registry_lookup(self, caller_id: str, params: dict) -> dict:
|
|
337
333
|
field = params.get("field")
|
|
338
334
|
module = params.get("module")
|
|
@@ -382,7 +378,9 @@ class RpcRouter:
|
|
|
382
378
|
# When a module publishes module.ready, update its status in registry
|
|
383
379
|
if event_type == "module.ready":
|
|
384
380
|
mid = (data or {}).get("module_id", caller_id)
|
|
381
|
+
print(f"[kernel] DEBUG: 收到 module.ready 事件,module_id={mid}, caller_id={caller_id}")
|
|
385
382
|
self.registry.set_ready(mid)
|
|
383
|
+
print(f"[kernel] DEBUG: 已调用 set_ready({mid})")
|
|
386
384
|
|
|
387
385
|
return self.event_hub.publish_event(caller_id, event_id, event_type, data, echo)
|
|
388
386
|
|
|
@@ -428,6 +426,34 @@ class RpcRouter:
|
|
|
428
426
|
"event_stats": eh_health.get("details", {}),
|
|
429
427
|
}
|
|
430
428
|
|
|
429
|
+
async def _kernel_latencies(self, caller_id: str, params: dict) -> dict:
|
|
430
|
+
"""Get ping/pong latencies for all connected modules.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
{
|
|
434
|
+
"latencies": {
|
|
435
|
+
"module1": {
|
|
436
|
+
"outbound": 12.34, # ms, Kernel → module
|
|
437
|
+
"inbound": 23.45, # ms, module → Kernel
|
|
438
|
+
"status": "ok" | "timeout" | "never"
|
|
439
|
+
},
|
|
440
|
+
...
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
"""
|
|
444
|
+
result = {}
|
|
445
|
+
for module_id in self.kernel_server.connections.keys():
|
|
446
|
+
latency_data = self.kernel_server._pong_latencies.get(module_id, {})
|
|
447
|
+
status = self.kernel_server._pong_status.get(module_id, "never")
|
|
448
|
+
|
|
449
|
+
result[module_id] = {
|
|
450
|
+
"outbound": latency_data.get("outbound"),
|
|
451
|
+
"inbound": latency_data.get("inbound"),
|
|
452
|
+
"status": status,
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return {"latencies": result}
|
|
456
|
+
|
|
431
457
|
async def _kernel_generate_tokens(self, caller_id: str, params: dict) -> dict:
|
|
432
458
|
"""Generate tokens for a list of module names.
|
|
433
459
|
|
package/kernel/server.py
CHANGED
|
@@ -33,12 +33,13 @@ class KernelServer:
|
|
|
33
33
|
- Event notifications (delivered to subscribers)
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
-
def __init__(self, launcher_token: str = None, advertise_ip: str = "127.0.0.1", module_id: str = None):
|
|
36
|
+
def __init__(self, launcher_token: str = None, advertise_ip: str = "127.0.0.1", module_id: str = None, boot_t0: float = 0):
|
|
37
37
|
if module_id is None:
|
|
38
38
|
raise ValueError("module_id is required")
|
|
39
39
|
self.module_id = module_id
|
|
40
40
|
self.advertise_ip = advertise_ip
|
|
41
41
|
self.port: int = 0 # set by entry.py before uvicorn.run
|
|
42
|
+
self.boot_t0 = boot_t0 # Startup time for ready event
|
|
42
43
|
|
|
43
44
|
# Core components
|
|
44
45
|
self.registry = RegistryStore(launcher_token) # Can be None
|
|
@@ -57,7 +58,6 @@ class KernelServer:
|
|
|
57
58
|
)
|
|
58
59
|
|
|
59
60
|
# Background tasks
|
|
60
|
-
self._ttl_task: asyncio.Task | None = None
|
|
61
61
|
self._dedup_task: asyncio.Task | None = None
|
|
62
62
|
self._uvicorn_server = None # set by entry.py for graceful shutdown
|
|
63
63
|
self._shutting_down = False
|
|
@@ -69,7 +69,7 @@ class KernelServer:
|
|
|
69
69
|
|
|
70
70
|
# Subscribe to events that Kernel needs to handle
|
|
71
71
|
# Kernel 通过订阅机制接收事件(与其他模块一致)
|
|
72
|
-
self.event_hub.handle_subscribe(self.module_id, ["module.shutdown"])
|
|
72
|
+
self.event_hub.handle_subscribe(self.module_id, ["module.shutdown", "system.pong"])
|
|
73
73
|
|
|
74
74
|
# Register internal event callback for Kernel
|
|
75
75
|
# Kernel 自身没有 WebSocket 连接,通过回调机制接收订阅的事件
|
|
@@ -80,6 +80,12 @@ class KernelServer:
|
|
|
80
80
|
# Launcher loss timer (35s after launcher offline)
|
|
81
81
|
self._launcher_loss_task: asyncio.Task | None = None
|
|
82
82
|
|
|
83
|
+
# Ping/Pong tracking
|
|
84
|
+
self._ping_sent_times: dict[str, float] = {} # module_id -> last ping sent time (t1)
|
|
85
|
+
self._pong_latencies: dict[str, dict] = {} # module_id -> {"outbound": ms, "inbound": ms, "last_update": timestamp}
|
|
86
|
+
self._pong_status: dict[str, str] = {} # module_id -> "ok" | "timeout" | "never"
|
|
87
|
+
self._ping_task: asyncio.Task | None = None # Global ping broadcast task
|
|
88
|
+
|
|
83
89
|
# Build FastAPI app
|
|
84
90
|
self.app = self._create_app()
|
|
85
91
|
|
|
@@ -92,15 +98,15 @@ class KernelServer:
|
|
|
92
98
|
@app.on_event("startup")
|
|
93
99
|
async def _startup():
|
|
94
100
|
server.event_hub.start_internal_senders()
|
|
95
|
-
server._ttl_task = asyncio.create_task(server._ttl_loop())
|
|
96
101
|
server._dedup_task = asyncio.create_task(server._dedup_loop())
|
|
102
|
+
server._ping_task = asyncio.create_task(server._ping_broadcast_loop())
|
|
97
103
|
|
|
98
104
|
@app.on_event("shutdown")
|
|
99
105
|
async def _shutdown():
|
|
100
|
-
if server._ttl_task:
|
|
101
|
-
server._ttl_task.cancel()
|
|
102
106
|
if server._dedup_task:
|
|
103
107
|
server._dedup_task.cancel()
|
|
108
|
+
if server._ping_task:
|
|
109
|
+
server._ping_task.cancel()
|
|
104
110
|
|
|
105
111
|
# ── WebSocket endpoint ──
|
|
106
112
|
|
|
@@ -150,9 +156,9 @@ class KernelServer:
|
|
|
150
156
|
print(f"[kernel] launcher reconnected, cancelled loss timer")
|
|
151
157
|
print(f"[kernel] launcher connected")
|
|
152
158
|
|
|
153
|
-
#
|
|
154
|
-
if module_id in server.
|
|
155
|
-
server.
|
|
159
|
+
# Initialize ping status for new connection
|
|
160
|
+
if module_id not in server._pong_status:
|
|
161
|
+
server._pong_status[module_id] = "never"
|
|
156
162
|
|
|
157
163
|
try:
|
|
158
164
|
while True:
|
|
@@ -243,17 +249,54 @@ class KernelServer:
|
|
|
243
249
|
|
|
244
250
|
# ── Background loops ──
|
|
245
251
|
|
|
246
|
-
async def
|
|
247
|
-
"""
|
|
252
|
+
async def _ping_broadcast_loop(self):
|
|
253
|
+
"""Broadcast system.ping event every 20s to all connected modules."""
|
|
254
|
+
import time
|
|
255
|
+
import uuid
|
|
248
256
|
while True:
|
|
249
|
-
await asyncio.sleep(10)
|
|
250
257
|
try:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
258
|
+
await asyncio.sleep(20)
|
|
259
|
+
|
|
260
|
+
# Record ping send time for all connected modules
|
|
261
|
+
t1 = time.time()
|
|
262
|
+
for module_id in list(self.connections.keys()):
|
|
263
|
+
self._ping_sent_times[module_id] = t1
|
|
264
|
+
# Mark as timeout if previous ping didn't get pong
|
|
265
|
+
if module_id in self._pong_status:
|
|
266
|
+
if self._pong_status[module_id] == "ok":
|
|
267
|
+
# Previous was ok, now waiting for new pong
|
|
268
|
+
pass
|
|
269
|
+
elif self._pong_status[module_id] == "never":
|
|
270
|
+
# Still never received pong
|
|
271
|
+
pass
|
|
272
|
+
# If was timeout, keep it as timeout until pong arrives
|
|
273
|
+
|
|
274
|
+
# Broadcast system.ping event
|
|
275
|
+
self.event_hub.publish_internal(
|
|
276
|
+
"system.ping",
|
|
277
|
+
{"ping_time": t1},
|
|
278
|
+
source=self.module_id
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Check for timeouts after 20s
|
|
282
|
+
await asyncio.sleep(20)
|
|
283
|
+
t_check = time.time()
|
|
284
|
+
for module_id in list(self.connections.keys()):
|
|
285
|
+
if module_id in self._ping_sent_times:
|
|
286
|
+
# If no pong received within 20s, mark as timeout
|
|
287
|
+
if module_id not in self._pong_latencies or \
|
|
288
|
+
self._pong_latencies[module_id].get("last_update", 0) < self._ping_sent_times[module_id]:
|
|
289
|
+
if self._pong_status.get(module_id) == "never":
|
|
290
|
+
# Keep as "never" if never received
|
|
291
|
+
pass
|
|
292
|
+
else:
|
|
293
|
+
self._pong_status[module_id] = "timeout"
|
|
294
|
+
print(f"[kernel] {module_id} ping timeout (no pong in 20s)")
|
|
295
|
+
|
|
296
|
+
except asyncio.CancelledError:
|
|
297
|
+
break
|
|
255
298
|
except Exception as e:
|
|
256
|
-
print(f"[kernel]
|
|
299
|
+
print(f"[kernel] Ping broadcast loop error: {e}")
|
|
257
300
|
|
|
258
301
|
async def _dedup_loop(self):
|
|
259
302
|
"""Clean up dedup table every 30s."""
|
|
@@ -325,10 +368,13 @@ class KernelServer:
|
|
|
325
368
|
|
|
326
369
|
def publish_ready(self):
|
|
327
370
|
"""Publish module.ready event for Kernel (internal, no WS needed)."""
|
|
371
|
+
import time
|
|
372
|
+
startup_time = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
|
|
328
373
|
self.event_hub.publish_internal("module.ready", {
|
|
329
374
|
"module_id": self.module_id,
|
|
330
375
|
"ws_endpoint": f"ws://{self.advertise_ip}:{self.port}/ws",
|
|
331
376
|
"graceful_shutdown": True,
|
|
377
|
+
"startup_time": startup_time,
|
|
332
378
|
}, source=self.module_id)
|
|
333
379
|
|
|
334
380
|
async def _handle_internal_event(self, event_type: str, data: dict):
|
|
@@ -347,6 +393,8 @@ class KernelServer:
|
|
|
347
393
|
# 处理 Kernel 订阅的事件
|
|
348
394
|
if event_type == "module.shutdown":
|
|
349
395
|
await self.handle_shutdown_event(data)
|
|
396
|
+
elif event_type == "system.pong":
|
|
397
|
+
await self._handle_pong_event(data)
|
|
350
398
|
# 忽略系统广播事件(Kernel 没有订阅但会收到广播)
|
|
351
399
|
elif event_type in ("module.ready", "module.registered", "module.started",
|
|
352
400
|
"module.stopped", "module.crashed", "module.exiting",
|
|
@@ -363,6 +411,47 @@ class KernelServer:
|
|
|
363
411
|
traceback.print_exc()
|
|
364
412
|
raise # 重新抛出,让 EventHub 的包装器记录
|
|
365
413
|
|
|
414
|
+
async def _handle_pong_event(self, data: dict):
|
|
415
|
+
"""处理 system.pong 事件,计算往返延迟
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
data: {
|
|
419
|
+
"module_id": str,
|
|
420
|
+
"ping_time": float, # t1 - Kernel 发送 ping 的时间
|
|
421
|
+
"pong_time": float, # t2 - 模块收到 ping 并发送 pong 的时间
|
|
422
|
+
}
|
|
423
|
+
"""
|
|
424
|
+
import time
|
|
425
|
+
module_id = data.get("module_id")
|
|
426
|
+
t1 = data.get("ping_time") # Kernel 发送 ping 的时间
|
|
427
|
+
t2 = data.get("pong_time") # 模块收到 ping 并发送 pong 的时间
|
|
428
|
+
t3 = time.time() # Kernel 收到 pong 的时间
|
|
429
|
+
|
|
430
|
+
if not module_id or t1 is None or t2 is None:
|
|
431
|
+
print(f"[kernel] Invalid pong event data: {data}")
|
|
432
|
+
return
|
|
433
|
+
|
|
434
|
+
# 验证 ping_time 是否匹配
|
|
435
|
+
if module_id not in self._ping_sent_times:
|
|
436
|
+
print(f"[kernel] Received pong from {module_id} but no ping record")
|
|
437
|
+
return
|
|
438
|
+
|
|
439
|
+
expected_t1 = self._ping_sent_times[module_id]
|
|
440
|
+
if abs(t1 - expected_t1) > 0.1: # 允许 100ms 误差
|
|
441
|
+
print(f"[kernel] Pong from {module_id} has mismatched ping_time (expected {expected_t1}, got {t1})")
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
# 计算两个延迟
|
|
445
|
+
outbound_ms = (t2 - t1) * 1000 # 去程:Kernel → 模块
|
|
446
|
+
inbound_ms = (t3 - t2) * 1000 # 回程:模块 → Kernel
|
|
447
|
+
|
|
448
|
+
self._pong_latencies[module_id] = {
|
|
449
|
+
"outbound": round(outbound_ms, 2),
|
|
450
|
+
"inbound": round(inbound_ms, 2),
|
|
451
|
+
"last_update": t3,
|
|
452
|
+
}
|
|
453
|
+
self._pong_status[module_id] = "ok"
|
|
454
|
+
|
|
366
455
|
async def handle_shutdown_event(self, event_data):
|
|
367
456
|
"""处理 module.shutdown 事件(标准优雅退出流程)
|
|
368
457
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
kite deps-install 命令
|
|
3
|
+
|
|
4
|
+
只负责安装依赖库
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_deps_install(args):
|
|
12
|
+
"""安装依赖库"""
|
|
13
|
+
project_root = Path(__file__).parent.parent.parent
|
|
14
|
+
sys.path.insert(0, str(project_root))
|
|
15
|
+
|
|
16
|
+
from core.env_checker import (
|
|
17
|
+
check_dependencies_installed,
|
|
18
|
+
install_dependencies,
|
|
19
|
+
load_dependencies_lock
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
print("=" * 60)
|
|
23
|
+
print(" Kite 依赖库安装")
|
|
24
|
+
print("=" * 60)
|
|
25
|
+
print()
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
# 1. 加载依赖锁定
|
|
29
|
+
lock_data = load_dependencies_lock()
|
|
30
|
+
if not lock_data:
|
|
31
|
+
print("✓ 无依赖锁定文件,跳过")
|
|
32
|
+
return 0
|
|
33
|
+
|
|
34
|
+
dependencies = lock_data.get("dependencies", {})
|
|
35
|
+
if not dependencies:
|
|
36
|
+
print("✓ 无依赖要求,跳过")
|
|
37
|
+
return 0
|
|
38
|
+
|
|
39
|
+
print(f"依赖锁定文件: {len(dependencies)} 个依赖")
|
|
40
|
+
|
|
41
|
+
# 2. 检查依赖
|
|
42
|
+
deps_ok, deps_msg, deps_details = check_dependencies_installed()
|
|
43
|
+
if deps_ok:
|
|
44
|
+
print(f"✓ 依赖库已安装且版本一致")
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
# 3. 安装依赖
|
|
48
|
+
print(f"✗ {deps_msg}")
|
|
49
|
+
if deps_details:
|
|
50
|
+
if deps_details.get("missing"):
|
|
51
|
+
print(f" 缺失: {len(deps_details['missing'])} 个")
|
|
52
|
+
if deps_details.get("version_mismatch"):
|
|
53
|
+
print(f" 版本不匹配: {len(deps_details['version_mismatch'])} 个")
|
|
54
|
+
|
|
55
|
+
print(f"\n正在安装依赖...")
|
|
56
|
+
if not install_dependencies():
|
|
57
|
+
print("✗ 依赖安装失败")
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
print(f"✓ 依赖安装成功")
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print(f"\n✗ 依赖安装失败: {e}")
|
|
65
|
+
import traceback
|
|
66
|
+
traceback.print_exc()
|
|
67
|
+
return 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
kite env-check 命令
|
|
3
|
+
|
|
4
|
+
环境检查和准备:
|
|
5
|
+
1. 检查虚拟环境是否存在
|
|
6
|
+
2. 检查依赖库是否安装且版本一致
|
|
7
|
+
3. 创建虚拟环境(如果需要)
|
|
8
|
+
4. 安装依赖库(如果需要)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run_env_check(args):
|
|
16
|
+
"""执行环境检查"""
|
|
17
|
+
# 导入环境检查器
|
|
18
|
+
project_root = Path(__file__).parent.parent.parent
|
|
19
|
+
|
|
20
|
+
# 添加到 sys.path
|
|
21
|
+
sys.path.insert(0, str(project_root))
|
|
22
|
+
|
|
23
|
+
# 导入并执行环境检查
|
|
24
|
+
from core.env_checker import check_environment
|
|
25
|
+
|
|
26
|
+
print("=" * 60)
|
|
27
|
+
print(" Kite 环境检查")
|
|
28
|
+
print("=" * 60)
|
|
29
|
+
print()
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
success = check_environment()
|
|
33
|
+
|
|
34
|
+
if success:
|
|
35
|
+
print("\n✓ 环境检查通过")
|
|
36
|
+
return 0
|
|
37
|
+
else:
|
|
38
|
+
print("\n✗ 环境检查失败")
|
|
39
|
+
return 1
|
|
40
|
+
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(f"\n✗ 环境检查异常: {e}")
|
|
43
|
+
import traceback
|
|
44
|
+
traceback.print_exc()
|
|
45
|
+
return 1
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
kite prepare 命令
|
|
3
|
+
|
|
4
|
+
打包前的准备工作:
|
|
5
|
+
1. 扫描所有模块的依赖
|
|
6
|
+
2. 生成 dependencies_lock.json
|
|
7
|
+
3. 更新 python_version.json
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def run_prepare(args):
|
|
15
|
+
"""执行打包准备"""
|
|
16
|
+
# 导入扫描脚本
|
|
17
|
+
project_root = Path(__file__).parent.parent.parent
|
|
18
|
+
|
|
19
|
+
# 添加到 sys.path
|
|
20
|
+
sys.path.insert(0, str(project_root))
|
|
21
|
+
|
|
22
|
+
# 导入并执行扫描
|
|
23
|
+
from scripts.scan_dependencies import main as scan_main
|
|
24
|
+
|
|
25
|
+
print("=" * 60)
|
|
26
|
+
print(" Kite 打包准备")
|
|
27
|
+
print("=" * 60)
|
|
28
|
+
print()
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
scan_main()
|
|
32
|
+
print()
|
|
33
|
+
print("✓ 打包准备完成")
|
|
34
|
+
print()
|
|
35
|
+
print("生成的文件:")
|
|
36
|
+
print(f" - {project_root / 'dependencies_lock.json'}")
|
|
37
|
+
print(f" - {project_root / 'python_version.json'}")
|
|
38
|
+
print()
|
|
39
|
+
print("下一步:")
|
|
40
|
+
print(" 1. 检查生成的文件")
|
|
41
|
+
print(" 2. 提交到 git: git add dependencies_lock.json python_version.json")
|
|
42
|
+
print(" 3. 发布: npm publish")
|
|
43
|
+
return 0
|
|
44
|
+
|
|
45
|
+
except Exception as e:
|
|
46
|
+
print(f"\n✗ 打包准备失败: {e}")
|
|
47
|
+
import traceback
|
|
48
|
+
traceback.print_exc()
|
|
49
|
+
return 1
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
kite venv-setup 命令
|
|
3
|
+
|
|
4
|
+
只负责创建虚拟环境
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_venv_setup(args):
|
|
12
|
+
"""创建虚拟环境"""
|
|
13
|
+
project_root = Path(__file__).parent.parent.parent
|
|
14
|
+
sys.path.insert(0, str(project_root))
|
|
15
|
+
|
|
16
|
+
from core.env_checker import (
|
|
17
|
+
get_venv_path,
|
|
18
|
+
check_venv_exists,
|
|
19
|
+
create_venv,
|
|
20
|
+
check_python_version
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
print("=" * 60)
|
|
24
|
+
print(" Kite 虚拟环境准备")
|
|
25
|
+
print("=" * 60)
|
|
26
|
+
print()
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
# 1. 检查 Python 版本
|
|
30
|
+
py_ok, py_version, required_version = check_python_version()
|
|
31
|
+
if not py_ok:
|
|
32
|
+
print(f"✗ Python 版本不匹配: {py_version}(需要 {required_version})")
|
|
33
|
+
return 1
|
|
34
|
+
|
|
35
|
+
print(f"✓ Python 版本: {py_version}")
|
|
36
|
+
|
|
37
|
+
# 2. 检查虚拟环境
|
|
38
|
+
venv_ok, venv_msg = check_venv_exists()
|
|
39
|
+
if venv_ok:
|
|
40
|
+
print(f"✓ 虚拟环境已存在: {get_venv_path()}")
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
# 3. 创建虚拟环境
|
|
44
|
+
print(f"正在创建虚拟环境...")
|
|
45
|
+
if not create_venv():
|
|
46
|
+
print("✗ 虚拟环境创建失败")
|
|
47
|
+
return 1
|
|
48
|
+
|
|
49
|
+
print(f"✓ 虚拟环境创建成功: {get_venv_path()}")
|
|
50
|
+
return 0
|
|
51
|
+
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(f"\n✗ 虚拟环境准备失败: {e}")
|
|
54
|
+
import traceback
|
|
55
|
+
traceback.print_exc()
|
|
56
|
+
return 1
|