@agentunion/kite 1.3.2 → 1.4.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 (78) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/cli.js +76 -0
  3. package/extensions/agents/assistant/entry.py +111 -1
  4. package/extensions/agents/assistant/server.py +263 -215
  5. package/extensions/channels/acp_channel/entry.py +111 -1
  6. package/extensions/channels/acp_channel/module.md +23 -22
  7. package/extensions/channels/acp_channel/server.py +263 -215
  8. package/extensions/event_hub_bench/entry.py +107 -1
  9. package/extensions/services/backup/entry.py +299 -21
  10. package/extensions/services/backup/module.md +24 -22
  11. package/extensions/services/model_service/entry.py +145 -19
  12. package/extensions/services/model_service/module.md +21 -22
  13. package/extensions/services/watchdog/entry.py +188 -25
  14. package/extensions/services/watchdog/monitor.py +144 -34
  15. package/extensions/services/web/WEBSOCKET_STATUS.md +143 -0
  16. package/extensions/services/web/config_example.py +35 -0
  17. package/extensions/services/web/config_loader.py +110 -0
  18. package/extensions/services/web/entry.py +114 -26
  19. package/extensions/services/web/module.md +35 -24
  20. package/extensions/services/web/pairing.py +250 -0
  21. package/extensions/services/web/pairing_codes.jsonl +16 -0
  22. package/extensions/services/web/relay.py +643 -0
  23. package/extensions/services/web/relay_config.json5 +67 -0
  24. package/extensions/services/web/routes/routes_management_ws.py +127 -0
  25. package/extensions/services/web/routes/routes_rpc.py +89 -0
  26. package/extensions/services/web/routes/routes_test.py +61 -0
  27. package/extensions/services/web/routes/schemas.py +0 -22
  28. package/extensions/services/web/server.py +421 -98
  29. package/extensions/services/web/static/css/style.css +67 -28
  30. package/extensions/services/web/static/index.html +234 -44
  31. package/extensions/services/web/static/js/app.js +1335 -48
  32. package/extensions/services/web/static/js/kernel-client-example.js +161 -0
  33. package/extensions/services/web/static/js/kernel-client.js +383 -0
  34. package/extensions/services/web/static/js/registry-tests.js +558 -0
  35. package/extensions/services/web/static/js/token-manager.js +175 -0
  36. package/extensions/services/web/static/pairing.html +248 -0
  37. package/extensions/services/web/static/test_registry.html +262 -0
  38. package/extensions/services/web/web_config.json5 +29 -0
  39. package/kernel/entry.py +120 -32
  40. package/kernel/event_hub.py +141 -16
  41. package/kernel/module.md +36 -33
  42. package/kernel/registry_store.py +48 -15
  43. package/kernel/rpc_router.py +120 -53
  44. package/kernel/server.py +219 -12
  45. package/kite_cli/__init__.py +3 -0
  46. package/kite_cli/__main__.py +5 -0
  47. package/kite_cli/commands/__init__.py +1 -0
  48. package/kite_cli/commands/clean.py +101 -0
  49. package/kite_cli/commands/doctor.py +35 -0
  50. package/kite_cli/commands/history.py +111 -0
  51. package/kite_cli/commands/info.py +96 -0
  52. package/kite_cli/commands/install.py +313 -0
  53. package/kite_cli/commands/list.py +143 -0
  54. package/kite_cli/commands/log.py +81 -0
  55. package/kite_cli/commands/rollback.py +88 -0
  56. package/kite_cli/commands/search.py +73 -0
  57. package/kite_cli/commands/uninstall.py +85 -0
  58. package/kite_cli/commands/update.py +118 -0
  59. package/kite_cli/core/__init__.py +1 -0
  60. package/kite_cli/core/checker.py +142 -0
  61. package/kite_cli/core/dependency.py +229 -0
  62. package/kite_cli/core/downloader.py +209 -0
  63. package/kite_cli/core/install_info.py +40 -0
  64. package/kite_cli/core/tool_installer.py +397 -0
  65. package/kite_cli/core/validator.py +78 -0
  66. package/kite_cli/main.py +289 -0
  67. package/kite_cli/utils/__init__.py +1 -0
  68. package/kite_cli/utils/i18n.py +252 -0
  69. package/kite_cli/utils/interactive.py +63 -0
  70. package/kite_cli/utils/operation_log.py +77 -0
  71. package/kite_cli/utils/paths.py +34 -0
  72. package/kite_cli/utils/version.py +308 -0
  73. package/launcher/entry.py +819 -158
  74. package/launcher/logging_setup.py +104 -0
  75. package/launcher/module.md +37 -37
  76. package/package.json +2 -1
  77. package/scripts/plan_manager.py +315 -0
  78. package/extensions/services/web/routes/routes_modules.py +0 -249
