@agentunion/kite 1.0.6 → 1.2.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/cli.js +127 -25
- package/core/event_hub/entry.py +384 -61
- package/core/event_hub/hub.py +8 -0
- package/core/event_hub/module.md +0 -1
- package/core/event_hub/server.py +169 -38
- package/core/kite_log.py +241 -0
- package/core/launcher/entry.py +1306 -425
- package/core/launcher/module_scanner.py +10 -9
- package/core/launcher/process_manager.py +555 -121
- package/core/registry/entry.py +335 -30
- package/core/registry/server.py +339 -256
- package/core/registry/store.py +13 -2
- package/extensions/agents/__init__.py +1 -0
- package/extensions/agents/assistant/__init__.py +1 -0
- package/extensions/agents/assistant/entry.py +380 -0
- package/extensions/agents/assistant/module.md +22 -0
- package/extensions/agents/assistant/server.py +236 -0
- package/extensions/channels/__init__.py +1 -0
- package/extensions/channels/acp_channel/__init__.py +1 -0
- package/extensions/channels/acp_channel/entry.py +380 -0
- package/extensions/channels/acp_channel/module.md +22 -0
- package/extensions/channels/acp_channel/server.py +236 -0
- package/{core → extensions}/event_hub_bench/entry.py +664 -371
- package/{core → extensions}/event_hub_bench/module.md +4 -2
- package/extensions/services/backup/__init__.py +1 -0
- package/extensions/services/backup/entry.py +380 -0
- package/extensions/services/backup/module.md +22 -0
- package/extensions/services/backup/server.py +244 -0
- package/extensions/services/model_service/__init__.py +1 -0
- package/extensions/services/model_service/entry.py +380 -0
- package/extensions/services/model_service/module.md +22 -0
- package/extensions/services/model_service/server.py +236 -0
- package/extensions/services/watchdog/entry.py +460 -143
- package/extensions/services/watchdog/module.md +3 -0
- package/extensions/services/watchdog/monitor.py +128 -13
- package/extensions/services/watchdog/server.py +75 -13
- package/extensions/services/web/__init__.py +1 -0
- package/extensions/services/web/config.yaml +149 -0
- package/extensions/services/web/entry.py +487 -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 +332 -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/__init__.py +0 -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/__init__.py +0 -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/__init__.py +0 -0
- package/extensions/services/web/vendor/tools/registry.py +321 -0
- package/main.py +344 -4
- package/package.json +11 -2
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/data_dir.cpython-313.pyc +0 -0
- package/core/data_dir.py +0 -62
- package/core/event_hub/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/bench.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/bench_perf.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/dedup.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/entry.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/hub.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/router.cpython-313.pyc +0 -0
- package/core/event_hub/__pycache__/server.cpython-313.pyc +0 -0
- package/core/event_hub/bench_results/2026-02-28_13-26-48.json +0 -51
- package/core/event_hub/bench_results/2026-02-28_13-44-45.json +0 -51
- package/core/event_hub/bench_results/2026-02-28_13-45-39.json +0 -51
- package/core/launcher/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/launcher/__pycache__/entry.cpython-313.pyc +0 -0
- package/core/launcher/__pycache__/module_scanner.cpython-313.pyc +0 -0
- package/core/launcher/__pycache__/process_manager.cpython-313.pyc +0 -0
- package/core/launcher/data/log/lifecycle.jsonl +0 -1158
- package/core/launcher/data/token.txt +0 -1
- package/core/registry/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/registry/__pycache__/entry.cpython-313.pyc +0 -0
- package/core/registry/__pycache__/server.cpython-313.pyc +0 -0
- package/core/registry/__pycache__/store.cpython-313.pyc +0 -0
- package/core/registry/data/port.txt +0 -1
- package/core/registry/data/port_484.txt +0 -1
- package/extensions/__pycache__/__init__.cpython-313.pyc +0 -0
- package/extensions/services/__pycache__/__init__.cpython-313.pyc +0 -0
- package/extensions/services/watchdog/__pycache__/__init__.cpython-313.pyc +0 -0
- package/extensions/services/watchdog/__pycache__/entry.cpython-313.pyc +0 -0
- package/extensions/services/watchdog/__pycache__/monitor.cpython-313.pyc +0 -0
- package/extensions/services/watchdog/__pycache__/server.cpython-313.pyc +0 -0
- /package/{core/event_hub/bench_results/.gitkeep → extensions/services/web/vendor/bluetooth/__init__.py} +0 -0
package/core/registry/entry.py
CHANGED
|
@@ -1,17 +1,249 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Registry entry point.
|
|
3
|
-
Reads
|
|
3
|
+
Reads token from stdin boot_info, starts HTTP server on dynamic port,
|
|
4
|
+
outputs port via stdout structured message, waits for Event Hub to trigger module.ready.
|
|
4
5
|
"""
|
|
5
6
|
|
|
7
|
+
import builtins
|
|
8
|
+
import json
|
|
6
9
|
import os
|
|
10
|
+
import re
|
|
7
11
|
import sys
|
|
8
12
|
import socket
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
import traceback
|
|
16
|
+
from datetime import datetime, timezone
|
|
9
17
|
|
|
10
18
|
import uvicorn
|
|
11
19
|
|
|
12
|
-
|
|
20
|
+
|
|
21
|
+
# ── Module configuration ──
|
|
22
|
+
MODULE_NAME = "registry"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _fmt_elapsed(t0: float) -> str:
|
|
26
|
+
"""Format elapsed time since t0: <1s → 'NNNms', >=1s → 'N.Ns', >=10s → 'NNs'."""
|
|
27
|
+
d = time.monotonic() - t0
|
|
28
|
+
if d < 1:
|
|
29
|
+
return f"{d * 1000:.0f}ms"
|
|
30
|
+
if d < 10:
|
|
31
|
+
return f"{d:.1f}s"
|
|
32
|
+
return f"{d:.0f}s"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ── Safe stdout/stderr: ignore BrokenPipeError after Launcher closes stdio ──
|
|
36
|
+
|
|
37
|
+
class _SafeWriter:
|
|
38
|
+
"""Wraps a stream to silently swallow BrokenPipeError on write/flush."""
|
|
39
|
+
def __init__(self, stream):
|
|
40
|
+
self._stream = stream
|
|
41
|
+
|
|
42
|
+
def write(self, s):
|
|
43
|
+
try:
|
|
44
|
+
self._stream.write(s)
|
|
45
|
+
except (BrokenPipeError, OSError):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def flush(self):
|
|
49
|
+
try:
|
|
50
|
+
self._stream.flush()
|
|
51
|
+
except (BrokenPipeError, OSError):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def __getattr__(self, name):
|
|
55
|
+
return getattr(self._stream, name)
|
|
56
|
+
|
|
57
|
+
sys.stdout = _SafeWriter(sys.stdout)
|
|
58
|
+
sys.stderr = _SafeWriter(sys.stderr)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ── Timestamped print + log file writer ──
|
|
62
|
+
# Implements format: [timestamp] HH:MM:SS.mmm +delta [module_name] message
|
|
63
|
+
# Independent implementation per module (no shared code dependency)
|
|
64
|
+
|
|
65
|
+
_builtin_print = builtins.print
|
|
66
|
+
_start_ts = time.monotonic()
|
|
67
|
+
_last_ts = time.monotonic()
|
|
68
|
+
_ANSI_RE = re.compile(r"\033\[[0-9;]*m")
|
|
69
|
+
_log_lock = threading.Lock()
|
|
70
|
+
_log_latest_path = None
|
|
71
|
+
_log_daily_path = None
|
|
72
|
+
_log_daily_date = ""
|
|
73
|
+
_log_dir = None
|
|
74
|
+
_crash_log_path = None
|
|
75
|
+
|
|
76
|
+
def _strip_ansi(s: str) -> str:
|
|
77
|
+
return _ANSI_RE.sub("", s)
|
|
78
|
+
|
|
79
|
+
def _resolve_daily_log_path():
|
|
80
|
+
"""Resolve daily log path based on current date."""
|
|
81
|
+
global _log_daily_path, _log_daily_date
|
|
82
|
+
if not _log_dir:
|
|
83
|
+
return
|
|
84
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
85
|
+
if today == _log_daily_date and _log_daily_path:
|
|
86
|
+
return
|
|
87
|
+
month_dir = os.path.join(_log_dir, today[:7])
|
|
88
|
+
os.makedirs(month_dir, exist_ok=True)
|
|
89
|
+
_log_daily_path = os.path.join(month_dir, f"{today}.log")
|
|
90
|
+
_log_daily_date = today
|
|
91
|
+
|
|
92
|
+
def _write_log(plain_line: str):
|
|
93
|
+
"""Write a plain-text line to both latest.log and daily log."""
|
|
94
|
+
with _log_lock:
|
|
95
|
+
if _log_latest_path:
|
|
96
|
+
try:
|
|
97
|
+
with open(_log_latest_path, "a", encoding="utf-8") as f:
|
|
98
|
+
f.write(plain_line)
|
|
99
|
+
except Exception:
|
|
100
|
+
pass
|
|
101
|
+
_resolve_daily_log_path()
|
|
102
|
+
if _log_daily_path:
|
|
103
|
+
try:
|
|
104
|
+
with open(_log_daily_path, "a", encoding="utf-8") as f:
|
|
105
|
+
f.write(plain_line)
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def _write_crash(exc_type, exc_value, exc_tb, thread_name=None, severity="critical", handled=False):
|
|
110
|
+
"""Write crash record to crashes.jsonl + daily crash archive."""
|
|
111
|
+
record = {
|
|
112
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
113
|
+
"module": MODULE_NAME,
|
|
114
|
+
"thread": thread_name or threading.current_thread().name,
|
|
115
|
+
"exception_type": exc_type.__name__ if exc_type else "Unknown",
|
|
116
|
+
"exception_message": str(exc_value),
|
|
117
|
+
"traceback": "".join(traceback.format_exception(exc_type, exc_value, exc_tb)),
|
|
118
|
+
"severity": severity,
|
|
119
|
+
"handled": handled,
|
|
120
|
+
"process_id": os.getpid(),
|
|
121
|
+
"platform": sys.platform,
|
|
122
|
+
"runtime_version": f"Python {sys.version.split()[0]}",
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if exc_tb:
|
|
126
|
+
tb_entries = traceback.extract_tb(exc_tb)
|
|
127
|
+
if tb_entries:
|
|
128
|
+
last = tb_entries[-1]
|
|
129
|
+
record["context"] = {
|
|
130
|
+
"function": last.name,
|
|
131
|
+
"file": os.path.basename(last.filename),
|
|
132
|
+
"line": last.lineno,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
line = json.dumps(record, ensure_ascii=False) + "\n"
|
|
136
|
+
|
|
137
|
+
# 1. Write to crashes.jsonl (current run)
|
|
138
|
+
if _crash_log_path:
|
|
139
|
+
try:
|
|
140
|
+
with open(_crash_log_path, "a", encoding="utf-8") as f:
|
|
141
|
+
f.write(line)
|
|
142
|
+
except Exception:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
# 2. Write to daily crash archive
|
|
146
|
+
if _log_dir:
|
|
147
|
+
try:
|
|
148
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
149
|
+
archive_dir = os.path.join(_log_dir, "crashes", today[:7])
|
|
150
|
+
os.makedirs(archive_dir, exist_ok=True)
|
|
151
|
+
archive_path = os.path.join(archive_dir, f"{today}.jsonl")
|
|
152
|
+
with open(archive_path, "a", encoding="utf-8") as f:
|
|
153
|
+
f.write(line)
|
|
154
|
+
except Exception:
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
def _print_crash_summary(exc_type, exc_tb, thread_name=None):
|
|
158
|
+
"""Print crash summary to console (red highlight)."""
|
|
159
|
+
RED = "\033[91m"
|
|
160
|
+
RESET = "\033[0m"
|
|
161
|
+
|
|
162
|
+
if exc_tb:
|
|
163
|
+
tb_entries = traceback.extract_tb(exc_tb)
|
|
164
|
+
if tb_entries:
|
|
165
|
+
last = tb_entries[-1]
|
|
166
|
+
location = f"{os.path.basename(last.filename)}:{last.lineno}"
|
|
167
|
+
else:
|
|
168
|
+
location = "unknown"
|
|
169
|
+
else:
|
|
170
|
+
location = "unknown"
|
|
171
|
+
|
|
172
|
+
prefix = f"[{MODULE_NAME}]"
|
|
173
|
+
if thread_name:
|
|
174
|
+
_builtin_print(f"{prefix} {RED}线程 {thread_name} 崩溃: "
|
|
175
|
+
f"{exc_type.__name__} in {location}{RESET}")
|
|
176
|
+
else:
|
|
177
|
+
_builtin_print(f"{prefix} {RED}崩溃: {exc_type.__name__} in {location}{RESET}")
|
|
178
|
+
if _crash_log_path:
|
|
179
|
+
_builtin_print(f"{prefix} 崩溃日志: {_crash_log_path}")
|
|
180
|
+
|
|
181
|
+
def _setup_exception_hooks():
|
|
182
|
+
"""Set up global exception hooks (sys.excepthook + threading.excepthook)."""
|
|
183
|
+
_orig_excepthook = sys.excepthook
|
|
184
|
+
|
|
185
|
+
def _excepthook(exc_type, exc_value, exc_tb):
|
|
186
|
+
_write_crash(exc_type, exc_value, exc_tb, severity="critical", handled=False)
|
|
187
|
+
_print_crash_summary(exc_type, exc_tb)
|
|
188
|
+
_orig_excepthook(exc_type, exc_value, exc_tb)
|
|
189
|
+
|
|
190
|
+
sys.excepthook = _excepthook
|
|
191
|
+
|
|
192
|
+
if hasattr(threading, "excepthook"):
|
|
193
|
+
def _thread_excepthook(args):
|
|
194
|
+
_write_crash(args.exc_type, args.exc_value, args.exc_traceback,
|
|
195
|
+
thread_name=args.thread.name if args.thread else "unknown",
|
|
196
|
+
severity="error", handled=False)
|
|
197
|
+
_print_crash_summary(args.exc_type, args.exc_traceback,
|
|
198
|
+
thread_name=args.thread.name if args.thread else None)
|
|
199
|
+
|
|
200
|
+
threading.excepthook = _thread_excepthook
|
|
201
|
+
|
|
202
|
+
def _tprint(*args, **kwargs):
|
|
203
|
+
"""Timestamped print that adds [timestamp] HH:MM:SS.mmm +delta prefix."""
|
|
204
|
+
global _last_ts
|
|
205
|
+
now = time.monotonic()
|
|
206
|
+
elapsed = now - _start_ts
|
|
207
|
+
delta = now - _last_ts
|
|
208
|
+
_last_ts = now
|
|
209
|
+
|
|
210
|
+
# Format elapsed (cumulative time since start)
|
|
211
|
+
if elapsed < 1:
|
|
212
|
+
elapsed_str = f"{elapsed * 1000:.0f}ms"
|
|
213
|
+
elif elapsed < 100:
|
|
214
|
+
elapsed_str = f"{elapsed:.1f}s"
|
|
215
|
+
else:
|
|
216
|
+
elapsed_str = f"{elapsed:.0f}s"
|
|
217
|
+
|
|
218
|
+
# Format delta (time since last print)
|
|
219
|
+
if delta < 0.001:
|
|
220
|
+
delta_str = ""
|
|
221
|
+
elif delta < 1:
|
|
222
|
+
delta_str = f"+{delta * 1000:.0f}ms"
|
|
223
|
+
elif delta < 100:
|
|
224
|
+
delta_str = f"+{delta:.1f}s"
|
|
225
|
+
else:
|
|
226
|
+
delta_str = f"+{delta:.0f}s"
|
|
227
|
+
|
|
228
|
+
# Current time
|
|
229
|
+
ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
|
230
|
+
|
|
231
|
+
# Print to console (original behavior)
|
|
232
|
+
_builtin_print(*args, **kwargs)
|
|
233
|
+
|
|
234
|
+
# Write to log files with timestamp prefix
|
|
235
|
+
if _log_latest_path or _log_daily_path:
|
|
236
|
+
sep = kwargs.get("sep", " ")
|
|
237
|
+
end = kwargs.get("end", "\n")
|
|
238
|
+
text = sep.join(str(a) for a in args)
|
|
239
|
+
prefix = f"[{elapsed_str:>6}] {ts} {delta_str:>8} "
|
|
240
|
+
_write_log(prefix + _strip_ansi(text) + end)
|
|
241
|
+
|
|
242
|
+
builtins.print = _tprint
|
|
243
|
+
|
|
244
|
+
# Ensure project root is on sys.path (set by main.py or cli.js)
|
|
13
245
|
_this_dir = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
-
_project_root = os.path.dirname(os.path.dirname(_this_dir))
|
|
246
|
+
_project_root = os.environ.get("KITE_PROJECT") or os.path.dirname(os.path.dirname(_this_dir))
|
|
15
247
|
if _project_root not in sys.path:
|
|
16
248
|
sys.path.insert(0, _project_root)
|
|
17
249
|
|
|
@@ -19,55 +251,128 @@ from core.registry.store import RegistryStore
|
|
|
19
251
|
from core.registry.server import RegistryServer
|
|
20
252
|
|
|
21
253
|
|
|
22
|
-
def
|
|
254
|
+
def _read_module_md() -> dict:
|
|
255
|
+
"""Read preferred_port and advertise_ip from own module.md."""
|
|
256
|
+
md_path = os.path.join(_this_dir, "module.md")
|
|
257
|
+
result = {"preferred_port": 0, "advertise_ip": "127.0.0.1"}
|
|
258
|
+
try:
|
|
259
|
+
with open(md_path, "r", encoding="utf-8") as f:
|
|
260
|
+
text = f.read()
|
|
261
|
+
import re
|
|
262
|
+
m = re.match(r'^---\s*\n(.*?)\n---', text, re.DOTALL)
|
|
263
|
+
if m:
|
|
264
|
+
try:
|
|
265
|
+
import yaml
|
|
266
|
+
fm = yaml.safe_load(m.group(1)) or {}
|
|
267
|
+
except ImportError:
|
|
268
|
+
fm = {}
|
|
269
|
+
result["preferred_port"] = int(fm.get("preferred_port", 0))
|
|
270
|
+
result["advertise_ip"] = fm.get("advertise_ip", "127.0.0.1")
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
return result
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _bind_port(preferred: int, host: str) -> int:
|
|
277
|
+
"""Try preferred port first, fall back to OS-assigned."""
|
|
278
|
+
if preferred:
|
|
279
|
+
try:
|
|
280
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
281
|
+
s.bind((host, preferred))
|
|
282
|
+
return preferred
|
|
283
|
+
except OSError:
|
|
284
|
+
pass
|
|
23
285
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
24
|
-
s.bind((
|
|
286
|
+
s.bind((host, 0))
|
|
25
287
|
return s.getsockname()[1]
|
|
26
288
|
|
|
27
289
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
os.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
print(f"[registry] Port written to {port_file}")
|
|
290
|
+
def main():
|
|
291
|
+
# Initialize log file paths
|
|
292
|
+
global _log_dir, _log_latest_path, _crash_log_path
|
|
293
|
+
module_data = os.environ.get("KITE_MODULE_DATA")
|
|
294
|
+
if module_data:
|
|
295
|
+
_log_dir = os.path.join(module_data, "log")
|
|
296
|
+
os.makedirs(_log_dir, exist_ok=True)
|
|
297
|
+
suffix = os.environ.get("KITE_INSTANCE_SUFFIX", "")
|
|
37
298
|
|
|
299
|
+
# latest.log — truncate on each startup
|
|
300
|
+
_log_latest_path = os.path.join(_log_dir, f"latest{suffix}.log")
|
|
301
|
+
try:
|
|
302
|
+
with open(_log_latest_path, "w", encoding="utf-8") as f:
|
|
303
|
+
pass # truncate
|
|
304
|
+
except Exception:
|
|
305
|
+
_log_latest_path = None
|
|
38
306
|
|
|
39
|
-
|
|
40
|
-
|
|
307
|
+
# crashes.jsonl — truncate on each startup
|
|
308
|
+
_crash_log_path = os.path.join(_log_dir, f"crashes{suffix}.jsonl")
|
|
309
|
+
try:
|
|
310
|
+
with open(_crash_log_path, "w", encoding="utf-8") as f:
|
|
311
|
+
pass # truncate
|
|
312
|
+
except Exception:
|
|
313
|
+
_crash_log_path = None
|
|
314
|
+
|
|
315
|
+
_resolve_daily_log_path()
|
|
316
|
+
|
|
317
|
+
# Setup exception hooks
|
|
318
|
+
_setup_exception_hooks()
|
|
319
|
+
|
|
320
|
+
_t0 = time.monotonic()
|
|
321
|
+
|
|
322
|
+
# Kite environment
|
|
323
|
+
kite_instance = os.environ.get("KITE_INSTANCE", "")
|
|
324
|
+
is_debug = os.environ.get("KITE_DEBUG") == "1"
|
|
325
|
+
|
|
326
|
+
# Step 1: Read token from stdin boot_info (only token)
|
|
41
327
|
launcher_token = ""
|
|
42
|
-
bind_host = "127.0.0.1"
|
|
43
|
-
instance_id = ""
|
|
44
328
|
try:
|
|
45
|
-
import json
|
|
46
329
|
line = sys.stdin.readline().strip()
|
|
47
330
|
if line:
|
|
48
331
|
boot_info = json.loads(line)
|
|
49
332
|
launcher_token = boot_info.get("token", "")
|
|
50
|
-
bind_host = boot_info.get("bind", "127.0.0.1")
|
|
51
|
-
instance_id = boot_info.get("instance_id", "")
|
|
52
333
|
except Exception:
|
|
53
334
|
pass
|
|
54
335
|
|
|
55
336
|
if not launcher_token:
|
|
56
|
-
print("[registry]
|
|
337
|
+
print("[registry] 错误: 未通过 stdin boot_info 提供启动器令牌")
|
|
57
338
|
sys.exit(1)
|
|
58
339
|
|
|
59
|
-
print(f"[registry]
|
|
340
|
+
print(f"[registry] 已收到启动器令牌 ({len(launcher_token)} 字符) ({_fmt_elapsed(_t0)})")
|
|
60
341
|
|
|
61
|
-
|
|
342
|
+
if is_debug:
|
|
343
|
+
print("[registry] 调试模式已启用 (KITE_DEBUG=1),接受所有令牌")
|
|
344
|
+
|
|
345
|
+
# Step 2: Read config from own module.md
|
|
346
|
+
md_config = _read_module_md()
|
|
347
|
+
advertise_ip = md_config["advertise_ip"]
|
|
348
|
+
preferred_port = md_config["preferred_port"]
|
|
349
|
+
|
|
350
|
+
# Step 3: Create store and server
|
|
62
351
|
store = RegistryStore(launcher_token)
|
|
63
|
-
server = RegistryServer(store, launcher_token=launcher_token)
|
|
352
|
+
server = RegistryServer(store, launcher_token=launcher_token, advertise_ip=advertise_ip)
|
|
353
|
+
|
|
354
|
+
# Step 4: Bind port
|
|
355
|
+
bind_host = advertise_ip
|
|
356
|
+
port = _bind_port(preferred_port, bind_host)
|
|
357
|
+
|
|
358
|
+
# Step 5: Output port via stdout structured message (Launcher reads this)
|
|
359
|
+
# This message proves HTTP is about to start — Launcher uses it as readiness signal
|
|
360
|
+
print(json.dumps({"kite": "port", "port": port}), flush=True)
|
|
64
361
|
|
|
65
|
-
|
|
66
|
-
port = _get_free_port()
|
|
67
|
-
_write_port_file(port, instance_id)
|
|
362
|
+
print(f"[registry] 启动中 {bind_host}:{port} ({_fmt_elapsed(_t0)})")
|
|
68
363
|
|
|
69
|
-
|
|
70
|
-
|
|
364
|
+
# Store port and advertise_ip for module.ready event later
|
|
365
|
+
server.port = port
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
config = uvicorn.Config(server.app, host=bind_host, port=port, log_level="warning")
|
|
369
|
+
uvi_server = uvicorn.Server(config)
|
|
370
|
+
server._uvicorn_server = uvi_server
|
|
371
|
+
uvi_server.run()
|
|
372
|
+
except Exception as e:
|
|
373
|
+
_write_crash(type(e), e, e.__traceback__, severity="critical", handled=True)
|
|
374
|
+
_print_crash_summary(type(e), e.__traceback__)
|
|
375
|
+
sys.exit(1)
|
|
71
376
|
|
|
72
377
|
|
|
73
378
|
if __name__ == "__main__":
|