ErisPulse 2.3.3__py3-none-any.whl → 2.3.4.dev0__py3-none-any.whl

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 (76) hide show
  1. ErisPulse/Core/Event/wrapper.py +2 -3
  2. ErisPulse/Core/Event/wrapper.pyi +2 -3
  3. ErisPulse/Core/__init__.py +0 -5
  4. ErisPulse/Core/__init__.pyi +0 -1
  5. ErisPulse/__init__.py +0 -4
  6. ErisPulse/__init__.pyi +0 -1
  7. ErisPulse/sdk_protocol.py +0 -7
  8. ErisPulse/sdk_protocol.pyi +0 -3
  9. ErisPulse/utils/__init__.py +1 -1
  10. ErisPulse/utils/cli/__init__.py +11 -0
  11. ErisPulse/utils/cli/__init__.pyi +13 -0
  12. ErisPulse/utils/cli/__main__.py +225 -0
  13. ErisPulse/utils/cli/__main__.pyi +81 -0
  14. ErisPulse/utils/cli/base.py +52 -0
  15. ErisPulse/utils/cli/base.pyi +50 -0
  16. ErisPulse/utils/cli/commands/__init__.py +6 -0
  17. ErisPulse/utils/cli/commands/__init__.pyi +12 -0
  18. ErisPulse/utils/cli/commands/init.py +400 -0
  19. ErisPulse/utils/cli/commands/init.pyi +82 -0
  20. ErisPulse/utils/cli/commands/install.py +307 -0
  21. ErisPulse/utils/cli/commands/install.pyi +70 -0
  22. ErisPulse/utils/cli/commands/list.py +165 -0
  23. ErisPulse/utils/cli/commands/list.pyi +56 -0
  24. ErisPulse/utils/cli/commands/list_remote.py +128 -0
  25. ErisPulse/utils/cli/commands/list_remote.pyi +47 -0
  26. ErisPulse/utils/cli/commands/run.py +112 -0
  27. ErisPulse/utils/cli/commands/run.pyi +48 -0
  28. ErisPulse/utils/cli/commands/self_update.py +237 -0
  29. ErisPulse/utils/cli/commands/self_update.pyi +59 -0
  30. ErisPulse/utils/cli/commands/uninstall.py +37 -0
  31. ErisPulse/utils/cli/commands/uninstall.pyi +37 -0
  32. ErisPulse/utils/cli/commands/upgrade.py +62 -0
  33. ErisPulse/utils/cli/commands/upgrade.pyi +38 -0
  34. ErisPulse/utils/cli/registry.py +112 -0
  35. ErisPulse/utils/cli/registry.pyi +99 -0
  36. {erispulse-2.3.3.dist-info → erispulse-2.3.4.dev0.dist-info}/METADATA +6 -20
  37. erispulse-2.3.4.dev0.dist-info/RECORD +89 -0
  38. {erispulse-2.3.3.dist-info → erispulse-2.3.4.dev0.dist-info}/licenses/LICENSE +3 -3
  39. ErisPulse/Core/ux.py +0 -635
  40. ErisPulse/Core/ux.pyi +0 -94
  41. ErisPulse/utils/cli.py +0 -1097
  42. ErisPulse/utils/cli.pyi +0 -145
  43. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Bases/__init__.pyi +0 -14
  44. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Bases/adapter.pyi +0 -140
  45. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Bases/module.pyi +0 -52
  46. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/__init__.pyi +0 -26
  47. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/base.pyi +0 -62
  48. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/command.pyi +0 -113
  49. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/exceptions.pyi +0 -43
  50. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/message.pyi +0 -93
  51. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/meta.pyi +0 -92
  52. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/notice.pyi +0 -108
  53. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/request.pyi +0 -76
  54. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/Event/wrapper.pyi +0 -404
  55. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/__init__.pyi +0 -17
  56. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/_self_config.pyi +0 -72
  57. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/adapter.pyi +0 -229
  58. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/config.pyi +0 -70
  59. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/exceptions.pyi +0 -60
  60. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/lifecycle.pyi +0 -92
  61. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/logger.pyi +0 -168
  62. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/module.pyi +0 -178
  63. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/router.pyi +0 -120
  64. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/storage.pyi +0 -273
  65. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/Core/ux.pyi +0 -94
  66. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/__init__.pyi +0 -310
  67. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/__main__.pyi +0 -24
  68. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/sdk_protocol.pyi +0 -100
  69. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/utils/__init__.pyi +0 -16
  70. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/utils/cli.pyi +0 -145
  71. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/utils/console.pyi +0 -20
  72. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/utils/package_manager.pyi +0 -224
  73. erispulse-2.3.3.data/data/ErisPulse-stubs/ErisPulse/utils/reload_handler.pyi +0 -64
  74. erispulse-2.3.3.dist-info/RECORD +0 -98
  75. {erispulse-2.3.3.dist-info → erispulse-2.3.4.dev0.dist-info}/WHEEL +0 -0
  76. {erispulse-2.3.3.dist-info → erispulse-2.3.4.dev0.dist-info}/entry_points.txt +0 -0
