@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
package/main.py CHANGED
@@ -1,16 +1,356 @@
1
+ """
2
+ Kite development entry point.
3
+ Loads .env, sets KITE_* defaults, parses --debug, starts Launcher.
4
+ """
5
+ import builtins
6
+ import json
7
+ import os
8
+ import re
1
9
  import secrets
2
10
  import sys
3
- import os
11
+ import threading
12
+ import time
13
+ import traceback
14
+ from datetime import datetime, timezone
15
+
16
+ # ── Timestamped print with delta + color ──
17
+ # Covers the entire Launcher process. Child processes (registry, event_hub) are
18
+ # separate PIDs — unaffected. Their stdout is relayed via ProcessManager._read_stdout
19
+ # → print(), so timestamps & deltas are added at the relay point automatically.
20
+
21
+ _builtin_print = builtins.print
22
+ _start_ts = time.monotonic()
23
+ _last_ts = time.monotonic()
24
+ _first_line = True
25
+ _module_last_ts: dict[str, float] = {} # module_name -> last print timestamp
26
+
27
+ # Regex to extract [module_name] prefix from print text
28
+ _MODULE_PREFIX_RE = re.compile(r"^\[([a-z_]+)\]")
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.
41
+ _log_lock = threading.Lock()
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)
49
+
50
+ # Strip ANSI escape sequences for plain-text log files
51
+ _ANSI_RE = re.compile(r"\033\[[0-9;]*m")
52
+
53
+ def _strip_ansi(s: str) -> str:
54
+ return _ANSI_RE.sub("", s)
55
+
56
+
57
+ def _init_log_files():
58
+ """Initialize log file paths. Called after KITE_MODULE_DATA is set."""
59
+ global _log_latest_path, _log_dir, _crash_log_path
60
+ module_data = os.environ.get("KITE_MODULE_DATA")
61
+ if not module_data:
62
+ return
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", "")
67
+
68
+ # latest.log — truncate on each startup (write empty to clear)
69
+ _log_latest_path = os.path.join(_log_dir, f"latest{suffix}.log")
70
+ try:
71
+ with open(_log_latest_path, "w", encoding="utf-8") as f:
72
+ pass # truncate
73
+ except Exception as e:
74
+ _builtin_print(f"[launcher] 警告: 无法初始化 {os.path.basename(_log_latest_path)}: {e}")
75
+ _log_latest_path = None
76
+
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()
87
+
88
+
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
94
+ today = datetime.now().strftime("%Y-%m-%d")
95
+ if today == _log_daily_date and _log_daily_path:
96
+ return
97
+ month_dir = os.path.join(_log_dir, today[:7]) # YYYY-MM
98
+ os.makedirs(month_dir, exist_ok=True)
99
+ _log_daily_path = os.path.join(month_dir, f"{today}.log")
100
+ _log_daily_date = today
101
+
102
+
103
+ def _write_log(plain_line: str):
104
+ """Write a plain-text line to both latest.log and daily log (open-write-close)."""
105
+ with _log_lock:
106
+ if _log_latest_path:
107
+ try:
108
+ with open(_log_latest_path, "a", encoding="utf-8") as f:
109
+ f.write(plain_line)
110
+ except Exception:
111
+ pass
112
+ # Check daily rotation
113
+ _resolve_daily_log_path()
114
+ if _log_daily_path:
115
+ try:
116
+ with open(_log_daily_path, "a", encoding="utf-8") as f:
117
+ f.write(plain_line)
118
+ except Exception:
119
+ pass
120
+
121
+
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
+
4
171
 
