ErisPulse 2.1.5__py3-none-any.whl → 2.1.7__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.
- ErisPulse/Core/mods.py +5 -12
- ErisPulse/__init__.py +56 -77
- ErisPulse/__main__.py +320 -186
- {erispulse-2.1.5.dist-info → erispulse-2.1.7.dist-info}/METADATA +4 -1
- {erispulse-2.1.5.dist-info → erispulse-2.1.7.dist-info}/RECORD +8 -9
- ErisPulse/Core/shellprint.py +0 -165
- {erispulse-2.1.5.dist-info → erispulse-2.1.7.dist-info}/WHEEL +0 -0
- {erispulse-2.1.5.dist-info → erispulse-2.1.7.dist-info}/entry_points.txt +0 -0
- {erispulse-2.1.5.dist-info → erispulse-2.1.7.dist-info}/licenses/LICENSE +0 -0
ErisPulse/Core/mods.py
CHANGED
|
@@ -26,8 +26,8 @@ class ModuleManager:
|
|
|
26
26
|
{!--< /tips >!--}
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
DEFAULT_MODULE_PREFIX = "erispulse.
|
|
30
|
-
DEFAULT_STATUS_PREFIX = "erispulse.
|
|
29
|
+
DEFAULT_MODULE_PREFIX = "erispulse.data.modules.info:"
|
|
30
|
+
DEFAULT_STATUS_PREFIX = "erispulse.data.modules.status:"
|
|
31
31
|
|
|
32
32
|
def __init__(self):
|
|
33
33
|
from .env import env
|
|
@@ -75,12 +75,9 @@ class ModuleManager:
|
|
|
75
75
|
>>> # 禁用模块
|
|
76
76
|
>>> mods.set_module_status("MyModule", False)
|
|
77
77
|
"""
|
|
78
|
+
from .logger import logger
|
|
78
79
|
self.env.set(f"{self.status_prefix}{module_name}", bool(status))
|
|
79
|
-
|
|
80
|
-
module_info = self.get_module(module_name)
|
|
81
|
-
if module_info:
|
|
82
|
-
module_info["status"] = bool(status)
|
|
83
|
-
self.env.set(f"{self.module_prefix}{module_name}", module_info)
|
|
80
|
+
logger.debug(f"模块 {module_name} 状态已设置为 {status}")
|
|
84
81
|
|
|
85
82
|
def get_module_status(self, module_name: str) -> bool:
|
|
86
83
|
"""
|
|
@@ -95,7 +92,7 @@ class ModuleManager:
|
|
|
95
92
|
"""
|
|
96
93
|
status = self.env.get(f"{self.status_prefix}{module_name}", True)
|
|
97
94
|
if isinstance(status, str):
|
|
98
|
-
return status.lower()
|
|
95
|
+
return status.lower() not in ('false', '0', 'no', 'off')
|
|
99
96
|
return bool(status)
|
|
100
97
|
|
|
101
98
|
def set_module(self, module_name: str, module_info: Dict[str, Any]) -> None:
|
|
@@ -109,11 +106,9 @@ class ModuleManager:
|
|
|
109
106
|
>>> mods.set_module("MyModule", {
|
|
110
107
|
>>> "version": "1.0.0",
|
|
111
108
|
>>> "description": "我的模块",
|
|
112
|
-
>>> "status": True
|
|
113
109
|
>>> })
|
|
114
110
|
"""
|
|
115
111
|
self.env.set(f"{self.module_prefix}{module_name}", module_info)
|
|
116
|
-
self.set_module_status(module_name, module_info.get('status', True))
|
|
117
112
|
|
|
118
113
|
def get_module(self, module_name: str) -> Optional[Dict[str, Any]]:
|
|
119
114
|
"""
|
|
@@ -164,8 +159,6 @@ class ModuleManager:
|
|
|
164
159
|
module_name = key[prefix_len:]
|
|
165
160
|
module_info = self.get_module(module_name)
|
|
166
161
|
if module_info:
|
|
167
|
-
status = self.get_module_status(module_name)
|
|
168
|
-
module_info['status'] = bool(status)
|
|
169
162
|
modules_info[module_name] = module_info
|
|
170
163
|
return modules_info
|
|
171
164
|
|
ErisPulse/__init__.py
CHANGED
|
@@ -27,9 +27,6 @@ from .Core import mods
|
|
|
27
27
|
from .Core import adapter, AdapterFather, SendDSL
|
|
28
28
|
from .Core import adapter_server
|
|
29
29
|
|
|
30
|
-
# 确保windows下的shell能正确显示颜色
|
|
31
|
-
os.system('')
|
|
32
|
-
|
|
33
30
|
sdk = sys.modules[__name__]
|
|
34
31
|
|
|
35
32
|
BaseModules = {
|
|
@@ -244,62 +241,47 @@ class AdapterLoader:
|
|
|
244
241
|
|
|
245
242
|
:raises ImportError: 当适配器加载失败时抛出
|
|
246
243
|
"""
|
|
244
|
+
meta_name = entry_point.name
|
|
245
|
+
adapter_status = mods.get_module_status(meta_name)
|
|
246
|
+
logger.debug(f"适配器 {meta_name} 状态: {adapter_status}")
|
|
247
|
+
|
|
248
|
+
if adapter_status is False:
|
|
249
|
+
disabled_adapters.append(meta_name)
|
|
250
|
+
logger.warning(f"适配器 {meta_name} 已禁用,跳过加载")
|
|
251
|
+
return adapter_objs, enabled_adapters, disabled_adapters
|
|
252
|
+
|
|
247
253
|
try:
|
|
248
|
-
|
|
249
|
-
adapter_obj = sys.modules[
|
|
254
|
+
loaded_class = entry_point.load()
|
|
255
|
+
adapter_obj = sys.modules[loaded_class.__module__]
|
|
250
256
|
dist = importlib.metadata.distribution(entry_point.dist.name)
|
|
251
257
|
|
|
252
|
-
# 创建adapterInfo
|
|
253
258
|
adapter_info = {
|
|
254
259
|
"meta": {
|
|
255
|
-
"name":
|
|
260
|
+
"name": meta_name,
|
|
256
261
|
"version": getattr(adapter_obj, "__version__", dist.version if dist else "1.0.0"),
|
|
257
262
|
"description": getattr(adapter_obj, "__description__", ""),
|
|
258
263
|
"author": getattr(adapter_obj, "__author__", ""),
|
|
259
264
|
"license": getattr(adapter_obj, "__license__", ""),
|
|
260
265
|
"package": entry_point.dist.name
|
|
261
266
|
},
|
|
262
|
-
"adapter_class":
|
|
267
|
+
"adapter_class": loaded_class
|
|
263
268
|
}
|
|
264
269
|
|
|
265
|
-
# 检查是否已经加载过这个适配器类
|
|
266
|
-
existing_instance = None
|
|
267
|
-
for existing_adapter in adapter_objs.values():
|
|
268
|
-
if hasattr(existing_adapter, 'adapterInfo'):
|
|
269
|
-
for existing_adapter_info in existing_adapter.adapterInfo.values():
|
|
270
|
-
if isinstance(existing_adapter_info, dict) and existing_adapter_info["adapter_class"] == loaded_obj:
|
|
271
|
-
existing_instance = existing_adapter_info["adapter_class"]
|
|
272
|
-
break
|
|
273
|
-
|
|
274
|
-
# 如果已经存在实例,则复用
|
|
275
|
-
if existing_instance is not None:
|
|
276
|
-
adapter_info["adapter_class"] = existing_instance
|
|
277
|
-
|
|
278
270
|
if not hasattr(adapter_obj, 'adapterInfo'):
|
|
279
271
|
adapter_obj.adapterInfo = {}
|
|
280
272
|
|
|
281
|
-
adapter_obj.adapterInfo[
|
|
282
|
-
|
|
283
|
-
# 检查适配器状态
|
|
284
|
-
meta_name = entry_point.name
|
|
285
|
-
stored_info = mods.get_module(meta_name) or {
|
|
286
|
-
"status": True,
|
|
287
|
-
"info": adapter_info
|
|
288
|
-
}
|
|
289
|
-
mods.set_module(meta_name, stored_info)
|
|
273
|
+
adapter_obj.adapterInfo[meta_name] = adapter_info
|
|
290
274
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
logger.warning(f"适配器 {meta_name} 已禁用,跳过加载")
|
|
294
|
-
return adapter_objs, enabled_adapters, disabled_adapters
|
|
275
|
+
# 存储适配器信息
|
|
276
|
+
mods.set_module(meta_name, adapter_info)
|
|
295
277
|
|
|
296
278
|
adapter_objs[meta_name] = adapter_obj
|
|
297
279
|
enabled_adapters.append(meta_name)
|
|
298
|
-
logger.debug(f"从PyPI
|
|
280
|
+
logger.debug(f"从PyPI包发现适配器: {meta_name}")
|
|
299
281
|
|
|
300
282
|
except Exception as e:
|
|
301
|
-
logger.warning(f"从entry-point加载适配器 {
|
|
302
|
-
raise ImportError(f"无法加载适配器 {
|
|
283
|
+
logger.warning(f"从entry-point加载适配器 {meta_name} 失败: {e}")
|
|
284
|
+
raise ImportError(f"无法加载适配器 {meta_name}: {e}")
|
|
303
285
|
|
|
304
286
|
return adapter_objs, enabled_adapters, disabled_adapters
|
|
305
287
|
|
|
@@ -374,18 +356,26 @@ class ModuleLoader:
|
|
|
374
356
|
|
|
375
357
|
:raises ImportError: 当模块加载失败时抛出
|
|
376
358
|
"""
|
|
359
|
+
meta_name = entry_point.name
|
|
360
|
+
module_status = mods.get_module_status(meta_name)
|
|
361
|
+
logger.debug(f"模块 {meta_name} 状态: {module_status}")
|
|
362
|
+
|
|
363
|
+
# 首先检查模块状态,如果明确为False则直接跳过
|
|
364
|
+
if module_status is False:
|
|
365
|
+
disabled_modules.append(meta_name)
|
|
366
|
+
logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
|
|
367
|
+
return module_objs, enabled_modules, disabled_modules
|
|
368
|
+
|
|
377
369
|
try:
|
|
378
370
|
loaded_obj = entry_point.load()
|
|
379
371
|
module_obj = sys.modules[loaded_obj.__module__]
|
|
380
372
|
dist = importlib.metadata.distribution(entry_point.dist.name)
|
|
381
373
|
|
|
382
|
-
|
|
383
|
-
lazy_load = ModuleLoader._should_lazy_load(dist)
|
|
374
|
+
lazy_load = ModuleLoader._should_lazy_load(loaded_obj)
|
|
384
375
|
|
|
385
|
-
# 创建moduleInfo
|
|
386
376
|
module_info = {
|
|
387
377
|
"meta": {
|
|
388
|
-
"name":
|
|
378
|
+
"name": meta_name,
|
|
389
379
|
"version": getattr(module_obj, "__version__", dist.version if dist else "1.0.0"),
|
|
390
380
|
"description": getattr(module_obj, "__description__", ""),
|
|
391
381
|
"author": getattr(module_obj, "__author__", ""),
|
|
@@ -398,26 +388,16 @@ class ModuleLoader:
|
|
|
398
388
|
|
|
399
389
|
module_obj.moduleInfo = module_info
|
|
400
390
|
|
|
401
|
-
#
|
|
402
|
-
meta_name
|
|
403
|
-
stored_info = mods.get_module(meta_name) or {
|
|
404
|
-
"status": True,
|
|
405
|
-
"info": module_info
|
|
406
|
-
}
|
|
407
|
-
mods.set_module(meta_name, stored_info)
|
|
408
|
-
|
|
409
|
-
if not stored_info.get('status', True):
|
|
410
|
-
disabled_modules.append(meta_name)
|
|
411
|
-
logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
|
|
412
|
-
return module_objs, enabled_modules, disabled_modules
|
|
391
|
+
# 存储模块信息
|
|
392
|
+
mods.set_module(meta_name, module_info)
|
|
413
393
|
|
|
414
394
|
module_objs[meta_name] = module_obj
|
|
415
395
|
enabled_modules.append(meta_name)
|
|
416
396
|
logger.debug(f"从PyPI包加载模块: {meta_name}")
|
|
417
397
|
|
|
418
398
|
except Exception as e:
|
|
419
|
-
logger.warning(f"从entry-point加载模块 {
|
|
420
|
-
raise ImportError(f"无法加载模块 {
|
|
399
|
+
logger.warning(f"从entry-point加载模块 {meta_name} 失败: {e}")
|
|
400
|
+
raise ImportError(f"无法加载模块 {meta_name}: {e}")
|
|
421
401
|
|
|
422
402
|
return module_objs, enabled_modules, disabled_modules
|
|
423
403
|
|
|
@@ -512,7 +492,7 @@ class ModuleInitializer:
|
|
|
512
492
|
|
|
513
493
|
:return: bool 模块初始化是否成功
|
|
514
494
|
"""
|
|
515
|
-
#
|
|
495
|
+
# 将所有模块挂载到LazyModule代理上
|
|
516
496
|
for module_name in modules:
|
|
517
497
|
module_obj = module_objs[module_name]
|
|
518
498
|
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
@@ -544,7 +524,7 @@ class ModuleInitializer:
|
|
|
544
524
|
setattr(sdk, meta_name, None)
|
|
545
525
|
return False
|
|
546
526
|
|
|
547
|
-
#
|
|
527
|
+
# 检查并初始化需要立即加载的模块
|
|
548
528
|
for module_name in modules:
|
|
549
529
|
module_obj = module_objs[module_name]
|
|
550
530
|
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
@@ -577,7 +557,6 @@ class ModuleInitializer:
|
|
|
577
557
|
return False
|
|
578
558
|
|
|
579
559
|
return True
|
|
580
|
-
|
|
581
560
|
@staticmethod
|
|
582
561
|
def _register_adapters(adapters: List[str], adapter_objs: Dict[str, Any]) -> bool:
|
|
583
562
|
"""
|
|
@@ -590,9 +569,6 @@ class ModuleInitializer:
|
|
|
590
569
|
:return: bool 适配器注册是否成功
|
|
591
570
|
"""
|
|
592
571
|
success = True
|
|
593
|
-
# 存储平台名到适配器类的映射
|
|
594
|
-
platform_to_adapter = {}
|
|
595
|
-
# 存储已注册的适配器类到实例的映射
|
|
596
572
|
registered_classes = {}
|
|
597
573
|
|
|
598
574
|
for adapter_name in adapters:
|
|
@@ -601,28 +577,31 @@ class ModuleInitializer:
|
|
|
601
577
|
try:
|
|
602
578
|
if hasattr(adapter_obj, "adapterInfo") and isinstance(adapter_obj.adapterInfo, dict):
|
|
603
579
|
for platform, adapter_info in adapter_obj.adapterInfo.items():
|
|
604
|
-
|
|
605
|
-
if platform in platform_to_adapter:
|
|
580
|
+
if platform in sdk.adapter._adapters:
|
|
606
581
|
continue
|
|
607
582
|
|
|
608
583
|
adapter_class = adapter_info["adapter_class"]
|
|
609
584
|
|
|
610
|
-
# 检查是否已经注册过这个适配器类
|
|
611
585
|
if adapter_class in registered_classes:
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
sdk.adapter.
|
|
616
|
-
|
|
586
|
+
instance = registered_classes[adapter_class]
|
|
587
|
+
# 改为直接操作适配器字典而不是调用register
|
|
588
|
+
sdk.adapter._adapters[platform] = instance
|
|
589
|
+
sdk.adapter._platform_to_instance[platform] = instance
|
|
590
|
+
logger.debug(f"复用适配器实例 {adapter_class.__name__} 到平台别称 {platform}")
|
|
617
591
|
else:
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
592
|
+
init_signature = inspect.signature(adapter_class.__init__)
|
|
593
|
+
params = init_signature.parameters
|
|
594
|
+
|
|
595
|
+
if 'sdk' in params:
|
|
596
|
+
instance = adapter_class(sdk)
|
|
597
|
+
else:
|
|
598
|
+
instance = adapter_class()
|
|
599
|
+
|
|
600
|
+
# 直接操作适配器字典
|
|
601
|
+
sdk.adapter._adapters[platform] = instance
|
|
602
|
+
sdk.adapter._platform_to_instance[platform] = instance
|
|
603
|
+
registered_classes[adapter_class] = instance
|
|
604
|
+
logger.info(f"注册适配器: {platform} ({adapter_class.__name__})")
|
|
626
605
|
except Exception as e:
|
|
627
606
|
logger.error(f"适配器 {adapter_name} 注册失败: {e}")
|
|
628
607
|
success = False
|
ErisPulse/__main__.py
CHANGED
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
# CLI 入口
|
|
3
|
-
|
|
4
|
-
提供命令行界面(CLI)用于包管理和启动入口。
|
|
5
|
-
|
|
6
|
-
## 主要命令
|
|
7
|
-
### 包管理:
|
|
8
|
-
install: 安装模块/适配器包
|
|
9
|
-
uninstall: 卸载模块/适配器包
|
|
10
|
-
list: 列出已安装的模块/适配器
|
|
11
|
-
list-remote: 列出远程PyPI上的ErisPulse模块和适配器
|
|
12
|
-
upgrade: 升级所有模块/适配器
|
|
13
|
-
|
|
14
|
-
### 启动:
|
|
15
|
-
run: 运行脚本
|
|
16
|
-
--reload: 启用热重载
|
|
17
|
-
|
|
18
|
-
### 示例用法:
|
|
19
|
-
```
|
|
20
|
-
# 安装模块
|
|
21
|
-
epsdk install Yunhu
|
|
22
|
-
|
|
23
|
-
# 启用热重载
|
|
24
|
-
epsdk run main.py --reload
|
|
25
|
-
```
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
1
|
import argparse
|
|
29
2
|
import importlib.metadata
|
|
30
3
|
import subprocess
|
|
@@ -38,21 +11,45 @@ from typing import List, Dict, Tuple, Optional
|
|
|
38
11
|
from importlib.metadata import version, PackageNotFoundError
|
|
39
12
|
from watchdog.observers import Observer
|
|
40
13
|
from watchdog.events import FileSystemEventHandler
|
|
41
|
-
|
|
14
|
+
|
|
15
|
+
# Rich console setup
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
from rich.progress import Progress, BarColumn, TextColumn
|
|
20
|
+
from rich.prompt import Confirm, Prompt
|
|
21
|
+
from rich.text import Text
|
|
22
|
+
from rich.box import SIMPLE, ROUNDED, DOUBLE
|
|
23
|
+
from rich.style import Style
|
|
24
|
+
from rich.theme import Theme
|
|
25
|
+
from rich.layout import Layout
|
|
26
|
+
from rich.live import Live
|
|
27
|
+
|
|
28
|
+
# 确保在Windows上启用颜色
|
|
29
|
+
import sys
|
|
30
|
+
if sys.platform == "win32":
|
|
31
|
+
from colorama import init
|
|
32
|
+
init()
|
|
33
|
+
|
|
34
|
+
theme = Theme({
|
|
35
|
+
"info": "cyan",
|
|
36
|
+
"success": "green",
|
|
37
|
+
"warning": "yellow",
|
|
38
|
+
"error": "red",
|
|
39
|
+
"title": "magenta",
|
|
40
|
+
"default": "default",
|
|
41
|
+
"progress": "green",
|
|
42
|
+
"progress.remaining": "white",
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
console = Console(theme=theme, color_system="auto", force_terminal=True)
|
|
42
46
|
|
|
43
47
|
class PyPIManager:
|
|
44
48
|
"""
|
|
45
49
|
PyPI包管理器
|
|
46
50
|
|
|
47
51
|
负责与PyPI交互,包括搜索、安装、卸载和升级ErisPulse模块/适配器
|
|
48
|
-
|
|
49
|
-
{!--< tips >!--}
|
|
50
|
-
1. 支持多个远程源作为备份
|
|
51
|
-
2. 自动区分模块和适配器
|
|
52
|
-
3. 提供详细的错误处理
|
|
53
|
-
{!--< /tips >!--}
|
|
54
52
|
"""
|
|
55
|
-
|
|
56
53
|
REMOTE_SOURCES = [
|
|
57
54
|
"https://erisdev.com/packages.json",
|
|
58
55
|
"https://raw.githubusercontent.com/ErisPulse/ErisPulse-ModuleRepo/main/packages.json"
|
|
@@ -73,7 +70,6 @@ class PyPIManager:
|
|
|
73
70
|
:raises ClientError: 当网络请求失败时抛出
|
|
74
71
|
:raises asyncio.TimeoutError: 当请求超时时抛出
|
|
75
72
|
"""
|
|
76
|
-
|
|
77
73
|
import aiohttp
|
|
78
74
|
from aiohttp import ClientError, ClientTimeout
|
|
79
75
|
|
|
@@ -92,7 +88,11 @@ class PyPIManager:
|
|
|
92
88
|
}
|
|
93
89
|
except (ClientError, asyncio.TimeoutError) as e:
|
|
94
90
|
last_error = e
|
|
95
|
-
|
|
91
|
+
console.print(Panel(
|
|
92
|
+
f"官方源请求失败,尝试备用源: {e}",
|
|
93
|
+
title="警告",
|
|
94
|
+
style="warning"
|
|
95
|
+
))
|
|
96
96
|
|
|
97
97
|
try:
|
|
98
98
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
@@ -108,7 +108,11 @@ class PyPIManager:
|
|
|
108
108
|
last_error = e
|
|
109
109
|
|
|
110
110
|
if last_error:
|
|
111
|
-
|
|
111
|
+
console.print(Panel(
|
|
112
|
+
f"获取远程模块列表失败: {last_error}",
|
|
113
|
+
title="错误",
|
|
114
|
+
style="error"
|
|
115
|
+
))
|
|
112
116
|
return {"modules": {}, "adapters": {}}
|
|
113
117
|
|
|
114
118
|
@staticmethod
|
|
@@ -145,33 +149,81 @@ class PyPIManager:
|
|
|
145
149
|
"summary": dist.metadata["Summary"]
|
|
146
150
|
}
|
|
147
151
|
except Exception as e:
|
|
148
|
-
|
|
152
|
+
console.print(Panel(
|
|
153
|
+
f"获取已安装包信息失败: {e}",
|
|
154
|
+
title="错误",
|
|
155
|
+
style="error"
|
|
156
|
+
))
|
|
149
157
|
|
|
150
158
|
return packages
|
|
151
159
|
|
|
160
|
+
@staticmethod
|
|
161
|
+
def uv_install_package(package_name: str, upgrade: bool = False) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
优先使用uv安装包
|
|
164
|
+
|
|
165
|
+
:param package_name: str 要安装的包名
|
|
166
|
+
:param upgrade: bool 是否升级已安装的包 (默认: False)
|
|
167
|
+
:return: bool 安装是否成功
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
# 检查uv是否可用
|
|
171
|
+
uv_check = subprocess.run(["uv", "--version"], capture_output=True)
|
|
172
|
+
if uv_check.returncode != 0:
|
|
173
|
+
return False # uv不可用
|
|
174
|
+
|
|
175
|
+
cmd = ["uv", "pip", "install"]
|
|
176
|
+
if upgrade:
|
|
177
|
+
cmd.append("--upgrade")
|
|
178
|
+
cmd.append(package_name)
|
|
179
|
+
|
|
180
|
+
with console.status(f"使用uv安装 {package_name}..."):
|
|
181
|
+
result = subprocess.run(cmd, check=True)
|
|
182
|
+
return result.returncode == 0
|
|
183
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
184
|
+
return False
|
|
185
|
+
|
|
152
186
|
@staticmethod
|
|
153
187
|
def install_package(package_name: str, upgrade: bool = False) -> bool:
|
|
154
188
|
"""
|
|
155
|
-
安装指定包
|
|
189
|
+
安装指定包 (修改后优先尝试uv)
|
|
156
190
|
|
|
157
191
|
:param package_name: str 要安装的包名
|
|
158
192
|
:param upgrade: bool 是否升级已安装的包 (默认: False)
|
|
159
193
|
:return: bool 安装是否成功
|
|
160
194
|
"""
|
|
195
|
+
# 优先尝试uv安装
|
|
196
|
+
if PyPIManager.uv_install_package(package_name, upgrade):
|
|
197
|
+
console.print(Panel(
|
|
198
|
+
f"包 {package_name} 安装成功 (使用uv)",
|
|
199
|
+
title="成功",
|
|
200
|
+
style="success"
|
|
201
|
+
))
|
|
202
|
+
return True
|
|
203
|
+
|
|
204
|
+
# 回退到pip安装
|
|
161
205
|
try:
|
|
162
206
|
cmd = [sys.executable, "-m", "pip", "install"]
|
|
163
207
|
if upgrade:
|
|
164
208
|
cmd.append("--upgrade")
|
|
165
209
|
cmd.append(package_name)
|
|
166
210
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
211
|
+
with console.status(f"使用pip安装 {package_name}..."):
|
|
212
|
+
result = subprocess.run(cmd, check=True)
|
|
213
|
+
if result.returncode == 0:
|
|
214
|
+
console.print(Panel(
|
|
215
|
+
f"包 {package_name} 安装成功",
|
|
216
|
+
title="成功",
|
|
217
|
+
style="success"
|
|
218
|
+
))
|
|
219
|
+
return True
|
|
220
|
+
return False
|
|
173
221
|
except subprocess.CalledProcessError as e:
|
|
174
|
-
|
|
222
|
+
console.print(Panel(
|
|
223
|
+
f"安装包 {package_name} 失败: {e}",
|
|
224
|
+
title="错误",
|
|
225
|
+
style="error"
|
|
226
|
+
))
|
|
175
227
|
return False
|
|
176
228
|
|
|
177
229
|
@staticmethod
|
|
@@ -183,17 +235,25 @@ class PyPIManager:
|
|
|
183
235
|
:return: bool 卸载是否成功
|
|
184
236
|
"""
|
|
185
237
|
try:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
238
|
+
with console.status(f"正在卸载 {package_name}..."):
|
|
239
|
+
result = subprocess.run(
|
|
240
|
+
[sys.executable, "-m", "pip", "uninstall", "-y", package_name],
|
|
241
|
+
check=True
|
|
242
|
+
)
|
|
243
|
+
if result.returncode == 0:
|
|
244
|
+
console.print(Panel(
|
|
245
|
+
f"包 {package_name} 卸载成功",
|
|
246
|
+
title="成功",
|
|
247
|
+
style="success"
|
|
248
|
+
))
|
|
249
|
+
return True
|
|
250
|
+
return False
|
|
195
251
|
except subprocess.CalledProcessError as e:
|
|
196
|
-
|
|
252
|
+
console.print(Panel(
|
|
253
|
+
f"卸载包 {package_name} 失败: {e}",
|
|
254
|
+
title="错误",
|
|
255
|
+
style="error"
|
|
256
|
+
))
|
|
197
257
|
return False
|
|
198
258
|
|
|
199
259
|
@staticmethod
|
|
@@ -202,11 +262,6 @@ class PyPIManager:
|
|
|
202
262
|
升级所有已安装的ErisPulse包
|
|
203
263
|
|
|
204
264
|
:return: bool 升级是否成功
|
|
205
|
-
|
|
206
|
-
{!--< tips >!--}
|
|
207
|
-
1. 会先列出所有可升级的包
|
|
208
|
-
2. 需要用户确认才会执行升级
|
|
209
|
-
{!--< /tips >!--}
|
|
210
265
|
"""
|
|
211
266
|
try:
|
|
212
267
|
installed = PyPIManager.get_installed_packages()
|
|
@@ -217,17 +272,21 @@ class PyPIManager:
|
|
|
217
272
|
all_packages.add(pkg_info["package"])
|
|
218
273
|
|
|
219
274
|
if not all_packages:
|
|
220
|
-
|
|
275
|
+
console.print(Panel(
|
|
276
|
+
"没有找到可升级的ErisPulse包",
|
|
277
|
+
title="提示",
|
|
278
|
+
style="info"
|
|
279
|
+
))
|
|
221
280
|
return False
|
|
222
281
|
|
|
223
|
-
|
|
282
|
+
console.print(Panel(
|
|
224
283
|
f"找到 {len(all_packages)} 个可升级的包:\n" +
|
|
225
284
|
"\n".join(f" - {pkg}" for pkg in all_packages),
|
|
226
|
-
"升级列表",
|
|
227
|
-
"info"
|
|
228
|
-
)
|
|
285
|
+
title="升级列表",
|
|
286
|
+
style="info"
|
|
287
|
+
))
|
|
229
288
|
|
|
230
|
-
if not
|
|
289
|
+
if not Confirm.ask("确认升级所有包吗?", default=False):
|
|
231
290
|
return False
|
|
232
291
|
|
|
233
292
|
for pkg in all_packages:
|
|
@@ -235,7 +294,11 @@ class PyPIManager:
|
|
|
235
294
|
|
|
236
295
|
return True
|
|
237
296
|
except Exception as e:
|
|
238
|
-
|
|
297
|
+
console.print(Panel(
|
|
298
|
+
f"升级包失败: {e}",
|
|
299
|
+
title="错误",
|
|
300
|
+
style="error"
|
|
301
|
+
))
|
|
239
302
|
return False
|
|
240
303
|
|
|
241
304
|
class ReloadHandler(FileSystemEventHandler):
|
|
@@ -243,31 +306,24 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
243
306
|
热重载处理器
|
|
244
307
|
|
|
245
308
|
监控文件变化并自动重启脚本
|
|
246
|
-
|
|
247
|
-
{!--< tips >!--}
|
|
248
|
-
1. 基于watchdog实现文件监控
|
|
249
|
-
2. 有1秒的防抖延迟
|
|
250
|
-
3. 会终止旧进程并启动新进程
|
|
251
|
-
{!--< /tips >!--}
|
|
252
309
|
"""
|
|
253
|
-
def __init__(self, script_path, *args, **kwargs):
|
|
310
|
+
def __init__(self, script_path, reload_mode=False, *args, **kwargs):
|
|
254
311
|
super().__init__(*args, **kwargs)
|
|
255
312
|
self.script_path = script_path
|
|
256
313
|
self.process = None
|
|
257
314
|
self.last_reload = time.time()
|
|
315
|
+
self.reload_mode = reload_mode
|
|
258
316
|
self.start_process()
|
|
259
317
|
|
|
260
318
|
def start_process(self):
|
|
261
319
|
"""
|
|
262
320
|
启动/重启被监控的进程
|
|
263
|
-
|
|
264
|
-
{!--< internal-use >!--}
|
|
265
321
|
"""
|
|
266
322
|
if self.process:
|
|
267
323
|
self.process.terminate()
|
|
268
324
|
self.process.wait()
|
|
269
325
|
|
|
270
|
-
|
|
326
|
+
console.print(f"[bold green]启动进程:[/] {self.script_path}")
|
|
271
327
|
self.process = subprocess.Popen([sys.executable, self.script_path])
|
|
272
328
|
self.last_reload = time.time()
|
|
273
329
|
|
|
@@ -281,68 +337,67 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
281
337
|
if now - self.last_reload < 1.0:
|
|
282
338
|
return
|
|
283
339
|
|
|
284
|
-
if event.src_path.endswith(".py"):
|
|
285
|
-
print(f"\n
|
|
340
|
+
if event.src_path.endswith(".py") and self.reload_mode:
|
|
341
|
+
console.print(f"\n[cyan][热重载] 文件发生变动: {event.src_path}[/]")
|
|
342
|
+
self.start_process()
|
|
343
|
+
elif event.src_path.endswith("config.toml"):
|
|
344
|
+
console.print(f"\n[cyan][热重载] 配置发生变动: {event.src_path}[/]")
|
|
286
345
|
self.start_process()
|
|
287
346
|
|
|
288
|
-
def start_reloader(script_path):
|
|
347
|
+
def start_reloader(script_path, reload_mode=False):
|
|
289
348
|
"""
|
|
290
349
|
启动热重载监控
|
|
291
350
|
|
|
292
351
|
:param script_path: str 要监控的脚本路径
|
|
293
|
-
|
|
294
|
-
{!--< tips >!--}
|
|
295
|
-
1. 监控脚本所在目录和modules目录
|
|
296
|
-
2. 按Ctrl+C可停止监控
|
|
297
|
-
{!--< /tips >!--}
|
|
352
|
+
:param reload_mode: bool 是否启用完整重载模式 (默认: False)
|
|
298
353
|
"""
|
|
299
|
-
|
|
354
|
+
if not os.path.exists(script_path):
|
|
355
|
+
console.print(Panel(
|
|
356
|
+
f"找不到指定文件: {script_path}",
|
|
357
|
+
title="错误",
|
|
358
|
+
style="error"
|
|
359
|
+
))
|
|
360
|
+
return
|
|
300
361
|
watch_dirs = [
|
|
301
362
|
os.path.dirname(os.path.abspath(script_path)),
|
|
302
|
-
os.path.join(project_root, "modules")
|
|
303
363
|
]
|
|
304
364
|
|
|
305
|
-
handler = ReloadHandler(script_path)
|
|
365
|
+
handler = ReloadHandler(script_path, reload_mode)
|
|
306
366
|
observer = Observer()
|
|
307
367
|
|
|
308
368
|
for d in watch_dirs:
|
|
309
369
|
if os.path.exists(d):
|
|
310
|
-
|
|
370
|
+
if reload_mode:
|
|
371
|
+
# 完整重载模式:监控所有.py文件
|
|
372
|
+
observer.schedule(handler, d, recursive=True)
|
|
373
|
+
else:
|
|
374
|
+
# 普通模式:只监控config.toml
|
|
375
|
+
observer.schedule(handler, d, recursive=False)
|
|
311
376
|
|
|
312
377
|
observer.start()
|
|
313
|
-
print(
|
|
314
|
-
|
|
378
|
+
console.print("\n[bold green][热重载] 已启动[/]")
|
|
379
|
+
mode_desc = "开发重载模式" if reload_mode else "配置监控模式"
|
|
380
|
+
console.print(f"[dim]模式: {mode_desc}\n监控目录: {', '.join(watch_dirs)}[/]\n")
|
|
315
381
|
try:
|
|
382
|
+
first_interrupt = True
|
|
316
383
|
while True:
|
|
317
384
|
time.sleep(1)
|
|
318
385
|
except KeyboardInterrupt:
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
shellprint.panel(f"找不到指定文件: {script_path}", "错误", "error")
|
|
335
|
-
return
|
|
336
|
-
|
|
337
|
-
if reload:
|
|
338
|
-
start_reloader(script_path)
|
|
339
|
-
else:
|
|
340
|
-
shellprint.panel(f"运行脚本: {Shell_Printer.BOLD}{script_path}{Shell_Printer.RESET}", "执行", "info")
|
|
341
|
-
import runpy
|
|
342
|
-
try:
|
|
343
|
-
runpy.run_path(script_path, run_name="__main__")
|
|
344
|
-
except KeyboardInterrupt:
|
|
345
|
-
shellprint.panel("脚本执行已中断", "中断", "info")
|
|
386
|
+
if first_interrupt:
|
|
387
|
+
first_interrupt = False
|
|
388
|
+
console.print("\n[bold green]正在安全关闭热重载...[/]")
|
|
389
|
+
console.print("[bold red]再次按下Ctrl+C以强制关闭[/]")
|
|
390
|
+
try:
|
|
391
|
+
observer.stop()
|
|
392
|
+
if handler.process:
|
|
393
|
+
handler.process.terminate()
|
|
394
|
+
observer.join()
|
|
395
|
+
except KeyboardInterrupt:
|
|
396
|
+
console.print("[bold red]强制关闭热重载[/]")
|
|
397
|
+
raise
|
|
398
|
+
else:
|
|
399
|
+
console.print(Panel("[bold red]强制关闭热重载[/]"))
|
|
400
|
+
raise
|
|
346
401
|
|
|
347
402
|
def get_erispulse_version():
|
|
348
403
|
"""
|
|
@@ -360,31 +415,46 @@ def main():
|
|
|
360
415
|
CLI主入口
|
|
361
416
|
|
|
362
417
|
解析命令行参数并执行相应命令
|
|
363
|
-
|
|
364
|
-
{!--< tips >!--}
|
|
365
|
-
1. 使用argparse处理命令行参数
|
|
366
|
-
2. 支持彩色输出和表格显示
|
|
367
|
-
3. 提供详细的错误处理
|
|
368
|
-
{!--< /tips >!--}
|
|
369
418
|
"""
|
|
370
419
|
|
|
371
420
|
parser = argparse.ArgumentParser(
|
|
372
421
|
prog="epsdk",
|
|
373
422
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
374
|
-
description=f"
|
|
423
|
+
description=f"ErisPulse SDK 命令行工具 {get_erispulse_version()}"
|
|
375
424
|
)
|
|
376
|
-
parser._positionals.title =
|
|
377
|
-
parser._optionals.title =
|
|
425
|
+
parser._positionals.title = "基本"
|
|
426
|
+
parser._optionals.title = "可选"
|
|
378
427
|
|
|
379
|
-
parser.add_argument("--version", action="store_true", help="
|
|
428
|
+
parser.add_argument("--version", action="store_true", help="显示版本信息")
|
|
380
429
|
|
|
381
430
|
subparsers = parser.add_subparsers(
|
|
382
431
|
dest='command',
|
|
383
|
-
|
|
384
|
-
metavar=f"{Shell_Printer.GREEN}<命令>{Shell_Printer.RESET}",
|
|
385
|
-
help='具体命令的帮助信息'
|
|
432
|
+
metavar="<命令>",
|
|
386
433
|
)
|
|
387
434
|
|
|
435
|
+
# 在main()函数中修改第三方命令加载部分
|
|
436
|
+
try:
|
|
437
|
+
entry_points = importlib.metadata.entry_points()
|
|
438
|
+
if hasattr(entry_points, 'select'):
|
|
439
|
+
cli_entries = entry_points.select(group='erispulse.cli')
|
|
440
|
+
else:
|
|
441
|
+
cli_entries = entry_points.get('erispulse.cli', [])
|
|
442
|
+
|
|
443
|
+
for entry in cli_entries:
|
|
444
|
+
try:
|
|
445
|
+
cli_func = entry.load()
|
|
446
|
+
if callable(cli_func):
|
|
447
|
+
# 直接调用函数并传入subparsers和console
|
|
448
|
+
cli_func(subparsers, console)
|
|
449
|
+
else:
|
|
450
|
+
console.print(f"[yellow]模块 {entry.name} 的入口点不是可调用对象[/]")
|
|
451
|
+
except Exception as e:
|
|
452
|
+
console.print(f"[red]加载第三方命令 {entry.name} 失败: {e}[/]")
|
|
453
|
+
import traceback
|
|
454
|
+
console.print(traceback.format_exc())
|
|
455
|
+
except Exception as e:
|
|
456
|
+
console.print(f"[yellow]加载第三方CLI命令失败: {e}[/]", style="warning")
|
|
457
|
+
|
|
388
458
|
# 安装命令
|
|
389
459
|
install_parser = subparsers.add_parser('install', help='安装模块/适配器包')
|
|
390
460
|
install_parser.add_argument('package', type=str, help='要安装的包名')
|
|
@@ -394,6 +464,14 @@ def main():
|
|
|
394
464
|
uninstall_parser = subparsers.add_parser('uninstall', help='卸载模块/适配器包')
|
|
395
465
|
uninstall_parser.add_argument('package', type=str, help='要卸载的包名')
|
|
396
466
|
|
|
467
|
+
# 启用命令
|
|
468
|
+
enable_parser = subparsers.add_parser('enable', help='启用模块')
|
|
469
|
+
enable_parser.add_argument('module', type=str, help='要启用的模块名')
|
|
470
|
+
|
|
471
|
+
# 禁用命令
|
|
472
|
+
disable_parser = subparsers.add_parser('disable', help='禁用模块')
|
|
473
|
+
disable_parser.add_argument('module', type=str, help='要禁用的模块名')
|
|
474
|
+
|
|
397
475
|
# 列表命令
|
|
398
476
|
list_parser = subparsers.add_parser('list', help='列出已安装的模块/适配器')
|
|
399
477
|
list_parser.add_argument('--type', '-t', choices=['modules', 'adapters', 'all'], default='all',
|
|
@@ -411,18 +489,19 @@ def main():
|
|
|
411
489
|
run_parser = subparsers.add_parser('run', help='运行指定主程序')
|
|
412
490
|
run_parser.add_argument('script', type=str, help='要运行的主程序路径')
|
|
413
491
|
run_parser.add_argument('--reload', action='store_true', help='启用热重载模式')
|
|
414
|
-
|
|
492
|
+
|
|
415
493
|
args = parser.parse_args()
|
|
416
494
|
|
|
417
495
|
if args.version:
|
|
418
|
-
print(f"
|
|
496
|
+
console.print(f"[green]ErisPulse {get_erispulse_version()}[/]")
|
|
419
497
|
return
|
|
420
498
|
|
|
421
499
|
if not args.command:
|
|
422
500
|
parser.print_help()
|
|
423
|
-
return
|
|
424
501
|
|
|
425
502
|
try:
|
|
503
|
+
if hasattr(args, 'func'):
|
|
504
|
+
args.func(args)
|
|
426
505
|
if args.command == "install":
|
|
427
506
|
import asyncio
|
|
428
507
|
# 首先检查是否是远程模块/适配器的简称
|
|
@@ -438,11 +517,11 @@ def main():
|
|
|
438
517
|
|
|
439
518
|
# 如果找到远程包,使用完整包名安装
|
|
440
519
|
if full_package_name:
|
|
441
|
-
|
|
442
|
-
f"找到远程包: {
|
|
443
|
-
"信息",
|
|
444
|
-
"info"
|
|
445
|
-
)
|
|
520
|
+
console.print(Panel(
|
|
521
|
+
f"找到远程包: [bold]{args.package}[/] → [blue]{full_package_name}[/]",
|
|
522
|
+
title="信息",
|
|
523
|
+
style="info"
|
|
524
|
+
))
|
|
446
525
|
PyPIManager.install_package(full_package_name, args.upgrade)
|
|
447
526
|
else:
|
|
448
527
|
# 否则按原样安装
|
|
@@ -450,77 +529,132 @@ def main():
|
|
|
450
529
|
|
|
451
530
|
elif args.command == "uninstall":
|
|
452
531
|
PyPIManager.uninstall_package(args.package)
|
|
453
|
-
|
|
532
|
+
|
|
533
|
+
elif args.command == "enable":
|
|
534
|
+
from ErisPulse.Core import mods
|
|
535
|
+
installed = PyPIManager.get_installed_packages()
|
|
536
|
+
if args.module not in installed["modules"]:
|
|
537
|
+
console.print(Panel(
|
|
538
|
+
f"模块 [red]{args.module}[/] 不存在或未安装",
|
|
539
|
+
title="错误",
|
|
540
|
+
style="error"
|
|
541
|
+
))
|
|
542
|
+
else:
|
|
543
|
+
mods.set_module_status(args.module, True)
|
|
544
|
+
console.print(Panel(
|
|
545
|
+
f"模块 [green]{args.module}[/] 已启用",
|
|
546
|
+
title="成功",
|
|
547
|
+
style="success"
|
|
548
|
+
))
|
|
549
|
+
|
|
550
|
+
elif args.command == "disable":
|
|
551
|
+
from ErisPulse.Core import mods
|
|
552
|
+
installed = PyPIManager.get_installed_packages()
|
|
553
|
+
if args.module not in installed["modules"]:
|
|
554
|
+
console.print(Panel(
|
|
555
|
+
f"模块 [red]{args.module}[/] 不存在或未安装",
|
|
556
|
+
title="错误",
|
|
557
|
+
style="error"
|
|
558
|
+
))
|
|
559
|
+
else:
|
|
560
|
+
mods.set_module_status(args.module, False)
|
|
561
|
+
console.print(Panel(
|
|
562
|
+
f"模块 [yellow]{args.module}[/] 已禁用",
|
|
563
|
+
title="成功",
|
|
564
|
+
style="warning"
|
|
565
|
+
))
|
|
566
|
+
|
|
454
567
|
elif args.command == "list":
|
|
455
568
|
installed = PyPIManager.get_installed_packages()
|
|
456
569
|
|
|
457
570
|
if args.type in ["modules", "all"] and installed["modules"]:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
571
|
+
table = Table(title="已安装模块", box=SIMPLE)
|
|
572
|
+
table.add_column("模块名", style="green")
|
|
573
|
+
table.add_column("包名", style="blue")
|
|
574
|
+
table.add_column("版本")
|
|
575
|
+
table.add_column("描述")
|
|
576
|
+
|
|
577
|
+
for name, info in installed["modules"].items():
|
|
578
|
+
table.add_row(name, info["package"], info["version"], info["summary"])
|
|
579
|
+
|
|
580
|
+
console.print(table)
|
|
467
581
|
|
|
468
582
|
if args.type in ["adapters", "all"] and installed["adapters"]:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
583
|
+
table = Table(title="已安装适配器", box=SIMPLE)
|
|
584
|
+
table.add_column("适配器名", style="yellow")
|
|
585
|
+
table.add_column("包名", style="blue")
|
|
586
|
+
table.add_column("版本")
|
|
587
|
+
table.add_column("描述")
|
|
588
|
+
|
|
589
|
+
for name, info in installed["adapters"].items():
|
|
590
|
+
table.add_row(name, info["package"], info["version"], info["summary"])
|
|
591
|
+
|
|
592
|
+
console.print(table)
|
|
478
593
|
|
|
479
594
|
if not installed["modules"] and not installed["adapters"]:
|
|
480
|
-
|
|
595
|
+
console.print(Panel(
|
|
596
|
+
"没有安装任何ErisPulse模块或适配器",
|
|
597
|
+
title="提示",
|
|
598
|
+
style="info"
|
|
599
|
+
))
|
|
481
600
|
|
|
482
601
|
elif args.command == "upgrade":
|
|
483
|
-
if args.force or
|
|
602
|
+
if args.force or Confirm.ask("确定要升级所有ErisPulse模块和适配器吗?", default=False):
|
|
484
603
|
PyPIManager.upgrade_all()
|
|
485
604
|
|
|
486
605
|
elif args.command == "run":
|
|
487
|
-
|
|
488
|
-
|
|
606
|
+
start_reloader(args.script, args.reload)
|
|
489
607
|
elif args.command == "list-remote":
|
|
490
608
|
import asyncio
|
|
491
609
|
try:
|
|
492
610
|
remote_packages = asyncio.run(PyPIManager.get_remote_packages())
|
|
493
611
|
|
|
494
612
|
if args.type in ["modules", "all"] and remote_packages["modules"]:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
613
|
+
table = Table(title="远程模块", box=SIMPLE)
|
|
614
|
+
table.add_column("模块名", style="green")
|
|
615
|
+
table.add_column("包名", style="blue")
|
|
616
|
+
table.add_column("版本")
|
|
617
|
+
table.add_column("描述")
|
|
618
|
+
|
|
619
|
+
for name, info in remote_packages["modules"].items():
|
|
620
|
+
table.add_row(name, info["package"], info["version"], info["description"])
|
|
621
|
+
|
|
622
|
+
console.print(table)
|
|
504
623
|
|
|
505
624
|
if args.type in ["adapters", "all"] and remote_packages["adapters"]:
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
625
|
+
table = Table(title="远程适配器", box=SIMPLE)
|
|
626
|
+
table.add_column("适配器名", style="yellow")
|
|
627
|
+
table.add_column("包名", style="blue")
|
|
628
|
+
table.add_column("版本")
|
|
629
|
+
table.add_column("描述")
|
|
630
|
+
|
|
631
|
+
for name, info in remote_packages["adapters"].items():
|
|
632
|
+
table.add_row(name, info["package"], info["version"], info["description"])
|
|
633
|
+
|
|
634
|
+
console.print(table)
|
|
515
635
|
|
|
516
636
|
if not remote_packages["modules"] and not remote_packages["adapters"]:
|
|
517
|
-
|
|
637
|
+
console.print(Panel(
|
|
638
|
+
"没有找到远程模块或适配器",
|
|
639
|
+
title="提示",
|
|
640
|
+
style="info"
|
|
641
|
+
))
|
|
518
642
|
|
|
519
643
|
except Exception as e:
|
|
520
|
-
|
|
521
|
-
|
|
644
|
+
console.print(Panel(
|
|
645
|
+
f"获取远程列表失败: {e}",
|
|
646
|
+
title="错误",
|
|
647
|
+
style="error"
|
|
648
|
+
))
|
|
649
|
+
|
|
650
|
+
except KeyboardInterrupt as e:
|
|
651
|
+
pass
|
|
522
652
|
except Exception as e:
|
|
523
|
-
|
|
653
|
+
console.print(Panel(
|
|
654
|
+
f"执行命令时出错: {e}",
|
|
655
|
+
title="错误",
|
|
656
|
+
style="error"
|
|
657
|
+
))
|
|
524
658
|
|
|
525
659
|
if __name__ == "__main__":
|
|
526
660
|
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.7
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -49,11 +49,14 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
49
49
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
50
50
|
Requires-Python: >=3.8
|
|
51
51
|
Requires-Dist: aiohttp
|
|
52
|
+
Requires-Dist: colorama
|
|
52
53
|
Requires-Dist: fastapi>=0.116.1
|
|
53
54
|
Requires-Dist: hypercorn>=0.14.0
|
|
55
|
+
Requires-Dist: keyboard
|
|
54
56
|
Requires-Dist: pip
|
|
55
57
|
Requires-Dist: pydantic>=2.10.6
|
|
56
58
|
Requires-Dist: python-multipart>=0.0.20
|
|
59
|
+
Requires-Dist: rich
|
|
57
60
|
Requires-Dist: toml
|
|
58
61
|
Requires-Dist: watchdog
|
|
59
62
|
Description-Content-Type: text/markdown
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=
|
|
2
|
-
ErisPulse/__main__.py,sha256=
|
|
1
|
+
ErisPulse/__init__.py,sha256=T-N56UQyBmpvlqwH2wGi5ptPzaJpbgF5tKkJt0DlkaM,26099
|
|
2
|
+
ErisPulse/__main__.py,sha256=QwHMu8lmWbjS3EyOYV-wea3I0xcGmH_McWNx7JSGwts,24853
|
|
3
3
|
ErisPulse/Core/__init__.py,sha256=XnQFMn4HMKzxa6KHJEvOpwt0v8jp21vAbw6aG0O8sPs,722
|
|
4
4
|
ErisPulse/Core/adapter.py,sha256=lw5T336EsNtjUgSPdevcOZReZNkA8oYvvTRCHk_fo_o,18149
|
|
5
5
|
ErisPulse/Core/env.py,sha256=D2BwtSbKggbeAsOzwwVv-kd5UkVmYlDUHtgLzFZqKUI,19841
|
|
6
6
|
ErisPulse/Core/logger.py,sha256=VYhWKPXn88ib7S_OKCr_MibdnMH8k5sHkwLtNDZP3QM,7034
|
|
7
|
-
ErisPulse/Core/mods.py,sha256=
|
|
7
|
+
ErisPulse/Core/mods.py,sha256=2yIq8t9Ca9CBPRiZU0yr8Lc0XGmmkB7LlH-5FWqXjw4,7023
|
|
8
8
|
ErisPulse/Core/raiserr.py,sha256=TQCKbYrfqu8tjX7dJiJemK0lZCQ4-5875nUGXgb8HUk,5606
|
|
9
9
|
ErisPulse/Core/server.py,sha256=FkDTeLuHD5IBnWVxvYU8pHb6yCt8GzyvC1bpOiJ7G7I,9217
|
|
10
|
-
ErisPulse/Core/shellprint.py,sha256=-BFoyFho_D3XEhxIoKt6x5gO4C62LKwmJWKDUGiPjNY,5908
|
|
11
10
|
ErisPulse/Core/util.py,sha256=7rdMmn6sBFqYd4znxBCcJjuv2eyTExdeKyZopgds868,3796
|
|
12
|
-
erispulse-2.1.
|
|
13
|
-
erispulse-2.1.
|
|
14
|
-
erispulse-2.1.
|
|
15
|
-
erispulse-2.1.
|
|
16
|
-
erispulse-2.1.
|
|
11
|
+
erispulse-2.1.7.dist-info/METADATA,sha256=4xIp6y-QyDEaC13715lWFeEYK2D965Ij1EuToHiJ37U,6258
|
|
12
|
+
erispulse-2.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
13
|
+
erispulse-2.1.7.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
|
|
14
|
+
erispulse-2.1.7.dist-info/licenses/LICENSE,sha256=4jyqikiB0G0n06CEEMMTzTXjE4IShghSlB74skMSPQs,1464
|
|
15
|
+
erispulse-2.1.7.dist-info/RECORD,,
|
ErisPulse/Core/shellprint.py
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
|
-
class Shell_Printer:
|
|
4
|
-
# ANSI 颜色代码
|
|
5
|
-
RESET = "\033[0m"
|
|
6
|
-
BOLD = "\033[1m"
|
|
7
|
-
RED = "\033[91m"
|
|
8
|
-
GREEN = "\033[92m"
|
|
9
|
-
YELLOW = "\033[93m"
|
|
10
|
-
BLUE = "\033[94m"
|
|
11
|
-
MAGENTA = "\033[95m"
|
|
12
|
-
CYAN = "\033[96m"
|
|
13
|
-
WHITE = "\033[97m"
|
|
14
|
-
DIM = "\033[2m"
|
|
15
|
-
UNDERLINE = "\033[4m"
|
|
16
|
-
BG_BLUE = "\033[44m"
|
|
17
|
-
BG_GREEN = "\033[42m"
|
|
18
|
-
BG_YELLOW = "\033[43m"
|
|
19
|
-
BG_RED = "\033[41m"
|
|
20
|
-
|
|
21
|
-
def __init__(self):
|
|
22
|
-
pass
|
|
23
|
-
|
|
24
|
-
@classmethod
|
|
25
|
-
def _get_color(cls, level):
|
|
26
|
-
return {
|
|
27
|
-
"info": cls.CYAN,
|
|
28
|
-
"success": cls.GREEN,
|
|
29
|
-
"warning": cls.YELLOW,
|
|
30
|
-
"error": cls.RED,
|
|
31
|
-
"title": cls.MAGENTA,
|
|
32
|
-
"default": cls.RESET,
|
|
33
|
-
}.get(level, cls.RESET)
|
|
34
|
-
|
|
35
|
-
@classmethod
|
|
36
|
-
def panel(cls, msg: str, title: str = None, level: str = "info") -> None:
|
|
37
|
-
color = cls._get_color(level)
|
|
38
|
-
width = 70
|
|
39
|
-
border_char = "─" * width
|
|
40
|
-
|
|
41
|
-
if level == "error":
|
|
42
|
-
border_char = "═" * width
|
|
43
|
-
msg = f"{cls.RED}✗ {msg}{cls.RESET}"
|
|
44
|
-
elif level == "warning":
|
|
45
|
-
border_char = "─" * width
|
|
46
|
-
msg = f"{cls.YELLOW}⚠ {msg}{cls.RESET}"
|
|
47
|
-
|
|
48
|
-
title_line = ""
|
|
49
|
-
if title:
|
|
50
|
-
title = f" {title.upper()} "
|
|
51
|
-
title_padding = (width - len(title)) // 2
|
|
52
|
-
left_pad = " " * title_padding
|
|
53
|
-
right_pad = " " * (width - len(title) - title_padding)
|
|
54
|
-
title_line = f"{cls.DIM}┌{left_pad}{cls.BOLD}{color}{title}{cls.RESET}{cls.DIM}{right_pad}┐{cls.RESET}\n"
|
|
55
|
-
|
|
56
|
-
lines = []
|
|
57
|
-
for line in msg.split("\n"):
|
|
58
|
-
if len(line) > width - 4:
|
|
59
|
-
words = line.split()
|
|
60
|
-
current_line = ""
|
|
61
|
-
for word in words:
|
|
62
|
-
if len(current_line) + len(word) + 1 > width - 4:
|
|
63
|
-
lines.append(f"{cls.DIM}│{cls.RESET} {current_line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
64
|
-
current_line = word
|
|
65
|
-
else:
|
|
66
|
-
current_line += (" " + word) if current_line else word
|
|
67
|
-
if current_line:
|
|
68
|
-
lines.append(f"{cls.DIM}│{cls.RESET} {current_line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
69
|
-
else:
|
|
70
|
-
lines.append(f"{cls.DIM}│{cls.RESET} {line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
71
|
-
|
|
72
|
-
if level == "error":
|
|
73
|
-
border_style = "╘"
|
|
74
|
-
elif level == "warning":
|
|
75
|
-
border_style = "╧"
|
|
76
|
-
else:
|
|
77
|
-
border_style = "└"
|
|
78
|
-
bottom_border = f"{cls.DIM}{border_style}{border_char}┘{cls.RESET}"
|
|
79
|
-
|
|
80
|
-
panel = f"{title_line}"
|
|
81
|
-
panel += f"{cls.DIM}├{border_char}┤{cls.RESET}\n"
|
|
82
|
-
panel += "\n".join(lines) + "\n"
|
|
83
|
-
panel += f"{bottom_border}\n"
|
|
84
|
-
|
|
85
|
-
print(panel)
|
|
86
|
-
|
|
87
|
-
@classmethod
|
|
88
|
-
def table(cls, headers, rows, title=None, level="info") -> None:
|
|
89
|
-
color = cls._get_color(level)
|
|
90
|
-
if title:
|
|
91
|
-
print(f"{cls.BOLD}{color}== {title} =={cls.RESET}")
|
|
92
|
-
|
|
93
|
-
col_widths = [len(h) for h in headers]
|
|
94
|
-
for row in rows:
|
|
95
|
-
for i, cell in enumerate(row):
|
|
96
|
-
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
97
|
-
|
|
98
|
-
fmt = "│".join(f" {{:<{w}}} " for w in col_widths)
|
|
99
|
-
|
|
100
|
-
top_border = "┌" + "┬".join("─" * (w+2) for w in col_widths) + "┐"
|
|
101
|
-
print(f"{cls.DIM}{top_border}{cls.RESET}")
|
|
102
|
-
|
|
103
|
-
header_line = fmt.format(*headers)
|
|
104
|
-
print(f"{cls.BOLD}{color}│{header_line}│{cls.RESET}")
|
|
105
|
-
|
|
106
|
-
separator = "├" + "┼".join("─" * (w+2) for w in col_widths) + "┤"
|
|
107
|
-
print(f"{cls.DIM}{separator}{cls.RESET}")
|
|
108
|
-
|
|
109
|
-
for row in rows:
|
|
110
|
-
row_line = fmt.format(*row)
|
|
111
|
-
print(f"│{row_line}│")
|
|
112
|
-
|
|
113
|
-
bottom_border = "└" + "┴".join("─" * (w+2) for w in col_widths) + "┘"
|
|
114
|
-
print(f"{cls.DIM}{bottom_border}{cls.RESET}")
|
|
115
|
-
|
|
116
|
-
@classmethod
|
|
117
|
-
def progress_bar(cls, current, total, prefix="", suffix="", length=50):
|
|
118
|
-
filled_length = int(length * current // total)
|
|
119
|
-
percent = min(100.0, 100 * (current / float(total)))
|
|
120
|
-
bar = f"{cls.GREEN}{'█' * filled_length}{cls.WHITE}{'░' * (length - filled_length)}{cls.RESET}"
|
|
121
|
-
sys.stdout.write(f"\r{cls.BOLD}{prefix}{cls.RESET} {bar} {cls.BOLD}{percent:.1f}%{cls.RESET} {suffix}")
|
|
122
|
-
sys.stdout.flush()
|
|
123
|
-
if current == total:
|
|
124
|
-
print()
|
|
125
|
-
|
|
126
|
-
@classmethod
|
|
127
|
-
def confirm(cls, msg, default=False) -> bool:
|
|
128
|
-
yes_options = {'y', 'yes'}
|
|
129
|
-
no_options = {'n', 'no'}
|
|
130
|
-
default_str = "Y/n" if default else "y/N"
|
|
131
|
-
prompt = f"{cls.BOLD}{msg}{cls.RESET} [{cls.CYAN}{default_str}{cls.RESET}]: "
|
|
132
|
-
|
|
133
|
-
while True:
|
|
134
|
-
ans = input(prompt).strip().lower()
|
|
135
|
-
if not ans:
|
|
136
|
-
return default
|
|
137
|
-
if ans in yes_options:
|
|
138
|
-
return True
|
|
139
|
-
if ans in no_options:
|
|
140
|
-
return False
|
|
141
|
-
print(f"{cls.YELLOW}请输入 'y' 或 'n'{cls.RESET}")
|
|
142
|
-
|
|
143
|
-
@classmethod
|
|
144
|
-
def ask(cls, msg, choices=None, default="") -> str:
|
|
145
|
-
prompt = f"{cls.BOLD}{msg}{cls.RESET}"
|
|
146
|
-
if choices:
|
|
147
|
-
prompt += f" ({cls.CYAN}{'/'.join(choices)}{cls.RESET})"
|
|
148
|
-
if default:
|
|
149
|
-
prompt += f" [{cls.BLUE}默认: {default}{cls.RESET}]"
|
|
150
|
-
prompt += ": "
|
|
151
|
-
|
|
152
|
-
while True:
|
|
153
|
-
ans = input(prompt).strip()
|
|
154
|
-
if not ans and default:
|
|
155
|
-
return default
|
|
156
|
-
if not choices or ans in choices:
|
|
157
|
-
return ans
|
|
158
|
-
print(f"{cls.YELLOW}请输入有效选项: {', '.join(choices)}{cls.RESET}")
|
|
159
|
-
|
|
160
|
-
@classmethod
|
|
161
|
-
def status(cls, msg, success=True):
|
|
162
|
-
symbol = f"{cls.GREEN}✓" if success else f"{cls.RED}✗"
|
|
163
|
-
print(f"\r{symbol}{cls.RESET} {msg}")
|
|
164
|
-
|
|
165
|
-
shellprint = Shell_Printer()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|