ErisPulse 2.2.1.dev0__py3-none-any.whl → 2.3.0.dev5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. ErisPulse/Core/Bases/__init__.py +14 -0
  2. ErisPulse/Core/Bases/adapter.py +196 -0
  3. ErisPulse/Core/Bases/module.py +54 -0
  4. ErisPulse/Core/Event/__init__.py +14 -0
  5. ErisPulse/Core/Event/base.py +15 -2
  6. ErisPulse/Core/Event/command.py +21 -2
  7. ErisPulse/Core/Event/message.py +15 -0
  8. ErisPulse/Core/Event/meta.py +15 -0
  9. ErisPulse/Core/Event/notice.py +15 -0
  10. ErisPulse/Core/Event/request.py +16 -1
  11. ErisPulse/Core/__init__.py +38 -19
  12. ErisPulse/Core/{erispulse_config.py → _self_config.py} +16 -3
  13. ErisPulse/Core/adapter.py +374 -377
  14. ErisPulse/Core/config.py +137 -38
  15. ErisPulse/Core/exceptions.py +6 -1
  16. ErisPulse/Core/lifecycle.py +167 -0
  17. ErisPulse/Core/logger.py +97 -49
  18. ErisPulse/Core/module.py +279 -56
  19. ErisPulse/Core/router.py +112 -23
  20. ErisPulse/Core/storage.py +258 -77
  21. ErisPulse/Core/ux.py +664 -0
  22. ErisPulse/__init__.py +587 -242
  23. ErisPulse/__main__.py +1 -1999
  24. ErisPulse/utils/__init__.py +15 -0
  25. ErisPulse/utils/cli.py +1102 -0
  26. ErisPulse/utils/package_manager.py +847 -0
  27. ErisPulse/utils/reload_handler.py +135 -0
  28. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/METADATA +24 -6
  29. erispulse-2.3.0.dev5.dist-info/RECORD +33 -0
  30. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/WHEEL +1 -1
  31. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/licenses/LICENSE +1 -1
  32. ErisPulse/Core/env.py +0 -15
  33. ErisPulse/Core/module_registry.py +0 -227
  34. erispulse-2.2.1.dev0.dist-info/RECORD +0 -26
  35. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/entry_points.txt +0 -0
