@agentunion/kite 1.0.7 → 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/core/event_hub/entry.py +305 -26
- package/core/event_hub/hub.py +8 -0
- package/core/event_hub/server.py +80 -17
- package/core/kite_log.py +241 -0
- package/core/launcher/entry.py +978 -284
- package/core/launcher/process_manager.py +456 -46
- package/core/registry/entry.py +272 -3
- package/core/registry/server.py +339 -289
- package/core/registry/store.py +10 -4
- 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/extensions/event_hub_bench/entry.py +664 -379
- package/extensions/event_hub_bench/module.md +2 -1
- 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 -147
- 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/__init__.py +0 -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 +230 -90
- package/package.json +1 -1
package/main.py
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
Kite development entry point.
|
|
3
3
|
Loads .env, sets KITE_* defaults, parses --debug, starts Launcher.
|
|
4
4
|
"""
|
|
5
|
-
import atexit
|
|
6
5
|
import builtins
|
|
6
|
+
import json
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
import secrets
|
|
9
10
|
import sys
|
|
10
11
|
import threading
|
|
11
12
|
import time
|
|
12
|
-
|
|
13
|
+
import traceback
|
|
14
|
+
from datetime import datetime, timezone
|
|
13
15
|
|
|
14
16
|
# ── Timestamped print with delta + color ──
|
|
15
17
|
# Covers the entire Launcher process. Child processes (registry, event_hub) are
|
|
@@ -17,23 +19,35 @@ from datetime import datetime
|
|
|
17
19
|
# → print(), so timestamps & deltas are added at the relay point automatically.
|
|
18
20
|
|
|
19
21
|
_builtin_print = builtins.print
|
|
22
|
+
_start_ts = time.monotonic()
|
|
20
23
|
_last_ts = time.monotonic()
|
|
24
|
+
_first_line = True
|
|
25
|
+
_module_last_ts: dict[str, float] = {} # module_name -> last print timestamp
|
|
21
26
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
_GREEN = "\033[32m" # green — noticeable but expected (subprocess launch, network wait)
|
|
25
|
-
_RED = "\033[91m" # red — abnormally slow, likely a problem
|
|
26
|
-
_RESET = "\033[0m"
|
|
27
|
+
# Regex to extract [module_name] prefix from print text
|
|
28
|
+
_MODULE_PREFIX_RE = re.compile(r"^\[([a-z_]+)\]")
|
|
27
29
|
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
+
# ANSI escape codes
|
|
31
|
+
_DIM = "\033[90m" # gray
|
|
32
|
+
_GREEN = "\033[32m" # green
|
|
33
|
+
_RED = "\033[91m" # red
|
|
34
|
+
_ORANGE = "\033[38;5;208m" # orange — first line highlight
|
|
35
|
+
_RESET = "\033[0m"
|
|
36
|
+
|
|
37
|
+
# ── Log file paths ──
|
|
38
|
+
# Initialized lazily after KITE_MODULE_DATA is resolved (see _init_log_files).
|
|
39
|
+
# We store paths (not handles) and open/write/close on each log line to avoid
|
|
40
|
+
# holding file handles for the entire process lifetime.
|
|
30
41
|
_log_lock = threading.Lock()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
_log_daily_date = ""
|
|
42
|
+
_log_latest_path = None # path to latest.log
|
|
43
|
+
_log_daily_path = None # path to {YYYY-MM}/{YYYY-MM-DD}.log
|
|
44
|
+
_log_daily_date = "" # current date string to detect day rollover
|
|
45
|
+
_log_dir = None # base log directory
|
|
46
|
+
|
|
47
|
+
# Crash log paths
|
|
48
|
+
_crash_log_path = None # path to crashes.jsonl (current run)
|
|
34
49
|
|
|
35
50
|
# Strip ANSI escape sequences for plain-text log files
|
|
36
|
-
import re
|
|
37
51
|
_ANSI_RE = re.compile(r"\033\[[0-9;]*m")
|
|
38
52
|
|
|
39
53
|
def _strip_ansi(s: str) -> str:
|
|
@@ -41,123 +55,238 @@ def _strip_ansi(s: str) -> str:
|
|
|
41
55
|
|
|
42
56
|
|
|
43
57
|
def _init_log_files():
|
|
44
|
-
"""Initialize log file
|
|
45
|
-
global
|
|
58
|
+
"""Initialize log file paths. Called after KITE_MODULE_DATA is set."""
|
|
59
|
+
global _log_latest_path, _log_dir, _crash_log_path
|
|
46
60
|
module_data = os.environ.get("KITE_MODULE_DATA")
|
|
47
61
|
if not module_data:
|
|
48
62
|
return
|
|
49
|
-
|
|
50
|
-
os.makedirs(
|
|
63
|
+
_log_dir = os.path.join(module_data, "log")
|
|
64
|
+
os.makedirs(_log_dir, exist_ok=True)
|
|
65
|
+
|
|
66
|
+
suffix = os.environ.get("KITE_INSTANCE_SUFFIX", "")
|
|
51
67
|
|
|
52
|
-
# latest.log — truncate on each startup
|
|
68
|
+
# latest.log — truncate on each startup (write empty to clear)
|
|
69
|
+
_log_latest_path = os.path.join(_log_dir, f"latest{suffix}.log")
|
|
53
70
|
try:
|
|
54
|
-
|
|
71
|
+
with open(_log_latest_path, "w", encoding="utf-8") as f:
|
|
72
|
+
pass # truncate
|
|
55
73
|
except Exception as e:
|
|
56
|
-
_builtin_print(f"[launcher] 警告:
|
|
74
|
+
_builtin_print(f"[launcher] 警告: 无法初始化 {os.path.basename(_log_latest_path)}: {e}")
|
|
75
|
+
_log_latest_path = None
|
|
57
76
|
|
|
58
|
-
#
|
|
59
|
-
|
|
77
|
+
# crashes.jsonl — truncate on each startup
|
|
78
|
+
_crash_log_path = os.path.join(_log_dir, f"crashes{suffix}.jsonl")
|
|
79
|
+
try:
|
|
80
|
+
with open(_crash_log_path, "w", encoding="utf-8") as f:
|
|
81
|
+
pass # truncate
|
|
82
|
+
except Exception:
|
|
83
|
+
_crash_log_path = None
|
|
84
|
+
|
|
85
|
+
# daily log — ensure directory exists
|
|
86
|
+
_resolve_daily_log_path()
|
|
60
87
|
|
|
61
88
|
|
|
62
|
-
def
|
|
63
|
-
"""
|
|
64
|
-
global
|
|
89
|
+
def _resolve_daily_log_path():
|
|
90
|
+
"""Resolve the daily log file path based on current date."""
|
|
91
|
+
global _log_daily_path, _log_daily_date
|
|
92
|
+
if not _log_dir:
|
|
93
|
+
return
|
|
65
94
|
today = datetime.now().strftime("%Y-%m-%d")
|
|
66
|
-
if today == _log_daily_date and
|
|
95
|
+
if today == _log_daily_date and _log_daily_path:
|
|
67
96
|
return
|
|
68
|
-
|
|
69
|
-
try:
|
|
70
|
-
_log_daily.close()
|
|
71
|
-
except Exception:
|
|
72
|
-
pass
|
|
73
|
-
if not log_dir:
|
|
74
|
-
module_data = os.environ.get("KITE_MODULE_DATA")
|
|
75
|
-
if not module_data:
|
|
76
|
-
return
|
|
77
|
-
log_dir = os.path.join(module_data, "log")
|
|
78
|
-
month_dir = os.path.join(log_dir, today[:7]) # YYYY-MM
|
|
97
|
+
month_dir = os.path.join(_log_dir, today[:7]) # YYYY-MM
|
|
79
98
|
os.makedirs(month_dir, exist_ok=True)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
_log_daily_date = today
|
|
83
|
-
except Exception as e:
|
|
84
|
-
_builtin_print(f"[launcher] 警告: 无法打开每日日志: {e}")
|
|
99
|
+
_log_daily_path = os.path.join(month_dir, f"{today}.log")
|
|
100
|
+
_log_daily_date = today
|
|
85
101
|
|
|
86
102
|
|
|
87
103
|
def _write_log(plain_line: str):
|
|
88
|
-
"""Write a plain-text line to both latest.log and daily log."""
|
|
89
|
-
global _log_daily
|
|
104
|
+
"""Write a plain-text line to both latest.log and daily log (open-write-close)."""
|
|
90
105
|
with _log_lock:
|
|
91
|
-
if
|
|
106
|
+
if _log_latest_path:
|
|
92
107
|
try:
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
with open(_log_latest_path, "a", encoding="utf-8") as f:
|
|
109
|
+
f.write(plain_line)
|
|
95
110
|
except Exception:
|
|
96
111
|
pass
|
|
97
112
|
# Check daily rotation
|
|
98
|
-
|
|
99
|
-
if
|
|
100
|
-
_open_daily_log()
|
|
101
|
-
if _log_daily:
|
|
113
|
+
_resolve_daily_log_path()
|
|
114
|
+
if _log_daily_path:
|
|
102
115
|
try:
|
|
103
|
-
|
|
104
|
-
|
|
116
|
+
with open(_log_daily_path, "a", encoding="utf-8") as f:
|
|
117
|
+
f.write(plain_line)
|
|
105
118
|
except Exception:
|
|
106
119
|
pass
|
|
107
120
|
|
|
108
121
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
def _write_crash(exc_type, exc_value, exc_tb,
|
|
123
|
+
thread_name=None, severity="critical", handled=False):
|
|
124
|
+
"""Write crash record to crashes.jsonl + daily archive."""
|
|
125
|
+
record = {
|
|
126
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
127
|
+
"module": "launcher",
|
|
128
|
+
"thread": thread_name or threading.current_thread().name,
|
|
129
|
+
"exception_type": exc_type.__name__ if exc_type else "Unknown",
|
|
130
|
+
"exception_message": str(exc_value),
|
|
131
|
+
"traceback": "".join(traceback.format_exception(exc_type, exc_value, exc_tb)),
|
|
132
|
+
"severity": severity,
|
|
133
|
+
"handled": handled,
|
|
134
|
+
"process_id": os.getpid(),
|
|
135
|
+
"platform": sys.platform,
|
|
136
|
+
"runtime_version": f"Python {sys.version.split()[0]}",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if exc_tb:
|
|
140
|
+
tb_entries = traceback.extract_tb(exc_tb)
|
|
141
|
+
if tb_entries:
|
|
142
|
+
last = tb_entries[-1]
|
|
143
|
+
record["context"] = {
|
|
144
|
+
"function": last.name,
|
|
145
|
+
"file": os.path.basename(last.filename),
|
|
146
|
+
"line": last.lineno,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
line = json.dumps(record, ensure_ascii=False) + "\n"
|
|
150
|
+
|
|
151
|
+
# 1. Write to crashes.jsonl (current run)
|
|
152
|
+
if _crash_log_path:
|
|
153
|
+
try:
|
|
154
|
+
with open(_crash_log_path, "a", encoding="utf-8") as f:
|
|
155
|
+
f.write(line)
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
# 2. Write to daily archive
|
|
160
|
+
if _log_dir:
|
|
161
|
+
try:
|
|
162
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
163
|
+
archive_dir = os.path.join(_log_dir, "crashes", today[:7])
|
|
164
|
+
os.makedirs(archive_dir, exist_ok=True)
|
|
165
|
+
archive_path = os.path.join(archive_dir, f"{today}.jsonl")
|
|
166
|
+
with open(archive_path, "a", encoding="utf-8") as f:
|
|
167
|
+
f.write(line)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _print_crash_summary(exc_type, exc_tb, thread_name=None):
|
|
173
|
+
"""Print crash summary to console (red highlight)."""
|
|
174
|
+
if exc_tb:
|
|
175
|
+
tb_entries = traceback.extract_tb(exc_tb)
|
|
176
|
+
if tb_entries:
|
|
177
|
+
last = tb_entries[-1]
|
|
178
|
+
location = f"{os.path.basename(last.filename)}:{last.lineno}"
|
|
179
|
+
else:
|
|
180
|
+
location = "unknown"
|
|
181
|
+
else:
|
|
182
|
+
location = "unknown"
|
|
183
|
+
|
|
184
|
+
prefix = "[launcher]"
|
|
185
|
+
if thread_name:
|
|
186
|
+
print(f"{prefix} {_RED}线程 {thread_name} 崩溃: "
|
|
187
|
+
f"{exc_type.__name__} in {location}{_RESET}")
|
|
188
|
+
else:
|
|
189
|
+
print(f"{prefix} {_RED}崩溃: {exc_type.__name__} in {location}{_RESET}")
|
|
190
|
+
if _crash_log_path:
|
|
191
|
+
print(f"{prefix} 崩溃日志: {_crash_log_path}")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _setup_exception_hooks():
|
|
195
|
+
"""Set up global exception hooks for launcher process."""
|
|
196
|
+
_orig_excepthook = sys.excepthook
|
|
197
|
+
|
|
198
|
+
def _excepthook(exc_type, exc_value, exc_tb):
|
|
199
|
+
_write_crash(exc_type, exc_value, exc_tb, severity="critical", handled=False)
|
|
200
|
+
_print_crash_summary(exc_type, exc_tb)
|
|
201
|
+
_orig_excepthook(exc_type, exc_value, exc_tb)
|
|
202
|
+
|
|
203
|
+
sys.excepthook = _excepthook
|
|
204
|
+
|
|
205
|
+
if hasattr(threading, "excepthook"):
|
|
206
|
+
def _thread_excepthook(args):
|
|
207
|
+
_write_crash(args.exc_type, args.exc_value, args.exc_traceback,
|
|
208
|
+
thread_name=args.thread.name if args.thread else "unknown",
|
|
209
|
+
severity="error", handled=False)
|
|
210
|
+
_print_crash_summary(args.exc_type, args.exc_traceback,
|
|
211
|
+
thread_name=args.thread.name if args.thread else None)
|
|
212
|
+
|
|
213
|
+
threading.excepthook = _thread_excepthook
|
|
121
214
|
|
|
122
|
-
atexit.register(_close_log_files)
|
|
123
215
|
|
|
124
216
|
|
|
125
217
|
def _tprint(*args, **kwargs):
|
|
126
|
-
global _last_ts
|
|
218
|
+
global _last_ts, _first_line
|
|
127
219
|
now = time.monotonic()
|
|
128
|
-
|
|
129
|
-
_last_ts = now
|
|
220
|
+
elapsed = now - _start_ts
|
|
130
221
|
|
|
131
222
|
# Timestamp
|
|
132
223
|
ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
|
133
224
|
|
|
225
|
+
# Format elapsed string (time since startup)
|
|
226
|
+
if elapsed < 1:
|
|
227
|
+
elapsed_str = f"{elapsed * 1000:.0f}ms"
|
|
228
|
+
elif elapsed < 100:
|
|
229
|
+
elapsed_str = f"{elapsed:.1f}s"
|
|
230
|
+
else:
|
|
231
|
+
elapsed_str = f"{elapsed:.0f}s"
|
|
232
|
+
|
|
233
|
+
# Extract module prefix from text for per-module delta tracking
|
|
234
|
+
text_for_module = " ".join(str(a) for a in args) if args else ""
|
|
235
|
+
plain_text = _ANSI_RE.sub("", text_for_module).strip()
|
|
236
|
+
m = _MODULE_PREFIX_RE.match(plain_text)
|
|
237
|
+
module_name = m.group(1) if m else None
|
|
238
|
+
|
|
239
|
+
if module_name:
|
|
240
|
+
# Per-module delta: interval since this module's last print
|
|
241
|
+
if module_name in _module_last_ts:
|
|
242
|
+
delta = now - _module_last_ts[module_name]
|
|
243
|
+
else:
|
|
244
|
+
delta = None # first line for this module — no delta
|
|
245
|
+
_module_last_ts[module_name] = now
|
|
246
|
+
else:
|
|
247
|
+
# Fallback: global last-line delta
|
|
248
|
+
delta = now - _last_ts
|
|
249
|
+
_last_ts = now
|
|
250
|
+
|
|
134
251
|
# Format delta string
|
|
135
|
-
if delta
|
|
252
|
+
if delta is None:
|
|
253
|
+
delta_str = ""
|
|
254
|
+
delta_color = _DIM
|
|
255
|
+
elif delta < 1:
|
|
136
256
|
delta_str = f"+{delta * 1000:.0f}ms"
|
|
137
257
|
elif delta < 100:
|
|
138
258
|
delta_str = f"+{delta:.1f}s"
|
|
139
259
|
else:
|
|
140
260
|
delta_str = f"+{delta:.0f}s"
|
|
141
261
|
|
|
142
|
-
# Color: gray < 1s, green 1–5s, red > 5s
|
|
143
|
-
if delta
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
262
|
+
# Color for delta: gray < 1s, green 1–5s, red > 5s
|
|
263
|
+
if delta is not None:
|
|
264
|
+
if delta >= 5:
|
|
265
|
+
delta_color = _RED
|
|
266
|
+
elif delta >= 1:
|
|
267
|
+
delta_color = _GREEN
|
|
268
|
+
else:
|
|
269
|
+
delta_color = _DIM
|
|
270
|
+
|
|
271
|
+
# First line: entire line in orange
|
|
272
|
+
if _first_line:
|
|
273
|
+
_first_line = False
|
|
274
|
+
prefix = f"{_ORANGE}[{elapsed_str:>6}] {ts} {delta_str:>8} "
|
|
275
|
+
_builtin_print(prefix, end="")
|
|
276
|
+
_builtin_print(*args, end="", **{k: v for k, v in kwargs.items() if k != "end"})
|
|
277
|
+
_builtin_print(_RESET, end=kwargs.get("end", "\n"))
|
|
147
278
|
else:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
_builtin_print(prefix, end="")
|
|
152
|
-
_builtin_print(*args, **kwargs)
|
|
279
|
+
prefix = f"[{elapsed_str:>6}] {ts} {delta_color}{delta_str:>8}{_RESET} "
|
|
280
|
+
_builtin_print(prefix, end="")
|
|
281
|
+
_builtin_print(*args, **kwargs)
|
|
153
282
|
|
|
154
283
|
# Write to log files (plain text, no ANSI)
|
|
155
|
-
if
|
|
284
|
+
if _log_latest_path or _log_daily_path:
|
|
156
285
|
sep = kwargs.get("sep", " ")
|
|
157
286
|
end = kwargs.get("end", "\n")
|
|
158
287
|
text = sep.join(str(a) for a in args)
|
|
159
|
-
plain_prefix = f"{ts} {delta_str:>8} "
|
|
160
|
-
_write_log(plain_prefix + text + end)
|
|
288
|
+
plain_prefix = f"[{elapsed_str:>6}] {ts} {delta_str:>8} "
|
|
289
|
+
_write_log(plain_prefix + _strip_ansi(text) + end)
|
|
161
290
|
|
|
162
291
|
builtins.print = _tprint
|
|
163
292
|
|
|
@@ -197,20 +326,31 @@ if "--debug" in sys.argv:
|
|
|
197
326
|
# Ensure project root is on sys.path
|
|
198
327
|
sys.path.insert(0, os.environ["KITE_PROJECT"])
|
|
199
328
|
|
|
329
|
+
_builtin_print("[launcher] 正在加载模块...")
|
|
200
330
|
from core.launcher.entry import Launcher
|
|
201
331
|
|
|
202
332
|
|
|
203
333
|
def main():
|
|
334
|
+
# Reset timing baseline to exclude import overhead
|
|
335
|
+
global _start_ts, _last_ts
|
|
336
|
+
_start_ts = time.monotonic()
|
|
337
|
+
_last_ts = time.monotonic()
|
|
338
|
+
|
|
204
339
|
token = secrets.token_hex(32)
|
|
205
|
-
print(f"[main] KITE_TOKEN 已生成 ({len(token)} 字符)")
|
|
206
|
-
env = os.environ.get("KITE_ENV", "development")
|
|
207
|
-
debug = os.environ.get("KITE_DEBUG") == "1"
|
|
208
|
-
print(f"[main] 环境={env}, 调试={debug}, 项目={os.environ['KITE_PROJECT']}")
|
|
209
340
|
launcher = Launcher(kite_token=token)
|
|
210
|
-
# KITE_MODULE_DATA is now set — initialize log files
|
|
341
|
+
# KITE_MODULE_DATA is now set by constructor — initialize log files
|
|
211
342
|
_init_log_files()
|
|
212
|
-
|
|
213
|
-
|
|
343
|
+
_setup_exception_hooks()
|
|
344
|
+
# First lines in both console AND log file
|
|
345
|
+
print("[launcher] Kite 启动中...")
|
|
346
|
+
log_dir = os.path.join(os.environ.get("KITE_MODULE_DATA", ""), "log")
|
|
347
|
+
print(f"[launcher] 日志: {log_dir}")
|
|
348
|
+
try:
|
|
349
|
+
launcher.run()
|
|
350
|
+
except Exception as e:
|
|
351
|
+
_write_crash(type(e), e, e.__traceback__, severity="critical", handled=True)
|
|
352
|
+
_print_crash_summary(type(e), e.__traceback__)
|
|
353
|
+
sys.exit(1)
|
|
214
354
|
|
|
215
355
|
|
|
216
356
|
if __name__ == "__main__":
|