@agentunion/kite 1.0.7 → 1.3.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 +208 -0
- package/README.md +48 -0
- package/cli.js +1 -1
- package/extensions/agents/__init__.py +1 -0
- package/extensions/agents/assistant/__init__.py +1 -0
- package/extensions/agents/assistant/entry.py +329 -0
- package/extensions/agents/assistant/module.md +22 -0
- package/extensions/agents/assistant/server.py +197 -0
- package/extensions/channels/__init__.py +1 -0
- package/extensions/channels/acp_channel/__init__.py +1 -0
- package/extensions/channels/acp_channel/entry.py +329 -0
- package/extensions/channels/acp_channel/module.md +22 -0
- package/extensions/channels/acp_channel/server.py +197 -0
- package/extensions/event_hub_bench/entry.py +624 -379
- package/extensions/event_hub_bench/module.md +2 -1
- package/extensions/services/backup/__init__.py +1 -0
- package/extensions/services/backup/entry.py +508 -0
- package/extensions/services/backup/module.md +22 -0
- package/extensions/services/model_service/__init__.py +1 -0
- package/extensions/services/model_service/entry.py +508 -0
- package/extensions/services/model_service/module.md +22 -0
- package/extensions/services/watchdog/entry.py +468 -102
- package/extensions/services/watchdog/module.md +3 -0
- package/extensions/services/watchdog/monitor.py +170 -69
- package/extensions/services/web/__init__.py +1 -0
- package/extensions/services/web/config.yaml +149 -0
- package/extensions/services/web/entry.py +390 -0
- package/extensions/services/web/module.md +24 -0
- package/extensions/services/web/routes/__init__.py +1 -0
- package/extensions/services/web/routes/routes_call.py +189 -0
- package/extensions/services/web/routes/routes_config.py +512 -0
- package/extensions/services/web/routes/routes_contacts.py +98 -0
- package/extensions/services/web/routes/routes_devlog.py +99 -0
- package/extensions/services/web/routes/routes_phone.py +81 -0
- package/extensions/services/web/routes/routes_sms.py +48 -0
- package/extensions/services/web/routes/routes_stats.py +17 -0
- package/extensions/services/web/routes/routes_voicechat.py +554 -0
- package/extensions/services/web/routes/schemas.py +216 -0
- package/extensions/services/web/server.py +375 -0
- package/extensions/services/web/static/css/style.css +1064 -0
- package/extensions/services/web/static/index.html +1445 -0
- package/extensions/services/web/static/js/app.js +4671 -0
- package/extensions/services/web/vendor/__init__.py +1 -0
- package/extensions/services/web/vendor/bluetooth/audio.py +348 -0
- package/extensions/services/web/vendor/bluetooth/contacts.py +251 -0
- package/extensions/services/web/vendor/bluetooth/manager.py +395 -0
- package/extensions/services/web/vendor/bluetooth/sms.py +290 -0
- package/extensions/services/web/vendor/bluetooth/telephony.py +274 -0
- package/extensions/services/web/vendor/config.py +139 -0
- package/extensions/services/web/vendor/conversation/asr.py +936 -0
- package/extensions/services/web/vendor/conversation/engine.py +548 -0
- package/extensions/services/web/vendor/conversation/llm.py +534 -0
- package/extensions/services/web/vendor/conversation/mcp_tools.py +190 -0
- package/extensions/services/web/vendor/conversation/tts.py +322 -0
- package/extensions/services/web/vendor/conversation/vad.py +138 -0
- package/extensions/services/web/vendor/storage/__init__.py +1 -0
- package/extensions/services/web/vendor/storage/identity.py +312 -0
- package/extensions/services/web/vendor/storage/store.py +507 -0
- package/extensions/services/web/vendor/task/manager.py +864 -0
- package/extensions/services/web/vendor/task/models.py +45 -0
- package/extensions/services/web/vendor/task/webhook.py +263 -0
- package/extensions/services/web/vendor/tools/registry.py +321 -0
- package/kernel/__init__.py +0 -0
- package/kernel/entry.py +407 -0
- package/{core/event_hub/hub.py → kernel/event_hub.py} +62 -74
- package/kernel/module.md +33 -0
- package/{core/registry/store.py → kernel/registry_store.py} +23 -8
- package/kernel/rpc_router.py +388 -0
- package/kernel/server.py +267 -0
- package/launcher/__init__.py +10 -0
- package/launcher/__main__.py +6 -0
- package/launcher/count_lines.py +258 -0
- package/launcher/entry.py +1778 -0
- package/launcher/logging_setup.py +289 -0
- package/{core/launcher → launcher}/module_scanner.py +11 -6
- package/launcher/process_manager.py +880 -0
- package/main.py +11 -210
- package/package.json +6 -9
- package/__init__.py +0 -1
- package/__main__.py +0 -15
- package/core/event_hub/BENCHMARK.md +0 -94
- package/core/event_hub/bench.py +0 -459
- package/core/event_hub/bench_extreme.py +0 -308
- package/core/event_hub/bench_perf.py +0 -350
- package/core/event_hub/entry.py +0 -157
- package/core/event_hub/module.md +0 -20
- package/core/event_hub/server.py +0 -206
- package/core/launcher/entry.py +0 -1158
- package/core/launcher/process_manager.py +0 -470
- package/core/registry/entry.py +0 -110
- package/core/registry/module.md +0 -30
- package/core/registry/server.py +0 -289
- package/extensions/services/watchdog/server.py +0 -167
- /package/{core → extensions/services/web/vendor/bluetooth}/__init__.py +0 -0
- /package/{core/event_hub → extensions/services/web/vendor/conversation}/__init__.py +0 -0
- /package/{core/launcher → extensions/services/web/vendor/task}/__init__.py +0 -0
- /package/{core/registry → extensions/services/web/vendor/tools}/__init__.py +0 -0
- /package/{core/event_hub → kernel}/dedup.py +0 -0
- /package/{core/event_hub → kernel}/router.py +0 -0
- /package/{core/launcher → launcher}/module.md +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ACP Channel WebSocket client.
|
|
3
|
+
Connects to Kernel via WebSocket JSON-RPC 2.0 for event publishing and subscription.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import time
|
|
9
|
+
import uuid
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
|
|
12
|
+
import websockets
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AcpChannelServer:
|
|
16
|
+
|
|
17
|
+
def __init__(self, token: str = "", kernel_port: int = 0, boot_t0: float = 0):
|
|
18
|
+
self.token = token
|
|
19
|
+
self.kernel_port = kernel_port
|
|
20
|
+
self.boot_t0 = boot_t0
|
|
21
|
+
self._ws_task: asyncio.Task | None = None
|
|
22
|
+
self._test_task: asyncio.Task | None = None
|
|
23
|
+
self._ws: object | None = None
|
|
24
|
+
self._ready_sent = False
|
|
25
|
+
self._shutting_down = False
|
|
26
|
+
self._start_time = time.time()
|
|
27
|
+
|
|
28
|
+
async def run(self):
|
|
29
|
+
"""Main entry point: start WebSocket loop and test event loop."""
|
|
30
|
+
if self.kernel_port:
|
|
31
|
+
self._ws_task = asyncio.create_task(self._ws_loop())
|
|
32
|
+
self._test_task = asyncio.create_task(self._test_event_loop())
|
|
33
|
+
|
|
34
|
+
# Wait for tasks to complete
|
|
35
|
+
tasks = [t for t in [self._ws_task, self._test_task] if t]
|
|
36
|
+
if tasks:
|
|
37
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
38
|
+
|
|
39
|
+
print("[acp_channel] Shutdown complete")
|
|
40
|
+
|
|
41
|
+
# ── Kernel WebSocket client ──
|
|
42
|
+
|
|
43
|
+
async def _ws_loop(self):
|
|
44
|
+
"""Connect to Kernel, subscribe, register, and listen. Reconnect on failure."""
|
|
45
|
+
retry_delay = 0.5
|
|
46
|
+
max_delay = 30
|
|
47
|
+
while not self._shutting_down:
|
|
48
|
+
try:
|
|
49
|
+
await self._ws_connect()
|
|
50
|
+
except asyncio.CancelledError:
|
|
51
|
+
return
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(f"[acp_channel] Kernel connection error: {e}, retrying in {retry_delay:.1f}s")
|
|
54
|
+
self._ws = None
|
|
55
|
+
if self._shutting_down:
|
|
56
|
+
return
|
|
57
|
+
await asyncio.sleep(retry_delay)
|
|
58
|
+
retry_delay = min(retry_delay * 2, max_delay)
|
|
59
|
+
|
|
60
|
+
async def _ws_connect(self):
|
|
61
|
+
"""Single WebSocket session: connect, subscribe, register, ready, receive loop."""
|
|
62
|
+
url = f"ws://127.0.0.1:{self.kernel_port}/ws?token={self.token}&id=acp_channel"
|
|
63
|
+
print(f"[acp_channel] Connecting to Kernel (port {self.kernel_port})")
|
|
64
|
+
async with websockets.connect(url, open_timeout=3, ping_interval=None, ping_timeout=None, close_timeout=10) as ws:
|
|
65
|
+
self._ws = ws
|
|
66
|
+
elapsed = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
|
|
67
|
+
elapsed_str = f" ({elapsed:.1f}s)" if elapsed else ""
|
|
68
|
+
print(f"[acp_channel] Connected to Kernel{elapsed_str}")
|
|
69
|
+
|
|
70
|
+
# Step 1: Subscribe to events (先订阅)
|
|
71
|
+
await self._rpc_call(ws, "event.subscribe", {
|
|
72
|
+
"events": ["module.started", "module.stopped", "module.shutdown"],
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
# Step 2: Register to Kernel (再注册)
|
|
76
|
+
await self._rpc_call(ws, "registry.register", {
|
|
77
|
+
"module_id": "acp_channel",
|
|
78
|
+
"module_type": "channel",
|
|
79
|
+
"name": "ACP Channel",
|
|
80
|
+
"events_publish": {
|
|
81
|
+
"acp_channel.test": {},
|
|
82
|
+
},
|
|
83
|
+
"events_subscribe": [
|
|
84
|
+
"module.started",
|
|
85
|
+
"module.stopped",
|
|
86
|
+
"module.shutdown",
|
|
87
|
+
],
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
# Step 3: Publish module.ready (once)
|
|
91
|
+
if not self._ready_sent:
|
|
92
|
+
await self._rpc_call(ws, "event.publish", {
|
|
93
|
+
"event_id": str(uuid.uuid4()),
|
|
94
|
+
"event": "module.ready",
|
|
95
|
+
"data": {
|
|
96
|
+
"module_id": "acp_channel",
|
|
97
|
+
"graceful_shutdown": True,
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
self._ready_sent = True
|
|
101
|
+
elapsed = time.monotonic() - self.boot_t0 if self.boot_t0 else 0
|
|
102
|
+
elapsed_str = f" ({elapsed:.1f}s)" if elapsed else ""
|
|
103
|
+
print(f"[acp_channel] module.ready published{elapsed_str}")
|
|
104
|
+
|
|
105
|
+
# Reset retry delay on successful connection
|
|
106
|
+
retry_delay = 0.5
|
|
107
|
+
|
|
108
|
+
# Receive loop
|
|
109
|
+
async for raw in ws:
|
|
110
|
+
try:
|
|
111
|
+
msg = json.loads(raw)
|
|
112
|
+
except (json.JSONDecodeError, TypeError):
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
has_method = "method" in msg
|
|
117
|
+
has_id = "id" in msg
|
|
118
|
+
|
|
119
|
+
if has_method and not has_id:
|
|
120
|
+
# JSON-RPC Notification (event delivery)
|
|
121
|
+
params = msg.get("params", {})
|
|
122
|
+
event_name = params.get("event", "")
|
|
123
|
+
if event_name == "module.shutdown":
|
|
124
|
+
data = params.get("data", {})
|
|
125
|
+
target = data.get("module_id", "")
|
|
126
|
+
if target == "acp_channel":
|
|
127
|
+
await self._handle_shutdown(ws)
|
|
128
|
+
return
|
|
129
|
+
elif not has_method and has_id:
|
|
130
|
+
# JSON-RPC Response (to our RPC calls)
|
|
131
|
+
pass
|
|
132
|
+
except Exception as e:
|
|
133
|
+
print(f"[acp_channel] 事件处理异常(已忽略): {e}")
|
|
134
|
+
|
|
135
|
+
async def _handle_shutdown(self, ws):
|
|
136
|
+
"""Handle module.shutdown: ack → cleanup → ready → exit."""
|
|
137
|
+
print("[acp_channel] Received module.shutdown")
|
|
138
|
+
self._shutting_down = True
|
|
139
|
+
|
|
140
|
+
# Step 1: Send ack
|
|
141
|
+
await self._rpc_call(ws, "event.publish", {
|
|
142
|
+
"event_id": str(uuid.uuid4()),
|
|
143
|
+
"event": "module.shutdown.ack",
|
|
144
|
+
"data": {"module_id": "acp_channel", "estimated_cleanup": 2},
|
|
145
|
+
})
|
|
146
|
+
print("[acp_channel] shutdown ack sent")
|
|
147
|
+
|
|
148
|
+
# Step 2: Cleanup (cancel background tasks)
|
|
149
|
+
if self._test_task:
|
|
150
|
+
self._test_task.cancel()
|
|
151
|
+
|
|
152
|
+
# Step 3: Send ready (before closing WS!)
|
|
153
|
+
await self._rpc_call(ws, "event.publish", {
|
|
154
|
+
"event_id": str(uuid.uuid4()),
|
|
155
|
+
"event": "module.shutdown.ready",
|
|
156
|
+
"data": {"module_id": "acp_channel"},
|
|
157
|
+
})
|
|
158
|
+
print("[acp_channel] Shutdown ready sent")
|
|
159
|
+
|
|
160
|
+
# Step 4: Exit process
|
|
161
|
+
import sys
|
|
162
|
+
sys.exit(0)
|
|
163
|
+
|
|
164
|
+
async def _rpc_call(self, ws, method: str, params: dict = None):
|
|
165
|
+
"""Send a JSON-RPC 2.0 request."""
|
|
166
|
+
msg = {"jsonrpc": "2.0", "id": str(uuid.uuid4()), "method": method}
|
|
167
|
+
if params:
|
|
168
|
+
msg["params"] = params
|
|
169
|
+
await ws.send(json.dumps(msg))
|
|
170
|
+
|
|
171
|
+
async def _publish_event(self, event: dict):
|
|
172
|
+
"""Publish an event via JSON-RPC event.publish."""
|
|
173
|
+
if not self._ws:
|
|
174
|
+
return
|
|
175
|
+
try:
|
|
176
|
+
await self._rpc_call(self._ws, "event.publish", {
|
|
177
|
+
"event_id": str(uuid.uuid4()),
|
|
178
|
+
"event": event.get("event", ""),
|
|
179
|
+
"data": event.get("data", {}),
|
|
180
|
+
})
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"[acp_channel] Failed to publish event: {e}")
|
|
183
|
+
|
|
184
|
+
# ── Test event loop ──
|
|
185
|
+
|
|
186
|
+
async def _test_event_loop(self):
|
|
187
|
+
"""Publish a test event every 10 seconds."""
|
|
188
|
+
while True:
|
|
189
|
+
await asyncio.sleep(10)
|
|
190
|
+
await self._publish_event({
|
|
191
|
+
"event": "acp_channel.test",
|
|
192
|
+
"data": {
|
|
193
|
+
"message": "test event from acp_channel",
|
|
194
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
195
|
+
},
|
|
196
|
+
})
|
|
197
|
+
print("[acp_channel] test event published")
|