ErisPulse 1.2.9__py3-none-any.whl → 2.1.1__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/__init__.py +19 -0
- ErisPulse/Core/adapter.py +548 -0
- ErisPulse/Core/env.py +614 -0
- ErisPulse/{logger.py → Core/logger.py} +51 -113
- ErisPulse/Core/mods.py +226 -0
- ErisPulse/Core/raiserr.py +152 -0
- ErisPulse/Core/server.py +276 -0
- ErisPulse/Core/shellprint.py +165 -0
- ErisPulse/Core/util.py +126 -0
- ErisPulse/__init__.py +673 -243
- ErisPulse/__main__.py +391 -1184
- {erispulse-1.2.9.dist-info → erispulse-2.1.1.dist-info}/METADATA +16 -41
- erispulse-2.1.1.dist-info/RECORD +16 -0
- {erispulse-1.2.9.dist-info → erispulse-2.1.1.dist-info}/entry_points.txt +1 -0
- {erispulse-1.2.9.dist-info → erispulse-2.1.1.dist-info}/licenses/LICENSE +4 -3
- ErisPulse/adapter.py +0 -465
- ErisPulse/db.py +0 -769
- ErisPulse/mods.py +0 -345
- ErisPulse/raiserr.py +0 -141
- ErisPulse/util.py +0 -144
- erispulse-1.2.9.dist-info/RECORD +0 -13
- {erispulse-1.2.9.dist-info → erispulse-2.1.1.dist-info}/WHEEL +0 -0
ErisPulse/__main__.py
CHANGED
|
@@ -1,360 +1,253 @@
|
|
|
1
1
|
"""
|
|
2
2
|
# CLI 入口
|
|
3
3
|
|
|
4
|
-
提供命令行界面(CLI)
|
|
5
|
-
|
|
6
|
-
## 主要功能
|
|
7
|
-
- 模块管理: 安装/卸载/启用/禁用
|
|
8
|
-
- 源管理: 添加/删除/更新源
|
|
9
|
-
- 热重载: 开发时自动重启
|
|
10
|
-
- 彩色终端输出
|
|
4
|
+
提供命令行界面(CLI)用于包管理和启动入口。
|
|
11
5
|
|
|
12
6
|
## 主要命令
|
|
13
|
-
###
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
upgrade: 升级模块
|
|
22
|
-
|
|
23
|
-
### 源管理:
|
|
24
|
-
origin add: 添加源
|
|
25
|
-
origin del: 删除源
|
|
26
|
-
origin list: 列出源
|
|
27
|
-
|
|
28
|
-
### 开发调试:
|
|
7
|
+
### 包管理:
|
|
8
|
+
install: 安装模块/适配器包
|
|
9
|
+
uninstall: 卸载模块/适配器包
|
|
10
|
+
list: 列出已安装的模块/适配器
|
|
11
|
+
list-remote: 列出远程PyPI上的ErisPulse模块和适配器
|
|
12
|
+
upgrade: 升级所有模块/适配器
|
|
13
|
+
|
|
14
|
+
### 启动:
|
|
29
15
|
run: 运行脚本
|
|
30
16
|
--reload: 启用热重载
|
|
31
17
|
|
|
32
18
|
### 示例用法:
|
|
33
|
-
|
|
34
19
|
```
|
|
35
20
|
# 安装模块
|
|
36
|
-
epsdk install
|
|
21
|
+
epsdk install Yunhu
|
|
37
22
|
|
|
38
23
|
# 启用热重载
|
|
39
24
|
epsdk run main.py --reload
|
|
40
|
-
|
|
41
|
-
# 管理源
|
|
42
|
-
epsdk origin add https://example.com/map.json
|
|
43
25
|
```
|
|
44
|
-
|
|
45
26
|
"""
|
|
46
27
|
|
|
47
28
|
import argparse
|
|
48
|
-
import importlib
|
|
49
|
-
import
|
|
29
|
+
import importlib.metadata
|
|
30
|
+
import subprocess
|
|
50
31
|
import sys
|
|
32
|
+
import os
|
|
51
33
|
import time
|
|
52
|
-
import shutil
|
|
53
|
-
import aiohttp
|
|
54
|
-
import zipfile
|
|
55
|
-
import fnmatch
|
|
56
|
-
import asyncio
|
|
57
|
-
import subprocess
|
|
58
|
-
import json
|
|
59
34
|
import json
|
|
60
|
-
|
|
61
|
-
from .
|
|
35
|
+
import asyncio
|
|
36
|
+
from urllib.parse import urlparse
|
|
37
|
+
from typing import List, Dict, Tuple, Optional
|
|
38
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
62
39
|
from watchdog.observers import Observer
|
|
63
40
|
from watchdog.events import FileSystemEventHandler
|
|
41
|
+
from .Core.shellprint import shellprint, Shell_Printer
|
|
64
42
|
|
|
65
|
-
class
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _get_color(cls, level):
|
|
88
|
-
return {
|
|
89
|
-
"info": cls.CYAN,
|
|
90
|
-
"success": cls.GREEN,
|
|
91
|
-
"warning": cls.YELLOW,
|
|
92
|
-
"error": cls.RED,
|
|
93
|
-
"title": cls.MAGENTA,
|
|
94
|
-
"default": cls.RESET,
|
|
95
|
-
}.get(level, cls.RESET)
|
|
96
|
-
|
|
97
|
-
@classmethod
|
|
98
|
-
def panel(cls, msg: str, title: str = None, level: str = "info") -> None:
|
|
99
|
-
color = cls._get_color(level)
|
|
100
|
-
width = 70
|
|
101
|
-
border_char = "─" * width
|
|
102
|
-
|
|
103
|
-
if level == "error":
|
|
104
|
-
border_char = "═" * width
|
|
105
|
-
msg = f"{cls.RED}✗ {msg}{cls.RESET}"
|
|
106
|
-
elif level == "warning":
|
|
107
|
-
border_char = "─" * width
|
|
108
|
-
msg = f"{cls.YELLOW}⚠ {msg}{cls.RESET}"
|
|
109
|
-
|
|
110
|
-
title_line = ""
|
|
111
|
-
if title:
|
|
112
|
-
title = f" {title.upper()} "
|
|
113
|
-
title_padding = (width - len(title)) // 2
|
|
114
|
-
left_pad = " " * title_padding
|
|
115
|
-
right_pad = " " * (width - len(title) - title_padding)
|
|
116
|
-
title_line = f"{cls.DIM}┌{left_pad}{cls.BOLD}{color}{title}{cls.RESET}{cls.DIM}{right_pad}┐{cls.RESET}\n"
|
|
117
|
-
|
|
118
|
-
lines = []
|
|
119
|
-
for line in msg.split("\n"):
|
|
120
|
-
if len(line) > width - 4:
|
|
121
|
-
words = line.split()
|
|
122
|
-
current_line = ""
|
|
123
|
-
for word in words:
|
|
124
|
-
if len(current_line) + len(word) + 1 > width - 4:
|
|
125
|
-
lines.append(f"{cls.DIM}│{cls.RESET} {current_line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
126
|
-
current_line = word
|
|
127
|
-
else:
|
|
128
|
-
current_line += (" " + word) if current_line else word
|
|
129
|
-
if current_line:
|
|
130
|
-
lines.append(f"{cls.DIM}│{cls.RESET} {current_line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
131
|
-
else:
|
|
132
|
-
lines.append(f"{cls.DIM}│{cls.RESET} {line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
133
|
-
|
|
134
|
-
if level == "error":
|
|
135
|
-
border_style = "╘"
|
|
136
|
-
elif level == "warning":
|
|
137
|
-
border_style = "╧"
|
|
138
|
-
else:
|
|
139
|
-
border_style = "└"
|
|
140
|
-
bottom_border = f"{cls.DIM}{border_style}{border_char}┘{cls.RESET}"
|
|
141
|
-
|
|
142
|
-
panel = f"{title_line}"
|
|
143
|
-
panel += f"{cls.DIM}├{border_char}┤{cls.RESET}\n"
|
|
144
|
-
panel += "\n".join(lines) + "\n"
|
|
145
|
-
panel += f"{bottom_border}\n"
|
|
43
|
+
class PyPIManager:
|
|
44
|
+
"""
|
|
45
|
+
PyPI包管理器
|
|
46
|
+
|
|
47
|
+
负责与PyPI交互,包括搜索、安装、卸载和升级ErisPulse模块/适配器
|
|
48
|
+
|
|
49
|
+
{!--< tips >!--}
|
|
50
|
+
1. 支持多个远程源作为备份
|
|
51
|
+
2. 自动区分模块和适配器
|
|
52
|
+
3. 提供详细的错误处理
|
|
53
|
+
{!--< /tips >!--}
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
REMOTE_SOURCES = [
|
|
57
|
+
"https://erisdev.com/packages.json",
|
|
58
|
+
"https://raw.githubusercontent.com/ErisPulse/ErisPulse-ModuleRepo/main/packages.json"
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
async def get_remote_packages() -> dict:
|
|
63
|
+
"""
|
|
64
|
+
获取远程包列表
|
|
146
65
|
|
|
147
|
-
|
|
66
|
+
从配置的远程源获取所有可用的ErisPulse模块和适配器
|
|
148
67
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
fmt = "│".join(f" {{:<{w}}} " for w in col_widths)
|
|
161
|
-
|
|
162
|
-
top_border = "┌" + "┬".join("─" * (w+2) for w in col_widths) + "┐"
|
|
163
|
-
print(f"{cls.DIM}{top_border}{cls.RESET}")
|
|
164
|
-
|
|
165
|
-
header_line = fmt.format(*headers)
|
|
166
|
-
print(f"{cls.BOLD}{color}│{header_line}│{cls.RESET}")
|
|
68
|
+
:return:
|
|
69
|
+
Dict[str, Dict]: 包含模块和适配器的字典
|
|
70
|
+
- modules: 模块字典 {模块名: 模块信息}
|
|
71
|
+
- adapters: 适配器字典 {适配器名: 适配器信息}
|
|
72
|
+
|
|
73
|
+
:raises ClientError: 当网络请求失败时抛出
|
|
74
|
+
:raises asyncio.TimeoutError: 当请求超时时抛出
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
import aiohttp
|
|
78
|
+
from aiohttp import ClientError, ClientTimeout
|
|
167
79
|
|
|
168
|
-
|
|
169
|
-
|
|
80
|
+
timeout = ClientTimeout(total=5)
|
|
81
|
+
last_error = None
|
|
170
82
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
83
|
+
try:
|
|
84
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
85
|
+
async with session.get(PyPIManager.REMOTE_SOURCES[0]) as response:
|
|
86
|
+
if response.status == 200:
|
|
87
|
+
data = await response.json()
|
|
88
|
+
return {
|
|
89
|
+
"modules": data.get("modules", {}),
|
|
90
|
+
"adapters": data.get("adapters", {})
|
|
91
|
+
}
|
|
92
|
+
except (ClientError, asyncio.TimeoutError) as e:
|
|
93
|
+
last_error = e
|
|
94
|
+
shellprint.panel(f"官方源请求失败,尝试备用源: {e}", "警告", "warning")
|
|
174
95
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
96
|
+
try:
|
|
97
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
98
|
+
async with session.get(PyPIManager.REMOTE_SOURCES[1]) as response:
|
|
99
|
+
if response.status == 200:
|
|
100
|
+
data = await response.json()
|
|
101
|
+
return {
|
|
102
|
+
"modules": data.get("modules", {}),
|
|
103
|
+
"adapters": data.get("adapters", {})
|
|
104
|
+
}
|
|
105
|
+
except (ClientError, asyncio.TimeoutError) as e:
|
|
106
|
+
last_error = e
|
|
107
|
+
|
|
108
|
+
if last_error:
|
|
109
|
+
shellprint.panel(f"获取远程模块列表失败: {last_error}", "错误", "error")
|
|
110
|
+
return {"modules": {}, "adapters": {}}
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def get_installed_packages() -> Dict[str, Dict[str, str]]:
|
|
114
|
+
"""
|
|
115
|
+
获取已安装的包信息
|
|
116
|
+
|
|
117
|
+
:return:
|
|
118
|
+
Dict[str, Dict[str, Dict[str, str]]]: 已安装包字典
|
|
119
|
+
- modules: 已安装模块 {模块名: 模块信息}
|
|
120
|
+
- adapters: 已安装适配器 {适配器名: 适配器信息}
|
|
121
|
+
"""
|
|
122
|
+
packages = {
|
|
123
|
+
"modules": {},
|
|
124
|
+
"adapters": {}
|
|
125
|
+
}
|
|
194
126
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
127
|
+
try:
|
|
128
|
+
# 查找模块
|
|
129
|
+
for dist in importlib.metadata.distributions():
|
|
130
|
+
if "ErisPulse-" in dist.metadata["Name"]:
|
|
131
|
+
entry_points = dist.entry_points
|
|
132
|
+
for ep in entry_points:
|
|
133
|
+
if ep.group == "erispulse.module":
|
|
134
|
+
packages["modules"][ep.name] = {
|
|
135
|
+
"package": dist.metadata["Name"],
|
|
136
|
+
"version": dist.version,
|
|
137
|
+
"summary": dist.metadata["Summary"]
|
|
138
|
+
}
|
|
139
|
+
elif ep.group == "erispulse.adapter":
|
|
140
|
+
packages["adapters"][ep.name] = {
|
|
141
|
+
"package": dist.metadata["Name"],
|
|
142
|
+
"version": dist.version,
|
|
143
|
+
"summary": dist.metadata["Summary"]
|
|
144
|
+
}
|
|
145
|
+
except Exception as e:
|
|
146
|
+
shellprint.panel(f"获取已安装包信息失败: {e}", "错误", "error")
|
|
213
147
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def __init__(self):
|
|
231
|
-
self._init_sources()
|
|
232
|
-
|
|
233
|
-
def _init_sources(self):
|
|
234
|
-
if not env.get('origins'):
|
|
235
|
-
env.set('origins', [])
|
|
236
|
-
|
|
237
|
-
primary_source = "https://erisdev.com/map.json"
|
|
238
|
-
secondary_source = "https://raw.githubusercontent.com/ErisPulse/ErisPulse-ModuleRepo/refs/heads/main/map.json"
|
|
239
|
-
|
|
240
|
-
shellprint.status("正在验证主源...")
|
|
241
|
-
validated_url = asyncio.run(self._validate_url(primary_source))
|
|
148
|
+
return packages
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def install_package(package_name: str, upgrade: bool = False) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
安装指定包
|
|
154
|
+
|
|
155
|
+
:param package_name: str 要安装的包名
|
|
156
|
+
:param upgrade: bool 是否升级已安装的包 (默认: False)
|
|
157
|
+
:return: bool 安装是否成功
|
|
158
|
+
"""
|
|
159
|
+
try:
|
|
160
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
|
161
|
+
if upgrade:
|
|
162
|
+
cmd.append("--upgrade")
|
|
163
|
+
cmd.append(package_name)
|
|
242
164
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
url = f"{url}/map.json"
|
|
165
|
+
shellprint.status(f"正在安装 {package_name}...")
|
|
166
|
+
result = subprocess.run(cmd, check=True)
|
|
167
|
+
if result.returncode == 0:
|
|
168
|
+
shellprint.panel(f"包 {package_name} 安装成功", "成功", "success")
|
|
169
|
+
return True
|
|
170
|
+
return False
|
|
171
|
+
except subprocess.CalledProcessError as e:
|
|
172
|
+
shellprint.panel(f"安装包 {package_name} 失败: {e}", "错误", "error")
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def uninstall_package(package_name: str) -> bool:
|
|
177
|
+
"""
|
|
178
|
+
卸载指定包
|
|
179
|
+
|
|
180
|
+
:param package_name: str 要卸载的包名
|
|
181
|
+
:return: bool 卸载是否成功
|
|
182
|
+
"""
|
|
262
183
|
try:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
shellprint.panel(f"源 {url} 返回的内容不是有效的 JSON 格式: {e}", "错误", "error")
|
|
272
|
-
return None
|
|
273
|
-
except Exception as e:
|
|
274
|
-
shellprint.panel(f"访问源 {url} 失败: {e}", "错误", "error")
|
|
275
|
-
return None
|
|
276
|
-
|
|
277
|
-
def add_source(self, value):
|
|
278
|
-
shellprint.status(f"验证源: {value}")
|
|
279
|
-
validated_url = asyncio.run(self._validate_url(value))
|
|
280
|
-
if not validated_url:
|
|
281
|
-
shellprint.panel("提供的源不是一个有效源,请检查后重试", "错误", "error")
|
|
184
|
+
shellprint.status(f"正在卸载 {package_name}...")
|
|
185
|
+
result = subprocess.run(
|
|
186
|
+
[sys.executable, "-m", "pip", "uninstall", "-y", package_name],
|
|
187
|
+
check=True
|
|
188
|
+
)
|
|
189
|
+
if result.returncode == 0:
|
|
190
|
+
shellprint.panel(f"包 {package_name} 卸载成功", "成功", "success")
|
|
191
|
+
return True
|
|
282
192
|
return False
|
|
193
|
+
except subprocess.CalledProcessError as e:
|
|
194
|
+
shellprint.panel(f"卸载包 {package_name} 失败: {e}", "错误", "error")
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def upgrade_all() -> bool:
|
|
199
|
+
"""
|
|
200
|
+
升级所有已安装的ErisPulse包
|
|
201
|
+
|
|
202
|
+
:return: bool 升级是否成功
|
|
203
|
+
|
|
204
|
+
{!--< tips >!--}
|
|
205
|
+
1. 会先列出所有可升级的包
|
|
206
|
+
2. 需要用户确认才会执行升级
|
|
207
|
+
{!--< /tips >!--}
|
|
208
|
+
"""
|
|
209
|
+
try:
|
|
210
|
+
installed = PyPIManager.get_installed_packages()
|
|
211
|
+
all_packages = set()
|
|
212
|
+
|
|
213
|
+
for pkg_type in ["modules", "adapters"]:
|
|
214
|
+
for pkg_info in installed[pkg_type].values():
|
|
215
|
+
all_packages.add(pkg_info["package"])
|
|
283
216
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
shellprint.panel(
|
|
217
|
+
if not all_packages:
|
|
218
|
+
shellprint.panel("没有找到可升级的ErisPulse包", "提示", "info")
|
|
219
|
+
return False
|
|
220
|
+
|
|
221
|
+
shellprint.panel(
|
|
222
|
+
f"找到 {len(all_packages)} 个可升级的包:\n" +
|
|
223
|
+
"\n".join(f" - {pkg}" for pkg in all_packages),
|
|
224
|
+
"升级列表",
|
|
225
|
+
"info"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if not shellprint.confirm("确认升级所有包吗?", default=False):
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
for pkg in all_packages:
|
|
232
|
+
PyPIManager.install_package(pkg, upgrade=True)
|
|
233
|
+
|
|
289
234
|
return True
|
|
290
|
-
|
|
291
|
-
shellprint.panel(f"
|
|
235
|
+
except Exception as e:
|
|
236
|
+
shellprint.panel(f"升级包失败: {e}", "错误", "error")
|
|
292
237
|
return False
|
|
293
238
|
|
|
294
|
-
def update_sources(self):
|
|
295
|
-
shellprint.status("更新模块源...")
|
|
296
|
-
origins = env.get('origins')
|
|
297
|
-
providers = {}
|
|
298
|
-
modules = {}
|
|
299
|
-
module_alias = {}
|
|
300
|
-
table_rows = []
|
|
301
|
-
async def fetch_source_data():
|
|
302
|
-
for i, origin in enumerate(origins):
|
|
303
|
-
shellprint.status(f"获取源数据 ({i+1}/{len(origins)}): {origin}")
|
|
304
|
-
try:
|
|
305
|
-
async with aiohttp.ClientSession() as session:
|
|
306
|
-
async with session.get(origin) as response:
|
|
307
|
-
response.raise_for_status()
|
|
308
|
-
try:
|
|
309
|
-
text = await response.text()
|
|
310
|
-
content = json.loads(text)
|
|
311
|
-
providers[content["name"]] = content["base"]
|
|
312
|
-
for module in content["modules"].keys():
|
|
313
|
-
module_content = content["modules"][module]
|
|
314
|
-
modules[f'{module}@{content["name"]}'] = module_content
|
|
315
|
-
module_origin_name = module_content["path"]
|
|
316
|
-
module_alias_name = module
|
|
317
|
-
module_alias[f'{module_origin_name}@{content["name"]}'] = module_alias_name
|
|
318
|
-
table_rows.append([
|
|
319
|
-
content['name'],
|
|
320
|
-
module,
|
|
321
|
-
f"{providers[content['name']]}{module_origin_name}"
|
|
322
|
-
])
|
|
323
|
-
except (ValueError, json.JSONDecodeError) as e:
|
|
324
|
-
shellprint.panel(f"源 {origin} 返回的内容不是有效的 JSON 格式: {e}", "错误", "error")
|
|
325
|
-
except Exception as e:
|
|
326
|
-
shellprint.panel(f"获取 {origin} 时出错: {e}", "错误", "error")
|
|
327
|
-
|
|
328
|
-
asyncio.run(fetch_source_data())
|
|
329
|
-
shellprint.table(["源", "模块", "地址"], table_rows, "源更新状态", "success")
|
|
330
|
-
from datetime import datetime
|
|
331
|
-
env.set('providers', providers)
|
|
332
|
-
env.set('modules', modules)
|
|
333
|
-
env.set('module_alias', module_alias)
|
|
334
|
-
env.set('last_origin_update_time', datetime.now().isoformat())
|
|
335
|
-
shellprint.panel("源更新完成", "成功", "success")
|
|
336
|
-
|
|
337
|
-
def list_sources(self):
|
|
338
|
-
origins = env.get('origins')
|
|
339
|
-
if not origins:
|
|
340
|
-
shellprint.panel("当前没有配置任何源", "提示", "info")
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
rows = [[str(idx), origin] for idx, origin in enumerate(origins, 1)]
|
|
344
|
-
shellprint.table(["序号", "源地址"], rows, "已配置的源", "info")
|
|
345
|
-
|
|
346
|
-
def del_source(self, value):
|
|
347
|
-
origins = env.get('origins')
|
|
348
|
-
if value in origins:
|
|
349
|
-
origins.remove(value)
|
|
350
|
-
env.set('origins', origins)
|
|
351
|
-
shellprint.panel(f"源 {value} 已成功删除", "成功", "success")
|
|
352
|
-
else:
|
|
353
|
-
shellprint.panel(f"源 {value} 不存在", "错误", "error")
|
|
354
|
-
|
|
355
|
-
source_manager = SourceManager()
|
|
356
|
-
|
|
357
239
|
class ReloadHandler(FileSystemEventHandler):
|
|
240
|
+
"""
|
|
241
|
+
热重载处理器
|
|
242
|
+
|
|
243
|
+
监控文件变化并自动重启脚本
|
|
244
|
+
|
|
245
|
+
{!--< tips >!--}
|
|
246
|
+
1. 基于watchdog实现文件监控
|
|
247
|
+
2. 有1秒的防抖延迟
|
|
248
|
+
3. 会终止旧进程并启动新进程
|
|
249
|
+
{!--< /tips >!--}
|
|
250
|
+
"""
|
|
358
251
|
def __init__(self, script_path, *args, **kwargs):
|
|
359
252
|
super().__init__(*args, **kwargs)
|
|
360
253
|
self.script_path = script_path
|
|
@@ -363,6 +256,11 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
363
256
|
self.start_process()
|
|
364
257
|
|
|
365
258
|
def start_process(self):
|
|
259
|
+
"""
|
|
260
|
+
启动/重启被监控的进程
|
|
261
|
+
|
|
262
|
+
{!--< internal-use >!--}
|
|
263
|
+
"""
|
|
366
264
|
if self.process:
|
|
367
265
|
self.process.terminate()
|
|
368
266
|
self.process.wait()
|
|
@@ -372,8 +270,12 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
372
270
|
self.last_reload = time.time()
|
|
373
271
|
|
|
374
272
|
def on_modified(self, event):
|
|
273
|
+
"""
|
|
274
|
+
文件修改事件处理
|
|
275
|
+
|
|
276
|
+
:param event: FileSystemEvent 文件系统事件对象
|
|
277
|
+
"""
|
|
375
278
|
now = time.time()
|
|
376
|
-
# 1秒后再次触发
|
|
377
279
|
if now - self.last_reload < 1.0:
|
|
378
280
|
return
|
|
379
281
|
|
|
@@ -382,6 +284,16 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
382
284
|
self.start_process()
|
|
383
285
|
|
|
384
286
|
def start_reloader(script_path):
|
|
287
|
+
"""
|
|
288
|
+
启动热重载监控
|
|
289
|
+
|
|
290
|
+
:param script_path: str 要监控的脚本路径
|
|
291
|
+
|
|
292
|
+
{!--< tips >!--}
|
|
293
|
+
1. 监控脚本所在目录和modules目录
|
|
294
|
+
2. 按Ctrl+C可停止监控
|
|
295
|
+
{!--< /tips >!--}
|
|
296
|
+
"""
|
|
385
297
|
project_root = os.path.dirname(os.path.abspath(__file__))
|
|
386
298
|
watch_dirs = [
|
|
387
299
|
os.path.dirname(os.path.abspath(script_path)),
|
|
@@ -407,911 +319,206 @@ def start_reloader(script_path):
|
|
|
407
319
|
handler.process.terminate()
|
|
408
320
|
observer.join()
|
|
409
321
|
|
|
410
|
-
def
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if module_info:
|
|
414
|
-
mods.set_module_status(module_name, True)
|
|
415
|
-
shellprint.panel(f"模块 {module_name} 已成功启用", "成功", "success")
|
|
416
|
-
else:
|
|
417
|
-
shellprint.panel(f"模块 {module_name} 不存在", "错误", "error")
|
|
418
|
-
|
|
419
|
-
def disable_module(module_name):
|
|
420
|
-
shellprint.status(f"禁用模块: {module_name}")
|
|
421
|
-
module_info = mods.get_module(module_name)
|
|
422
|
-
if module_info:
|
|
423
|
-
mods.set_module_status(module_name, False)
|
|
424
|
-
shellprint.panel(f"模块 {module_name} 已成功禁用", "成功", "success")
|
|
425
|
-
else:
|
|
426
|
-
shellprint.panel(f"模块 {module_name} 不存在", "错误", "error")
|
|
427
|
-
|
|
428
|
-
async def fetch_url(session, url, progress_callback=None):
|
|
429
|
-
try:
|
|
430
|
-
async with session.get(url) as response:
|
|
431
|
-
response.raise_for_status()
|
|
432
|
-
total_size = int(response.headers.get('Content-Length', 0))
|
|
433
|
-
downloaded = 0
|
|
434
|
-
data = b''
|
|
435
|
-
|
|
436
|
-
async for chunk in response.content.iter_any():
|
|
437
|
-
data += chunk
|
|
438
|
-
downloaded += len(chunk)
|
|
439
|
-
if total_size and progress_callback:
|
|
440
|
-
progress_callback(downloaded, total_size)
|
|
441
|
-
|
|
442
|
-
return data
|
|
443
|
-
except Exception as e:
|
|
444
|
-
print(f"请求失败: {e}")
|
|
445
|
-
return None
|
|
446
|
-
|
|
447
|
-
def extract_and_setup_module(module_name, module_url, zip_path, module_dir):
|
|
448
|
-
try:
|
|
449
|
-
print(f"正在下载模块: {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}")
|
|
450
|
-
current_downloaded = [0]
|
|
451
|
-
total_size = [0]
|
|
452
|
-
|
|
453
|
-
def progress_callback(downloaded, total):
|
|
454
|
-
if total > 0 and total_size[0] == 0:
|
|
455
|
-
total_size[0] = total
|
|
456
|
-
if downloaded > current_downloaded[0]:
|
|
457
|
-
current_downloaded[0] = downloaded
|
|
458
|
-
prefix = f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} {Shell_Printer.DIM}下载中{Shell_Printer.RESET}"
|
|
459
|
-
suffix = f"{downloaded/1024:.1f}KB/{total/1024:.1f}KB" if total > 0 else f"{downloaded/1024:.1f}KB"
|
|
460
|
-
shellprint.progress_bar(downloaded, total or downloaded, prefix, suffix, 40)
|
|
461
|
-
|
|
462
|
-
async def download_module():
|
|
463
|
-
async with aiohttp.ClientSession() as session:
|
|
464
|
-
content = await fetch_url(session, module_url, progress_callback)
|
|
465
|
-
if content is None:
|
|
466
|
-
return False
|
|
467
|
-
|
|
468
|
-
with open(zip_path, 'wb') as zip_file:
|
|
469
|
-
zip_file.write(content)
|
|
470
|
-
|
|
471
|
-
if total_size[0] > 0:
|
|
472
|
-
shellprint.progress_bar(total_size[0], total_size[0],
|
|
473
|
-
f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}",
|
|
474
|
-
"下载完成", 40)
|
|
475
|
-
|
|
476
|
-
if not os.path.exists(module_dir):
|
|
477
|
-
os.makedirs(module_dir)
|
|
478
|
-
|
|
479
|
-
shellprint.status(f"解压模块: {module_name}")
|
|
480
|
-
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
481
|
-
file_list = zip_ref.namelist()
|
|
482
|
-
for i, file in enumerate(file_list):
|
|
483
|
-
zip_ref.extract(file, module_dir)
|
|
484
|
-
if len(file_list) > 10 and i % (len(file_list) // 10) == 0:
|
|
485
|
-
shellprint.progress_bar(i, len(file_list),
|
|
486
|
-
f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}",
|
|
487
|
-
"解压中", 30)
|
|
488
|
-
if len(file_list) > 10:
|
|
489
|
-
shellprint.progress_bar(len(file_list), len(file_list),
|
|
490
|
-
f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}",
|
|
491
|
-
"解压完成", 30)
|
|
492
|
-
print()
|
|
493
|
-
|
|
494
|
-
init_file_path = os.path.join(module_dir, '__init__.py')
|
|
495
|
-
if not os.path.exists(init_file_path):
|
|
496
|
-
sub_module_dir = os.path.join(module_dir, module_name)
|
|
497
|
-
m_sub_module_dir = os.path.join(module_dir, f"m_{module_name}")
|
|
498
|
-
for sub_dir in [sub_module_dir, m_sub_module_dir]:
|
|
499
|
-
if os.path.exists(sub_dir) and os.path.isdir(sub_dir):
|
|
500
|
-
for item in os.listdir(sub_dir):
|
|
501
|
-
source_item = os.path.join(sub_dir, item)
|
|
502
|
-
target_item = os.path.join(module_dir, item)
|
|
503
|
-
if os.path.exists(target_item):
|
|
504
|
-
os.remove(target_item)
|
|
505
|
-
shutil.move(source_item, module_dir)
|
|
506
|
-
os.rmdir(sub_dir)
|
|
507
|
-
print(f"模块 {module_name} 文件已成功解压并设置")
|
|
508
|
-
return True
|
|
509
|
-
|
|
510
|
-
return asyncio.run(download_module())
|
|
511
|
-
except Exception as e:
|
|
512
|
-
shellprint.panel(f"处理模块 {module_name} 文件失败: {e}", "错误", "error")
|
|
513
|
-
if os.path.exists(zip_path):
|
|
514
|
-
try:
|
|
515
|
-
os.remove(zip_path)
|
|
516
|
-
except Exception as cleanup_error:
|
|
517
|
-
print(f"清理失败: {cleanup_error}")
|
|
518
|
-
return False
|
|
519
|
-
finally:
|
|
520
|
-
if os.path.exists(zip_path):
|
|
521
|
-
try:
|
|
522
|
-
os.remove(zip_path)
|
|
523
|
-
except Exception as cleanup_error:
|
|
524
|
-
print(f"清理失败: {cleanup_error}")
|
|
525
|
-
|
|
526
|
-
def install_pip_dependencies(dependencies):
|
|
527
|
-
if not dependencies:
|
|
528
|
-
return True
|
|
529
|
-
|
|
530
|
-
print(f"{Shell_Printer.CYAN}正在安装pip依赖: {', '.join(dependencies)}{Shell_Printer.RESET}")
|
|
531
|
-
try:
|
|
532
|
-
# 使用子进程获取安装进度
|
|
533
|
-
process = subprocess.Popen(
|
|
534
|
-
[sys.executable, "-m", "pip", "install"] + dependencies,
|
|
535
|
-
stdout=subprocess.PIPE,
|
|
536
|
-
stderr=subprocess.STDOUT,
|
|
537
|
-
universal_newlines=True
|
|
538
|
-
)
|
|
539
|
-
|
|
540
|
-
# 实时输出安装过程
|
|
541
|
-
while True:
|
|
542
|
-
output = process.stdout.readline()
|
|
543
|
-
if output == '' and process.poll() is not None:
|
|
544
|
-
break
|
|
545
|
-
if output:
|
|
546
|
-
print(f"{Shell_Printer.DIM}{output.strip()}{Shell_Printer.RESET}")
|
|
547
|
-
|
|
548
|
-
return process.returncode == 0
|
|
549
|
-
|
|
550
|
-
except subprocess.CalledProcessError as e:
|
|
551
|
-
shellprint.panel(f"安装pip依赖失败: {e.stderr}", "错误", "error")
|
|
552
|
-
return False
|
|
553
|
-
|
|
554
|
-
def install_local_module(module_path, force=False):
|
|
555
|
-
try:
|
|
556
|
-
module_path = os.path.abspath(module_path)
|
|
557
|
-
if not os.path.exists(module_path):
|
|
558
|
-
shellprint.panel(f"路径不存在: {module_path}", "错误", "error")
|
|
559
|
-
return False
|
|
560
|
-
|
|
561
|
-
module_name = os.path.basename(module_path.rstrip('/\\'))
|
|
562
|
-
init_py = os.path.join(module_path, '__init__.py')
|
|
563
|
-
|
|
564
|
-
if not os.path.exists(init_py):
|
|
565
|
-
shellprint.panel(f"目录 {module_path} 不是一个有效的Python模块", "错误", "error")
|
|
566
|
-
return False
|
|
567
|
-
import sys
|
|
568
|
-
sys.path.insert(0, os.path.dirname(module_path))
|
|
569
|
-
try:
|
|
570
|
-
module = importlib.import_module(module_name)
|
|
571
|
-
if not hasattr(module, 'moduleInfo'):
|
|
572
|
-
shellprint.panel(f"模块 {module_name} 缺少 moduleInfo 定义", "错误", "error")
|
|
573
|
-
return False
|
|
574
|
-
finally:
|
|
575
|
-
sys.path.remove(os.path.dirname(module_path))
|
|
576
|
-
except Exception as e:
|
|
577
|
-
shellprint.panel(f"导入模块 {module_name} 失败: {e}", "错误", "error")
|
|
578
|
-
return False
|
|
579
|
-
|
|
580
|
-
module_info = mods.get_module(module_name)
|
|
581
|
-
if module_info and not force:
|
|
582
|
-
meta = module_info.get('info', {}).get('meta', {})
|
|
583
|
-
shellprint.panel(
|
|
584
|
-
f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}\n版本: {meta.get('version', '未知')}\n描述: {meta.get('description', '无描述')}",
|
|
585
|
-
"模块已存在",
|
|
586
|
-
"info"
|
|
587
|
-
)
|
|
588
|
-
if not shellprint.confirm("是否要强制重新安装?", default=False):
|
|
589
|
-
return False
|
|
322
|
+
def run_script(script_path: str, reload: bool = False):
|
|
323
|
+
"""
|
|
324
|
+
运行指定脚本
|
|
590
325
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
target_dir = os.path.join(script_dir, 'modules', module_name)
|
|
326
|
+
:param script_path: str 要运行的脚本路径
|
|
327
|
+
:param reload: bool 是否启用热重载 (默认: False)
|
|
594
328
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
except Exception as e:
|
|
600
|
-
shellprint.panel(f"复制模块文件失败: {e}", "错误", "error")
|
|
601
|
-
return False
|
|
602
|
-
|
|
603
|
-
# 安装依赖
|
|
604
|
-
dependencies = module.moduleInfo.get('dependencies', {})
|
|
605
|
-
for dep in dependencies.get('requires', []):
|
|
606
|
-
print(f"\n{Shell_Printer.BOLD}处理依赖: {dep}{Shell_Printer.RESET}")
|
|
607
|
-
install_module(dep)
|
|
608
|
-
|
|
609
|
-
# 安装pip依赖
|
|
610
|
-
pip_dependencies = dependencies.get('pip', [])
|
|
611
|
-
if pip_dependencies:
|
|
612
|
-
print(f"{Shell_Printer.YELLOW}模块 {module_name} 需要以下pip依赖: {', '.join(pip_dependencies)}{Shell_Printer.RESET}")
|
|
613
|
-
if not install_pip_dependencies(pip_dependencies):
|
|
614
|
-
print(f"{Shell_Printer.RED}无法安装模块 {module_name} 的pip依赖,安装终止{Shell_Printer.RESET}")
|
|
615
|
-
return False
|
|
616
|
-
|
|
617
|
-
# 注册模块信息
|
|
618
|
-
mods.set_module(module_name, {
|
|
619
|
-
'status': True,
|
|
620
|
-
'info': {
|
|
621
|
-
'meta': module.moduleInfo.get('meta', {}),
|
|
622
|
-
'dependencies': module.moduleInfo.get('dependencies', {})
|
|
623
|
-
}
|
|
624
|
-
})
|
|
625
|
-
|
|
626
|
-
shellprint.panel(f"本地模块 {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} 安装成功", "成功", "success")
|
|
627
|
-
return True
|
|
628
|
-
|
|
629
|
-
def install_module(module_name, force=False):
|
|
630
|
-
# 检查是否是本地路径
|
|
631
|
-
if module_name.startswith('.') or os.path.isabs(module_name):
|
|
632
|
-
return install_local_module(module_name, force)
|
|
633
|
-
|
|
634
|
-
shellprint.panel(f"准备安装模块: {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}", "安装摘要", "info")
|
|
635
|
-
last_update_time = env.get('last_origin_update_time', None)
|
|
636
|
-
if last_update_time:
|
|
637
|
-
from datetime import datetime, timedelta
|
|
638
|
-
last_update = datetime.fromisoformat(last_update_time)
|
|
639
|
-
if datetime.now() - last_update > timedelta(hours=720):
|
|
640
|
-
shellprint.panel("距离上次源更新已超过30天,源内可能有新模块或更新。", "提示", "warning")
|
|
641
|
-
if shellprint.confirm("是否在安装模块前更新源?", default=True):
|
|
642
|
-
source_manager.update_sources()
|
|
643
|
-
env.set('last_origin_update_time', datetime.now().isoformat())
|
|
644
|
-
shellprint.status("源更新完成")
|
|
645
|
-
|
|
646
|
-
module_info = mods.get_module(module_name)
|
|
647
|
-
if module_info and not force:
|
|
648
|
-
meta = module_info.get('info', {}).get('meta', {})
|
|
649
|
-
shellprint.panel(
|
|
650
|
-
f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}\n版本: {meta.get('version', '未知')}\n描述: {meta.get('description', '无描述')}",
|
|
651
|
-
"模块已存在",
|
|
652
|
-
"info"
|
|
653
|
-
)
|
|
654
|
-
if not shellprint.confirm("是否要强制重新安装?", default=False):
|
|
655
|
-
return
|
|
656
|
-
|
|
657
|
-
providers = env.get('providers', {})
|
|
658
|
-
if isinstance(providers, str):
|
|
659
|
-
providers = json.loads(providers)
|
|
660
|
-
module_info_list = []
|
|
661
|
-
|
|
662
|
-
for provider, url in providers.items():
|
|
663
|
-
module_key = f"{module_name}@{provider}"
|
|
664
|
-
modules_data = env.get('modules', {})
|
|
665
|
-
if isinstance(modules_data, str):
|
|
666
|
-
modules_data = json.loads(modules_data)
|
|
667
|
-
if module_key in modules_data:
|
|
668
|
-
module_data = modules_data[module_key]
|
|
669
|
-
meta = module_data.get("meta", {})
|
|
670
|
-
depsinfo = module_data.get("dependencies", {})
|
|
671
|
-
module_info_list.append({
|
|
672
|
-
'provider': provider,
|
|
673
|
-
'url': url,
|
|
674
|
-
'path': module_data.get('path', ''),
|
|
675
|
-
'version': meta.get('version', '未知'),
|
|
676
|
-
'description': meta.get('description', '无描述'),
|
|
677
|
-
'author': meta.get('author', '未知'),
|
|
678
|
-
'dependencies': depsinfo.get("requires", []),
|
|
679
|
-
'optional_dependencies': depsinfo.get("optional", []),
|
|
680
|
-
'pip_dependencies': depsinfo.get("pip", [])
|
|
681
|
-
})
|
|
682
|
-
|
|
683
|
-
if not module_info_list:
|
|
684
|
-
shellprint.panel(f"未找到模块 {module_name}", "错误", "error")
|
|
685
|
-
if providers:
|
|
686
|
-
print(f"{Shell_Printer.BOLD}当前可用源:{Shell_Printer.RESET}")
|
|
687
|
-
for provider in providers:
|
|
688
|
-
print(f" {Shell_Printer.CYAN}- {provider}{Shell_Printer.RESET}")
|
|
689
|
-
return
|
|
690
|
-
|
|
691
|
-
if len(module_info_list) > 1:
|
|
692
|
-
print(f"找到 {Shell_Printer.BOLD}{len(module_info_list)}{Shell_Printer.RESET} 个源的 {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} 模块:")
|
|
693
|
-
rows = []
|
|
694
|
-
for i, info in enumerate(module_info_list):
|
|
695
|
-
rows.append([
|
|
696
|
-
f"{Shell_Printer.CYAN}{i+1}{Shell_Printer.RESET}",
|
|
697
|
-
info['provider'],
|
|
698
|
-
info['version'],
|
|
699
|
-
info['description'],
|
|
700
|
-
info['author']
|
|
701
|
-
])
|
|
702
|
-
shellprint.table(["编号", "源", "版本", "描述", "作者"], rows, "可选模块源", "info")
|
|
703
|
-
|
|
704
|
-
while True:
|
|
705
|
-
choice = shellprint.ask("请选择要安装的源 (输入编号)", [str(i) for i in range(1, len(module_info_list)+1)])
|
|
706
|
-
if choice and 1 <= int(choice) <= len(module_info_list):
|
|
707
|
-
selected_module = module_info_list[int(choice)-1]
|
|
708
|
-
break
|
|
709
|
-
else:
|
|
710
|
-
print(f"{Shell_Printer.YELLOW}输入无效,请重新选择{Shell_Printer.RESET}")
|
|
711
|
-
else:
|
|
712
|
-
selected_module = module_info_list[0]
|
|
713
|
-
|
|
714
|
-
# 安装依赖
|
|
715
|
-
for dep in selected_module['dependencies']:
|
|
716
|
-
print(f"\n{Shell_Printer.BOLD}处理依赖: {dep}{Shell_Printer.RESET}")
|
|
717
|
-
install_module(dep)
|
|
718
|
-
|
|
719
|
-
# 安装pip依赖
|
|
720
|
-
third_party_deps = selected_module.get('pip_dependencies', [])
|
|
721
|
-
if third_party_deps:
|
|
722
|
-
print(f"{Shell_Printer.YELLOW}模块 {module_name} 需要以下pip依赖: {', '.join(third_party_deps)}{Shell_Printer.RESET}")
|
|
723
|
-
if not install_pip_dependencies(third_party_deps):
|
|
724
|
-
print(f"{Shell_Printer.RED}无法安装模块 {module_name} 的pip依赖,安装终止{Shell_Printer.RESET}")
|
|
725
|
-
return
|
|
726
|
-
|
|
727
|
-
# 下载并安装模块
|
|
728
|
-
module_url = selected_module['url'] + selected_module['path']
|
|
729
|
-
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
730
|
-
module_dir = os.path.join(script_dir, 'modules', module_name)
|
|
731
|
-
zip_path = os.path.join(script_dir, f"{module_name}.zip")
|
|
732
|
-
|
|
733
|
-
if not extract_and_setup_module(
|
|
734
|
-
module_name=module_name,
|
|
735
|
-
module_url=module_url,
|
|
736
|
-
zip_path=zip_path,
|
|
737
|
-
module_dir=module_dir
|
|
738
|
-
):
|
|
329
|
+
:raises FileNotFoundError: 当脚本不存在时抛出
|
|
330
|
+
"""
|
|
331
|
+
if not os.path.exists(script_path):
|
|
332
|
+
shellprint.panel(f"找不到指定文件: {script_path}", "错误", "error")
|
|
739
333
|
return
|
|
740
|
-
|
|
741
|
-
# 注册模块信息
|
|
742
|
-
mods.set_module(module_name, {
|
|
743
|
-
'status': True,
|
|
744
|
-
'info': {
|
|
745
|
-
'meta': {
|
|
746
|
-
'version': selected_module['version'],
|
|
747
|
-
'description': selected_module['description'],
|
|
748
|
-
'author': selected_module['author'],
|
|
749
|
-
'pip_dependencies': selected_module['pip_dependencies']
|
|
750
|
-
},
|
|
751
|
-
'dependencies': {
|
|
752
|
-
'requires': selected_module['dependencies'],
|
|
753
|
-
'optional': selected_module['optional_dependencies'],
|
|
754
|
-
'pip': selected_module['pip_dependencies']
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
})
|
|
758
|
-
|
|
759
|
-
shellprint.panel(f"模块 {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} 安装成功", "成功", "success")
|
|
760
334
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
module_info = mods.get_module(module_name)
|
|
764
|
-
if not module_info:
|
|
765
|
-
shellprint.panel(f"模块 {module_name} 不存在", "错误", "error")
|
|
766
|
-
return
|
|
767
|
-
|
|
768
|
-
meta = module_info.get('info', {}).get('meta', {})
|
|
769
|
-
depsinfo = module_info.get('info', {}).get('dependencies', {})
|
|
770
|
-
shellprint.panel(
|
|
771
|
-
f"版本: {Shell_Printer.BOLD}{meta.get('version', '未知')}{Shell_Printer.RESET}\n"
|
|
772
|
-
f"描述: {meta.get('description', '无描述')}\n"
|
|
773
|
-
f"pip依赖: {Shell_Printer.YELLOW}{', '.join(depsinfo.get('pip', [])) or '无'}{Shell_Printer.RESET}",
|
|
774
|
-
"模块信息",
|
|
775
|
-
"info"
|
|
776
|
-
)
|
|
777
|
-
|
|
778
|
-
if not shellprint.confirm("确认要卸载此模块吗?", default=False):
|
|
779
|
-
print(f"{Shell_Printer.BLUE}卸载已取消{Shell_Printer.RESET}")
|
|
780
|
-
return
|
|
781
|
-
|
|
782
|
-
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
783
|
-
module_path = os.path.join(script_dir, 'modules', module_name)
|
|
784
|
-
module_file_path = module_path + '.py'
|
|
785
|
-
|
|
786
|
-
if os.path.exists(module_file_path):
|
|
787
|
-
try:
|
|
788
|
-
shellprint.status(f"删除模块文件: {module_name}.py")
|
|
789
|
-
os.remove(module_file_path)
|
|
790
|
-
except Exception as e:
|
|
791
|
-
shellprint.panel(f"删除模块文件 {module_name} 时出错: {e}", "错误", "error")
|
|
792
|
-
elif os.path.exists(module_path) and os.path.isdir(module_path):
|
|
793
|
-
try:
|
|
794
|
-
shellprint.status(f"删除模块目录: {module_name}")
|
|
795
|
-
shutil.rmtree(module_path)
|
|
796
|
-
except Exception as e:
|
|
797
|
-
shellprint.panel(f"删除模块目录 {module_name} 时出错: {e}", "错误", "error")
|
|
798
|
-
else:
|
|
799
|
-
shellprint.panel(f"模块 {module_name} 文件不存在", "错误", "error")
|
|
800
|
-
return
|
|
801
|
-
|
|
802
|
-
pip_dependencies = depsinfo.get('pip', [])
|
|
803
|
-
if pip_dependencies:
|
|
804
|
-
all_modules = mods.get_all_modules()
|
|
805
|
-
unused_pip_dependencies = []
|
|
806
|
-
essential_packages = {'aiohttp'}
|
|
807
|
-
for dep in pip_dependencies:
|
|
808
|
-
if dep in essential_packages:
|
|
809
|
-
print(f"{Shell_Printer.CYAN}跳过必要模块 {dep} 的卸载{Shell_Printer.RESET}")
|
|
810
|
-
continue
|
|
811
|
-
|
|
812
|
-
is_dependency_used = False
|
|
813
|
-
for name, info in all_modules.items():
|
|
814
|
-
if name != module_name and dep in info.get('info', {}).get('dependencies', {}).get('pip', []):
|
|
815
|
-
is_dependency_used = True
|
|
816
|
-
break
|
|
817
|
-
|
|
818
|
-
if not is_dependency_used:
|
|
819
|
-
unused_pip_dependencies.append(dep)
|
|
820
|
-
|
|
821
|
-
if unused_pip_dependencies:
|
|
822
|
-
shellprint.panel(
|
|
823
|
-
f"以下 pip 依赖不再被其他模块使用:\n{Shell_Printer.YELLOW}{', '.join(unused_pip_dependencies)}{Shell_Printer.RESET}",
|
|
824
|
-
"可卸载依赖",
|
|
825
|
-
"info"
|
|
826
|
-
)
|
|
827
|
-
if shellprint.confirm("是否卸载这些 pip 依赖?", default=False):
|
|
828
|
-
try:
|
|
829
|
-
shellprint.status(f"卸载pip依赖: {', '.join(unused_pip_dependencies)}")
|
|
830
|
-
subprocess.run(
|
|
831
|
-
[sys.executable, "-m", "pip", "uninstall", "-y"] + unused_pip_dependencies,
|
|
832
|
-
check=True,
|
|
833
|
-
stdout=subprocess.PIPE,
|
|
834
|
-
stderr=subprocess.PIPE
|
|
835
|
-
)
|
|
836
|
-
shellprint.panel(
|
|
837
|
-
f"成功卸载 pip 依赖: {', '.join(unused_pip_dependencies)}",
|
|
838
|
-
"成功",
|
|
839
|
-
"success"
|
|
840
|
-
)
|
|
841
|
-
except subprocess.CalledProcessError as e:
|
|
842
|
-
shellprint.panel(
|
|
843
|
-
f"卸载 pip 依赖失败: {e.stderr.decode()}",
|
|
844
|
-
"错误",
|
|
845
|
-
"error"
|
|
846
|
-
)
|
|
847
|
-
|
|
848
|
-
if mods.remove_module(module_name):
|
|
849
|
-
shellprint.panel(f"模块 {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} 已成功卸载", "成功", "success")
|
|
335
|
+
if reload:
|
|
336
|
+
start_reloader(script_path)
|
|
850
337
|
else:
|
|
851
|
-
shellprint.panel(f"
|
|
338
|
+
shellprint.panel(f"运行脚本: {Shell_Printer.BOLD}{script_path}{Shell_Printer.RESET}", "执行", "info")
|
|
339
|
+
import runpy
|
|
340
|
+
try:
|
|
341
|
+
runpy.run_path(script_path, run_name="__main__")
|
|
342
|
+
except KeyboardInterrupt:
|
|
343
|
+
shellprint.panel("脚本执行已中断", "中断", "info")
|
|
852
344
|
|
|
853
|
-
def
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
print(f"{Shell_Printer.YELLOW}未找到任何模块,无法更新{Shell_Printer.RESET}")
|
|
857
|
-
return
|
|
858
|
-
|
|
859
|
-
providers = env.get('providers', {})
|
|
860
|
-
if isinstance(providers, str):
|
|
861
|
-
providers = json.loads(providers)
|
|
862
|
-
modules_data = env.get('modules', {})
|
|
863
|
-
if isinstance(modules_data, str):
|
|
864
|
-
modules_data = json.loads(modules_data)
|
|
865
|
-
|
|
866
|
-
updates_available = []
|
|
867
|
-
for module_name, module_info in all_modules.items():
|
|
868
|
-
shellprint.status(f"检查更新: {module_name}")
|
|
869
|
-
local_version = module_info.get('info', {}).get('meta', {}).get('version', '0.0.0')
|
|
870
|
-
for provider, url in providers.items():
|
|
871
|
-
module_key = f"{module_name}@{provider}"
|
|
872
|
-
if module_key in modules_data:
|
|
873
|
-
remote_module = modules_data[module_key]
|
|
874
|
-
remote_version = remote_module.get('meta', {}).get('version', '1.14.514')
|
|
875
|
-
if remote_version > local_version:
|
|
876
|
-
updates_available.append({
|
|
877
|
-
'name': module_name,
|
|
878
|
-
'local_version': local_version,
|
|
879
|
-
'remote_version': remote_version,
|
|
880
|
-
'provider': provider,
|
|
881
|
-
'url': url,
|
|
882
|
-
'path': remote_module.get('path', ''),
|
|
883
|
-
})
|
|
884
|
-
|
|
885
|
-
if not updates_available:
|
|
886
|
-
print(f"{Shell_Printer.GREEN}所有模块已是最新版本,无需更新{Shell_Printer.RESET}")
|
|
887
|
-
return
|
|
888
|
-
|
|
889
|
-
print(f"\n{Shell_Printer.BOLD}以下模块有可用更新:{Shell_Printer.RESET}")
|
|
890
|
-
rows = []
|
|
891
|
-
for update in updates_available:
|
|
892
|
-
rows.append([
|
|
893
|
-
update['name'],
|
|
894
|
-
update['local_version'],
|
|
895
|
-
f"{Shell_Printer.GREEN}{update['remote_version']}{Shell_Printer.RESET}",
|
|
896
|
-
update['provider']
|
|
897
|
-
])
|
|
898
|
-
shellprint.table(["模块", "当前版本", "最新版本", "源"], rows, "可用更新", "info")
|
|
345
|
+
def get_erispulse_version():
|
|
346
|
+
"""
|
|
347
|
+
获取当前安装的ErisPulse版本
|
|
899
348
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
if not shellprint.confirm(warning_msg, default=False):
|
|
907
|
-
print(f"{Shell_Printer.BLUE}更新已取消{Shell_Printer.RESET}")
|
|
908
|
-
return
|
|
909
|
-
|
|
910
|
-
for i, update in enumerate(updates_available, 1):
|
|
911
|
-
print(f"\n{Shell_Printer.BOLD}[{i}/{len(updates_available)}]{Shell_Printer.RESET} 更新模块 {Shell_Printer.BOLD}{update['name']}{Shell_Printer.RESET}")
|
|
912
|
-
|
|
913
|
-
# 检查新版本的依赖
|
|
914
|
-
module_key = f"{update['name']}@{update['provider']}"
|
|
915
|
-
new_module_info = modules_data[module_key]
|
|
916
|
-
new_dependencies = new_module_info.get('dependencies', {}).get('requires', [])
|
|
917
|
-
|
|
918
|
-
# 检查缺失的依赖
|
|
919
|
-
missing_deps = []
|
|
920
|
-
for dep in new_dependencies:
|
|
921
|
-
if dep not in all_modules or not all_modules[dep].get('status', True):
|
|
922
|
-
missing_deps.append(dep)
|
|
923
|
-
|
|
924
|
-
if missing_deps:
|
|
925
|
-
shellprint.panel(
|
|
926
|
-
f"模块 {update['name']} 需要以下依赖:\n{Shell_Printer.YELLOW}{', '.join(missing_deps)}{Shell_Printer.RESET}",
|
|
927
|
-
"缺失依赖",
|
|
928
|
-
"warning"
|
|
929
|
-
)
|
|
930
|
-
if not shellprint.confirm("是否安装这些依赖?", default=True):
|
|
931
|
-
print(f"{Shell_Printer.BLUE}跳过模块 {update['name']} 的更新{Shell_Printer.RESET}")
|
|
932
|
-
continue
|
|
933
|
-
|
|
934
|
-
for dep in missing_deps:
|
|
935
|
-
print(f"\n{Shell_Printer.BOLD}安装依赖: {dep}{Shell_Printer.RESET}")
|
|
936
|
-
install_module(dep)
|
|
937
|
-
|
|
938
|
-
module_url = update['url'] + update['path']
|
|
939
|
-
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
940
|
-
module_dir = os.path.join(script_dir, 'modules', update['name'])
|
|
941
|
-
zip_path = os.path.join(script_dir, f"{update['name']}.zip")
|
|
942
|
-
|
|
943
|
-
# 检查新版本的pip依赖
|
|
944
|
-
new_pip_deps = new_module_info.get('dependencies', {}).get('pip', [])
|
|
945
|
-
current_pip_deps = all_modules[update['name']].get('info', {}).get('dependencies', {}).get('pip', [])
|
|
946
|
-
added_pip_deps = [dep for dep in new_pip_deps if dep not in current_pip_deps]
|
|
947
|
-
|
|
948
|
-
if added_pip_deps:
|
|
949
|
-
shellprint.panel(
|
|
950
|
-
f"模块 {update['name']} 需要以下新的pip依赖:\n{Shell_Printer.YELLOW}{', '.join(added_pip_deps)}{Shell_Printer.RESET}",
|
|
951
|
-
"新增pip依赖",
|
|
952
|
-
"warning"
|
|
953
|
-
)
|
|
954
|
-
if not shellprint.confirm("是否安装这些pip依赖?", default=True):
|
|
955
|
-
print(f"{Shell_Printer.BLUE}跳过模块 {update['name']} 的更新{Shell_Printer.RESET}")
|
|
956
|
-
continue
|
|
957
|
-
|
|
958
|
-
if not install_pip_dependencies(added_pip_deps):
|
|
959
|
-
print(f"{Shell_Printer.RED}无法安装模块 {update['name']} 的pip依赖,更新终止{Shell_Printer.RESET}")
|
|
960
|
-
continue
|
|
961
|
-
|
|
962
|
-
if not extract_and_setup_module(
|
|
963
|
-
module_name=update['name'],
|
|
964
|
-
module_url=module_url,
|
|
965
|
-
zip_path=zip_path,
|
|
966
|
-
module_dir=module_dir
|
|
967
|
-
):
|
|
968
|
-
continue
|
|
969
|
-
|
|
970
|
-
# 更新模块信息,包括新的依赖
|
|
971
|
-
all_modules[update['name']]['info']['version'] = update['remote_version']
|
|
972
|
-
all_modules[update['name']]['info']['dependencies'] = {
|
|
973
|
-
'requires': new_dependencies,
|
|
974
|
-
'pip': new_pip_deps
|
|
975
|
-
}
|
|
976
|
-
mods.set_all_modules(all_modules)
|
|
977
|
-
print(f"{Shell_Printer.GREEN}模块 {update['name']} 已更新至版本 {update['remote_version']}{Shell_Printer.RESET}")
|
|
978
|
-
|
|
979
|
-
shellprint.panel("所有可用更新已处理完成", "完成", "success")
|
|
349
|
+
:return: str ErisPulse版本号或"unknown version"
|
|
350
|
+
"""
|
|
351
|
+
try:
|
|
352
|
+
return version("ErisPulse")
|
|
353
|
+
except PackageNotFoundError:
|
|
354
|
+
return "unknown version"
|
|
980
355
|
|
|
981
|
-
def
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
shellprint.panel("未在数据库中发现注册模块,正在初始化模块列表...", "提示", "info")
|
|
985
|
-
from . import init as init_module
|
|
986
|
-
init_module()
|
|
987
|
-
all_modules = mods.get_all_modules()
|
|
988
|
-
|
|
989
|
-
if not all_modules:
|
|
990
|
-
shellprint.panel("未找到任何模块", "错误", "error")
|
|
991
|
-
return
|
|
992
|
-
|
|
993
|
-
shellprint.panel(f"找到 {Shell_Printer.BOLD}{len(all_modules)}{Shell_Printer.RESET} 个模块", "统计", "info")
|
|
356
|
+
def main():
|
|
357
|
+
"""
|
|
358
|
+
CLI主入口
|
|
994
359
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
optional_deps = depsinfo.get('optional', [])
|
|
1004
|
-
|
|
1005
|
-
available_optional_deps = []
|
|
1006
|
-
missing_optional_deps = []
|
|
1007
|
-
if optional_deps:
|
|
1008
|
-
for dep in optional_deps:
|
|
1009
|
-
if isinstance(dep, list):
|
|
1010
|
-
available_deps = [d for d in dep if d in all_modules]
|
|
1011
|
-
if available_deps:
|
|
1012
|
-
available_optional_deps.extend(available_deps)
|
|
1013
|
-
else:
|
|
1014
|
-
missing_optional_deps.extend(dep)
|
|
1015
|
-
elif dep in all_modules:
|
|
1016
|
-
available_optional_deps.append(dep)
|
|
1017
|
-
else:
|
|
1018
|
-
missing_optional_deps.append(dep)
|
|
1019
|
-
|
|
1020
|
-
if missing_optional_deps:
|
|
1021
|
-
optional_dependencies = f"可用: {', '.join(available_optional_deps)} 缺失: {Shell_Printer.RED}{', '.join(missing_optional_deps)}{Shell_Printer.RESET}"
|
|
1022
|
-
else:
|
|
1023
|
-
optional_dependencies = ', '.join(available_optional_deps) or '无'
|
|
1024
|
-
else:
|
|
1025
|
-
optional_dependencies = '无'
|
|
1026
|
-
|
|
1027
|
-
# 依赖项使用不同颜色标识
|
|
1028
|
-
dependencies = Shell_Printer.YELLOW + ', '.join(depsinfo.get('requires', [])) + Shell_Printer.RESET or '无'
|
|
1029
|
-
pip_dependencies = Shell_Printer.CYAN + ', '.join(depsinfo.get('pip', [])) + Shell_Printer.RESET or '无'
|
|
1030
|
-
|
|
1031
|
-
rows.append([
|
|
1032
|
-
Shell_Printer.BOLD + name + Shell_Printer.RESET,
|
|
1033
|
-
status,
|
|
1034
|
-
Shell_Printer.BLUE + meta.get('version', '未知') + Shell_Printer.RESET,
|
|
1035
|
-
meta.get('description', '无描述'),
|
|
1036
|
-
dependencies,
|
|
1037
|
-
optional_dependencies,
|
|
1038
|
-
pip_dependencies
|
|
1039
|
-
])
|
|
1040
|
-
|
|
1041
|
-
shellprint.table(
|
|
1042
|
-
["模块名称", "状态", "版本", "描述", "依赖", "可选依赖", "pip依赖"],
|
|
1043
|
-
rows,
|
|
1044
|
-
"模块列表",
|
|
1045
|
-
"info"
|
|
1046
|
-
)
|
|
360
|
+
解析命令行参数并执行相应命令
|
|
361
|
+
|
|
362
|
+
{!--< tips >!--}
|
|
363
|
+
1. 使用argparse处理命令行参数
|
|
364
|
+
2. 支持彩色输出和表格显示
|
|
365
|
+
3. 提供详细的错误处理
|
|
366
|
+
{!--< /tips >!--}
|
|
367
|
+
"""
|
|
1047
368
|
|
|
1048
|
-
enabled_count = sum(1 for m in all_modules.values() if m.get("status", True))
|
|
1049
|
-
disabled_count = len(all_modules) - enabled_count
|
|
1050
|
-
shellprint.panel(
|
|
1051
|
-
f"{Shell_Printer.GREEN}已启用: {enabled_count}{Shell_Printer.RESET} "
|
|
1052
|
-
f"{Shell_Printer.RED}已禁用: {disabled_count}{Shell_Printer.RESET}",
|
|
1053
|
-
"模块状态统计",
|
|
1054
|
-
"info"
|
|
1055
|
-
)
|
|
1056
|
-
|
|
1057
|
-
def main():
|
|
1058
369
|
parser = argparse.ArgumentParser(
|
|
1059
370
|
prog="epsdk",
|
|
1060
|
-
formatter_class=argparse.RawTextHelpFormatter
|
|
371
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
372
|
+
description=f"{Shell_Printer.BOLD}ErisPulse SDK 命令行工具 {get_erispulse_version()}{Shell_Printer.RESET}"
|
|
1061
373
|
)
|
|
1062
374
|
parser._positionals.title = f"{Shell_Printer.BOLD}{Shell_Printer.CYAN}基本命令{Shell_Printer.RESET}"
|
|
1063
375
|
parser._optionals.title = f"{Shell_Printer.BOLD}{Shell_Printer.MAGENTA}可选参数{Shell_Printer.RESET}"
|
|
1064
376
|
|
|
377
|
+
parser.add_argument("--version", action="store_true", help="显示版本信息并退出")
|
|
378
|
+
|
|
1065
379
|
subparsers = parser.add_subparsers(
|
|
1066
380
|
dest='command',
|
|
1067
381
|
title='可用的命令',
|
|
1068
382
|
metavar=f"{Shell_Printer.GREEN}<命令>{Shell_Printer.RESET}",
|
|
1069
383
|
help='具体命令的帮助信息'
|
|
1070
384
|
)
|
|
385
|
+
|
|
386
|
+
# 安装命令
|
|
387
|
+
install_parser = subparsers.add_parser('install', help='安装模块/适配器包')
|
|
388
|
+
install_parser.add_argument('package', type=str, help='要安装的包名')
|
|
389
|
+
install_parser.add_argument('--upgrade', '-U', action='store_true', help='升级已安装的包')
|
|
1071
390
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
'list': '列出所有模块信息',
|
|
1076
|
-
'update': '更新模块列表',
|
|
1077
|
-
'upgrade': '升级所有可用模块',
|
|
1078
|
-
'uninstall': '删除指定模块',
|
|
1079
|
-
'install': '安装指定模块',
|
|
1080
|
-
'origin': '管理模块源',
|
|
1081
|
-
'run': '运行指定主程序'
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
# 模块管理
|
|
1085
|
-
def add_module_command(name, help_text):
|
|
1086
|
-
cmd = subparsers.add_parser(name, help=help_text, description=help_text)
|
|
1087
|
-
cmd.add_argument('module_names', nargs='+', help='模块名称')
|
|
1088
|
-
if name in ['enable', 'disable', 'install']:
|
|
1089
|
-
cmd.add_argument('--init', action='store_true', help='在操作前初始化模块数据库')
|
|
1090
|
-
if name in ['install']:
|
|
1091
|
-
cmd.add_argument('--force', action='store_true', help='强制重新安装模块')
|
|
1092
|
-
return cmd
|
|
1093
|
-
|
|
1094
|
-
enable_parser = add_module_command('enable', '启用指定模块')
|
|
1095
|
-
disable_parser = add_module_command('disable', '禁用指定模块')
|
|
1096
|
-
uninstall_parser = add_module_command('uninstall', '删除指定模块')
|
|
1097
|
-
install_parser = add_module_command('install', '安装指定模块')
|
|
1098
|
-
|
|
1099
|
-
# 其他命令
|
|
1100
|
-
list_parser = subparsers.add_parser('list', help='列出所有模块信息')
|
|
1101
|
-
list_parser.add_argument('--module', '-m', type=str, help='指定要展示的模块名称')
|
|
1102
|
-
|
|
1103
|
-
update_parser = subparsers.add_parser('update', help='更新模块列表')
|
|
1104
|
-
|
|
1105
|
-
upgrade_parser = subparsers.add_parser('upgrade', help='升级模块列表')
|
|
1106
|
-
upgrade_parser.add_argument('--force', action='store_true', help='跳过二次确认,强制更新')
|
|
1107
|
-
|
|
1108
|
-
# 初始化命令
|
|
1109
|
-
init_parser = subparsers.add_parser('init', help='初始化模块数据库')
|
|
1110
|
-
|
|
1111
|
-
origin_parser = subparsers.add_parser('origin', help='管理模块源')
|
|
1112
|
-
origin_subparsers = origin_parser.add_subparsers(
|
|
1113
|
-
dest='origin_command',
|
|
1114
|
-
title='源管理命令',
|
|
1115
|
-
metavar=f"{Shell_Printer.CYAN}<子命令>{Shell_Printer.RESET}"
|
|
1116
|
-
)
|
|
391
|
+
# 卸载命令
|
|
392
|
+
uninstall_parser = subparsers.add_parser('uninstall', help='卸载模块/适配器包')
|
|
393
|
+
uninstall_parser.add_argument('package', type=str, help='要卸载的包名')
|
|
1117
394
|
|
|
1118
|
-
|
|
1119
|
-
|
|
395
|
+
# 列表命令
|
|
396
|
+
list_parser = subparsers.add_parser('list', help='列出已安装的模块/适配器')
|
|
397
|
+
list_parser.add_argument('--type', '-t', choices=['modules', 'adapters', 'all'], default='all',
|
|
398
|
+
help='列出类型 (modules: 仅模块, adapters: 仅适配器, all: 全部)')
|
|
1120
399
|
|
|
1121
|
-
|
|
400
|
+
# 远程列表命令
|
|
401
|
+
list_remote_parser = subparsers.add_parser('list-remote', help='列出远程可用的模块和适配器')
|
|
402
|
+
list_remote_parser.add_argument('--type', '-t', choices=['modules', 'adapters', 'all'], default='all',
|
|
403
|
+
help='列出类型 (modules: 仅模块, adapters: 仅适配器, all: 全部)')
|
|
404
|
+
# 升级命令
|
|
405
|
+
upgrade_parser = subparsers.add_parser('upgrade', help='升级所有模块/适配器')
|
|
406
|
+
upgrade_parser.add_argument('--force', '-f', action='store_true', help='跳过确认直接升级')
|
|
1122
407
|
|
|
1123
|
-
|
|
1124
|
-
del_origin_parser.add_argument('url', type=str, help='要删除的模块源URL')
|
|
1125
|
-
|
|
408
|
+
# 运行命令
|
|
1126
409
|
run_parser = subparsers.add_parser('run', help='运行指定主程序')
|
|
1127
410
|
run_parser.add_argument('script', type=str, help='要运行的主程序路径')
|
|
1128
|
-
run_parser.add_argument('--reload', action='store_true', help='
|
|
1129
|
-
|
|
411
|
+
run_parser.add_argument('--reload', action='store_true', help='启用热重载模式')
|
|
412
|
+
|
|
1130
413
|
args = parser.parse_args()
|
|
1131
414
|
|
|
1132
|
-
if
|
|
1133
|
-
print(f"{Shell_Printer.GREEN}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
415
|
+
if args.version:
|
|
416
|
+
print(f"{Shell_Printer.GREEN}ErisPulse {get_erispulse_version()}{Shell_Printer.RESET}")
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
if not args.command:
|
|
420
|
+
parser.print_help()
|
|
421
|
+
return
|
|
1137
422
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if not shellprint.confirm("是否启用所有匹配模块?", default=True):
|
|
1162
|
-
print(f"{Shell_Printer.BLUE}操作已取消{Shell_Printer.RESET}")
|
|
1163
|
-
continue
|
|
1164
|
-
|
|
1165
|
-
for matched_module in matched_modules:
|
|
1166
|
-
enable_module(matched_module)
|
|
423
|
+
try:
|
|
424
|
+
if args.command == "install":
|
|
425
|
+
import asyncio
|
|
426
|
+
# 首先检查是否是远程模块/适配器的简称
|
|
427
|
+
remote_packages = asyncio.run(PyPIManager.get_remote_packages())
|
|
428
|
+
full_package_name = None
|
|
429
|
+
|
|
430
|
+
# 检查模块
|
|
431
|
+
if args.package in remote_packages["modules"]:
|
|
432
|
+
full_package_name = remote_packages["modules"][args.package]["package"]
|
|
433
|
+
# 检查适配器
|
|
434
|
+
elif args.package in remote_packages["adapters"]:
|
|
435
|
+
full_package_name = remote_packages["adapters"][args.package]["package"]
|
|
436
|
+
|
|
437
|
+
# 如果找到远程包,使用完整包名安装
|
|
438
|
+
if full_package_name:
|
|
439
|
+
shellprint.panel(
|
|
440
|
+
f"找到远程包: {Shell_Printer.BOLD}{args.package}{Shell_Printer.RESET} → {Shell_Printer.BLUE}{full_package_name}{Shell_Printer.RESET}",
|
|
441
|
+
"信息",
|
|
442
|
+
"info"
|
|
443
|
+
)
|
|
444
|
+
PyPIManager.install_package(full_package_name, args.upgrade)
|
|
1167
445
|
else:
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
446
|
+
# 否则按原样安装
|
|
447
|
+
PyPIManager.install_package(args.package, args.upgrade)
|
|
448
|
+
|
|
449
|
+
elif args.command == "uninstall":
|
|
450
|
+
PyPIManager.uninstall_package(args.package)
|
|
451
|
+
|
|
452
|
+
elif args.command == "list":
|
|
453
|
+
installed = PyPIManager.get_installed_packages()
|
|
454
|
+
|
|
455
|
+
if args.type in ["modules", "all"] and installed["modules"]:
|
|
456
|
+
rows = [
|
|
457
|
+
[
|
|
458
|
+
f"{Shell_Printer.GREEN}{name}{Shell_Printer.RESET}",
|
|
459
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
460
|
+
info["version"],
|
|
461
|
+
info["summary"]
|
|
462
|
+
] for name, info in installed["modules"].items()
|
|
463
|
+
]
|
|
464
|
+
shellprint.table(["模块名", "包名", "版本", "描述"], rows, "已安装模块", "info")
|
|
1175
465
|
|
|
1176
|
-
if
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
continue
|
|
1187
|
-
|
|
1188
|
-
print(f"{Shell_Printer.GREEN}找到 {len(matched_modules)} 个匹配模块:{Shell_Printer.RESET}")
|
|
1189
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
1190
|
-
print(f" {Shell_Printer.CYAN}{i}. {matched_module}{Shell_Printer.RESET}")
|
|
1191
|
-
|
|
1192
|
-
if not shellprint.confirm("是否禁用所有匹配模块?", default=True):
|
|
1193
|
-
print(f"{Shell_Printer.BLUE}操作已取消{Shell_Printer.RESET}")
|
|
1194
|
-
continue
|
|
1195
|
-
|
|
1196
|
-
for matched_module in matched_modules:
|
|
1197
|
-
disable_module(matched_module)
|
|
1198
|
-
else:
|
|
1199
|
-
disable_module(module_name)
|
|
466
|
+
if args.type in ["adapters", "all"] and installed["adapters"]:
|
|
467
|
+
rows = [
|
|
468
|
+
[
|
|
469
|
+
f"{Shell_Printer.YELLOW}{name}{Shell_Printer.RESET}",
|
|
470
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
471
|
+
info["version"],
|
|
472
|
+
info["summary"]
|
|
473
|
+
] for name, info in installed["adapters"].items()
|
|
474
|
+
]
|
|
475
|
+
shellprint.table(["适配器名", "包名", "版本", "描述"], rows, "已安装适配器", "info")
|
|
1200
476
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
elif args.command == 'uninstall':
|
|
1205
|
-
for module_name in args.module_names:
|
|
1206
|
-
module_name = module_name.strip()
|
|
1207
|
-
if not module_name:
|
|
1208
|
-
continue
|
|
477
|
+
if not installed["modules"] and not installed["adapters"]:
|
|
478
|
+
shellprint.panel("没有安装任何ErisPulse模块或适配器", "提示", "info")
|
|
1209
479
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
if not all_modules:
|
|
1214
|
-
shellprint.panel("未找到任何模块,请先更新源或检查配置", "错误", "error")
|
|
1215
|
-
continue
|
|
1216
|
-
|
|
1217
|
-
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
1218
|
-
if not matched_modules:
|
|
1219
|
-
shellprint.panel(f"未找到匹配模块模式 {module_name} 的模块", "错误", "error")
|
|
1220
|
-
continue
|
|
1221
|
-
|
|
1222
|
-
print(f"{Shell_Printer.GREEN}找到 {len(matched_modules)} 个匹配模块:{Shell_Printer.RESET}")
|
|
1223
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
1224
|
-
print(f" {Shell_Printer.CYAN}{i}. {matched_module}{Shell_Printer.RESET}")
|
|
1225
|
-
|
|
1226
|
-
if not shellprint.confirm("是否卸载所有匹配模块?", default=True):
|
|
1227
|
-
print(f"{Shell_Printer.BLUE}操作已取消{Shell_Printer.RESET}")
|
|
1228
|
-
continue
|
|
1229
|
-
|
|
1230
|
-
for matched_module in matched_modules:
|
|
1231
|
-
uninstall_module(matched_module)
|
|
1232
|
-
else:
|
|
1233
|
-
uninstall_module(module_name)
|
|
480
|
+
elif args.command == "upgrade":
|
|
481
|
+
if args.force or shellprint.confirm("确定要升级所有ErisPulse模块和适配器吗?", default=False):
|
|
482
|
+
PyPIManager.upgrade_all()
|
|
1234
483
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
484
|
+
elif args.command == "run":
|
|
485
|
+
run_script(args.script, args.reload)
|
|
486
|
+
|
|
487
|
+
elif args.command == "list-remote":
|
|
488
|
+
import asyncio
|
|
489
|
+
try:
|
|
490
|
+
remote_packages = asyncio.run(PyPIManager.get_remote_packages())
|
|
1240
491
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
shellprint.
|
|
1251
|
-
continue
|
|
492
|
+
if args.type in ["modules", "all"] and remote_packages["modules"]:
|
|
493
|
+
rows = [
|
|
494
|
+
[
|
|
495
|
+
f"{Shell_Printer.GREEN}{name}{Shell_Printer.RESET}",
|
|
496
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
497
|
+
info["version"],
|
|
498
|
+
info["description"]
|
|
499
|
+
] for name, info in remote_packages["modules"].items()
|
|
500
|
+
]
|
|
501
|
+
shellprint.table(["模块名", "包名", "版本", "描述"], rows, "远程模块", "info")
|
|
1252
502
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
503
|
+
if args.type in ["adapters", "all"] and remote_packages["adapters"]:
|
|
504
|
+
rows = [
|
|
505
|
+
[
|
|
506
|
+
f"{Shell_Printer.YELLOW}{name}{Shell_Printer.RESET}",
|
|
507
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
508
|
+
info["version"],
|
|
509
|
+
info["description"]
|
|
510
|
+
] for name, info in remote_packages["adapters"].items()
|
|
511
|
+
]
|
|
512
|
+
shellprint.table(["适配器名", "包名", "版本", "描述"], rows, "远程适配器", "info")
|
|
1256
513
|
|
|
1257
|
-
if not
|
|
1258
|
-
|
|
1259
|
-
continue
|
|
514
|
+
if not remote_packages["modules"] and not remote_packages["adapters"]:
|
|
515
|
+
shellprint.panel("没有找到远程模块或适配器", "提示", "info")
|
|
1260
516
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
else:
|
|
1264
|
-
install_module(module_name, args.force)
|
|
1265
|
-
|
|
1266
|
-
elif args.command == 'update':
|
|
1267
|
-
source_manager.update_sources()
|
|
1268
|
-
|
|
1269
|
-
elif args.command == 'upgrade':
|
|
1270
|
-
upgrade_all_modules(args.force)
|
|
1271
|
-
|
|
1272
|
-
elif args.command == 'run':
|
|
1273
|
-
script_path = args.script
|
|
1274
|
-
if not os.path.exists(script_path):
|
|
1275
|
-
shellprint.panel(f"找不到指定文件: {script_path}", "错误", "error")
|
|
1276
|
-
return
|
|
517
|
+
except Exception as e:
|
|
518
|
+
shellprint.panel(f"获取远程列表失败: {e}", "错误", "error")
|
|
1277
519
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
else:
|
|
1281
|
-
shellprint.panel(f"运行脚本: {Shell_Printer.BOLD}{script_path}{Shell_Printer.RESET}", "执行", "info")
|
|
1282
|
-
import runpy
|
|
1283
|
-
|
|
1284
|
-
# 添加KeyboardInterrupt异常捕捉
|
|
1285
|
-
try:
|
|
1286
|
-
runpy.run_path(script_path, run_name="__main__")
|
|
1287
|
-
except KeyboardInterrupt:
|
|
1288
|
-
shellprint.panel("脚本执行已中断", "中断", "info")
|
|
1289
|
-
|
|
1290
|
-
elif args.command == 'init':
|
|
1291
|
-
print(f"{Shell_Printer.GREEN}正在初始化项目...{Shell_Printer.RESET}")
|
|
1292
|
-
from . import init as init_module
|
|
1293
|
-
try:
|
|
1294
|
-
init_module()
|
|
1295
|
-
print(f"{Shell_Printer.GREEN}项目初始化完成{Shell_Printer.RESET}")
|
|
1296
|
-
except Exception as e:
|
|
1297
|
-
print(f"{Shell_Printer.RED}项目初始化失败: {e}{Shell_Printer.RESET}")
|
|
1298
|
-
|
|
1299
|
-
elif args.command == 'origin':
|
|
1300
|
-
if args.origin_command == 'add':
|
|
1301
|
-
success = source_manager.add_source(args.url)
|
|
1302
|
-
if success and shellprint.confirm("源已添加,是否立即更新源以获取最新模块信息?", default=True):
|
|
1303
|
-
source_manager.update_sources()
|
|
1304
|
-
|
|
1305
|
-
elif args.origin_command == 'list':
|
|
1306
|
-
source_manager.list_sources()
|
|
1307
|
-
|
|
1308
|
-
elif args.origin_command == 'del':
|
|
1309
|
-
source_manager.del_source(args.url)
|
|
1310
|
-
|
|
1311
|
-
else:
|
|
1312
|
-
origin_parser.print_help()
|
|
1313
|
-
else:
|
|
1314
|
-
parser.print_help()
|
|
520
|
+
except Exception as e:
|
|
521
|
+
shellprint.panel(f"执行命令时出错: {e}", "错误", "error")
|
|
1315
522
|
|
|
1316
523
|
if __name__ == "__main__":
|
|
1317
|
-
main()
|
|
524
|
+
main()
|