ErisPulse/Core/ux.py ADDED
@@ -0,0 +1,664 @@
1
+ """
2
+ ErisPulse UX优化模块
3
+
4
+ 提供更友好的初始化和API接口,简化常用操作
5
+ """
6
+
7
+ import json
8
+ import asyncio
9
+ from typing import List, Dict
10
+ from pathlib import Path
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+ from rich.panel import Panel
14
+ from rich.prompt import Confirm
15
+
16
+ class UXManager:
17
+ """
18
+ UX优化管理器
19
+
20
+ 提供用户友好的界面和简化操作
21
+ """
22
+
23
+ def __init__(self):
24
+ self.console = Console()
25
+ self._adapter_cache = None
26
+ self._adapter_cache_time = 0
27
+ self._cache_duration = 300 # 缓存5分钟
28
+
29
+ async def _fetch_available_adapters(self) -> Dict[str, str]:
30
+ """
31
+ 从云端获取可用适配器列表
32
+
33
+ :return: 适配器名称到描述的映射
34
+ """
35
+ # 检查缓存是否有效
36
+ current_time = asyncio.get_event_loop().time()
37
+ if self._adapter_cache and (current_time - self._adapter_cache_time) < self._cache_duration:
38
+ return self._adapter_cache
39
+
40
+ try:
41
+ # 使用与 PackageManager 相同的机制获取远程包列表
42
+ from ..utils.package_manager import PackageManager
43
+ package_manager = PackageManager()
44
+ remote_packages = await package_manager.get_remote_packages()
45
+
46
+ adapters = {}
47
+ for name, info in remote_packages.get("adapters", {}).items():
48
+ adapters[name] = info.get("description", "")
49
+
50
+ if adapters:
51
+ # 更新缓存
52
+ self._adapter_cache = adapters
53
+ self._adapter_cache_time = current_time
54
+ return adapters
55
+ else:
56
+ self.console.print("[yellow]从远程源获取的适配器列表为空[/yellow]")
57
+ except Exception as e:
58
+ self.console.print(f"[red]从远程源获取适配器列表时出错: {e}[/red]")
59
+
60
+ # 如果云端请求失败,返回默认适配器列表
61
+ self.console.print("[yellow]使用默认适配器列表[/yellow]")
62
+ return {
63
+ "yunhu": "云湖平台适配器",
64
+ "telegram": "Telegram机器人适配器",
65
+ "onebot11": "OneBot11标准适配器",
66
+ "email": "邮件适配器"
67
+ }
68
+
69
+ def welcome(self, version: str = None) -> None:
70
+ """
71
+ 显示欢迎信息
72
+
73
+ :param version: 框架版本号
74
+ """
75
+ version_text = f" v{version}" if version else ""
76
+ welcome_text = f"[bold blue]欢迎使用 ErisPulse{version_text}[/bold blue]\n"
77
+ welcome_text += "[dim]异步机器人开发框架,让开发更简单[/dim]"
78
+
79
+ panel = Panel(
80
+ welcome_text,
81
+ border_style="blue",
82
+ padding=(1, 2)
83
+ )
84
+ self.console.print(panel)
85
+
86
+ def show_status(self) -> None:
87
+ """
88
+ 显示系统状态概览
89
+ """
90
+ table = Table(title="系统状态概览")
91
+ table.add_column("组件", style="cyan", no_wrap=True)
92
+ table.add_column("状态", style="magenta")
93
+ table.add_column("详情", style="green")
94
+
95
+ # 框架核心状态
96
+ table.add_row("框架核心", "[green]运行中[/green]", "ErisPulse SDK 已加载")
97
+
98
+ # 模块状态
99
+ try:
100
+ from .. import sdk
101
+ loaded_modules = sdk.module.list_loaded()
102
+ table.add_row(
103
+ "模块系统",
104
+ "[green]运行中[/green]" if loaded_modules else "[yellow]空闲[/yellow]",
105
+ f"已加载 {len(loaded_modules)} 个模块"
106
+ )
107
+ except Exception:
108
+ table.add_row("模块系统", "[red]错误[/red]", "无法获取模块状态")
109
+
110
+ # 适配器状态
111
+ try:
112
+ from .. import sdk
113
+ adapter_list = sdk.adapter.list_adapters()
114
+ enabled_count = sum(1 for status in adapter_list.values() if status)
115
+ table.add_row(
116
+ "适配器系统",
117
+ "[green]运行中[/green]" if adapter_list else "[yellow]空闲[/yellow]",
118
+ f"已注册 {len(adapter_list)} 个适配器,{enabled_count} 个已启用"
119
+ )
120
+ except Exception:
121
+ table.add_row("适配器系统", "[red]错误[/red]", "无法获取适配器状态")
122
+
123
+ self.console.print(table)
124
+
125
+ def list_modules(self, detailed: bool = False) -> None:
126
+ """
127
+ 列出所有模块状态
128
+
129
+ :param detailed: 是否显示详细信息
130
+ """
131
+ try:
132
+ from .. import sdk
133
+ modules = sdk.module.list_modules()
134
+
135
+ if not modules:
136
+ self.console.print("[yellow]没有找到任何模块[/yellow]")
137
+ return
138
+
139
+ table = Table(title="模块状态")
140
+ table.add_column("模块名", style="cyan", no_wrap=True)
141
+ table.add_column("状态", style="magenta")
142
+ table.add_column("是否已加载", style="green")
143
+
144
+ for module_name, enabled in modules.items():
145
+ loaded = sdk.module.is_loaded(module_name)
146
+ status = "[green]启用[/green]" if enabled else "[red]禁用[/red]"
147
+ is_loaded = "[green]已加载[/green]" if loaded else "[red]未加载[/red]"
148
+
149
+ table.add_row(module_name, status, is_loaded)
150
+
151
+ self.console.print(table)
152
+
153
+ if detailed:
154
+ loaded_modules = sdk.module.list_loaded()
155
+ if loaded_modules:
156
+ self.console.print("\n[bold]已加载模块详情:[/bold]")
157
+ for module_name in loaded_modules:
158
+ try:
159
+ module_instance = sdk.module.get(module_name)
160
+ info = getattr(module_instance, "moduleInfo", {})
161
+ if info:
162
+ self.console.print(f"[cyan]{module_name}:[/cyan] {json.dumps(info, indent=2, ensure_ascii=False)}")
163
+ except Exception as e:
164
+ self.console.print(f"[cyan]{module_name}:[/cyan] [red]获取信息失败: {e}[/red]")
165
+
166
+ except Exception as e:
167
+ self.console.print(f"[red]获取模块列表失败: {e}[/red]")
168
+
169
+ def list_adapters(self, detailed: bool = False) -> None:
170
+ """
171
+ 列出所有适配器状态
172
+
173
+ :param detailed: 是否显示详细信息
174
+ """
175
+ try:
176
+ from .. import sdk
177
+ adapters = sdk.adapter.list_adapters()
178
+
179
+ if not adapters:
180
+ self.console.print("[yellow]没有找到任何适配器[/yellow]")
181
+ return
182
+
183
+ table = Table(title="适配器状态")
184
+ table.add_column("适配器名", style="cyan", no_wrap=True)
185
+ table.add_column("状态", style="magenta")
186
+
187
+ for adapter_name, enabled in adapters.items():
188
+ status = "[green]启用[/green]" if enabled else "[red]禁用[/red]"
189
+ table.add_row(adapter_name, status)
190
+
191
+ self.console.print(table)
192
+
193
+ if detailed:
194
+ self.console.print("\n[bold]已注册适配器详情:[/bold]")
195
+ for adapter_name in adapters:
196
+ try:
197
+ adapter_instance = sdk.adapter.get(adapter_name)
198
+ if adapter_instance:
199
+ info = getattr(adapter_instance, "_adapter_info", {})
200
+ if info:
201
+ self.console.print(f"[cyan]{adapter_name}:[/cyan] {json.dumps(info, indent=2, ensure_ascii=False)}")
202
+ except Exception as e:
203
+ self.console.print(f"[cyan]{adapter_name}:[/cyan] [red]获取信息失败: {e}[/red]")
204
+
205
+ except Exception as e:
206
+ self.console.print(f"[red]获取适配器列表失败: {e}[/red]")
207
+
208
+
209
+ def init_project(self, project_name: str, adapter_list: List[str] = None) -> bool:
210
+ """
211
+ 初始化新项目
212
+
213
+ :param project_name: 项目名称
214
+ :param adapter_list: 需要初始化的适配器列表
215
+ :return: 是否初始化成功
216
+ """
217
+ try:
218
+ project_path = Path(project_name)
219
+ if project_path.exists():
220
+ if project_path.is_dir():
221
+ self.console.print(f"[yellow]目录 {project_name} 已存在[/yellow]")
222
+ else:
223
+ self.console.print(f"[red]文件 {project_name} 已存在且不是目录[/red]")
224
+ return False
225
+ else:
226
+ project_path.mkdir()
227
+ self.console.print(f"[green]创建项目目录: {project_name}[/green]")
228
+
229
+ # 创建基本目录结构
230
+ dirs = ["modules", "config", "logs"]
231
+ for dir_name in dirs:
232
+ dir_path = project_path / dir_name
233
+ dir_path.mkdir(exist_ok=True)
234
+ self.console.print(f"[green]创建目录: {dir_name}[/green]")
235
+
236
+ # 创建配置文件
237
+ config_file = project_path / "config.toml"
238
+ if not config_file.exists():
239
+ with open(config_file, "w", encoding="utf-8") as f:
240
+ f.write("# ErisPulse 配置文件\n\n")
241
+ f.write("[ErisPulse]\n")
242
+ f.write("# 全局配置\n\n")
243
+ f.write("[ErisPulse.server]\n")
244
+ f.write('host = "0.0.0.0"\n')
245
+ f.write("port = 8000\n\n")
246
+ f.write("[ErisPulse.logger]\n")
247
+ f.write('level = "INFO"\n')
248
+ f.write("log_files = [\"logs/app.log\"]\n")
249
+ f.write("memory_limit = 1000\n\n")
250
+
251
+ # 添加适配器配置
252
+ if adapter_list:
253
+ f.write("[ErisPulse.adapters]\n")
254
+ f.write("# 适配器配置\n\n")
255
+ f.write("[ErisPulse.adapters.status]\n")
256
+ for adapter in adapter_list:
257
+ f.write(f'{adapter} = false # 默认禁用,需要时启用\n')
258
+ f.write("\n")
259
+
260
+ self.console.print("[green]创建配置文件: config.toml[/green]")
261
+
262
+ # 创建主程序文件
263
+ main_file = project_path / "main.py"
264
+ if not main_file.exists():
265
+ with open(main_file, "w", encoding="utf-8") as f:
266
+ f.write('"""')
267
+ f.write(f"\n{project_name} 主程序\n\n")
268
+ f.write("这是 ErisPulse 自动生成的主程序文件\n")
269
+ f.write("您可以根据需要修改此文件\n")
270
+ f.write('"""\n\n')
271
+ f.write("import asyncio\n")
272
+ f.write("from ErisPulse import sdk\n\n")
273
+ f.write("async def main():\n")
274
+ f.write(' """主程序入口"""\n')
275
+ f.write(" # 初始化 SDK\n")
276
+ f.write(" await sdk.init()\n\n")
277
+ f.write(" # 启动适配器\n")
278
+ f.write(" await sdk.adapter.startup()\n\n")
279
+ f.write(' print("ErisPulse 已启动,按 Ctrl+C 退出")\n')
280
+ f.write(" try:\n")
281
+ f.write(" while True:\n")
282
+ f.write(" await asyncio.sleep(1)\n")
283
+ f.write(" except KeyboardInterrupt:\n")
284
+ f.write(" print(\"\\n正在关闭...\")\n")
285
+ f.write(" await sdk.adapter.shutdown()\n\n")
286
+ f.write("if __name__ == \"__main__\":\n")
287
+ f.write(" asyncio.run(main())\n")
288
+
289
+ self.console.print("[green]创建主程序文件: main.py[/green]")
290
+
291
+ # 创建模块目录和示例模块
292
+ if adapter_list:
293
+ example_file = project_path / "modules" / "example.py"
294
+ if not example_file.exists():
295
+ with open(example_file, "w", encoding="utf-8") as f:
296
+ f.write('"""')
297
+ f.write("\n示例模块\n\n")
298
+ f.write("这是一个示例模块,展示了如何创建基本的消息处理器\n")
299
+ f.write('"""\n\n')
300
+ f.write("from ErisPulse.Core.Event import command, message\n\n")
301
+ f.write("class Main:\n")
302
+ f.write(" def __init__(self, sdk=None):\n")
303
+ f.write(" self.sdk = sdk or globals().get('sdk', __import__('ErisPulse').sdk)\n")
304
+ f.write(" self.logger = self.sdk.logger.get_child(\"Example\")\n")
305
+ f.write(" self.logger.info(\"示例模块已加载\")\n\n")
306
+ f.write(" # 注册命令处理器\n")
307
+ f.write(" @command(\"hello\", help=\"发送问候消息\")\n")
308
+ f.write(" async def hello_command(event):\n")
309
+ f.write(" platform = event[\"platform\"]\n")
310
+ f.write(" user_id = event[\"user_id\"]\n")
311
+ f.write(" \n")
312
+ f.write(" if hasattr(self.sdk.adapter, platform):\n")
313
+ f.write(" adapter_instance = getattr(self.sdk.adapter, platform)\n")
314
+ f.write(" await adapter_instance.Send.To(\"user\", user_id).Text(\"你好!这是一个示例消息。\")\n")
315
+
316
+ self.console.print("[green]创建示例模块: modules/example.py[/green]")
317
+
318
+ self.console.print("\n[bold green]项目 {} 初始化成功![/bold green]".format(project_name))
319
+ self.console.print("\n[cyan]接下来您可以:[/cyan]")
320
+ self.console.print(f"1. 编辑 {project_name}/config.toml 配置适配器")
321
+ self.console.print(f"2. 在 {project_name}/modules/ 目录下创建自定义模块")
322
+ self.console.print(f"3. 运行 [cyan]cd {project_name} \n ep run[/cyan] 启动项目")
323
+
324
+ return True
325
+
326
+ except Exception as e:
327
+ self.console.print(f"[red]初始化项目失败: {e}[/red]")
328
+ return False
329
+
330
+ def interactive_init(self, project_name: str = None, force: bool = False) -> bool:
331
+ """
332
+ 交互式初始化项目,包括项目创建和配置设置
333
+
334
+ :param project_name: 项目名称,可为None
335
+ :param force: 是否强制覆盖现有配置
336
+ :return: 是否初始化成功
337
+ """
338
+ try:
339
+ # 获取项目名称(如果未提供)
340
+ if not project_name:
341
+ project_name = self.console.input("[cyan]请输入项目名称 (默认: my_erispulse_project):[/cyan] ")
342
+ if not project_name:
343
+ project_name = "my_erispulse_project"
344
+
345
+ # 检查项目是否已存在
346
+ project_path = Path(project_name)
347
+ if project_path.exists() and not force:
348
+ if not Confirm.ask("[yellow]目录 {} 已存在,是否覆盖?[/yellow]".format(project_name), default=False):
349
+ self.console.print("[info]操作已取消[/]")
350
+ return False
351
+
352
+ # 创建项目
353
+ if not self.init_project(project_name, []):
354
+ return False
355
+
356
+ # 加载项目配置
357
+ from .. import config as config
358
+ project_config_path = project_path / "config.toml"
359
+
360
+ # 更新配置文件路径并重新加载配置
361
+ config.CONFIG_FILE = str(project_config_path)
362
+ config.reload()
363
+
364
+ # 交互式配置向导
365
+ self.console.print("\n[bold blue]现在进行基本配置:[/bold blue]")
366
+
367
+ # 获取日志级别配置
368
+ current_level = config.getConfig("ErisPulse.logger.level", "INFO")
369
+ self.console.print("\n当前日志级别: [cyan]{}[/cyan]".format(current_level))
370
+ new_level = self.console.input("[yellow]请输入新的日志级别 (DEBUG/INFO/WARNING/ERROR/CRITICAL),回车保持当前值:[/yellow] ")
371
+
372
+ if new_level and new_level.upper() in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
373
+ config.setConfig("ErisPulse.logger.level", new_level.upper())
374
+ self.console.print("[green]日志级别已更新为: {}[/green]".format(new_level.upper()))
375
+ elif new_level:
376
+ self.console.print("[red]无效的日志级别: {}[/red]".format(new_level))
377
+
378
+ # 获取服务器配置
379
+ self.console.print("\n[bold]服务器配置[/bold]")
380
+ current_host = config.getConfig("ErisPulse.server.host", "0.0.0.0")
381
+ current_port = config.getConfig("ErisPulse.server.port", 8000)
382
+
383
+ self.console.print("当前主机: [cyan]{}[/cyan]".format(current_host))
384
+ new_host = self.console.input("[yellow]请输入主机地址,回车保持当前值:[/yellow] ")
385
+
386
+ if new_host:
387
+ config.setConfig("ErisPulse.server.host", new_host)
388
+ self.console.print("[green]主机地址已更新为: {}[/green]".format(new_host))
389
+
390
+ self.console.print("当前端口: [cyan]{}[/cyan]".format(current_port))
391
+ new_port = self.console.input("[yellow]请输入端口号,回车保持当前值:[/yellow] ")
392
+
393
+ if new_port:
394
+ try:
395
+ port_int = int(new_port)
396
+ config.setConfig("ErisPulse.server.port", port_int)
397
+ self.console.print("[green]端口已更新为: {}[/green]".format(port_int))
398
+ except ValueError:
399
+ self.console.print("[red]无效的端口号: {}[/red]".format(new_port))
400
+
401
+ # 询问是否要配置适配器
402
+ if Confirm.ask("\n[cyan]是否要配置适配器?[/cyan]", default=True):
403
+ # 使用同步版本的适配器配置方法
404
+ self._configure_adapters_interactive_sync(str(project_path))
405
+
406
+ # 保存配置
407
+ config.force_save()
408
+ self.console.print("\n[bold green]项目和配置初始化完成![/bold green]")
409
+
410
+ # 显示下一步操作
411
+ self.console.print("\n[cyan]接下来您可以:[/cyan]")
412
+ self.console.print("1. 编辑 {}/config.toml 进一步配置".format(project_name))
413
+ self.console.print("2. 在 {}/modules/ 目录下创建自定义模块".format(project_name))
414
+ self.console.print("3. 运行 [cyan]cd {} \n ep run[/cyan] 启动项目".format(project_name))
415
+
416
+ return True
417
+
418
+ except Exception as e:
419
+ self.console.print("[red]交互式初始化失败: {}[/red]".format(e))
420
+ return False
421
+
422
+ def _configure_adapters_interactive_sync(self, project_path: str = None) -> None:
423
+ """
424
+ 交互式配置适配器的同步版本,从云端获取适配器列表
425
+
426
+ :param project_path: 项目路径,用于加载项目特定的配置
427
+ """
428
+ from . import config
429
+ from ..utils.package_manager import PackageManager
430
+
431
+ # 如果提供了项目路径,则加载项目配置
432
+ if project_path:
433
+ project_config_path = Path(project_path) / "config.toml"
434
+ if project_config_path.exists():
435
+ # 更新配置文件路径并重新加载配置
436
+ config.CONFIG_FILE = str(project_config_path)
437
+ config.reload()
438
+ self.console.print(f"[green]已加载项目配置: {project_config_path}[/green]")
439
+
440
+ self.console.print("\n[bold]配置适配器[/bold]")
441
+ self.console.print("[info]正在从云端获取可用适配器列表...[/info]")
442
+
443
+ # 获取可用适配器列表(同步方式)
444
+ try:
445
+ # 使用线程池在同步上下文中运行异步函数
446
+ import concurrent.futures
447
+
448
+ with concurrent.futures.ThreadPoolExecutor() as executor:
449
+ future = executor.submit(asyncio.run, self._fetch_available_adapters())
450
+ adapters = future.result(timeout=10)
451
+ except Exception as e:
452
+ self.console.print(f"[red]获取适配器列表失败: {e}[/red]")
453
+ adapters = {}
454
+
455
+ if not adapters:
456
+ self.console.print("[red]未能获取到适配器列表[/red]")
457
+ return
458
+
459
+ # 显示可用适配器列表
460
+ adapter_list = list(adapters.items())
461
+ for i, (name, desc) in enumerate(adapter_list, 1):
462
+ self.console.print(f" {i}. {name} - {desc}")
463
+
464
+ # 选择适配器
465
+ selected_indices = self.console.input("\n[cyan]请输入要启用的适配器序号,多个用逗号分隔 (如: 1,3):[/cyan] ")
466
+ if not selected_indices:
467
+ self.console.print("[info]未选择任何适配器[/info]")
468
+ return
469
+
470
+ try:
471
+ indices = [int(idx.strip()) for idx in selected_indices.split(",")]
472
+ enabled_adapters = []
473
+
474
+ for idx in indices:
475
+ if 1 <= idx <= len(adapter_list):
476
+ adapter_name = adapter_list[idx-1][0]
477
+ enabled_adapters.append(adapter_name)
478
+ config.setConfig(f"ErisPulse.adapters.status.{adapter_name}", True)
479
+ self.console.print("[green]已启用适配器: {}[/green]".format(adapter_name))
480
+ else:
481
+ self.console.print("[red]无效的序号: {}[/red]".format(idx))
482
+
483
+ # 禁用未选择的适配器
484
+ all_adapter_names = [name for name, _ in adapter_list]
485
+ for name in all_adapter_names:
486
+ if name not in enabled_adapters:
487
+ config.setConfig(f"ErisPulse.adapters.status.{name}", False)
488
+
489
+ self.console.print("\n[info]已启用 {} 个适配器[/info]".format(len(enabled_adapters)))
490
+
491
+ # 询问是否要安装适配器
492
+ if enabled_adapters and Confirm.ask("\n[cyan]是否要安装选中的适配器?[/cyan]", default=True):
493
+ package_manager = PackageManager()
494
+
495
+ for adapter_name in enabled_adapters:
496
+ # 从适配器列表中获取包名
497
+ package_name = None
498
+ # 尝试通过 PackageManager 获取包名
499
+ try:
500
+ # 使用同步方式获取远程包信息
501
+ remote_packages = package_manager._cache.get("remote_packages", {})
502
+ if not remote_packages:
503
+ # 如果没有缓存,尝试同步获取
504
+ with concurrent.futures.ThreadPoolExecutor() as executor:
505
+ future = executor.submit(asyncio.run, package_manager.get_remote_packages())
506
+ remote_packages = future.result(timeout=10)
507
+
508
+ if adapter_name in remote_packages.get("adapters", {}):
509
+ package_name = remote_packages["adapters"][adapter_name].get("package")
510
+ except Exception:
511
+ pass
512
+
513
+ # 如果没有找到包名,使用适配器名称作为包名
514
+ if not package_name:
515
+ package_name = adapter_name
516
+
517
+ # 安装适配器
518
+ self.console.print(f"[info]正在安装适配器: {adapter_name} ({package_name})[/info]")
519
+ success = package_manager.install_package([package_name])
520
+
521
+ if success:
522
+ self.console.print(f"[green]适配器 {adapter_name} 安装成功[/green]")
523
+ else:
524
+ # 如果标准安装失败,尝试使用 uv
525
+ self.console.print("[yellow]标准安装失败,尝试使用 uv 安装...[/yellow]")
526
+ try:
527
+ import subprocess
528
+ import sys
529
+
530
+ # 尝试使用 uv pip install
531
+ result = subprocess.run(
532
+ [sys.executable, "-m", "uv", "pip", "install", package_name],
533
+ capture_output=True,
534
+ text=True,
535
+ timeout=300
536
+ )
537
+
538
+ if result.returncode == 0:
539
+ self.console.print(f"[green]适配器 {adapter_name} 通过 uv 安装成功[/green]")
540
+ success = True
541
+ else:
542
+ self.console.print(f"[red]适配器 {adapter_name} 通过 uv 安装失败[/red]")
543
+ self.console.print(f"[dim]{result.stderr}[/dim]")
544
+ except Exception as e:
545
+ self.console.print(f"[red]适配器 {adapter_name} 通过 uv 安装时出错: {e}[/red]")
546
+
547
+ # 如果 uv 也失败了,尝试直接使用 pip
548
+ if not success:
549
+ self.console.print("[yellow]尝试使用 pip 直接安装...[/yellow]")
550
+ try:
551
+ import subprocess
552
+ import sys
553
+
554
+ # 尝试直接使用 pip
555
+ result = subprocess.run(
556
+ [sys.executable, "-m", "pip", "install", package_name],
557
+ capture_output=True,
558
+ text=True,
559
+ timeout=300
560
+ )
561
+
562
+ if result.returncode == 0:
563
+ self.console.print(f"[green]适配器 {adapter_name} 通过 pip 直接安装成功[/green]")
564
+ success = True
565
+ else:
566
+ self.console.print(f"[red]适配器 {adapter_name} 通过 pip 直接安装失败[/red]")
567
+ self.console.print(f"[dim]{result.stderr}[/dim]")
568
+ except Exception as e:
569
+ self.console.print(f"[red]适配器 {adapter_name} 通过 pip 直接安装时出错: {e}[/red]")
570
+
571
+ if not success:
572
+ self.console.print(f"[red]适配器 {adapter_name} 安装失败,请手动安装: pip install {package_name}[/red]")
573
+
574
+ # 保存配置
575
+ config.force_save()
576
+
577
+ except ValueError:
578
+ self.console.print("[red]输入格式错误,请输入数字序号[/red]")
579
+
580
+ async def _configure_adapters_interactive(self, project_path: str = None) -> None:
581
+ """
582
+ 交互式配置适配器,从云端获取适配器列表
583
+
584
+ :param project_path: 项目路径,用于加载项目特定的配置
585
+ """
586
+ from .. import config
587
+ from ..utils.package_manager import PackageManager
588
+ package_manager = PackageManager()
589
+
590
+ # 如果提供了项目路径,则加载项目配置
591
+ if project_path:
592
+ project_config_path = Path(project_path) / "config.toml"
593
+ if project_config_path.exists():
594
+ # 更新配置文件路径并重新加载配置
595
+ config.CONFIG_FILE = str(project_config_path)
596
+ config.reload()
597
+ self.console.print(f"[green]已加载项目配置: {project_config_path}[/green]")
598
+
599
+ self.console.print("\n[bold]配置适配器[/bold]")
600
+ self.console.print("[info]正在从云端获取可用适配器列表...[/info]")
601
+
602
+ # 从云端获取可用适配器列表
603
+ adapters = await self._fetch_available_adapters()
604
+
605
+ if not adapters:
606
+ self.console.print("[red]未能获取到适配器列表[/red]")
607
+ return
608
+
609
+ # 显示可用适配器列表
610
+ adapter_list = list(adapters.items())
611
+ for i, (name, desc) in enumerate(adapter_list, 1):
612
+ self.console.print(f" {i}. {name} - {desc}")
613
+
614
+ # 选择适配器
615
+ selected_indices = self.console.input("\n[cyan]请输入要启用的适配器序号,多个用逗号分隔 (如: 1,3):[/cyan] ")
616
+ if not selected_indices:
617
+ self.console.print("[info]未选择任何适配器[/info]")
618
+ return
619
+
620
+ try:
621
+ indices = [int(idx.strip()) for idx in selected_indices.split(",")]
622
+ enabled_adapters = []
623
+
624
+ for idx in indices:
625
+ if 1 <= idx <= len(adapter_list):
626
+ adapter_name = adapter_list[idx-1][0]
627
+ enabled_adapters.append(adapter_name)
628
+ config.setConfig(f"ErisPulse.adapters.status.{adapter_name}", True)
629
+ self.console.print("[green]已启用适配器: {}[/green]".format(adapter_name))
630
+ else:
631
+ self.console.print("[red]无效的序号: {}[/red]".format(idx))
632
+
633
+ # 禁用未选择的适配器
634
+ all_adapter_names = [name for name, _ in adapter_list]
635
+ for name in all_adapter_names:
636
+ if name not in enabled_adapters:
637
+ config.setConfig(f"ErisPulse.adapters.status.{name}", False)
638
+
639
+ self.console.print("\n[info]已启用 {} 个适配器[/info]".format(len(enabled_adapters)))
640
+
641
+ # 询问是否要安装适配器
642
+ if enabled_adapters and Confirm.ask("\n[cyan]是否要安装选中的适配器?[/cyan]", default=True):
643
+ # 直接使用CLI中的安装功能
644
+ success = package_manager.install_package(enabled_adapters)
645
+
646
+ if success:
647
+ self.console.print("[green]适配器安装成功[/green]")
648
+ else:
649
+ self.console.print("[red]适配器安装失败,请手动安装或检查网络连接[/red]")
650
+
651
+ # 保存配置
652
+ config.force_save()
653
+
654
+ except ValueError:
655
+ self.console.print("[red]输入格式错误,请输入数字序号[/red]")
656
+
657
+
658
+ # 创建全局UX管理器实例
659
+ ux = UXManager()
660
+
661
+ __all__ = [
662
+ "ux",
663
+ "UXManager"
664
+ ]