mofox-plugin-dev-toolkit 0.3.3__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.
- mofox_plugin_dev_toolkit-0.3.3.dist-info/METADATA +730 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/RECORD +46 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/WHEEL +5 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/entry_points.txt +2 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/licenses/LICENSE +674 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/top_level.txt +1 -0
- mpdt/__init__.py +15 -0
- mpdt/__main__.py +8 -0
- mpdt/cli.py +316 -0
- mpdt/commands/__init__.py +9 -0
- mpdt/commands/check.py +498 -0
- mpdt/commands/dev.py +318 -0
- mpdt/commands/generate.py +448 -0
- mpdt/commands/init.py +686 -0
- mpdt/dev/bridge_plugin/__init__.py +17 -0
- mpdt/dev/bridge_plugin/cleanup_handler.py +65 -0
- mpdt/dev/bridge_plugin/dev_config.py +24 -0
- mpdt/dev/bridge_plugin/file_watcher.py +169 -0
- mpdt/dev/bridge_plugin/plugin.py +219 -0
- mpdt/templates/__init__.py +165 -0
- mpdt/templates/action_template.py +102 -0
- mpdt/templates/adapter_template.py +129 -0
- mpdt/templates/chatter_template.py +103 -0
- mpdt/templates/event_template.py +116 -0
- mpdt/templates/plus_command_template.py +150 -0
- mpdt/templates/prompt_template.py +92 -0
- mpdt/templates/router_template.py +175 -0
- mpdt/templates/tool_template.py +98 -0
- mpdt/utils/__init__.py +10 -0
- mpdt/utils/code_parser.py +401 -0
- mpdt/utils/color_printer.py +99 -0
- mpdt/utils/config_loader.py +171 -0
- mpdt/utils/config_manager.py +297 -0
- mpdt/utils/file_ops.py +207 -0
- mpdt/utils/license_generator.py +980 -0
- mpdt/utils/plugin_parser.py +195 -0
- mpdt/utils/template_engine.py +112 -0
- mpdt/validators/__init__.py +26 -0
- mpdt/validators/auto_fix_validator.py +990 -0
- mpdt/validators/base.py +129 -0
- mpdt/validators/component_validator.py +842 -0
- mpdt/validators/config_validator.py +119 -0
- mpdt/validators/metadata_validator.py +107 -0
- mpdt/validators/structure_validator.py +72 -0
- mpdt/validators/style_validator.py +117 -0
- mpdt/validators/type_validator.py +206 -0
mpdt/commands/dev.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mpdt dev 命令实现
|
|
3
|
+
启动开发模式:注入开发插件到主程序,由开发插件负责文件监控和热重载
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
|
|
13
|
+
from mpdt.utils.config_manager import MPDTConfig, interactive_config
|
|
14
|
+
from mpdt.utils.plugin_parser import extract_plugin_name
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DevServer:
|
|
20
|
+
"""开发服务器 - 注入开发插件并启动主程序"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, plugin_path: Path, config: MPDTConfig, mofox_path: Path | None = None):
|
|
23
|
+
self.plugin_path = plugin_path.absolute()
|
|
24
|
+
self.config = config
|
|
25
|
+
|
|
26
|
+
resolved_path = mofox_path or config.mofox_path
|
|
27
|
+
if not resolved_path:
|
|
28
|
+
raise ValueError("未配置 mofox 主程序路径")
|
|
29
|
+
self.mofox_path: Path = resolved_path
|
|
30
|
+
|
|
31
|
+
self.plugin_name: str | None = None
|
|
32
|
+
self.process: subprocess.Popen[str] | None = None
|
|
33
|
+
|
|
34
|
+
def start(self):
|
|
35
|
+
"""启动开发模式(同步方法)"""
|
|
36
|
+
try:
|
|
37
|
+
# 1. 解析插件名称
|
|
38
|
+
self._parse_plugin_info()
|
|
39
|
+
|
|
40
|
+
# 2. 注入目标开发插件
|
|
41
|
+
self._inject_target_plugin()
|
|
42
|
+
|
|
43
|
+
# 4. 注入 DevBridge 插件(包含配置)
|
|
44
|
+
self._inject_bridge_plugin()
|
|
45
|
+
|
|
46
|
+
# 5. 启动主程序
|
|
47
|
+
self._start_main_process()
|
|
48
|
+
|
|
49
|
+
console.print("\n[bold green]✨ 开发模式已启动![/bold green]")
|
|
50
|
+
console.print("[dim]主程序窗口中会显示文件监控和重载信息[/dim]")
|
|
51
|
+
console.print("[dim]DevBridge 插件会在主程序退出时自动清理[/dim]\n")
|
|
52
|
+
|
|
53
|
+
# 启动完成,直接退出,让插件自己管理生命周期
|
|
54
|
+
console.print("[green]✓ 开发服务器启动完成,此窗口将关闭[/green]")
|
|
55
|
+
|
|
56
|
+
except Exception as e:
|
|
57
|
+
console.print(f"[red]错误: {e}[/red]")
|
|
58
|
+
import traceback
|
|
59
|
+
traceback.print_exc()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _parse_plugin_info(self):
|
|
64
|
+
"""解析插件信息"""
|
|
65
|
+
console.print(
|
|
66
|
+
Panel.fit(
|
|
67
|
+
f"[bold cyan]🚀 MoFox Plugin Dev Server[/bold cyan]\n\n"
|
|
68
|
+
f"📂 目录: {self.plugin_path.name}\n"
|
|
69
|
+
f"📍 路径: {self.plugin_path}"
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# 提取插件名称
|
|
74
|
+
self.plugin_name = extract_plugin_name(self.plugin_path)
|
|
75
|
+
|
|
76
|
+
if not self.plugin_name:
|
|
77
|
+
console.print("[red]❌ 无法读取插件名称[/red]")
|
|
78
|
+
console.print("\n请确保 plugin.py 中有:")
|
|
79
|
+
console.print("```python")
|
|
80
|
+
console.print("class YourPlugin(BasePlugin):")
|
|
81
|
+
console.print(' plugin_name = "your_plugin"')
|
|
82
|
+
console.print("```")
|
|
83
|
+
raise ValueError("无法解析插件名称")
|
|
84
|
+
|
|
85
|
+
console.print(f"[green]✓ 插件名: {self.plugin_name}[/green]")
|
|
86
|
+
|
|
87
|
+
def _inject_target_plugin(self):
|
|
88
|
+
"""将目标插件复制到 mofox 的 plugins 目录"""
|
|
89
|
+
assert self.plugin_name is not None, "plugin_name 未初始化"
|
|
90
|
+
plugins_dir = self.mofox_path / "plugins"
|
|
91
|
+
target_dir = plugins_dir / self.plugin_name
|
|
92
|
+
|
|
93
|
+
# 检查插件是否已经在 plugins 目录下
|
|
94
|
+
if self.plugin_path.parent.resolve() == plugins_dir.resolve():
|
|
95
|
+
console.print("[dim]📦 插件已在 plugins 目录下,跳过复制[/dim]")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
console.print("[cyan]📦 注入目标插件...[/cyan]")
|
|
99
|
+
|
|
100
|
+
# 如果已存在,先删除
|
|
101
|
+
if target_dir.exists():
|
|
102
|
+
shutil.rmtree(target_dir)
|
|
103
|
+
|
|
104
|
+
# 复制插件
|
|
105
|
+
shutil.copytree(self.plugin_path, target_dir)
|
|
106
|
+
|
|
107
|
+
console.print(f"[green]✓ 目标插件已注入: {target_dir}[/green]")
|
|
108
|
+
|
|
109
|
+
def _inject_bridge_plugin(self):
|
|
110
|
+
"""注入 DevBridge 插件到主程序,并修改配置常量"""
|
|
111
|
+
console.print("[cyan]🔗 注入开发模式插件...[/cyan]")
|
|
112
|
+
|
|
113
|
+
# DevBridge 插件源路径
|
|
114
|
+
bridge_source = Path(__file__).parent.parent / "dev" / "bridge_plugin"
|
|
115
|
+
|
|
116
|
+
if not bridge_source.exists():
|
|
117
|
+
raise FileNotFoundError(f"DevBridge 插件源不存在: {bridge_source}")
|
|
118
|
+
|
|
119
|
+
# 目标路径
|
|
120
|
+
bridge_target = self.mofox_path / "plugins" / "dev_bridge"
|
|
121
|
+
|
|
122
|
+
# 如果已存在,先删除
|
|
123
|
+
if bridge_target.exists():
|
|
124
|
+
shutil.rmtree(bridge_target)
|
|
125
|
+
|
|
126
|
+
# 复制插件
|
|
127
|
+
shutil.copytree(bridge_source, bridge_target)
|
|
128
|
+
|
|
129
|
+
# 动态修改 dev_config.py 中的常量
|
|
130
|
+
self._update_dev_config(bridge_target)
|
|
131
|
+
|
|
132
|
+
console.print(f"[green]✓ DevBridge 插件已注入: {bridge_target}[/green]")
|
|
133
|
+
console.print(f"[dim] 目标插件: {self.plugin_name}[/dim]")
|
|
134
|
+
console.print(f"[dim] 监控路径: {self.plugin_path}[/dim]")
|
|
135
|
+
|
|
136
|
+
def _update_dev_config(self, bridge_target: Path):
|
|
137
|
+
"""更新开发插件的配置文件"""
|
|
138
|
+
config_file = bridge_target / "dev_config.py"
|
|
139
|
+
|
|
140
|
+
# 生成新的配置内容
|
|
141
|
+
config_content = f'''"""
|
|
142
|
+
开发模式配置文件
|
|
143
|
+
此文件由 mpdt dev 自动生成,请勿手动修改
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
# ==================== 开发目标插件配置 ====================
|
|
147
|
+
|
|
148
|
+
# 目标插件的绝对路径
|
|
149
|
+
TARGET_PLUGIN_PATH: str = r"{self.plugin_path}"
|
|
150
|
+
|
|
151
|
+
# 目标插件名称
|
|
152
|
+
TARGET_PLUGIN_NAME: str = "{self.plugin_name}"
|
|
153
|
+
|
|
154
|
+
# 是否启用文件监控
|
|
155
|
+
ENABLE_FILE_WATCHER: bool = True
|
|
156
|
+
|
|
157
|
+
# 文件监控防抖延迟(秒)
|
|
158
|
+
DEBOUNCE_DELAY: float = 0.3
|
|
159
|
+
|
|
160
|
+
# ==================== 其他配置 ====================
|
|
161
|
+
|
|
162
|
+
# 发现服务器端口(保留,暂未使用)
|
|
163
|
+
DISCOVERY_PORT: int = 12318
|
|
164
|
+
'''
|
|
165
|
+
|
|
166
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
167
|
+
f.write(config_content)
|
|
168
|
+
|
|
169
|
+
console.print("[dim] 配置已写入 dev_config.py[/dim]")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _start_main_process(self):
|
|
174
|
+
"""启动主程序"""
|
|
175
|
+
console.print(f"[cyan]🚀 启动主程序: {self.mofox_path / 'bot.py'}[/cyan]")
|
|
176
|
+
|
|
177
|
+
# 获取 Python 命令
|
|
178
|
+
venv_type = self.config.venv_type
|
|
179
|
+
venv_path = self.config.venv_path
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
import os
|
|
183
|
+
import sys
|
|
184
|
+
|
|
185
|
+
# Windows 下打开新窗口
|
|
186
|
+
if os.name == "nt":
|
|
187
|
+
if venv_type in ["venv", "uv"] and venv_path:
|
|
188
|
+
activate_script = venv_path / "Scripts" / "activate.bat"
|
|
189
|
+
if activate_script.exists():
|
|
190
|
+
cmd = [
|
|
191
|
+
"cmd",
|
|
192
|
+
"/c",
|
|
193
|
+
f"chcp 65001 && cd /d {self.mofox_path} && {activate_script} && python bot.py",
|
|
194
|
+
]
|
|
195
|
+
console.print(f"[dim]命令: 激活 {venv_type} 环境并启动[/dim]")
|
|
196
|
+
else:
|
|
197
|
+
python_cmd = self.config.get_python_command()
|
|
198
|
+
cmd = ["cmd", "/c", f"chcp 65001 && cd /d {self.mofox_path} && {python_cmd[0]} bot.py"]
|
|
199
|
+
console.print("[yellow]警告: 未找到激活脚本,使用直接启动[/yellow]")
|
|
200
|
+
elif venv_type == "conda" and venv_path:
|
|
201
|
+
cmd = [
|
|
202
|
+
"cmd",
|
|
203
|
+
"/c",
|
|
204
|
+
f"chcp 65001 && cd /d {self.mofox_path} && conda activate {venv_path} && python bot.py",
|
|
205
|
+
]
|
|
206
|
+
console.print("[dim]命令: 激活 conda 环境并启动[/dim]")
|
|
207
|
+
elif venv_type == "poetry":
|
|
208
|
+
cmd = ["cmd", "/c", f"chcp 65001 && cd /d {self.mofox_path} && poetry run python bot.py"]
|
|
209
|
+
console.print("[dim]命令: 使用 poetry run 启动[/dim]")
|
|
210
|
+
else:
|
|
211
|
+
cmd = ["cmd", "/c", f"chcp 65001 && cd /d {self.mofox_path} && python bot.py"]
|
|
212
|
+
console.print("[dim]命令: 使用系统 Python 启动[/dim]")
|
|
213
|
+
|
|
214
|
+
self.process = subprocess.Popen(
|
|
215
|
+
cmd, creationflags=subprocess.CREATE_NEW_CONSOLE, encoding="utf-8", errors="ignore"
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
# Linux/Mac
|
|
219
|
+
if venv_type in ["venv", "uv"] and venv_path:
|
|
220
|
+
activate_script = venv_path / "bin" / "activate"
|
|
221
|
+
if activate_script.exists():
|
|
222
|
+
shell_cmd = f"cd {self.mofox_path} && source {activate_script} && python bot.py"
|
|
223
|
+
else:
|
|
224
|
+
python_cmd = self.config.get_python_command()
|
|
225
|
+
shell_cmd = f"cd {self.mofox_path} && {python_cmd[0]} bot.py"
|
|
226
|
+
console.print("[yellow]警告: 未找到激活脚本,使用直接启动[/yellow]")
|
|
227
|
+
console.print(f"[dim]命令: 激活 {venv_type} 环境并启动[/dim]")
|
|
228
|
+
elif venv_type == "conda" and venv_path:
|
|
229
|
+
shell_cmd = f"cd {self.mofox_path} && conda activate {venv_path} && python bot.py"
|
|
230
|
+
console.print("[dim]命令: 激活 conda 环境并启动[/dim]")
|
|
231
|
+
elif venv_type == "poetry":
|
|
232
|
+
shell_cmd = f"cd {self.mofox_path} && poetry run python bot.py"
|
|
233
|
+
console.print("[dim]命令: 使用 poetry run 启动[/dim]")
|
|
234
|
+
else:
|
|
235
|
+
shell_cmd = f"cd {self.mofox_path} && python bot.py"
|
|
236
|
+
console.print("[dim]命令: 使用系统 Python 启动[/dim]")
|
|
237
|
+
|
|
238
|
+
if sys.platform == "darwin":
|
|
239
|
+
cmd = ["osascript", "-e", f'tell application "Terminal" to do script "{shell_cmd}"']
|
|
240
|
+
else:
|
|
241
|
+
terminals = [
|
|
242
|
+
("gnome-terminal", ["gnome-terminal", "--", "bash", "-c", shell_cmd]),
|
|
243
|
+
("konsole", ["konsole", "-e", "bash", "-c", shell_cmd]),
|
|
244
|
+
("xfce4-terminal", ["xfce4-terminal", "-e", f"bash -c '{shell_cmd}'"]),
|
|
245
|
+
("xterm", ["xterm", "-e", f"bash -c '{shell_cmd}'"]),
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
cmd = None
|
|
249
|
+
for term_name, term_cmd in terminals:
|
|
250
|
+
if (
|
|
251
|
+
subprocess.run(
|
|
252
|
+
["which", term_name], capture_output=True, encoding="utf-8", errors="ignore"
|
|
253
|
+
).returncode
|
|
254
|
+
== 0
|
|
255
|
+
):
|
|
256
|
+
cmd = term_cmd
|
|
257
|
+
break
|
|
258
|
+
|
|
259
|
+
if cmd is None:
|
|
260
|
+
console.print("[yellow]警告: 未找到支持的终端模拟器,使用后台启动[/yellow]")
|
|
261
|
+
cmd = ["bash", "-c", shell_cmd]
|
|
262
|
+
self.process = subprocess.Popen(
|
|
263
|
+
cmd,
|
|
264
|
+
stdout=subprocess.PIPE,
|
|
265
|
+
stderr=subprocess.PIPE,
|
|
266
|
+
text=True,
|
|
267
|
+
encoding="utf-8",
|
|
268
|
+
errors="ignore",
|
|
269
|
+
)
|
|
270
|
+
console.print("[green]✓ 主程序已启动(后台)[/green]")
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
self.process = subprocess.Popen(cmd, encoding="utf-8", errors="ignore")
|
|
274
|
+
console.print("[green]✓ 主程序已启动(新窗口)[/green]")
|
|
275
|
+
except Exception as e:
|
|
276
|
+
raise RuntimeError(f"启动主程序失败: {e}")
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def dev_command(
|
|
282
|
+
plugin_path: Path | None = None,
|
|
283
|
+
mofox_path: Path | None = None,
|
|
284
|
+
):
|
|
285
|
+
"""启动开发模式
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
plugin_path: 插件路径,默认为当前目录
|
|
289
|
+
mofox_path: mmc 主程序路径,默认从配置读取
|
|
290
|
+
"""
|
|
291
|
+
# 确定插件路径
|
|
292
|
+
if plugin_path is None:
|
|
293
|
+
plugin_path = Path.cwd()
|
|
294
|
+
|
|
295
|
+
# 加载配置
|
|
296
|
+
config = MPDTConfig()
|
|
297
|
+
|
|
298
|
+
# 如果未配置,运行配置向导
|
|
299
|
+
if not config.is_configured() and mofox_path is None:
|
|
300
|
+
console.print("[yellow]未找到配置,启动配置向导...[/yellow]\n")
|
|
301
|
+
config = interactive_config()
|
|
302
|
+
|
|
303
|
+
# 如果提供了 mofox_path,使用它
|
|
304
|
+
if mofox_path:
|
|
305
|
+
config.mofox_path = mofox_path
|
|
306
|
+
|
|
307
|
+
# 验证配置
|
|
308
|
+
valid, errors = config.validate()
|
|
309
|
+
if not valid:
|
|
310
|
+
console.print("[red]配置验证失败:[/red]")
|
|
311
|
+
for error in errors:
|
|
312
|
+
console.print(f" - {error}")
|
|
313
|
+
console.print("\n请运行 [cyan]mpdt config init[/cyan] 重新配置")
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
# 创建并启动开发服务器(同步方法)
|
|
317
|
+
server = DevServer(plugin_path, config, mofox_path)
|
|
318
|
+
server.start()
|