ErisPulse 1.2.8__py3-none-any.whl → 2.1.0__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 +619 -0
- ErisPulse/Core/env.py +614 -0
- ErisPulse/{logger.py → Core/logger.py} +1 -118
- 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 +654 -243
- ErisPulse/__main__.py +322 -1187
- {erispulse-1.2.8.dist-info → erispulse-2.1.0.dist-info}/METADATA +16 -41
- erispulse-2.1.0.dist-info/RECORD +16 -0
- {erispulse-1.2.8.dist-info → erispulse-2.1.0.dist-info}/entry_points.txt +1 -0
- {erispulse-1.2.8.dist-info → erispulse-2.1.0.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 -137
- ErisPulse/util.py +0 -144
- erispulse-1.2.8.dist-info/RECORD +0 -13
- {erispulse-1.2.8.dist-info → erispulse-2.1.0.dist-info}/WHEEL +0 -0
ErisPulse/__main__.py
CHANGED
|
@@ -1,359 +1,209 @@
|
|
|
1
1
|
"""
|
|
2
2
|
# CLI 入口
|
|
3
3
|
|
|
4
|
-
提供命令行界面(CLI)
|
|
5
|
-
|
|
6
|
-
## 主要功能
|
|
7
|
-
- 模块管理: 安装/卸载/启用/禁用
|
|
8
|
-
- 源管理: 添加/删除/更新源
|
|
9
|
-
- 热重载: 开发时自动重启
|
|
10
|
-
- 彩色终端输出
|
|
4
|
+
提供命令行界面(CLI)用于包管理和启动入口。
|
|
11
5
|
|
|
12
6
|
## 主要命令
|
|
13
|
-
###
|
|
14
|
-
|
|
15
|
-
install:
|
|
16
|
-
uninstall:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
### 源管理:
|
|
24
|
-
origin add: 添加源
|
|
25
|
-
origin del: 删除源
|
|
26
|
-
origin list: 列出源
|
|
27
|
-
|
|
28
|
-
### 开发调试:
|
|
7
|
+
### 包管理:
|
|
8
|
+
search: 搜索PyPI上的ErisPulse模块
|
|
9
|
+
install: 安装模块/适配器包
|
|
10
|
+
uninstall: 卸载模块/适配器包
|
|
11
|
+
list: 列出已安装的模块/适配器
|
|
12
|
+
list-remote: 列出远程PyPI上的ErisPulse模块和适配器
|
|
13
|
+
upgrade: 升级所有模块/适配器
|
|
14
|
+
|
|
15
|
+
### 启动:
|
|
29
16
|
run: 运行脚本
|
|
30
17
|
--reload: 启用热重载
|
|
31
18
|
|
|
32
19
|
### 示例用法:
|
|
33
|
-
|
|
34
20
|
```
|
|
35
21
|
# 安装模块
|
|
36
|
-
epsdk install
|
|
22
|
+
epsdk install Yunhu
|
|
37
23
|
|
|
38
24
|
# 启用热重载
|
|
39
25
|
epsdk run main.py --reload
|
|
40
|
-
|
|
41
|
-
# 管理源
|
|
42
|
-
epsdk origin add https://example.com/map.json
|
|
43
26
|
```
|
|
44
|
-
|
|
45
27
|
"""
|
|
46
28
|
|
|
47
29
|
import argparse
|
|
48
|
-
import importlib
|
|
49
|
-
import
|
|
30
|
+
import importlib.metadata
|
|
31
|
+
import subprocess
|
|
50
32
|
import sys
|
|
33
|
+
import os
|
|
51
34
|
import time
|
|
52
|
-
import shutil
|
|
53
|
-
import aiohttp
|
|
54
|
-
import zipfile
|
|
55
|
-
import fnmatch
|
|
56
|
-
import asyncio
|
|
57
|
-
import subprocess
|
|
58
|
-
import json
|
|
59
35
|
import json
|
|
60
|
-
|
|
61
|
-
from .
|
|
36
|
+
import asyncio
|
|
37
|
+
from urllib.parse import urlparse
|
|
38
|
+
from typing import List, Dict, Tuple, Optional
|
|
39
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
62
40
|
from watchdog.observers import Observer
|
|
63
41
|
from watchdog.events import FileSystemEventHandler
|
|
42
|
+
from .Core.shellprint import shellprint, Shell_Printer
|
|
64
43
|
|
|
65
|
-
class
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
UNDERLINE = "\033[4m"
|
|
78
|
-
BG_BLUE = "\033[44m"
|
|
79
|
-
BG_GREEN = "\033[42m"
|
|
80
|
-
BG_YELLOW = "\033[43m"
|
|
81
|
-
BG_RED = "\033[41m"
|
|
82
|
-
|
|
83
|
-
def __init__(self):
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
@classmethod
|
|
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"
|
|
146
|
-
|
|
147
|
-
print(panel)
|
|
148
|
-
|
|
149
|
-
@classmethod
|
|
150
|
-
def table(cls, headers, rows, title=None, level="info") -> None:
|
|
151
|
-
color = cls._get_color(level)
|
|
152
|
-
if title:
|
|
153
|
-
print(f"{cls.BOLD}{color}== {title} =={cls.RESET}")
|
|
154
|
-
|
|
155
|
-
col_widths = [len(h) for h in headers]
|
|
156
|
-
for row in rows:
|
|
157
|
-
for i, cell in enumerate(row):
|
|
158
|
-
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
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}")
|
|
167
|
-
|
|
168
|
-
separator = "├" + "┼".join("─" * (w+2) for w in col_widths) + "┤"
|
|
169
|
-
print(f"{cls.DIM}{separator}{cls.RESET}")
|
|
170
|
-
|
|
171
|
-
for row in rows:
|
|
172
|
-
row_line = fmt.format(*row)
|
|
173
|
-
print(f"│{row_line}│")
|
|
44
|
+
class PyPIManager:
|
|
45
|
+
"""管理PyPI上的ErisPulse模块和适配器"""
|
|
46
|
+
|
|
47
|
+
REMOTE_SOURCES = [
|
|
48
|
+
"https://erisdev.com/packages.json",
|
|
49
|
+
"https://raw.githubusercontent.com/ErisPulse/ErisPulse-ModuleRepo/main/packages.json"
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
async def get_remote_packages() -> dict:
|
|
54
|
+
import aiohttp
|
|
55
|
+
from aiohttp import ClientError, ClientTimeout
|
|
174
56
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
@classmethod
|
|
179
|
-
def progress_bar(cls, current, total, prefix="", suffix="", length=50):
|
|
180
|
-
filled_length = int(length * current // total)
|
|
181
|
-
percent = min(100.0, 100 * (current / float(total)))
|
|
182
|
-
bar = f"{cls.GREEN}{'█' * filled_length}{cls.WHITE}{'░' * (length - filled_length)}{cls.RESET}"
|
|
183
|
-
sys.stdout.write(f"\r{cls.BOLD}{prefix}{cls.RESET} {bar} {cls.BOLD}{percent:.1f}%{cls.RESET} {suffix}")
|
|
184
|
-
sys.stdout.flush()
|
|
185
|
-
if current == total:
|
|
186
|
-
print()
|
|
187
|
-
|
|
188
|
-
@classmethod
|
|
189
|
-
def confirm(cls, msg, default=False) -> bool:
|
|
190
|
-
yes_options = {'y', 'yes'}
|
|
191
|
-
no_options = {'n', 'no'}
|
|
192
|
-
default_str = "Y/n" if default else "y/N"
|
|
193
|
-
prompt = f"{cls.BOLD}{msg}{cls.RESET} [{cls.CYAN}{default_str}{cls.RESET}]: "
|
|
57
|
+
timeout = ClientTimeout(total=5)
|
|
58
|
+
last_error = None
|
|
194
59
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
prompt = f"{cls.BOLD}{msg}{cls.RESET}"
|
|
208
|
-
if choices:
|
|
209
|
-
prompt += f" ({cls.CYAN}{'/'.join(choices)}{cls.RESET})"
|
|
210
|
-
if default:
|
|
211
|
-
prompt += f" [{cls.BLUE}默认: {default}{cls.RESET}]"
|
|
212
|
-
prompt += ": "
|
|
60
|
+
try:
|
|
61
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
62
|
+
async with session.get(PyPIManager.REMOTE_SOURCES[0]) as response:
|
|
63
|
+
if response.status == 200:
|
|
64
|
+
data = await response.json()
|
|
65
|
+
return {
|
|
66
|
+
"modules": data.get("modules", {}),
|
|
67
|
+
"adapters": data.get("adapters", {})
|
|
68
|
+
}
|
|
69
|
+
except (ClientError, asyncio.TimeoutError) as e:
|
|
70
|
+
last_error = e
|
|
71
|
+
shellprint.panel(f"官方源请求失败,尝试备用源: {e}", "警告", "warning")
|
|
213
72
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
print(f"\r{symbol}{cls.RESET} {msg}")
|
|
226
|
-
|
|
227
|
-
shellprint = Shell_Printer()
|
|
228
|
-
|
|
229
|
-
class SourceManager:
|
|
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"
|
|
73
|
+
try:
|
|
74
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
75
|
+
async with session.get(PyPIManager.REMOTE_SOURCES[1]) as response:
|
|
76
|
+
if response.status == 200:
|
|
77
|
+
data = await response.json()
|
|
78
|
+
return {
|
|
79
|
+
"modules": data.get("modules", {}),
|
|
80
|
+
"adapters": data.get("adapters", {})
|
|
81
|
+
}
|
|
82
|
+
except (ClientError, asyncio.TimeoutError) as e:
|
|
83
|
+
last_error = e
|
|
239
84
|
|
|
240
|
-
|
|
241
|
-
|
|
85
|
+
if last_error:
|
|
86
|
+
shellprint.panel(f"获取远程模块列表失败: {last_error}", "错误", "error")
|
|
87
|
+
return {"modules": {}, "adapters": {}}
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def search_packages(query: str) -> List[Dict[str, str]]:
|
|
91
|
+
try:
|
|
92
|
+
result = subprocess.run(
|
|
93
|
+
[sys.executable, "-m", "pip", "search", query],
|
|
94
|
+
capture_output=True,
|
|
95
|
+
text=True
|
|
96
|
+
)
|
|
242
97
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
98
|
+
packages = []
|
|
99
|
+
for line in result.stdout.split('\n'):
|
|
100
|
+
if "ErisPulse-" in line:
|
|
101
|
+
parts = line.split(' ', 1)
|
|
102
|
+
if len(parts) >= 1:
|
|
103
|
+
name = parts[0].strip()
|
|
104
|
+
desc = parts[1].strip() if len(parts) > 1 else ""
|
|
105
|
+
packages.append({"name": name, "description": desc})
|
|
106
|
+
return packages
|
|
107
|
+
except Exception as e:
|
|
108
|
+
shellprint.panel(f"搜索PyPI包失败: {e}", "错误", "error")
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def get_installed_packages() -> Dict[str, Dict[str, str]]:
|
|
113
|
+
packages = {
|
|
114
|
+
"modules": {},
|
|
115
|
+
"adapters": {}
|
|
116
|
+
}
|
|
117
|
+
|
|
262
118
|
try:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
119
|
+
# 查找模块
|
|
120
|
+
for dist in importlib.metadata.distributions():
|
|
121
|
+
if "ErisPulse-" in dist.metadata["Name"]:
|
|
122
|
+
entry_points = dist.entry_points
|
|
123
|
+
for ep in entry_points:
|
|
124
|
+
if ep.group == "erispulse.module":
|
|
125
|
+
packages["modules"][ep.name] = {
|
|
126
|
+
"package": dist.metadata["Name"],
|
|
127
|
+
"version": dist.version,
|
|
128
|
+
"summary": dist.metadata["Summary"]
|
|
129
|
+
}
|
|
130
|
+
elif ep.group == "erispulse.adapter":
|
|
131
|
+
packages["adapters"][ep.name] = {
|
|
132
|
+
"package": dist.metadata["Name"],
|
|
133
|
+
"version": dist.version,
|
|
134
|
+
"summary": dist.metadata["Summary"]
|
|
135
|
+
}
|
|
273
136
|
except Exception as e:
|
|
274
|
-
shellprint.panel(f"
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
137
|
+
shellprint.panel(f"获取已安装包信息失败: {e}", "错误", "error")
|
|
138
|
+
|
|
139
|
+
return packages
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def install_package(package_name: str, upgrade: bool = False) -> bool:
|
|
143
|
+
try:
|
|
144
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
|
145
|
+
if upgrade:
|
|
146
|
+
cmd.append("--upgrade")
|
|
147
|
+
cmd.append(package_name)
|
|
148
|
+
|
|
149
|
+
shellprint.status(f"正在安装 {package_name}...")
|
|
150
|
+
result = subprocess.run(cmd, check=True)
|
|
151
|
+
if result.returncode == 0:
|
|
152
|
+
shellprint.panel(f"包 {package_name} 安装成功", "成功", "success")
|
|
153
|
+
return True
|
|
282
154
|
return False
|
|
155
|
+
except subprocess.CalledProcessError as e:
|
|
156
|
+
shellprint.panel(f"安装包 {package_name} 失败: {e}", "错误", "error")
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
def uninstall_package(package_name: str) -> bool:
|
|
161
|
+
try:
|
|
162
|
+
shellprint.status(f"正在卸载 {package_name}...")
|
|
163
|
+
result = subprocess.run(
|
|
164
|
+
[sys.executable, "-m", "pip", "uninstall", "-y", package_name],
|
|
165
|
+
check=True
|
|
166
|
+
)
|
|
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 upgrade_all() -> bool:
|
|
177
|
+
try:
|
|
178
|
+
installed = PyPIManager.get_installed_packages()
|
|
179
|
+
all_packages = set()
|
|
180
|
+
|
|
181
|
+
for pkg_type in ["modules", "adapters"]:
|
|
182
|
+
for pkg_info in installed[pkg_type].values():
|
|
183
|
+
all_packages.add(pkg_info["package"])
|
|
283
184
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
shellprint.panel(
|
|
185
|
+
if not all_packages:
|
|
186
|
+
shellprint.panel("没有找到可升级的ErisPulse包", "提示", "info")
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
shellprint.panel(
|
|
190
|
+
f"找到 {len(all_packages)} 个可升级的包:\n" +
|
|
191
|
+
"\n".join(f" - {pkg}" for pkg in all_packages),
|
|
192
|
+
"升级列表",
|
|
193
|
+
"info"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if not shellprint.confirm("确认升级所有包吗?", default=False):
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
for pkg in all_packages:
|
|
200
|
+
PyPIManager.install_package(pkg, upgrade=True)
|
|
201
|
+
|
|
289
202
|
return True
|
|
290
|
-
|
|
291
|
-
shellprint.panel(f"
|
|
203
|
+
except Exception as e:
|
|
204
|
+
shellprint.panel(f"升级包失败: {e}", "错误", "error")
|
|
292
205
|
return False
|
|
293
206
|
|
|
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
207
|
class ReloadHandler(FileSystemEventHandler):
|
|
358
208
|
def __init__(self, script_path, *args, **kwargs):
|
|
359
209
|
super().__init__(*args, **kwargs)
|
|
@@ -373,7 +223,6 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
373
223
|
|
|
374
224
|
def on_modified(self, event):
|
|
375
225
|
now = time.time()
|
|
376
|
-
# 1秒后再次触发
|
|
377
226
|
if now - self.last_reload < 1.0:
|
|
378
227
|
return
|
|
379
228
|
|
|
@@ -407,661 +256,37 @@ def start_reloader(script_path):
|
|
|
407
256
|
handler.process.terminate()
|
|
408
257
|
observer.join()
|
|
409
258
|
|
|
410
|
-
def
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
mods.set_module_status(module_name, True)
|
|
415
|
-
shellprint.panel(f"模块 {module_name} 已成功启用", "成功", "success")
|
|
416
|
-
else:
|
|
417
|
-
shellprint.panel(f"模块 {module_name} 不存在", "错误", "error")
|
|
259
|
+
def run_script(script_path: str, reload: bool = False):
|
|
260
|
+
if not os.path.exists(script_path):
|
|
261
|
+
shellprint.panel(f"找不到指定文件: {script_path}", "错误", "error")
|
|
262
|
+
return
|
|
418
263
|
|
|
419
|
-
|
|
420
|
-
|
|
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")
|
|
264
|
+
if reload:
|
|
265
|
+
start_reloader(script_path)
|
|
425
266
|
else:
|
|
426
|
-
shellprint.panel(f"
|
|
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))
|
|
267
|
+
shellprint.panel(f"运行脚本: {Shell_Printer.BOLD}{script_path}{Shell_Printer.RESET}", "执行", "info")
|
|
268
|
+
import runpy
|
|
569
269
|
try:
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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
|
|
590
|
-
|
|
591
|
-
# 复制模块到modules目录
|
|
592
|
-
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
593
|
-
target_dir = os.path.join(script_dir, 'modules', module_name)
|
|
594
|
-
|
|
270
|
+
runpy.run_path(script_path, run_name="__main__")
|
|
271
|
+
except KeyboardInterrupt:
|
|
272
|
+
shellprint.panel("脚本执行已中断", "中断", "info")
|
|
273
|
+
def get_erispulse_version():
|
|
595
274
|
try:
|
|
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
|
-
):
|
|
739
|
-
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
|
-
|
|
761
|
-
def uninstall_module(module_name):
|
|
762
|
-
shellprint.panel(f"准备卸载模块: {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}", "卸载摘要", "warning")
|
|
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")
|
|
850
|
-
else:
|
|
851
|
-
shellprint.panel(f"模块 {module_name} 不存在", "错误", "error")
|
|
852
|
-
|
|
853
|
-
def upgrade_all_modules(force=False):
|
|
854
|
-
all_modules = mods.get_all_modules()
|
|
855
|
-
if not all_modules:
|
|
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")
|
|
899
|
-
|
|
900
|
-
if not force:
|
|
901
|
-
warning_msg = (
|
|
902
|
-
f"{Shell_Printer.BOLD}{Shell_Printer.RED}警告:{Shell_Printer.RESET} "
|
|
903
|
-
"更新模块可能会导致兼容性问题,请在更新前查看插件作者的相关声明。\n"
|
|
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")
|
|
980
|
-
|
|
981
|
-
def list_modules(module_name=None):
|
|
982
|
-
all_modules = mods.get_all_modules()
|
|
983
|
-
if not all_modules:
|
|
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")
|
|
994
|
-
|
|
995
|
-
rows = []
|
|
996
|
-
for name, info in all_modules.items():
|
|
997
|
-
# 根据状态设置颜色
|
|
998
|
-
status_color = Shell_Printer.GREEN if info.get("status", True) else Shell_Printer.RED
|
|
999
|
-
status = f"{status_color}✓" if info.get("status", True) else f"{Shell_Printer.RED}✗"
|
|
1000
|
-
|
|
1001
|
-
meta = info.get('info', {}).get('meta', {})
|
|
1002
|
-
depsinfo = info.get('info', {}).get('dependencies', {})
|
|
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
|
-
)
|
|
1047
|
-
|
|
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
|
-
)
|
|
275
|
+
return version("ErisPulse")
|
|
276
|
+
except PackageNotFoundError:
|
|
277
|
+
return "unknown version"
|
|
1056
278
|
|
|
1057
279
|
def main():
|
|
1058
280
|
parser = argparse.ArgumentParser(
|
|
1059
281
|
prog="epsdk",
|
|
1060
|
-
formatter_class=argparse.RawTextHelpFormatter
|
|
282
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
283
|
+
description=f"{Shell_Printer.BOLD}ErisPulse SDK 命令行工具 {get_erispulse_version()}{Shell_Printer.RESET}"
|
|
1061
284
|
)
|
|
1062
285
|
parser._positionals.title = f"{Shell_Printer.BOLD}{Shell_Printer.CYAN}基本命令{Shell_Printer.RESET}"
|
|
1063
286
|
parser._optionals.title = f"{Shell_Printer.BOLD}{Shell_Printer.MAGENTA}可选参数{Shell_Printer.RESET}"
|
|
1064
287
|
|
|
288
|
+
parser.add_argument("--version", action="store_true", help="显示版本信息并退出")
|
|
289
|
+
|
|
1065
290
|
subparsers = parser.add_subparsers(
|
|
1066
291
|
dest='command',
|
|
1067
292
|
title='可用的命令',
|
|
@@ -1069,249 +294,159 @@ def main():
|
|
|
1069
294
|
help='具体命令的帮助信息'
|
|
1070
295
|
)
|
|
1071
296
|
|
|
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
|
|
297
|
+
# 搜索命令
|
|
298
|
+
search_parser = subparsers.add_parser('search', help='搜索PyPI上的ErisPulse模块')
|
|
299
|
+
search_parser.add_argument('query', type=str, help='搜索关键词')
|
|
1093
300
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
install_parser =
|
|
301
|
+
# 安装命令
|
|
302
|
+
install_parser = subparsers.add_parser('install', help='安装模块/适配器包')
|
|
303
|
+
install_parser.add_argument('package', type=str, help='要安装的包名')
|
|
304
|
+
install_parser.add_argument('--upgrade', '-U', action='store_true', help='升级已安装的包')
|
|
1098
305
|
|
|
1099
|
-
#
|
|
1100
|
-
|
|
1101
|
-
|
|
306
|
+
# 卸载命令
|
|
307
|
+
uninstall_parser = subparsers.add_parser('uninstall', help='卸载模块/适配器包')
|
|
308
|
+
uninstall_parser.add_argument('package', type=str, help='要卸载的包名')
|
|
1102
309
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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
|
-
)
|
|
310
|
+
# 列表命令
|
|
311
|
+
list_parser = subparsers.add_parser('list', help='列出已安装的模块/适配器')
|
|
312
|
+
list_parser.add_argument('--type', '-t', choices=['modules', 'adapters', 'all'], default='all',
|
|
313
|
+
help='列出类型 (modules: 仅模块, adapters: 仅适配器, all: 全部)')
|
|
1117
314
|
|
|
1118
|
-
|
|
1119
|
-
|
|
315
|
+
# 远程列表命令
|
|
316
|
+
list_remote_parser = subparsers.add_parser('list-remote', help='列出远程可用的模块和适配器')
|
|
317
|
+
list_remote_parser.add_argument('--type', '-t', choices=['modules', 'adapters', 'all'], default='all',
|
|
318
|
+
help='列出类型 (modules: 仅模块, adapters: 仅适配器, all: 全部)')
|
|
319
|
+
# 升级命令
|
|
320
|
+
upgrade_parser = subparsers.add_parser('upgrade', help='升级所有模块/适配器')
|
|
321
|
+
upgrade_parser.add_argument('--force', '-f', action='store_true', help='跳过确认直接升级')
|
|
1120
322
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
del_origin_parser = origin_subparsers.add_parser('del', help='删除模块源')
|
|
1124
|
-
del_origin_parser.add_argument('url', type=str, help='要删除的模块源URL')
|
|
1125
|
-
|
|
323
|
+
# 运行命令
|
|
1126
324
|
run_parser = subparsers.add_parser('run', help='运行指定主程序')
|
|
1127
325
|
run_parser.add_argument('script', type=str, help='要运行的主程序路径')
|
|
1128
|
-
run_parser.add_argument('--reload', action='store_true', help='
|
|
1129
|
-
|
|
326
|
+
run_parser.add_argument('--reload', action='store_true', help='启用热重载模式')
|
|
327
|
+
|
|
1130
328
|
args = parser.parse_args()
|
|
1131
329
|
|
|
1132
|
-
if
|
|
1133
|
-
print(f"{Shell_Printer.GREEN}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
330
|
+
if args.version:
|
|
331
|
+
print(f"{Shell_Printer.GREEN}ErisPulse {get_erispulse_version()}{Shell_Printer.RESET}")
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
if not args.command:
|
|
335
|
+
parser.print_help()
|
|
336
|
+
return
|
|
1137
337
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
shellprint.panel("未找到任何模块,请先更新源或检查配置", "错误", "error")
|
|
1150
|
-
continue
|
|
1151
|
-
|
|
1152
|
-
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
1153
|
-
if not matched_modules:
|
|
1154
|
-
shellprint.panel(f"未找到匹配模块模式 {module_name} 的模块", "错误", "error")
|
|
1155
|
-
continue
|
|
1156
|
-
|
|
1157
|
-
print(f"{Shell_Printer.GREEN}找到 {len(matched_modules)} 个匹配模块:{Shell_Printer.RESET}")
|
|
1158
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
1159
|
-
print(f" {Shell_Printer.CYAN}{i}. {matched_module}{Shell_Printer.RESET}")
|
|
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)
|
|
338
|
+
try:
|
|
339
|
+
if args.command == "search":
|
|
340
|
+
packages = PyPIManager.search_packages(args.query)
|
|
341
|
+
if packages:
|
|
342
|
+
rows = [
|
|
343
|
+
[
|
|
344
|
+
f"{Shell_Printer.BLUE}{pkg['name']}{Shell_Printer.RESET}",
|
|
345
|
+
pkg['description']
|
|
346
|
+
] for pkg in packages
|
|
347
|
+
]
|
|
348
|
+
shellprint.table(["包名", "描述"], rows, "搜索结果", "info")
|
|
1167
349
|
else:
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
elif args.command == 'disable':
|
|
1171
|
-
for module_name in args.module_names:
|
|
1172
|
-
module_name = module_name.strip()
|
|
1173
|
-
if not module_name:
|
|
1174
|
-
continue
|
|
350
|
+
shellprint.panel(f"未找到匹配 '{args.query}' 的ErisPulse包", "提示", "info")
|
|
1175
351
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
disable_module(matched_module)
|
|
352
|
+
elif args.command == "install":
|
|
353
|
+
import asyncio
|
|
354
|
+
# 首先检查是否是远程模块/适配器的简称
|
|
355
|
+
remote_packages = asyncio.run(PyPIManager.get_remote_packages())
|
|
356
|
+
full_package_name = None
|
|
357
|
+
|
|
358
|
+
# 检查模块
|
|
359
|
+
if args.package in remote_packages["modules"]:
|
|
360
|
+
full_package_name = remote_packages["modules"][args.package]["package"]
|
|
361
|
+
# 检查适配器
|
|
362
|
+
elif args.package in remote_packages["adapters"]:
|
|
363
|
+
full_package_name = remote_packages["adapters"][args.package]["package"]
|
|
364
|
+
|
|
365
|
+
# 如果找到远程包,使用完整包名安装
|
|
366
|
+
if full_package_name:
|
|
367
|
+
shellprint.panel(
|
|
368
|
+
f"找到远程包: {Shell_Printer.BOLD}{args.package}{Shell_Printer.RESET} → {Shell_Printer.BLUE}{full_package_name}{Shell_Printer.RESET}",
|
|
369
|
+
"信息",
|
|
370
|
+
"info"
|
|
371
|
+
)
|
|
372
|
+
PyPIManager.install_package(full_package_name, args.upgrade)
|
|
1198
373
|
else:
|
|
1199
|
-
|
|
374
|
+
# 否则按原样安装
|
|
375
|
+
PyPIManager.install_package(args.package, args.upgrade)
|
|
376
|
+
|
|
377
|
+
elif args.command == "uninstall":
|
|
378
|
+
PyPIManager.uninstall_package(args.package)
|
|
379
|
+
|
|
380
|
+
elif args.command == "list":
|
|
381
|
+
installed = PyPIManager.get_installed_packages()
|
|
382
|
+
|
|
383
|
+
if args.type in ["modules", "all"] and installed["modules"]:
|
|
384
|
+
rows = [
|
|
385
|
+
[
|
|
386
|
+
f"{Shell_Printer.GREEN}{name}{Shell_Printer.RESET}",
|
|
387
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
388
|
+
info["version"],
|
|
389
|
+
info["summary"]
|
|
390
|
+
] for name, info in installed["modules"].items()
|
|
391
|
+
]
|
|
392
|
+
shellprint.table(["模块名", "包名", "版本", "描述"], rows, "已安装模块", "info")
|
|
1200
393
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
394
|
+
if args.type in ["adapters", "all"] and installed["adapters"]:
|
|
395
|
+
rows = [
|
|
396
|
+
[
|
|
397
|
+
f"{Shell_Printer.YELLOW}{name}{Shell_Printer.RESET}",
|
|
398
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
399
|
+
info["version"],
|
|
400
|
+
info["summary"]
|
|
401
|
+
] for name, info in installed["adapters"].items()
|
|
402
|
+
]
|
|
403
|
+
shellprint.table(["适配器名", "包名", "版本", "描述"], rows, "已安装适配器", "info")
|
|
1209
404
|
|
|
1210
|
-
if
|
|
1211
|
-
shellprint.
|
|
1212
|
-
all_modules = mods.get_all_modules()
|
|
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)
|
|
405
|
+
if not installed["modules"] and not installed["adapters"]:
|
|
406
|
+
shellprint.panel("没有安装任何ErisPulse模块或适配器", "提示", "info")
|
|
1234
407
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
if not module_name:
|
|
1239
|
-
continue
|
|
408
|
+
elif args.command == "upgrade":
|
|
409
|
+
if args.force or shellprint.confirm("确定要升级所有ErisPulse模块和适配器吗?", default=False):
|
|
410
|
+
PyPIManager.upgrade_all()
|
|
1240
411
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
if
|
|
1250
|
-
|
|
1251
|
-
|
|
412
|
+
elif args.command == "run":
|
|
413
|
+
run_script(args.script, args.reload)
|
|
414
|
+
|
|
415
|
+
elif args.command == "list-remote":
|
|
416
|
+
import asyncio
|
|
417
|
+
try:
|
|
418
|
+
remote_packages = asyncio.run(PyPIManager.get_remote_packages())
|
|
419
|
+
|
|
420
|
+
if args.type in ["modules", "all"] and remote_packages["modules"]:
|
|
421
|
+
rows = [
|
|
422
|
+
[
|
|
423
|
+
f"{Shell_Printer.GREEN}{name}{Shell_Printer.RESET}",
|
|
424
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
425
|
+
info["version"],
|
|
426
|
+
info["description"]
|
|
427
|
+
] for name, info in remote_packages["modules"].items()
|
|
428
|
+
]
|
|
429
|
+
shellprint.table(["模块名", "包名", "版本", "描述"], rows, "远程模块", "info")
|
|
1252
430
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
431
|
+
if args.type in ["adapters", "all"] and remote_packages["adapters"]:
|
|
432
|
+
rows = [
|
|
433
|
+
[
|
|
434
|
+
f"{Shell_Printer.YELLOW}{name}{Shell_Printer.RESET}",
|
|
435
|
+
f"{Shell_Printer.BLUE}{info['package']}{Shell_Printer.RESET}",
|
|
436
|
+
info["version"],
|
|
437
|
+
info["description"]
|
|
438
|
+
] for name, info in remote_packages["adapters"].items()
|
|
439
|
+
]
|
|
440
|
+
shellprint.table(["适配器名", "包名", "版本", "描述"], rows, "远程适配器", "info")
|
|
1256
441
|
|
|
1257
|
-
if not
|
|
1258
|
-
|
|
1259
|
-
continue
|
|
442
|
+
if not remote_packages["modules"] and not remote_packages["adapters"]:
|
|
443
|
+
shellprint.panel("没有找到远程模块或适配器", "提示", "info")
|
|
1260
444
|
|
|
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
|
|
445
|
+
except Exception as e:
|
|
446
|
+
shellprint.panel(f"获取远程列表失败: {e}", "错误", "error")
|
|
1277
447
|
|
|
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()
|
|
448
|
+
except Exception as e:
|
|
449
|
+
shellprint.panel(f"执行命令时出错: {e}", "错误", "error")
|
|
1315
450
|
|
|
1316
451
|
if __name__ == "__main__":
|
|
1317
|
-
main()
|
|
452
|
+
main()
|