ErisPulse/utils/cli.py DELETED
@@ -1,1097 +0,0 @@
1
- import argparse
2
- import importlib.metadata
3
- import sys
4
- import os
5
- import time
6
- import asyncio
7
- from typing import List, Dict, Optional, Any
8
- from watchdog.observers import Observer
9
-
10
- # Rich console setup
11
- from rich.panel import Panel
12
- from rich.table import Table
13
- from rich.prompt import Confirm, Prompt
14
- from rich.box import SIMPLE
15
-
16
- from .console import console
17
-
18
- class CLI:
19
- """
20
- ErisPulse命令行接口
21
-
22
- 提供完整的命令行交互功能
23
-
24
- {!--< tips >!--}
25
- 1. 支持动态加载第三方命令
26
- 2. 支持模块化子命令系统
27
- {!--< /tips >!--}
28
- """
29
-
30
- def __init__(self):
31
- """初始化CLI"""
32
- from .package_manager import PackageManager
33
- self.parser = self._create_parser()
34
- self.package_manager = PackageManager()
35
- self.observer = None
36
- self.handler = None
37
-
38
- def _create_parser(self) -> argparse.ArgumentParser:
39
- """
40
- 创建命令行参数解析器
41
-
42
- :return: 配置好的ArgumentParser实例
43
- """
44
- parser = argparse.ArgumentParser(
45
- prog="epsdk",
46
- formatter_class=argparse.RawDescriptionHelpFormatter,
47
- description="ErisPulse SDK 命令行工具\n\n一个功能强大的模块化系统管理工具,用于管理ErisPulse生态系统中的模块、适配器和扩展。",
48
- )
49
- parser._positionals.title = "命令"
50
- parser._optionals.title = "选项"
51
-
52
- # 全局选项
53
- parser.add_argument(
54
- "--version", "-V",
55
- action="store_true",
56
- help="显示版本信息"
57
- )
58
- parser.add_argument(
59
- "--verbose", "-v",
60
- action="count",
61
- default=0,
62
- help="增加输出详细程度 (-v, -vv, -vvv)"
63
- )
64
-
65
- # 子命令
66
- subparsers = parser.add_subparsers(
67
- dest='command',
68
- metavar="<命令>",
69
- help="要执行的操作"
70
- )
71
-
72
- # 安装命令
73
- install_parser = subparsers.add_parser(
74
- 'install',
75
- help='安装模块/适配器包(支持多个,用空格分隔)'
76
- )
77
- install_parser.add_argument(
78
- 'package',
79
- nargs='+', # 改为接受多个参数
80
- help='要安装的包名或模块/适配器简称(可指定多个)'
81
- )
82
- install_parser.add_argument(
83
- '--upgrade', '-U',
84
- action='store_true',
85
- help='升级已安装的包'
86
- )
87
- install_parser.add_argument(
88
- '--pre',
89
- action='store_true',
90
- help='包含预发布版本'
91
- )
92
-
93
- # 卸载命令
94
- uninstall_parser = subparsers.add_parser(
95
- 'uninstall',
96
- help='卸载模块/适配器包(支持多个,用空格分隔)'
97
- )
98
- uninstall_parser.add_argument(
99
- 'package',
100
- nargs='+', # 改为接受多个参数
101
- help='要卸载的包名(可指定多个)'
102
- )
103
-
104
- # 模块管理命令
105
- module_parser = subparsers.add_parser(
106
- 'module',
107
- help='模块管理'
108
- )
109
- module_subparsers = module_parser.add_subparsers(
110
- dest='module_command',
111
- metavar="<子命令>"
112
- )
113
-
114
- # 启用模块
115
- enable_parser = module_subparsers.add_parser(
116
- 'enable',
117
- help='启用模块'
118
- )
119
- enable_parser.add_argument(
120
- 'module',
121
- help='要启用的模块名'
122
- )
123
-
124
- # 禁用模块
125
- disable_parser = module_subparsers.add_parser(
126
- 'disable',
127
- help='禁用模块'
128
- )
129
- disable_parser.add_argument(
130
- 'module',
131
- help='要禁用的模块名'
132
- )
133
-
134
- # 列表命令
135
- list_parser = subparsers.add_parser(
136
- 'list',
137
- help='列出已安装的组件'
138
- )
139
- list_parser.add_argument(
140
- '--type', '-t',
141
- choices=['modules', 'adapters', 'cli', 'all'],
142
- default='all',
143
- help='列出类型 (默认: all)'
144
- )
145
- list_parser.add_argument(
146
- '--outdated', '-o',
147
- action='store_true',
148
- help='仅显示可升级的包'
149
- )
150
-
151
- # 远程列表命令
152
- list_remote_parser = subparsers.add_parser(
153
- 'list-remote',
154
- help='列出远程可用的组件'
155
- )
156
- list_remote_parser.add_argument(
157
- '--type', '-t',
158
- choices=['modules', 'adapters', 'cli', 'all'],
159
- default='all',
160
- help='列出类型 (默认: all)'
161
- )
162
- list_remote_parser.add_argument(
163
- '--refresh', '-r',
164
- action='store_true',
165
- help='强制刷新远程包列表'
166
- )
167
-
168
- # 升级命令
169
- upgrade_parser = subparsers.add_parser(
170
- 'upgrade',
171
- help='升级组件(支持多个,用空格分隔)'
172
- )
173
- upgrade_parser.add_argument(
174
- 'package',
175
- nargs='*', # 改为接受可选的多个参数
176
- help='要升级的包名 (可选,不指定则升级所有)'
177
- )
178
- upgrade_parser.add_argument(
179
- '--force', '-f',
180
- action='store_true',
181
- help='跳过确认直接升级'
182
- )
183
- upgrade_parser.add_argument(
184
- '--pre',
185
- action='store_true',
186
- help='包含预发布版本'
187
- )
188
-
189
- # 搜索命令
190
- search_parser = subparsers.add_parser(
191
- 'search',
192
- help='搜索模块/适配器包'
193
- )
194
- search_parser.add_argument(
195
- 'query',
196
- help='搜索关键词'
197
- )
198
- search_parser.add_argument(
199
- '--installed', '-i',
200
- action='store_true',
201
- help='仅搜索已安装的包'
202
- )
203
- search_parser.add_argument(
204
- '--remote', '-r',
205
- action='store_true',
206
- help='仅搜索远程包'
207
- )
208
-
209
- # 自更新命令
210
- self_update_parser = subparsers.add_parser(
211
- 'self-update',
212
- help='更新ErisPulse SDK本身'
213
- )
214
- self_update_parser.add_argument(
215
- 'version',
216
- nargs='?',
217
- help='要更新到的版本号 (可选,默认为最新版本)'
218
- )
219
- self_update_parser.add_argument(
220
- '--pre',
221
- action='store_true',
222
- help='包含预发布版本'
223
- )
224
- self_update_parser.add_argument(
225
- '--force', '-f',
226
- action='store_true',
227
- help='强制更新,即使版本相同'
228
- )
229
-
230
- # 运行命令
231
- run_parser = subparsers.add_parser(
232
- 'run',
233
- help='运行主程序'
234
- )
235
- run_parser.add_argument(
236
- 'script',
237
- nargs='?',
238
- help='要运行的主程序路径 (默认: main.py)'
239
- )
240
- run_parser.add_argument(
241
- '--reload',
242
- action='store_true',
243
- help='启用热重载模式'
244
- )
245
- run_parser.add_argument(
246
- '--no-reload',
247
- action='store_true',
248
- help='禁用热重载模式'
249
- )
250
-
251
- # 初始化命令
252
- init_parser = subparsers.add_parser(
253
- 'init',
254
- help='交互式初始化ErisPulse项目'
255
- )
256
- init_parser.add_argument(
257
- '--project-name', '-n',
258
- help='项目名称 (可选,交互式初始化时将会询问)'
259
- )
260
- init_parser.add_argument(
261
- '--quick', '-q',
262
- action='store_true',
263
- help='快速模式,跳过交互式配置'
264
- )
265
- init_parser.add_argument(
266
- '--force', '-f',
267
- action='store_true',
268
- help='强制覆盖现有配置'
269
- )
270
-
271
- # 状态命令
272
- status_parser = subparsers.add_parser(
273
- 'status',
274
- help='显示ErisPulse系统状态'
275
- )
276
- status_parser.add_argument(
277
- '--type', '-t',
278
- choices=['modules', 'adapters', 'all'],
279
- default='all',
280
- help='显示类型 (默认: all)'
281
- )
282
-
283
-
284
-
285
- # 加载第三方命令
286
- self._load_external_commands(subparsers)
287
-
288
- return parser
289
-
290
- def _get_external_commands(self) -> List[str]:
291
- """
292
- 获取所有已注册的第三方命令名称
293
-
294
- :return: 第三方命令名称列表
295
- """
296
- try:
297
- entry_points = importlib.metadata.entry_points()
298
- if hasattr(entry_points, 'select'):
299
- cli_entries = entry_points.select(group='erispulse.cli')
300
- else:
301
- cli_entries = entry_points.get('erispulse.cli', [])
302
- return [entry.name for entry in cli_entries]
303
- except Exception:
304
- return []
305
-
306
- def _load_external_commands(self, subparsers):
307
- """
308
- 加载第三方CLI命令
309
-
310
- :param subparsers: 子命令解析器
311
-
312
- :raises ImportError: 加载命令失败时抛出
313
- """
314
- try:
315
- entry_points = importlib.metadata.entry_points()
316
- if hasattr(entry_points, 'select'):
317
- cli_entries = entry_points.select(group='erispulse.cli')
318
- else:
319
- cli_entries = entry_points.get('erispulse.cli', [])
320
-
321
- for entry in cli_entries:
322
- try:
323
- cli_func = entry.load()
324
- if callable(cli_func):
325
- cli_func(subparsers, console)
326
- else:
327
- console.print(f"[warning]模块 {entry.name} 的入口点不是可调用对象[/]")
328
- except Exception as e:
329
- console.print(f"[error]加载第三方命令 {entry.name} 失败: {e}[/]")
330
- except Exception as e:
331
- console.print(f"[warning]加载第三方CLI命令失败: {e}[/]")
332
-
333
- def _print_version(self):
334
- """打印版本信息"""
335
- from ErisPulse import __version__
336
- console.print(Panel(
337
- f"[title]ErisPulse SDK[/] 版本: [bold]{__version__}[/]",
338
- subtitle=f"Python {sys.version.split()[0]}",
339
- style="title"
340
- ))
341
-
342
- def _print_installed_packages(self, pkg_type: str, outdated_only: bool = False):
343
- """
344
- 打印已安装包信息
345
-
346
- :param pkg_type: 包类型 (modules/adapters/cli/all)
347
- :param outdated_only: 是否只显示可升级的包
348
- """
349
- installed = self.package_manager.get_installed_packages()
350
-
351
- if pkg_type == "modules" and installed["modules"]:
352
- table = Table(
353
- title="已安装模块",
354
- box=SIMPLE,
355
- header_style="module"
356
- )
357
- table.add_column("模块名", style="module")
358
- table.add_column("包名")
359
- table.add_column("版本")
360
- table.add_column("状态")
361
- table.add_column("描述")
362
-
363
- for name, info in installed["modules"].items():
364
- if outdated_only and not self._is_package_outdated(info["package"], info["version"]):
365
- continue
366
-
367
- status = "[green]已启用[/]" if info.get("enabled", True) else "[yellow]已禁用[/]"
368
- table.add_row(
369
- name,
370
- info["package"],
371
- info["version"],
372
- status,
373
- info["summary"]
374
- )
375
-
376
- console.print(table)
377
-
378
- if pkg_type == "adapters" and installed["adapters"]:
379
- table = Table(
380
- title="已安装适配器",
381
- box=SIMPLE,
382
- header_style="adapter"
383
- )
384
- table.add_column("适配器名", style="adapter")
385
- table.add_column("包名")
386
- table.add_column("版本")
387
- table.add_column("描述")
388
-
389
- for name, info in installed["adapters"].items():
390
- if outdated_only and not self._is_package_outdated(info["package"], info["version"]):
391
- continue
392
-
393
- table.add_row(
394
- name,
395
- info["package"],
396
- info["version"],
397
- info["summary"]
398
- )
399
-
400
- console.print(table)
401
-
402
- if pkg_type == "cli" and installed["cli_extensions"]:
403
- table = Table(
404
- title="已安装CLI扩展",
405
- box=SIMPLE,
406
- header_style="cli"
407
- )
408
- table.add_column("命令名", style="cli")
409
- table.add_column("包名")
410
- table.add_column("版本")
411
- table.add_column("描述")
412
-
413
- for name, info in installed["cli_extensions"].items():
414
- if outdated_only and not self._is_package_outdated(info["package"], info["version"]):
415
- continue
416
-
417
- table.add_row(
418
- name,
419
- info["package"],
420
- info["version"],
421
- info["summary"]
422
- )
423
-
424
- console.print(table)
425
-
426
- def _print_remote_packages(self, pkg_type: str):
427
- """
428
- 打印远程包信息
429
-
430
- :param pkg_type: 包类型 (modules/adapters/cli/all)
431
- """
432
- remote_packages = asyncio.run(self.package_manager.get_remote_packages())
433
-
434
- if pkg_type == "modules" and remote_packages["modules"]:
435
- table = Table(
436
- title="远程模块",
437
- box=SIMPLE,
438
- header_style="module"
439
- )
440
- table.add_column("模块名", style="module")
441
- table.add_column("包名")
442
- table.add_column("最新版本")
443
- table.add_column("描述")
444
-
445
- for name, info in remote_packages["modules"].items():
446
- table.add_row(
447
- name,
448
- info["package"],
449
- info["version"],
450
- info["description"]
451
- )
452
-
453
- console.print(table)
454
-
455
- if pkg_type == "adapters" and remote_packages["adapters"]:
456
- table = Table(
457
- title="远程适配器",
458
- box=SIMPLE,
459
- header_style="adapter"
460
- )
461
- table.add_column("适配器名", style="adapter")
462
- table.add_column("包名")
463
- table.add_column("最新版本")
464
- table.add_column("描述")
465
-
466
- for name, info in remote_packages["adapters"].items():
467
- table.add_row(
468
- name,
469
- info["package"],
470
- info["version"],
471
- info["description"]
472
- )
473
-
474
- console.print(table)
475
-
476
- if pkg_type == "cli" and remote_packages.get("cli_extensions"):
477
- table = Table(
478
- title="远程CLI扩展",
479
- box=SIMPLE,
480
- header_style="cli"
481
- )
482
- table.add_column("命令名", style="cli")
483
- table.add_column("包名")
484
- table.add_column("最新版本")
485
- table.add_column("描述")
486
-
487
- for name, info in remote_packages["cli_extensions"].items():
488
- table.add_row(
489
- name,
490
- info["package"],
491
- info["version"],
492
- info["description"]
493
- )
494
-
495
- console.print(table)
496
-
497
- def _is_package_outdated(self, package_name: str, current_version: str) -> bool:
498
- """
499
- 检查包是否过时
500
-
501
- :param package_name: 包名
502
- :param current_version: 当前版本
503
- :return: 是否有新版本可用
504
- """
505
- remote_packages = asyncio.run(self.package_manager.get_remote_packages())
506
-
507
- # 检查模块
508
- for module_info in remote_packages["modules"].values():
509
- if module_info["package"] == package_name:
510
- return module_info["version"] != current_version
511
-
512
- # 检查适配器
513
- for adapter_info in remote_packages["adapters"].values():
514
- if adapter_info["package"] == package_name:
515
- return adapter_info["version"] != current_version
516
-
517
- # 检查CLI扩展
518
- for cli_info in remote_packages.get("cli_extensions", {}).values():
519
- if cli_info["package"] == package_name:
520
- return cli_info["version"] != current_version
521
-
522
- return False
523
-
524
- def _resolve_package_name(self, short_name: str) -> Optional[str]:
525
- """
526
- 解析简称到完整包名(大小写不敏感)
527
-
528
- :param short_name: 模块/适配器简称
529
- :return: 完整包名,未找到返回None
530
- """
531
- normalized_name = self.package_manager._normalize_name(short_name)
532
- remote_packages = asyncio.run(self.package_manager.get_remote_packages())
533
-
534
- # 检查模块
535
- for name, info in remote_packages["modules"].items():
536
- if self.package_manager._normalize_name(name) == normalized_name:
537
- return info["package"]
538
-
539
- # 检查适配器
540
- for name, info in remote_packages["adapters"].items():
541
- if self.package_manager._normalize_name(name) == normalized_name:
542
- return info["package"]
543
-
544
- return None
545
-
546
- def _print_search_results(self, query: str, results: Dict[str, List[Dict[str, str]]]):
547
- """
548
- 打印搜索结果
549
-
550
- :param query: 搜索关键词
551
- :param results: 搜索结果
552
- """
553
- if not results["installed"] and not results["remote"]:
554
- console.print(f"[info]未找到与 '[bold]{query}[/]' 匹配的包[/]")
555
- return
556
-
557
- # 打印已安装的包
558
- if results["installed"]:
559
- table = Table(
560
- title="已安装的包",
561
- box=SIMPLE,
562
- header_style="info"
563
- )
564
- table.add_column("类型")
565
- table.add_column("名称")
566
- table.add_column("包名")
567
- table.add_column("版本")
568
- table.add_column("描述")
569
-
570
- for item in results["installed"]:
571
- table.add_row(
572
- item["type"],
573
- item["name"],
574
- item["package"],
575
- item["version"],
576
- item["summary"]
577
- )
578
-
579
- console.print(table)
580
-
581
- # 打印远程包
582
- if results["remote"]:
583
- table = Table(
584
- title="远程包",
585
- box=SIMPLE,
586
- header_style="info"
587
- )
588
- table.add_column("类型")
589
- table.add_column("名称")
590
- table.add_column("包名")
591
- table.add_column("版本")
592
- table.add_column("描述")
593
-
594
- for item in results["remote"]:
595
- table.add_row(
596
- item["type"],
597
- item["name"],
598
- item["package"],
599
- item["version"],
600
- item["summary"]
601
- )
602
-
603
- console.print(table)
604
-
605
- def _print_version_list(self, versions: List[Dict[str, Any]], include_pre: bool = False):
606
- """
607
- 打印版本列表
608
-
609
- :param versions: 版本信息列表
610
- :param include_pre: 是否包含预发布版本
611
- """
612
- if not versions:
613
- console.print("[info]未找到可用版本[/]")
614
- return
615
-
616
- table = Table(
617
- title="可用版本",
618
- box=SIMPLE,
619
- header_style="info"
620
- )
621
- table.add_column("序号")
622
- table.add_column("版本")
623
- table.add_column("类型")
624
- table.add_column("上传时间")
625
-
626
- displayed = 0
627
- version_list = []
628
- for version_info in versions:
629
- # 如果不包含预发布版本,则跳过预发布版本
630
- if not include_pre and version_info["pre_release"]:
631
- continue
632
-
633
- version_list.append(version_info)
634
- version_type = "[yellow]预发布[/]" if version_info["pre_release"] else "[green]稳定版[/]"
635
- table.add_row(
636
- str(displayed + 1),
637
- version_info["version"],
638
- version_type,
639
- version_info["uploaded"][:10] if version_info["uploaded"] else "未知"
640
- )
641
- displayed += 1
642
-
643
- # 只显示前10个版本
644
- if displayed >= 10:
645
- break
646
-
647
- if displayed == 0:
648
- console.print("[info]没有找到符合条件的版本[/]")
649
- else:
650
- console.print(table)
651
- return version_list
652
-
653
- def _setup_watchdog(self, script_path: str, reload_mode: bool):
654
- """
655
- 设置文件监控
656
-
657
- :param script_path: 要监控的脚本路径
658
- :param reload_mode: 是否启用重载模式
659
- """
660
- from .reload_handler import ReloadHandler
661
-
662
- watch_dirs = [
663
- os.path.dirname(os.path.abspath(script_path)),
664
- ]
665
-
666
- # 添加配置目录
667
- config_dir = os.path.abspath(os.getcwd())
668
- if config_dir not in watch_dirs:
669
- watch_dirs.append(config_dir)
670
-
671
- self.handler = ReloadHandler(script_path, reload_mode)
672
- self.observer = Observer()
673
-
674
- for d in watch_dirs:
675
- if os.path.exists(d):
676
- self.observer.schedule(
677
- self.handler,
678
- d,
679
- recursive=reload_mode
680
- )
681
- console.print(f"[dim]监控目录: [path]{d}[/][/]")
682
-
683
- self.observer.start()
684
-
685
- mode_desc = "[bold]开发重载模式[/]" if reload_mode else "[bold]配置监控模式[/]"
686
- console.print(Panel(
687
- f"{mode_desc}\n监控目录: [path]{', '.join(watch_dirs)}[/]",
688
- title="热重载已启动",
689
- border_style="info"
690
- ))
691
-
692
- def _cleanup(self):
693
- """清理资源"""
694
- if self.observer:
695
- self.observer.stop()
696
- if self.handler and self.handler.process:
697
- self.handler._terminate_process()
698
- self.observer.join()
699
-
700
- def run(self):
701
- """
702
- 运行CLI
703
-
704
- :raises KeyboardInterrupt: 用户中断时抛出
705
- :raises Exception: 命令执行失败时抛出
706
- """
707
- args = self.parser.parse_args()
708
-
709
- if args.version:
710
- self._print_version()
711
- return
712
-
713
- if not args.command:
714
- self.parser.print_help()
715
- return
716
-
717
- try:
718
- if args.command == "install":
719
- success = self.package_manager.install_package(
720
- args.package,
721
- upgrade=args.upgrade,
722
- pre=args.pre
723
- )
724
- if not success:
725
- sys.exit(1)
726
-
727
- elif args.command == "uninstall":
728
- success = self.package_manager.uninstall_package(args.package)
729
- if not success:
730
- sys.exit(1)
731
-
732
- elif args.command == "module":
733
- from ErisPulse.Core import module as module_manager
734
- installed = self.package_manager.get_installed_packages()
735
-
736
- if args.module_command == "enable":
737
- if args.module not in installed["modules"]:
738
- console.print(f"[error]模块 [bold]{args.module}[/] 不存在或未安装[/]")
739
- else:
740
- module_manager.enable(args.module)
741
- console.print(f"[success]模块 [bold]{args.module}[/] 已启用[/]")
742
-
743
- elif args.module_command == "disable":
744
- if args.module not in installed["modules"]:
745
- console.print(f"[error]模块 [bold]{args.module}[/] 不存在或未安装[/]")
746
- else:
747
- module_manager.disable(args.module)
748
- console.print(f"[warning]模块 [bold]{args.module}[/] 已禁用[/]")
749
- else:
750
- self.parser.parse_args(["module", "--help"])
751
-
752
- elif args.command == "list":
753
- pkg_type = args.type
754
- if pkg_type == "all":
755
- self._print_installed_packages("modules", args.outdated)
756
- self._print_installed_packages("adapters", args.outdated)
757
- self._print_installed_packages("cli", args.outdated)
758
- else:
759
- self._print_installed_packages(pkg_type, args.outdated)
760
-
761
- elif args.command == "list-remote":
762
- pkg_type = args.type
763
- if pkg_type == "all":
764
- self._print_remote_packages("modules")
765
- self._print_remote_packages("adapters")
766
- self._print_remote_packages("cli")
767
- else:
768
- self._print_remote_packages(pkg_type)
769
-
770
- elif args.command == "upgrade":
771
- if args.package:
772
- success = self.package_manager.upgrade_package(
773
- args.package,
774
- pre=args.pre
775
- )
776
- if not success:
777
- sys.exit(1)
778
- else:
779
- if args.force or Confirm.ask("确定要升级所有ErisPulse组件吗?", default=False):
780
- success = self.package_manager.upgrade_all()
781
- if not success:
782
- sys.exit(1)
783
-
784
- elif args.command == "search":
785
- results = self.package_manager.search_package(args.query)
786
-
787
- # 根据选项过滤结果
788
- if args.installed:
789
- results["remote"] = []
790
- elif args.remote:
791
- results["installed"] = []
792
-
793
- self._print_search_results(args.query, results)
794
-
795
- elif args.command == "self-update":
796
- current_version = self.package_manager.get_installed_version()
797
- console.print(Panel(
798
- f"[title]ErisPulse SDK 自更新[/]\n"
799
- f"当前版本: [bold]{current_version}[/]",
800
- title_align="left"
801
- ))
802
-
803
- # 获取可用版本
804
- with console.status("[bold green]正在获取版本信息...", spinner="dots"):
805
- versions = asyncio.run(self.package_manager.get_pypi_versions())
806
-
807
- if not versions:
808
- console.print("[error]无法获取版本信息[/]")
809
- sys.exit(1)
810
-
811
- # 交互式选择更新选项
812
- if not args.version:
813
- # 显示最新版本
814
- stable_versions = [v for v in versions if not v["pre_release"]]
815
- pre_versions = [v for v in versions if v["pre_release"]]
816
-
817
- latest_stable = stable_versions[0] if stable_versions else None
818
- latest_pre = pre_versions[0] if pre_versions and args.pre else None
819
-
820
- choices = []
821
- choice_versions = {}
822
- choice_index = {}
823
-
824
- if latest_stable:
825
- choice = f"最新稳定版 ({latest_stable['version']})"
826
- choices.append(choice)
827
- choice_versions[choice] = latest_stable['version']
828
- choice_index[len(choices)] = choice
829
-
830
- if args.pre and latest_pre:
831
- choice = f"最新预发布版 ({latest_pre['version']})"
832
- choices.append(choice)
833
- choice_versions[choice] = latest_pre['version']
834
- choice_index[len(choices)] = choice
835
-
836
- # 添加其他选项
837
- choices.append("查看所有版本")
838
- choices.append("手动指定版本")
839
- choices.append("取消")
840
-
841
- # 创建数字索引映射
842
- for i, choice in enumerate(choices, 1):
843
- choice_index[i] = choice
844
-
845
- # 显示选项
846
- console.print("\n[info]请选择更新选项:[/]")
847
- for i, choice in enumerate(choices, 1):
848
- console.print(f" {i}. {choice}")
849
-
850
- while True:
851
- try:
852
- selected_input = Prompt.ask(
853
- "请输入选项编号",
854
- default="1"
855
- )
856
-
857
- if selected_input.isdigit():
858
- selected_index = int(selected_input)
859
- if selected_index in choice_index:
860
- selected = choice_index[selected_index]
861
- break
862
- else:
863
- console.print("[warning]请输入有效的选项编号[/]")
864
- else:
865
- # 检查是否是选项文本
866
- if selected_input in choices:
867
- selected = selected_input
868
- break
869
- else:
870
- console.print("[warning]请输入有效的选项编号或选项名称[/]")
871
- except KeyboardInterrupt:
872
- console.print("\n[info]操作已取消[/]")
873
- sys.exit(0)
874
-
875
- if selected == "取消":
876
- console.print("[info]操作已取消[/]")
877
- sys.exit(0)
878
- elif selected == "手动指定版本":
879
- target_version = Prompt.ask("请输入要更新到的版本号")
880
- if not any(v['version'] == target_version for v in versions):
881
- console.print(f"[warning]版本 {target_version} 可能不存在[/]")
882
- if not Confirm.ask("是否继续?", default=False):
883
- sys.exit(0)
884
- elif selected == "查看所有版本":
885
- version_list = self._print_version_list(versions, include_pre=args.pre)
886
- if not version_list:
887
- console.print("[info]没有可用版本[/]")
888
- sys.exit(0)
889
-
890
- # 显示版本选择
891
- console.print("\n[info]请选择要更新到的版本:[/]")
892
- while True:
893
- try:
894
- version_input = Prompt.ask("请输入版本序号或版本号")
895
- if version_input.isdigit():
896
- version_index = int(version_input)
897
- if 1 <= version_index <= len(version_list):
898
- target_version = version_list[version_index - 1]['version']
899
- break
900
- else:
901
- console.print("[warning]请输入有效的版本序号[/]")
902
- else:
903
- # 检查是否是有效的版本号
904
- if any(v['version'] == version_input for v in version_list):
905
- target_version = version_input
906
- break
907
- else:
908
- console.print("[warning]请输入有效的版本序号或版本号[/]")
909
- except KeyboardInterrupt:
910
- console.print("\n[info]操作已取消[/]")
911
- sys.exit(0)
912
- else:
913
- target_version = choice_versions[selected]
914
- else:
915
- target_version = args.version
916
-
917
- # 确认更新
918
- if target_version == current_version and not args.force:
919
- console.print(f"[info]当前已是目标版本 [bold]{current_version}[/][/]")
920
- sys.exit(0)
921
- elif not args.force:
922
- if not Confirm.ask(f"确认将ErisPulse SDK从 [bold]{current_version}[/] 更新到 [bold]{target_version}[/] 吗?", default=False):
923
- console.print("[info]操作已取消[/]")
924
- sys.exit(0)
925
-
926
- # 执行更新
927
- success = self.package_manager.update_self(target_version, args.force)
928
- if not success:
929
- sys.exit(1)
930
-
931
- elif args.command == "run":
932
- script = args.script or "main.py"
933
- if not os.path.exists(script):
934
- console.print(f"[error]找不到指定文件: [path]{script}[/][/]")
935
- return
936
-
937
- reload_mode = args.reload and not args.no_reload
938
- self._setup_watchdog(script, reload_mode)
939
-
940
- try:
941
- while True:
942
- time.sleep(0.5)
943
- except KeyboardInterrupt:
944
- console.print("\n[info]正在安全关闭...[/]")
945
- _cleanup_adapters()
946
- _cleanup_modules()
947
- self._cleanup()
948
- console.print("[success]已安全退出[/]")
949
-
950
- elif args.command == "init":
951
- from ErisPulse import ux
952
-
953
- # 显示欢迎信息
954
- try:
955
- version = importlib.metadata.version('ErisPulse')
956
- ux.welcome(version)
957
- except Exception:
958
- ux.welcome()
959
-
960
- # 使用交互式或快速模式初始化项目
961
- if args.quick and args.project_name:
962
- # 快速模式:只创建项目,不进行交互配置
963
- success = ux.init_project(args.project_name, [])
964
- else:
965
- # 交互式模式:引导用户完成项目和配置设置
966
- success = ux.interactive_init(args.project_name, args.force)
967
-
968
- if success:
969
- console.print("[success]项目初始化完成[/]")
970
- else:
971
- console.print("[error]项目初始化失败[/]")
972
- sys.exit(1)
973
-
974
- elif args.command == "status":
975
- from ErisPulse import ux
976
-
977
- # 显示状态概览
978
- ux.show_status()
979
-
980
- # 根据类型显示详细信息
981
- if args.type == "modules" or args.type == "all":
982
- ux.list_modules(detailed=True)
983
-
984
- if args.type == "adapters" or args.type == "all":
985
- ux.list_adapters(detailed=True)
986
-
987
-
988
-
989
- # 处理第三方命令
990
- elif args.command in self._get_external_commands():
991
- # 获取第三方命令的处理函数并执行
992
- entry_points = importlib.metadata.entry_points()
993
- if hasattr(entry_points, 'select'):
994
- cli_entries = entry_points.select(group='erispulse.cli')
995
- else:
996
- cli_entries = entry_points.get('erispulse.cli', [])
997
-
998
- for entry in cli_entries:
999
- if entry.name == args.command:
1000
- cli_func = entry.load()
1001
- if callable(cli_func):
1002
- # 创建一个新的解析器来解析第三方命令的参数
1003
- subparser = self.parser._subparsers._group_actions[0].choices[args.command]
1004
- parsed_args = subparser.parse_args(sys.argv[2:])
1005
- # 调用第三方命令处理函数(支持异步函数)
1006
- handler_func = parsed_args.func
1007
- if asyncio.iscoroutinefunction(handler_func):
1008
- # 异步函数:使用 asyncio.run() 运行
1009
- asyncio.run(handler_func(parsed_args))
1010
- else:
1011
- # 同步函数:直接调用
1012
- handler_func(parsed_args)
1013
- break
1014
-
1015
- except KeyboardInterrupt:
1016
- console.print("\n[warning]操作被用户中断[/]")
1017
- self._cleanup()
1018
- except Exception as e:
1019
- console.print(f"[error]执行命令时出错: {e}[/]")
1020
- if args.verbose >= 1:
1021
- import traceback
1022
- console.print(traceback.format_exc())
1023
- self._cleanup()
1024
- sys.exit(1)
1025
-
1026
- def _cleanup_adapters():
1027
- """
1028
- 清理适配器资源
1029
- """
1030
-
1031
- from ErisPulse import adapter
1032
- try:
1033
- import asyncio
1034
- import threading
1035
-
1036
- # 检查是否有正在运行的适配器
1037
- if adapter.list_adapters():
1038
-
1039
- console.print("[info]正在停止所有适配器...[/]")
1040
-
1041
- if threading.current_thread() is threading.main_thread():
1042
- try:
1043
- loop = asyncio.get_running_loop()
1044
- if loop.is_running():
1045
- # 在新线程中运行
1046
- stop_thread = threading.Thread(
1047
- target=lambda: asyncio.run(adapter.shutdown())
1048
- )
1049
- stop_thread.start()
1050
- stop_thread.join(timeout=5)
1051
- else:
1052
- asyncio.run(adapter.shutdown())
1053
- except RuntimeError:
1054
- asyncio.run(adapter.shutdown())
1055
- else:
1056
- new_loop = asyncio.new_event_loop()
1057
- asyncio.set_event_loop(new_loop)
1058
- new_loop.run_until_complete(adapter.shutdown())
1059
-
1060
- console.print("[success]适配器已全部停止[/]")
1061
- else:
1062
- console.print("[dim]没有需要停止的适配器[/]")
1063
- except Exception as e:
1064
- console.print(f"[error]清理适配器资源时出错: {e}[/]")
1065
-
1066
- def _cleanup_modules():
1067
- """
1068
- 清理模块资源
1069
- """
1070
- from ErisPulse import module
1071
- try:
1072
- import asyncio
1073
- import threading
1074
-
1075
- console.print("[info]正在卸载所有模块...[/]")
1076
-
1077
- if threading.current_thread() is threading.main_thread():
1078
- try:
1079
- loop = asyncio.get_running_loop()
1080
- if loop.is_running():
1081
- stop_thread = threading.Thread(
1082
- target=lambda: asyncio.run(module.unload())
1083
- )
1084
- stop_thread.start()
1085
- stop_thread.join(timeout=5)
1086
- else:
1087
- asyncio.run(module.unload())
1088
- except RuntimeError:
1089
- asyncio.run(module.unload())
1090
- else:
1091
- new_loop = asyncio.new_event_loop()
1092
- asyncio.set_event_loop(new_loop)
1093
- new_loop.run_until_complete(module.unload())
1094
-
1095
- console.print("[success]模块已全部卸载[/]")
1096
- except Exception as e:
1097
- console.print(f"[error]清理模块资源时出错: {e}[/]")