@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.
Files changed (112) hide show
  1. package/cli.js +127 -25
  2. package/core/event_hub/entry.py +384 -61
  3. package/core/event_hub/hub.py +8 -0
  4. package/core/event_hub/module.md +0 -1
  5. package/core/event_hub/server.py +169 -38
  6. package/core/kite_log.py +241 -0
  7. package/core/launcher/entry.py +1306 -425
  8. package/core/launcher/module_scanner.py +10 -9
  9. package/core/launcher/process_manager.py +555 -121
  10. package/core/registry/entry.py +335 -30
  11. package/core/registry/server.py +339 -256
  12. package/core/registry/store.py +13 -2
  13. package/extensions/agents/__init__.py +1 -0
  14. package/extensions/agents/assistant/__init__.py +1 -0
  15. package/extensions/agents/assistant/entry.py +380 -0
  16. package/extensions/agents/assistant/module.md +22 -0
  17. package/extensions/agents/assistant/server.py +236 -0
  18. package/extensions/channels/__init__.py +1 -0
  19. package/extensions/channels/acp_channel/__init__.py +1 -0
  20. package/extensions/channels/acp_channel/entry.py +380 -0
  21. package/extensions/channels/acp_channel/module.md +22 -0
  22. package/extensions/channels/acp_channel/server.py +236 -0
  23. package/{core → extensions}/event_hub_bench/entry.py +664 -371
  24. package/{core → extensions}/event_hub_bench/module.md +4 -2
  25. package/extensions/services/backup/__init__.py +1 -0
  26. package/extensions/services/backup/entry.py +380 -0
  27. package/extensions/services/backup/module.md +22 -0
  28. package/extensions/services/backup/server.py +244 -0
  29. package/extensions/services/model_service/__init__.py +1 -0
  30. package/extensions/services/model_service/entry.py +380 -0
  31. package/extensions/services/model_service/module.md +22 -0
  32. package/extensions/services/model_service/server.py +236 -0
  33. package/extensions/services/watchdog/entry.py +460 -143
  34. package/extensions/services/watchdog/module.md +3 -0
  35. package/extensions/services/watchdog/monitor.py +128 -13
  36. package/extensions/services/watchdog/server.py +75 -13
  37. package/extensions/services/web/__init__.py +1 -0
  38. package/extensions/services/web/config.yaml +149 -0
  39. package/extensions/services/web/entry.py +487 -0
  40. package/extensions/services/web/module.md +24 -0
  41. package/extensions/services/web/routes/__init__.py +1 -0
  42. package/extensions/services/web/routes/routes_call.py +189 -0
  43. package/extensions/services/web/routes/routes_config.py +512 -0
  44. package/extensions/services/web/routes/routes_contacts.py +98 -0
  45. package/extensions/services/web/routes/routes_devlog.py +99 -0
  46. package/extensions/services/web/routes/routes_phone.py +81 -0
  47. package/extensions/services/web/routes/routes_sms.py +48 -0
  48. package/extensions/services/web/routes/routes_stats.py +17 -0
  49. package/extensions/services/web/routes/routes_voicechat.py +554 -0
  50. package/extensions/services/web/routes/schemas.py +216 -0
  51. package/extensions/services/web/server.py +332 -0
  52. package/extensions/services/web/static/css/style.css +1064 -0
  53. package/extensions/services/web/static/index.html +1445 -0
  54. package/extensions/services/web/static/js/app.js +4671 -0
  55. package/extensions/services/web/vendor/__init__.py +1 -0
  56. package/extensions/services/web/vendor/bluetooth/audio.py +348 -0
  57. package/extensions/services/web/vendor/bluetooth/contacts.py +251 -0
  58. package/extensions/services/web/vendor/bluetooth/manager.py +395 -0
  59. package/extensions/services/web/vendor/bluetooth/sms.py +290 -0
  60. package/extensions/services/web/vendor/bluetooth/telephony.py +274 -0
  61. package/extensions/services/web/vendor/config.py +139 -0
  62. package/extensions/services/web/vendor/conversation/__init__.py +0 -0
  63. package/extensions/services/web/vendor/conversation/asr.py +936 -0
  64. package/extensions/services/web/vendor/conversation/engine.py +548 -0
  65. package/extensions/services/web/vendor/conversation/llm.py +534 -0
  66. package/extensions/services/web/vendor/conversation/mcp_tools.py +190 -0
  67. package/extensions/services/web/vendor/conversation/tts.py +322 -0
  68. package/extensions/services/web/vendor/conversation/vad.py +138 -0
  69. package/extensions/services/web/vendor/storage/__init__.py +1 -0
  70. package/extensions/services/web/vendor/storage/identity.py +312 -0
  71. package/extensions/services/web/vendor/storage/store.py +507 -0
  72. package/extensions/services/web/vendor/task/__init__.py +0 -0
  73. package/extensions/services/web/vendor/task/manager.py +864 -0
  74. package/extensions/services/web/vendor/task/models.py +45 -0
  75. package/extensions/services/web/vendor/task/webhook.py +263 -0
  76. package/extensions/services/web/vendor/tools/__init__.py +0 -0
  77. package/extensions/services/web/vendor/tools/registry.py +321 -0
  78. package/main.py +344 -4
  79. package/package.json +11 -2
  80. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  81. package/core/__pycache__/data_dir.cpython-313.pyc +0 -0
  82. package/core/data_dir.py +0 -62
  83. package/core/event_hub/__pycache__/__init__.cpython-313.pyc +0 -0
  84. package/core/event_hub/__pycache__/bench.cpython-313.pyc +0 -0
  85. package/core/event_hub/__pycache__/bench_perf.cpython-313.pyc +0 -0
  86. package/core/event_hub/__pycache__/dedup.cpython-313.pyc +0 -0
  87. package/core/event_hub/__pycache__/entry.cpython-313.pyc +0 -0
  88. package/core/event_hub/__pycache__/hub.cpython-313.pyc +0 -0
  89. package/core/event_hub/__pycache__/router.cpython-313.pyc +0 -0
  90. package/core/event_hub/__pycache__/server.cpython-313.pyc +0 -0
  91. package/core/event_hub/bench_results/2026-02-28_13-26-48.json +0 -51
  92. package/core/event_hub/bench_results/2026-02-28_13-44-45.json +0 -51
  93. package/core/event_hub/bench_results/2026-02-28_13-45-39.json +0 -51
  94. package/core/launcher/__pycache__/__init__.cpython-313.pyc +0 -0
  95. package/core/launcher/__pycache__/entry.cpython-313.pyc +0 -0
  96. package/core/launcher/__pycache__/module_scanner.cpython-313.pyc +0 -0
  97. package/core/launcher/__pycache__/process_manager.cpython-313.pyc +0 -0
  98. package/core/launcher/data/log/lifecycle.jsonl +0 -1158
  99. package/core/launcher/data/token.txt +0 -1
  100. package/core/registry/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/core/registry/__pycache__/entry.cpython-313.pyc +0 -0
  102. package/core/registry/__pycache__/server.cpython-313.pyc +0 -0
  103. package/core/registry/__pycache__/store.cpython-313.pyc +0 -0
  104. package/core/registry/data/port.txt +0 -1
  105. package/core/registry/data/port_484.txt +0 -1
  106. package/extensions/__pycache__/__init__.cpython-313.pyc +0 -0
  107. package/extensions/services/__pycache__/__init__.cpython-313.pyc +0 -0
  108. package/extensions/services/watchdog/__pycache__/__init__.cpython-313.pyc +0 -0
  109. package/extensions/services/watchdog/__pycache__/entry.cpython-313.pyc +0 -0
  110. package/extensions/services/watchdog/__pycache__/monitor.cpython-313.pyc +0 -0
  111. package/extensions/services/watchdog/__pycache__/server.cpython-313.pyc +0 -0
  112. /package/{core/event_hub/bench_results/.gitkeep → extensions/services/web/vendor/bluetooth/__init__.py} +0 -0