5
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
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"
6
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
214
+
215
+
216
+
217
+ def _tprint(*args, **kwargs):
218
+ global _last_ts, _first_line
219
+ now = time.monotonic()
220
+ elapsed = now - _start_ts
221
+
222
+ # Timestamp
223
+ ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]
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
+
251
+ # Format delta string
252
+ if delta is None:
253
+ delta_str = ""
254
+ delta_color = _DIM
255
+ elif delta < 1:
256
+ delta_str = f"+{delta * 1000:.0f}ms"
257
+ elif delta < 100:
258
+ delta_str = f"+{delta:.1f}s"
259
+ else:
260
+ delta_str = f"+{delta:.0f}s"
261
+
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"))
278
+ else:
279
+ prefix = f"[{elapsed_str:>6}] {ts} {delta_color}{delta_str:>8}{_RESET} "
280
+ _builtin_print(prefix, end="")
281
+ _builtin_print(*args, **kwargs)
282
+
283
+ # Write to log files (plain text, no ANSI)
284
+ if _log_latest_path or _log_daily_path:
285
+ sep = kwargs.get("sep", " ")
286
+ end = kwargs.get("end", "\n")
287
+ text = sep.join(str(a) for a in args)
288
+ plain_prefix = f"[{elapsed_str:>6}] {ts} {delta_str:>8} "
289
+ _write_log(plain_prefix + _strip_ansi(text) + end)
290
+
291
+ builtins.print = _tprint
292
+
293
+ # Load .env (development convenience, not required in production)
294
+ try:
295
+ from dotenv import load_dotenv
296
+ load_dotenv()
297
+ except ImportError:
298
+ pass
299
+
300
+ # Resolve project root (directory containing this file)
301
+ _project_root = os.path.dirname(os.path.abspath(__file__))
302
+
303
+ # Home base for Kite data
304
+ _home = os.environ.get("HOME") or os.environ.get("USERPROFILE") or os.path.expanduser("~")
305
+ _kite_home = os.path.join(_home, ".kite")
306
+
307
+ # Set KITE_* defaults (only if not already set by cli.js or .env)
308
+ _defaults = {
309
+ "KITE_PROJECT": _project_root,
310
+ "KITE_CWD": os.getcwd(),
311
+ "KITE_WORKSPACE": os.path.join(_kite_home, "workspace"),
312
+ "KITE_DATA": os.path.join(_kite_home, "data"),
313
+ "KITE_MODULES": os.path.join(_kite_home, "modules"),
314
+ "KITE_REPO": os.path.join(_kite_home, "repo"),
315
+ "KITE_ENV": "development",
316
+ }
317
+ for key, value in _defaults.items():
318
+ if not os.environ.get(key):
319
+ os.environ[key] = value
320
+
321
+ # Parse --debug flag
322
+ if "--debug" in sys.argv:
323
+ os.environ["KITE_DEBUG"] = "1"
324
+ sys.argv.remove("--debug")
325
+
326
+ # Ensure project root is on sys.path
327
+ sys.path.insert(0, os.environ["KITE_PROJECT"])
328
+
329
+ _builtin_print("[launcher] 正在加载模块...")
7
330
  from core.launcher.entry import Launcher
8
331
 
9
332
 
10
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
+
11
339
  token = secrets.token_hex(32)
12
- print(f"[main] KITE_TOKEN generated ({len(token)} chars)")
13
- Launcher(kite_token=token).run()
340
+ launcher = Launcher(kite_token=token)
341
+ # KITE_MODULE_DATA is now set by constructor — initialize log files
342
+ _init_log_files()
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)
14
354
 
15
355
 
16
356
  if __name__ == "__main__":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentunion/kite",
3
- "version": "1.0.6",
3
+ "version": "1.2.0",
4
4
  "description": "Kite framework launcher — start Kite from anywhere",
5
5
  "bin": {
6
6
  "kite": "./cli.js"
@@ -11,7 +11,16 @@
11
11
  "__main__.py",
12
12
  "__init__.py",
13
13
  "core/**",
14
- "extensions/**"
14
+ "extensions/**",
15
+ "!**/__pycache__",
16
+ "!**/__pycache__/**",
17
+ "!**/*.pyc",
18
+ "!**/data",
19
+ "!**/data/**",
20
+ "!**/bench_results",
21
+ "!**/bench_results/**",
22
+ "!core/event_hub_bench",
23
+ "!core/event_hub_bench/**"
15
24
  ],