@@ -0,0 +1,63 @@
1
+ """交互式问答工具"""
2
+
3
+
4
+ def select_source(candidates: list) -> dict:
5
+ """让用户选择来源"""
6
+ print("\n找到多个来源:")
7
+ for i, candidate in enumerate(candidates, 1):
8
+ platform = candidate["platform"]
9
+ name = candidate.get("name", "")
10
+ version = candidate.get("latest", candidate.get("version", ""))
11
+ desc = candidate.get("description", "")
12
+
13
+ if platform == "pypi":
14
+ print(f" [{i}] PyPI: {name} v{version}")
15
+ elif platform == "npm":
16
+ print(f" [{i}] npm: {name} v{version}")
17
+ elif platform == "git":
18
+ full_name = candidate.get("full_name", name)
19
+ print(f" [{i}] Git: {full_name}")
20
+ if desc:
21
+ print(f" {desc}")
22
+ elif platform == "local":
23
+ print(f" [{i}] 本地: {candidate.get('path', '')}")
24
+
25
+ while True:
26
+ try:
27
+ choice = input("\n? 选择来源 [1]: ").strip() or "1"
28
+ index = int(choice) - 1
29
+ if 0 <= index < len(candidates):
30
+ return candidates[index]
31
+ print("无效选择,请重试")
32
+ except (ValueError, KeyboardInterrupt):
33
+ return None
34
+
35
+
36
+ def select_location() -> str:
37
+ """让用户选择安装位置"""
38
+ print("\n? 选择安装位置:")
39
+ print(" [1] 开发环境 (Kite/extensions/) — 用于模块开发")
40
+ print(" [2] 本地实例 (~/.kite/workspace/Kite/extensions/) — 当前实例专用")
41
+ print(" [3] 全局共享 (~/.kite/modules/) — 所有实例共享")
42
+
43
+ while True:
44
+ try:
45
+ choice = input("\n选择 [3]: ").strip() or "3"
46
+ if choice == "1":
47
+ return "dev"
48
+ elif choice == "2":
49
+ return "local"
50
+ elif choice == "3":
51
+ return "global"
52
+ print("无效选择,请重试")
53
+ except KeyboardInterrupt:
54
+ return None
55
+
56
+
57
+ def confirm_action(message: str) -> bool:
58
+ """确认操作"""
59
+ try:
60
+ answer = input(f"{message} [y/N]: ").strip().lower()
61
+ return answer in ("y", "yes")
62
+ except KeyboardInterrupt:
63
+ return False
@@ -0,0 +1,77 @@
1
+ """操作日志记录"""
2
+ import json
3
+ import os
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+
7
+
8
+ def get_log_file() -> Path:
9
+ """获取日志文件路径"""
10
+ # 使用 KITE_DATA 环境变量,如果没有则使用 ~/.kite/data
11
+ kite_data = os.environ.get("KITE_DATA")
12
+ if kite_data:
13
+ log_dir = Path(kite_data) / "cli_logs"
14
+ else:
15
+ home = Path.home()
16
+ log_dir = home / ".kite" / "data" / "cli_logs"
17
+
18
+ log_dir.mkdir(parents=True, exist_ok=True)
19
+ return log_dir / "operations.jsonl"
20
+
21
+
22
+ def log_operation(operation: str, details: dict, success: bool = True, error: str = None):
23
+ """记录操作日志"""
24
+ log_entry = {
25
+ "timestamp": datetime.utcnow().isoformat() + "Z",
26
+ "operation": operation,
27
+ "details": details,
28
+ "success": success,
29
+ "error": error
30
+ }
31
+
32
+ try:
33
+ log_file = get_log_file()
34
+ with open(log_file, "a", encoding="utf-8") as f:
35
+ f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
36
+ except Exception as e:
37
+ # 日志记录失败不应该影响主流程
38
+ print(f"[Warning] 记录操作日志失败: {e}")
39
+
40
+
41
+ def read_operations(limit: int = 50) -> list:
42
+ """读取最近的操作日志"""
43
+ log_file = get_log_file()
44
+ if not log_file.exists():
45
+ return []
46
+
47
+ try:
48
+ with open(log_file, "r", encoding="utf-8") as f:
49
+ lines = f.readlines()
50
+
51
+ # 返回最后 N 条
52
+ operations = []
53
+ for line in lines[-limit:]:
54
+ try:
55
+ operations.append(json.loads(line.strip()))
56
+ except json.JSONDecodeError:
57
+ continue
58
+
59
+ return operations
60
+ except Exception as e:
61
+ print(f"[Error] 读取操作日志失败: {e}")
62
+ return []
63
+
64
+
65
+ def get_last_operation(operation_type: str = None) -> dict:
66
+ """获取最后一次操作"""
67
+ operations = read_operations(limit=100)
68
+
69
+ if operation_type:
70
+ # 查找指定类型的最后一次操作
71
+ for op in reversed(operations):
72
+ if op.get("operation") == operation_type:
73
+ return op
74
+ return None
75
+ else:
76
+ # 返回最后一次操作
77
+ return operations[-1] if operations else None
@@ -0,0 +1,34 @@
1
+ """路径处理工具"""
2
+ import os
3
+ from pathlib import Path
4
+
5
+
6
+ def get_install_path(location: str, module_name: str) -> Path:
7
+ """获取安装路径"""
8
+ if location == "dev":
9
+ # 开发环境: {KITE_PROJECT}/extensions/
10
+ kite_project = os.environ.get("KITE_PROJECT")
11
+ if not kite_project:
12
+ # 如果没有环境变量,使用当前目录
13
+ kite_project = Path.cwd()
14
+ return Path(kite_project) / "extensions" / module_name
15
+
16
+ elif location == "local":
17
+ # 本地实例: {KITE_INSTANCE_DIR}/extensions/
18
+ kite_instance = os.environ.get("KITE_INSTANCE_DIR")
19
+ if not kite_instance:
20
+ # 默认路径
21
+ home = Path.home()
22
+ kite_instance = home / ".kite" / "workspace" / "Kite"
23
+ return Path(kite_instance) / "extensions" / module_name
24
+
25
+ elif location == "global":
26
+ # 全局共享: {KITE_MODULES}
27
+ kite_modules = os.environ.get("KITE_MODULES")
28
+ if not kite_modules:
29
+ # 默认路径
30
+ home = Path.home()
31
+ kite_modules = home / ".kite" / "modules"
32
+ return Path(kite_modules) / module_name
33
+
34
+ raise ValueError(f"未知的安装位置: {location}")
@@ -0,0 +1,308 @@
1
+ """版本号解析和比较工具
2
+
3
+ 支持 semver 版本号解析、比较和匹配。
4
+ """
5
+ import re
6
+ from typing import Tuple, Optional, List
7
+
8
+
9
+ class Version:
10
+ """版本号类"""
11
+
12
+ def __init__(self, version_string: str):
13
+ """解析版本号字符串"""
14
+ self.original = version_string
15
+ self.major = 0
16
+ self.minor = 0
17
+ self.patch = 0
18
+ self.prerelease = None
19
+ self.build = None
20
+
21
+ self._parse(version_string)
22
+
23
+ def _parse(self, version_string: str):
24
+ """解析版本号"""
25
+ # 移除 'v' 前缀
26
+ version_string = version_string.lstrip('v')
27
+
28
+ # 分离 prerelease 和 build
29
+ if '+' in version_string:
30
+ version_string, self.build = version_string.split('+', 1)
31
+
32
+ if '-' in version_string:
33
+ version_string, self.prerelease = version_string.split('-', 1)
34
+
35
+ # 解析主版本号
36
+ parts = version_string.split('.')
37
+ if len(parts) >= 1:
38
+ self.major = int(parts[0]) if parts[0].isdigit() else 0
39
+ if len(parts) >= 2:
40
+ self.minor = int(parts[1]) if parts[1].isdigit() else 0
41
+ if len(parts) >= 3:
42
+ self.patch = int(parts[2]) if parts[2].isdigit() else 0
43
+
44
+ def __str__(self):
45
+ """转为字符串"""
46
+ version = f"{self.major}.{self.minor}.{self.patch}"
47
+ if self.prerelease:
48
+ version += f"-{self.prerelease}"
49
+ if self.build:
50
+ version += f"+{self.build}"
51
+ return version
52
+
53
+ def __repr__(self):
54
+ return f"Version('{self}')"
55
+
56
+ def __eq__(self, other):
57
+ """相等比较"""
58
+ if not isinstance(other, Version):
59
+ other = Version(str(other))
60
+ return (self.major, self.minor, self.patch, self.prerelease) == \
61
+ (other.major, other.minor, other.patch, other.prerelease)
62
+
63
+ def __lt__(self, other):
64
+ """小于比较"""
65
+ if not isinstance(other, Version):
66
+ other = Version(str(other))
67
+
68
+ # 比较主版本号
69
+ if self.major != other.major:
70
+ return self.major < other.major
71
+ if self.minor != other.minor:
72
+ return self.minor < other.minor
73
+ if self.patch != other.patch:
74
+ return self.patch < other.patch
75
+
76
+ # 比较 prerelease
77
+ if self.prerelease is None and other.prerelease is None:
78
+ return False
79
+ if self.prerelease is None:
80
+ return False # 正式版 > 预发布版
81
+ if other.prerelease is None:
82
+ return True
83
+ return self.prerelease < other.prerelease
84
+
85
+ def __le__(self, other):
86
+ return self == other or self < other
87
+
88
+ def __gt__(self, other):
89
+ return not self <= other
90
+
91
+ def __ge__(self, other):
92
+ return not self < other
93
+
94
+
95
+ class VersionSpec:
96
+ """版本规范类"""
97
+
98
+ def __init__(self, spec: str):
99
+ """解析版本规范"""
100
+ self.original = spec
101
+ self.operator = None
102
+ self.version = None
103
+ self.range_start = None
104
+ self.range_end = None
105
+
106
+ self._parse(spec)
107
+
108
+ def _parse(self, spec: str):
109
+ """解析版本规范"""
110
+ spec = spec.strip()
111
+
112
+ # latest
113
+ if spec == "latest":
114
+ self.operator = "latest"
115
+ return
116
+
117
+ # 范围: 1.0.0 - 2.0.0
118
+ if ' - ' in spec:
119
+ parts = spec.split(' - ')
120
+ if len(parts) == 2:
121
+ self.operator = "range"
122
+ self.range_start = Version(parts[0].strip())
123
+ self.range_end = Version(parts[1].strip())
124
+ return
125
+
126
+ # ^1.2.3 (兼容版本)
127
+ if spec.startswith('^'):
128
+ self.operator = "^"
129
+ self.version = Version(spec[1:])
130
+ return
131
+
132
+ # ~1.2.3 (近似版本)
133
+ if spec.startswith('~'):
134
+ self.operator = "~"
135
+ self.version = Version(spec[1:])
136
+ return
137
+
138
+ # >=1.2.3
139
+ if spec.startswith('>='):
140
+ self.operator = ">="
141
+ self.version = Version(spec[2:])
142
+ return
143
+
144
+ # <=1.2.3
145
+ if spec.startswith('<='):
146
+ self.operator = "<="
147
+ self.version = Version(spec[2:])
148
+ return
149
+
150
+ # >1.2.3
151
+ if spec.startswith('>'):
152
+ self.operator = ">"
153
+ self.version = Version(spec[1:])
154
+ return
155
+
156
+ # <1.2.3
157
+ if spec.startswith('<'):
158
+ self.operator = "<"
159
+ self.version = Version(spec[1:])
160
+ return
161
+
162
+ # =1.2.3 或 1.2.3 (精确版本)
163
+ if spec.startswith('='):
164
+ spec = spec[1:]
165
+ self.operator = "="
166
+ self.version = Version(spec)
167
+
168
+ def match(self, version: Version) -> bool:
169
+ """检查版本是否匹配规范"""
170
+ if not isinstance(version, Version):
171
+ version = Version(str(version))
172
+
173
+ if self.operator == "latest":
174
+ return True # latest 匹配任何版本,由调用者选择最新
175
+
176
+ if self.operator == "=":
177
+ return version == self.version
178
+
179
+ if self.operator == ">":
180
+ return version > self.version
181
+
182
+ if self.operator == ">=":
183
+ return version >= self.version
184
+
185
+ if self.operator == "<":
186
+ return version < self.version
187
+
188
+ if self.operator == "<=":
189
+ return version <= self.version
190
+
191
+ if self.operator == "^":
192
+ # ^1.2.3 匹配 >=1.2.3 且 <2.0.0
193
+ if self.version.major == 0:
194
+ # ^0.x.y 匹配 >=0.x.y 且 <0.(x+1).0
195
+ return version >= self.version and \
196
+ version.major == 0 and \
197
+ version.minor == self.version.minor
198
+ else:
199
+ return version >= self.version and \
200
+ version.major == self.version.major
201
+
202
+ if self.operator == "~":
203
+ # ~1.2.3 匹配 >=1.2.3 且 <1.3.0
204
+ return version >= self.version and \
205
+ version.major == self.version.major and \
206
+ version.minor == self.version.minor
207
+
208
+ if self.operator == "range":
209
+ return self.range_start <= version <= self.range_end
210
+
211
+ return False
212
+
213
+
214
+ def parse_version_spec(spec: str) -> Tuple[Optional[str], Optional[str]]:
215
+ """解析版本规范字符串
216
+
217
+ Args:
218
+ spec: 版本规范,如 "pkg@1.2.0", "pkg@^1.0.0", "pkg@latest", "pkg"
219
+
220
+ Returns:
221
+ (package_name, version_spec) 元组
222
+ """
223
+ if '@' not in spec:
224
+ return spec, None
225
+
226
+ parts = spec.rsplit('@', 1)
227
+ if len(parts) == 2:
228
+ return parts[0], parts[1]
229
+
230
+ return spec, None
231
+
232
+
233
+ def compare_versions(v1: str, v2: str) -> int:
234
+ """比较两个版本号
235
+
236
+ Args:
237
+ v1: 版本号 1
238
+ v2: 版本号 2
239
+
240
+ Returns:
241
+ -1 if v1 < v2
242
+ 0 if v1 == v2
243
+ 1 if v1 > v2
244
+ """
245
+ version1 = Version(v1)
246
+ version2 = Version(v2)
247
+
248
+ if version1 < version2:
249
+ return -1
250
+ elif version1 > version2:
251
+ return 1
252
+ else:
253
+ return 0
254
+
255
+
256
+ def match_version(version: str, spec: str) -> bool:
257
+ """检查版本是否匹配规范
258
+
259
+ Args:
260
+ version: 版本号,如 "1.2.3"
261
+ spec: 版本规范,如 "^1.0.0", ">=1.2.0", "1.2.3"
262
+
263
+ Returns:
264
+ 是否匹配
265
+ """
266
+ v = Version(version)
267
+ s = VersionSpec(spec)
268
+ return s.match(v)
269
+
270
+
271
+ def get_latest_version(versions: List[str]) -> Optional[str]:
272
+ """从版本列表中获取最新版本
273
+
274
+ Args:
275
+ versions: 版本号列表
276
+
277
+ Returns:
278
+ 最新版本号,如果列表为空则返回 None
279
+ """
280
+ if not versions:
281
+ return None
282
+
283
+ version_objects = [Version(v) for v in versions]
284
+ latest = max(version_objects)
285
+ return str(latest)
286
+
287
+
288
+ def filter_versions(versions: List[str], spec: str) -> List[str]:
289
+ """根据版本规范过滤版本列表
290
+
291
+ Args:
292
+ versions: 版本号列表
293
+ spec: 版本规范
294
+
295
+ Returns:
296
+ 匹配的版本列表,按版本号降序排列
297
+ """
298
+ version_spec = VersionSpec(spec)
299
+ matched = []
300
+
301
+ for v in versions:
302
+ version = Version(v)
303
+ if version_spec.match(version):
304
+ matched.append(v)
305
+
306
+ # 按版本号降序排列
307
+ matched.sort(key=lambda x: Version(x), reverse=True)
308
+ return matched