@@ -1,17 +1,249 @@
1
1
  """
2
2
  Registry entry point.
3
- Reads launcher_token, starts HTTP server on dynamic port, writes port.txt.
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
- # Ensure project root is on sys.path
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 _get_free_port() -> int:
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(("127.0.0.1", 0))
286
+ s.bind((host, 0))
25
287
  return s.getsockname()[1]
26
288
 
27
289
 
28
- def _write_port_file(port: int, instance_id: str = ""):
29
- from core.data_dir import get_registry_data_dir
30
- data_dir = get_registry_data_dir()
31
- os.makedirs(data_dir, exist_ok=True)
32
- filename = f"port_{instance_id}.txt" if instance_id else "port.txt"
33
- port_file = os.path.join(data_dir, filename)
34
- with open(port_file, "w") as f:
35
- f.write(str(port))
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
- def main():
40
- # Read launcher_token and config from stdin (boot_info JSON pipe)
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] ERROR: No launcher token provided via stdin boot_info")
337
+ print("[registry] 错误: 未通过 stdin boot_info 提供启动器令牌")
57
338
  sys.exit(1)
58
339
 
59
- print(f"[registry] Launcher token received ({len(launcher_token)} chars)")
340
+ print(f"[registry] 已收到启动器令牌 ({len(launcher_token)} 字符) ({_fmt_elapsed(_t0)})")
60
341
 
61
- # Create store and server
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
- # Bind to dynamic port
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
- print(f"[registry] Starting on {bind_host}:{port}")
70
- uvicorn.run(server.app, host=bind_host, port=port, log_level="warning")
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__":