16
25
  "engines": {
17
26
  "node": ">=16"
package/core/data_dir.py DELETED
@@ -1,62 +0,0 @@
1
- """
2
- Data directory management for Kite.
3
- When installed globally via npm, use ~/.kite/ for runtime data.
4
- When running locally, use project_root/core/*/data/.
5
- """
6
- import os
7
- import sys
8
-
9
-
10
- def get_data_root() -> str:
11
- """Get the root directory for runtime data.
12
-
13
- Returns:
14
- - ~/.kite/ if installed globally (no write permission to install dir)
15
- - project_root if running locally (development mode)
16
- """
17
- # Check if we're in a global npm install (no write permission)
18
- project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19
- test_file = os.path.join(project_root, "core", "launcher", "data", ".write_test")
20
-
21
- try:
22
- os.makedirs(os.path.dirname(test_file), exist_ok=True)
23
- with open(test_file, "w") as f:
24
- f.write("test")
25
- os.remove(test_file)
26
- # Write permission OK, use project_root
27
- return project_root
28
- except (OSError, PermissionError):
29
- # No write permission, use user home
30
- home = os.path.expanduser("~")
31
- kite_home = os.path.join(home, ".kite")
32
- os.makedirs(kite_home, exist_ok=True)
33
- return kite_home
34
-
35
-
36
- def get_launcher_data_dir() -> str:
37
- """Get launcher data directory."""
38
- root = get_data_root()
39
- if root.endswith(".kite"):
40
- # User home mode: ~/.kite/launcher/
41
- return os.path.join(root, "launcher")
42
- else:
43
- # Project mode: project_root/core/launcher/data/
44
- return os.path.join(root, "core", "launcher", "data")
45
-
46
-
47
- def get_registry_data_dir() -> str:
48
- """Get registry data directory."""
49
- root = get_data_root()
50
- if root.endswith(".kite"):
51
- return os.path.join(root, "registry")
52
- else:
53
- return os.path.join(root, "core", "registry", "data")
54
-
55
-
56
- def get_event_hub_data_dir() -> str:
57
- """Get event hub data directory."""
58
- root = get_data_root()
59
- if root.endswith(".kite"):
60
- return os.path.join(root, "event_hub")
61
- else:
62
- return os.path.join(root, "core", "event_hub", "data")
@@ -1,51 +0,0 @@
1
- {
2
- "timestamp": "2026-02-28T13:26:48.644977",
3
- "env": {
4
- "platform": "win32",
5
- "python": "3.13.12"
6
- },
7
- "throughput": {
8
- "events": 10000,
9
- "send_rate": 13121,
10
- "client_recv": 10000,
11
- "hub_queued": 10000,
12
- "hub_routed": 10000,
13
- "send_time": 0.76,
14
- "recv_time": 1.59
15
- },
16
- "latency": {
17
- "samples": 200,
18
- "avg_ms": 0.69,
19
- "p50_ms": 0.65,
20
- "p95_ms": 0.92,
21
- "p99_ms": 1.27
22
- },
23
- "fanout_1": {
24
- "subs": 1,
25
- "events": 2000,
26
- "send_rate": 17292,
27
- "avg_recv": 2000,
28
- "min_recv": 2000
29
- },
30
- "fanout_10": {
31
- "subs": 10,
32
- "events": 2000,
33
- "send_rate": 18074,
34
- "avg_recv": 2000,
35
- "min_recv": 2000
36
- },
37
- "fanout_50": {
38
- "subs": 50,
39
- "events": 2000,
40
- "send_rate": 6138,
41
- "avg_recv": 2000,
42
- "min_recv": 2000
43
- },
44
- "hub_counters": {
45
- "events_received": 16200,
46
- "events_routed": 132200,
47
- "events_queued": 132200,
48
- "events_deduplicated": 0,
49
- "errors": 0
50
- }
51
- }
@@ -1,51 +0,0 @@
1
- {
2
- "timestamp": "2026-02-28T13:44:45.723676",
3
- "env": {
4
- "platform": "win32",
5
- "python": "3.13.12"
6
- },
7
- "throughput": {
8
- "events": 10000,
9
- "send_rate": 7576,
10
- "client_recv": 10000,
11
- "hub_queued": 10000,
12
- "hub_routed": 10000,
13
- "send_time": 1.32,
14
- "recv_time": 1.43
15
- },
16
- "latency": {
17
- "samples": 200,
18
- "avg_ms": 0.5,
19
- "p50_ms": 0.48,
20
- "p95_ms": 0.66,
21
- "p99_ms": 0.78
22
- },
23
- "fanout_1": {
24
- "subs": 1,
25
- "events": 2000,
26
- "send_rate": 8811,
27
- "avg_recv": 2000,
28
- "min_recv": 2000
29
- },
30
- "fanout_10": {
31
- "subs": 10,
32
- "events": 2000,
33
- "send_rate": 10057,
34
- "avg_recv": 2000,
35
- "min_recv": 2000
36
- },
37
- "fanout_50": {
38
- "subs": 50,
39
- "events": 2000,
40
- "send_rate": 22422,
41
- "avg_recv": 2000,
42
- "min_recv": 2000
43
- },
44
- "hub_counters": {
45
- "events_received": 16200,
46
- "events_routed": 132200,
47
- "events_queued": 132200,
48
- "events_deduplicated": 0,
49
- "errors": 0
50
- }
51
- }
@@ -1,51 +0,0 @@
1
- {
2
- "timestamp": "2026-02-28T13:45:39.158400",
3
- "env": {
4
- "platform": "win32",
5
- "python": "3.13.12"
6
- },
7
- "throughput": {
8
- "events": 10000,
9
- "send_rate": 7464,
10
- "client_recv": 10000,
11
- "hub_queued": 10000,
12
- "hub_routed": 10000,
13
- "send_time": 1.34,
14
- "recv_time": 1.45
15
- },
16
- "latency": {
17
- "samples": 200,
18
- "avg_ms": 0.59,
19
- "p50_ms": 0.53,
20
- "p95_ms": 0.93,
21
- "p99_ms": 1.68
22
- },
23
- "fanout_1": {
24
- "subs": 1,
25
- "events": 2000,
26
- "send_rate": 8694,
27
- "avg_recv": 2000,
28
- "min_recv": 2000
29
- },
30
- "fanout_10": {
31
- "subs": 10,
32
- "events": 2000,
33
- "send_rate": 7599,
34
- "avg_recv": 2000,
35
- "min_recv": 2000
36
- },
37
- "fanout_50": {
38
- "subs": 50,
39
- "events": 2000,
40
- "send_rate": 6664,
41
- "avg_recv": 2000,
42
- "min_recv": 2000
43
- },
44
- "hub_counters": {
45
- "events_received": 16200,
46
- "events_routed": 132200,
47
- "events_queued": 132200,
48
- "events_deduplicated": 0,
49
- "errors": 0
50
- }
51
- }