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/init.py
ADDED
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
"""
|
|
2
|
+
初始化命令实现
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import questionary
|
|
9
|
+
|
|
10
|
+
from mpdt.utils.color_printer import (
|
|
11
|
+
console,
|
|
12
|
+
print_error,
|
|
13
|
+
print_panel,
|
|
14
|
+
print_step,
|
|
15
|
+
print_success,
|
|
16
|
+
print_tree,
|
|
17
|
+
)
|
|
18
|
+
from mpdt.utils.file_ops import ensure_dir, get_git_user_info, safe_write_file, validate_plugin_name
|
|
19
|
+
from mpdt.utils.license_generator import get_license_text
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def init_plugin(
|
|
23
|
+
plugin_name: str | None = None,
|
|
24
|
+
template: str = "basic",
|
|
25
|
+
author: str | None = None,
|
|
26
|
+
license_type: str = "GPL-v3.0",
|
|
27
|
+
with_docs: bool = False,
|
|
28
|
+
output_dir: str | None = None,
|
|
29
|
+
init_git: bool | None = None,
|
|
30
|
+
verbose: bool = False,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
初始化新插件
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
plugin_name: 插件名称
|
|
37
|
+
template: 模板类型
|
|
38
|
+
author: 作者名称
|
|
39
|
+
license_type: 开源协议
|
|
40
|
+
with_docs: 是否创建文档
|
|
41
|
+
output_dir: 输出目录
|
|
42
|
+
init_git: 是否初始化 Git 仓库 (None 表示交互式询问)
|
|
43
|
+
verbose: 是否详细输出
|
|
44
|
+
"""
|
|
45
|
+
print_step("开始初始化插件...")
|
|
46
|
+
|
|
47
|
+
# 交互式获取插件信息
|
|
48
|
+
if not plugin_name:
|
|
49
|
+
plugin_info = _interactive_init()
|
|
50
|
+
plugin_name = plugin_info["plugin_name"]
|
|
51
|
+
template = plugin_info["template"]
|
|
52
|
+
author = plugin_info.get("author")
|
|
53
|
+
license_type = plugin_info["license"]
|
|
54
|
+
with_docs = plugin_info.get("with_docs", False)
|
|
55
|
+
init_git = plugin_info.get("init_git", False)
|
|
56
|
+
|
|
57
|
+
# 此时 plugin_name 必定不为 None
|
|
58
|
+
assert plugin_name is not None
|
|
59
|
+
|
|
60
|
+
# 验证插件名称
|
|
61
|
+
if not validate_plugin_name(plugin_name):
|
|
62
|
+
print_error("插件名称无效!必须使用小写字母、数字和下划线,以字母开头")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# 确定输出目录
|
|
66
|
+
if output_dir:
|
|
67
|
+
base_dir = Path(output_dir)
|
|
68
|
+
else:
|
|
69
|
+
base_dir = Path.cwd()
|
|
70
|
+
|
|
71
|
+
plugin_dir = base_dir / plugin_name
|
|
72
|
+
|
|
73
|
+
# 检查目录是否已存在
|
|
74
|
+
if plugin_dir.exists():
|
|
75
|
+
print_error(f"目录已存在: {plugin_dir}")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# 创建插件结构
|
|
79
|
+
_create_plugin_structure(
|
|
80
|
+
plugin_dir=plugin_dir,
|
|
81
|
+
plugin_name=plugin_name,
|
|
82
|
+
template=template,
|
|
83
|
+
author=author,
|
|
84
|
+
license_type=license_type,
|
|
85
|
+
with_docs=with_docs,
|
|
86
|
+
verbose=verbose,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# 初始化 Git 仓库
|
|
90
|
+
if init_git is None:
|
|
91
|
+
# 如果未指定,则询问用户
|
|
92
|
+
init_git = questionary.confirm(
|
|
93
|
+
"是否初始化 Git 仓库?",
|
|
94
|
+
default=True,
|
|
95
|
+
).ask()
|
|
96
|
+
|
|
97
|
+
if init_git:
|
|
98
|
+
_init_git_repository(plugin_dir, verbose)
|
|
99
|
+
|
|
100
|
+
# 打印成功信息
|
|
101
|
+
print_success("插件创建成功!")
|
|
102
|
+
|
|
103
|
+
# 根据模板类型构建目录树显示
|
|
104
|
+
components_tree = _build_components_tree(template)
|
|
105
|
+
|
|
106
|
+
print_tree(
|
|
107
|
+
plugin_name,
|
|
108
|
+
{
|
|
109
|
+
".gitignore": None,
|
|
110
|
+
"__init__.py": None,
|
|
111
|
+
plugin_name: {
|
|
112
|
+
"__init__.py": None,
|
|
113
|
+
"plugin.py": None,
|
|
114
|
+
"components": components_tree,
|
|
115
|
+
"utils": ["__init__.py"],
|
|
116
|
+
},
|
|
117
|
+
"docs": ["README.md"] if with_docs else [],
|
|
118
|
+
"pyproject.toml": None,
|
|
119
|
+
"requirements.txt": None,
|
|
120
|
+
"README.md": None,
|
|
121
|
+
"LICENSE": None,
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# 打印下一步指引
|
|
126
|
+
next_steps = f"""
|
|
127
|
+
1. cd {plugin_name}
|
|
128
|
+
2. mpdt generate action MyAction # 创建 Action 组件
|
|
129
|
+
3. mpdt dev # 启动开发模式
|
|
130
|
+
4. mpdt check # 运行检查
|
|
131
|
+
"""
|
|
132
|
+
print_panel("📝 下一步", next_steps, style="cyan")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _interactive_init() -> dict[str, Any]:
|
|
136
|
+
"""交互式初始化"""
|
|
137
|
+
console.print("\n[bold cyan]🚀 欢迎使用 MPDT 插件初始化向导[/bold cyan]\n")
|
|
138
|
+
|
|
139
|
+
git_info = get_git_user_info()
|
|
140
|
+
|
|
141
|
+
answers = questionary.form(
|
|
142
|
+
plugin_name=questionary.text(
|
|
143
|
+
"插件名称 (使用下划线命名):",
|
|
144
|
+
validate=lambda x: validate_plugin_name(x) or "插件名称格式无效",
|
|
145
|
+
),
|
|
146
|
+
display_name=questionary.text(
|
|
147
|
+
"显示名称 (用户可见):",
|
|
148
|
+
),
|
|
149
|
+
description=questionary.text(
|
|
150
|
+
"插件描述:",
|
|
151
|
+
),
|
|
152
|
+
template=questionary.select(
|
|
153
|
+
"选择插件模板:",
|
|
154
|
+
choices=[
|
|
155
|
+
questionary.Choice("基础插件", value="basic"),
|
|
156
|
+
questionary.Choice("Action 插件", value="action"),
|
|
157
|
+
questionary.Choice("Tool 插件", value="tool"),
|
|
158
|
+
questionary.Choice("Plus_Command 插件", value="plus_command"),
|
|
159
|
+
questionary.Choice("完整插件", value="full"),
|
|
160
|
+
questionary.Choice("Adapter 插件", value="adapter"),
|
|
161
|
+
],
|
|
162
|
+
),
|
|
163
|
+
author=questionary.text(
|
|
164
|
+
"作者名称:",
|
|
165
|
+
default=git_info.get("name", ""),
|
|
166
|
+
),
|
|
167
|
+
license=questionary.select(
|
|
168
|
+
"选择开源协议:",
|
|
169
|
+
choices=["GPL-v3.0", "MIT", "Apache-2.0", "BSD-3-Clause"],
|
|
170
|
+
),
|
|
171
|
+
with_docs=questionary.confirm(
|
|
172
|
+
"创建文档文件?",
|
|
173
|
+
default=True,
|
|
174
|
+
),
|
|
175
|
+
init_git=questionary.confirm(
|
|
176
|
+
"初始化 Git 仓库?",
|
|
177
|
+
default=True,
|
|
178
|
+
),
|
|
179
|
+
).ask()
|
|
180
|
+
|
|
181
|
+
return answers
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _create_plugin_structure(
|
|
185
|
+
plugin_dir: Path,
|
|
186
|
+
plugin_name: str,
|
|
187
|
+
template: str,
|
|
188
|
+
author: str | None,
|
|
189
|
+
license_type: str,
|
|
190
|
+
with_docs: bool,
|
|
191
|
+
verbose: bool,
|
|
192
|
+
) -> None:
|
|
193
|
+
"""创建插件目录结构"""
|
|
194
|
+
|
|
195
|
+
# 创建主目录
|
|
196
|
+
ensure_dir(plugin_dir)
|
|
197
|
+
|
|
198
|
+
# 创建根目录下的 __init__.py (给 MoFox-Plugin-Repo读取)
|
|
199
|
+
root_init_content = _generate_init_file(plugin_name, author, license_type)
|
|
200
|
+
safe_write_file(plugin_dir / "__init__.py", root_init_content)
|
|
201
|
+
|
|
202
|
+
# 创建插件代码子目录
|
|
203
|
+
plugin_code_dir = ensure_dir(plugin_dir / plugin_name)
|
|
204
|
+
|
|
205
|
+
# 创建插件代码目录下的 __init__.py (给插件系统读取,内容与根目录的相同)
|
|
206
|
+
safe_write_file(plugin_code_dir / "__init__.py", root_init_content)
|
|
207
|
+
|
|
208
|
+
# 创建 plugin.py
|
|
209
|
+
plugin_content = _generate_plugin_file(plugin_name, template)
|
|
210
|
+
safe_write_file(plugin_code_dir / "plugin.py", plugin_content)
|
|
211
|
+
|
|
212
|
+
# 创建 components 目录
|
|
213
|
+
components_dir = ensure_dir(plugin_code_dir / "components")
|
|
214
|
+
safe_write_file(components_dir / "__init__.py", '"""\n组件模块\n"""\n')
|
|
215
|
+
|
|
216
|
+
for comp_type in ["actions", "plus_command", "tools", "events"]:
|
|
217
|
+
comp_dir = ensure_dir(components_dir / comp_type)
|
|
218
|
+
safe_write_file(comp_dir / "__init__.py", f'"""\n{comp_type.title()} 组件\n"""\n')
|
|
219
|
+
|
|
220
|
+
# 创建 utils 目录
|
|
221
|
+
utils_dir = ensure_dir(plugin_code_dir / "utils")
|
|
222
|
+
safe_write_file(utils_dir / "__init__.py", '"""\n工具函数\n"""\n')
|
|
223
|
+
|
|
224
|
+
# 根据模板类型自动生成示例组件
|
|
225
|
+
_generate_example_components(
|
|
226
|
+
components_dir=components_dir,
|
|
227
|
+
plugin_name=plugin_name,
|
|
228
|
+
template=template,
|
|
229
|
+
author=author,
|
|
230
|
+
verbose=verbose,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# 创建文档目录
|
|
234
|
+
if with_docs:
|
|
235
|
+
docs_dir = ensure_dir(plugin_dir / "docs")
|
|
236
|
+
safe_write_file(docs_dir / "README.md", _generate_readme_file(plugin_name))
|
|
237
|
+
|
|
238
|
+
# 创建 pyproject.toml
|
|
239
|
+
pyproject_content = _generate_pyproject_file(plugin_name, author, license_type)
|
|
240
|
+
safe_write_file(plugin_dir / "pyproject.toml", pyproject_content)
|
|
241
|
+
|
|
242
|
+
# 创建 requirements.txt
|
|
243
|
+
safe_write_file(plugin_dir / "requirements.txt", "# 插件依赖列表\n")
|
|
244
|
+
|
|
245
|
+
# 创建 README.md
|
|
246
|
+
readme_content = _generate_main_readme_file(plugin_name, license_type)
|
|
247
|
+
safe_write_file(plugin_dir / "README.md", readme_content)
|
|
248
|
+
|
|
249
|
+
# 创建 LICENSE 文件
|
|
250
|
+
license_content = get_license_text(license_type, author or "")
|
|
251
|
+
safe_write_file(plugin_dir / "LICENSE", license_content)
|
|
252
|
+
if verbose:
|
|
253
|
+
console.print(f"[dim]✓ 生成许可证文件: {license_type}[/dim]")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _generate_init_file(plugin_name: str, author: str | None, license_type: str) -> str:
|
|
257
|
+
"""生成 __init__.py 文件内容"""
|
|
258
|
+
from mpdt.utils.template_engine import prepare_common_context
|
|
259
|
+
|
|
260
|
+
context = prepare_common_context(
|
|
261
|
+
plugin_name=plugin_name,
|
|
262
|
+
author=author or "",
|
|
263
|
+
license=license_type,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return f'''"""
|
|
267
|
+
{plugin_name} - MoFox-Bot Plugin
|
|
268
|
+
|
|
269
|
+
Author: {context['author']}
|
|
270
|
+
License: {context['license']}
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
from src.plugin_system.base.plugin_metadata import PluginMetadata
|
|
274
|
+
|
|
275
|
+
__plugin_meta__ = PluginMetadata(
|
|
276
|
+
name="{plugin_name}",
|
|
277
|
+
description="插件描述",
|
|
278
|
+
usage="该插件提供 XXX 功能",
|
|
279
|
+
version="1.0.0",
|
|
280
|
+
author="{context['author']}",
|
|
281
|
+
license="{context['license']}",
|
|
282
|
+
repository_url="https://github.com/{context['author']}/{plugin_name}",
|
|
283
|
+
keywords=[],
|
|
284
|
+
categories=[],
|
|
285
|
+
extra={{"is_built_in": False}},
|
|
286
|
+
)
|
|
287
|
+
'''
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _generate_plugin_file(plugin_name: str, template: str) -> str:
|
|
291
|
+
"""生成 plugin.py 文件内容"""
|
|
292
|
+
|
|
293
|
+
# 根据模板类型生成导入语句和组件注册
|
|
294
|
+
imports, component_registrations = _get_component_imports_and_registrations(plugin_name, template)
|
|
295
|
+
|
|
296
|
+
return f'''"""
|
|
297
|
+
{plugin_name} 插件主类
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
from src.common.logger import get_logger
|
|
301
|
+
from src.plugin_system import BasePlugin, ComponentInfo, register_plugin
|
|
302
|
+
{imports}
|
|
303
|
+
logger = get_logger("{plugin_name}")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@register_plugin
|
|
307
|
+
class {_to_pascal_case(plugin_name)}Plugin(BasePlugin):
|
|
308
|
+
"""
|
|
309
|
+
{plugin_name} 插件
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
plugin_name: str = "{plugin_name}"
|
|
313
|
+
enable_plugin: bool = True
|
|
314
|
+
dependencies: list[str] = []
|
|
315
|
+
config_file_name: str = "config.toml"
|
|
316
|
+
config_schema: dict = {{}}
|
|
317
|
+
|
|
318
|
+
def get_plugin_components(self) -> list[tuple[ComponentInfo, type]]:
|
|
319
|
+
"""
|
|
320
|
+
获取插件包含的组件列表
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
组件信息和组件类的列表
|
|
324
|
+
"""
|
|
325
|
+
components = []
|
|
326
|
+
|
|
327
|
+
{component_registrations}
|
|
328
|
+
return components
|
|
329
|
+
'''
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _get_component_imports_and_registrations(plugin_name: str, template: str) -> tuple[str, str]:
|
|
333
|
+
"""
|
|
334
|
+
根据模板类型获取组件导入语句和注册代码
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
plugin_name: 插件名称
|
|
338
|
+
template: 模板类型
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
(导入语句, 组件注册代码)
|
|
342
|
+
"""
|
|
343
|
+
# 模板类型与组件配置的映射
|
|
344
|
+
# (组件类型, 模块名, 类名, 目录名, 获取info的方法名)
|
|
345
|
+
template_components = {
|
|
346
|
+
"basic": [],
|
|
347
|
+
"action": [
|
|
348
|
+
("action", "example_action", "ExampleActionAction", "actions", "get_action_info"),
|
|
349
|
+
],
|
|
350
|
+
"tool": [
|
|
351
|
+
("tool", "example_tool", "ExampleToolTool", "tools", "get_tool_info"),
|
|
352
|
+
],
|
|
353
|
+
"plus_command": [
|
|
354
|
+
("plus_command", "example_command", "ExampleCommandPlusCommand", "plus_command", "get_plus_command_info"),
|
|
355
|
+
],
|
|
356
|
+
"adapter": [
|
|
357
|
+
("adapter", "example_adapter", "ExampleAdapterAdapter", "adapters", "get_adapter_info"),
|
|
358
|
+
],
|
|
359
|
+
"full": [
|
|
360
|
+
("action", "example_action", "ExampleActionAction", "actions", "get_action_info"),
|
|
361
|
+
("tool", "example_tool", "ExampleToolTool", "tools", "get_tool_info"),
|
|
362
|
+
("plus_command", "example_command", "ExampleCommandPlusCommand", "plus_command", "get_plus_command_info"),
|
|
363
|
+
("event", "example_event", "ExampleEventEventHandler", "events", "get_handler_info"),
|
|
364
|
+
],
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
components = template_components.get(template, [])
|
|
368
|
+
|
|
369
|
+
if not components:
|
|
370
|
+
return "", " # TODO: 在这里添加你的组件\n"
|
|
371
|
+
|
|
372
|
+
# 生成导入语句
|
|
373
|
+
import_lines = []
|
|
374
|
+
for comp_type, module_name, class_name, folder, _ in components:
|
|
375
|
+
import_lines.append(
|
|
376
|
+
f"from {plugin_name}.components.{folder}.{module_name} import {class_name}"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
imports = "\n" + "\n".join(import_lines) + "\n"
|
|
380
|
+
|
|
381
|
+
# 生成组件注册代码
|
|
382
|
+
registration_lines = []
|
|
383
|
+
for comp_type, module_name, class_name, folder, info_method in components:
|
|
384
|
+
comp_type_display = comp_type.replace("_", " ").title()
|
|
385
|
+
registration_lines.append(
|
|
386
|
+
f" # 注册 {comp_type_display} 组件\n"
|
|
387
|
+
f" components.append(({class_name}.{info_method}(), {class_name}))\n"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
registrations = "\n".join(registration_lines)
|
|
391
|
+
|
|
392
|
+
return imports, registrations
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _generate_readme_file(plugin_name: str) -> str:
|
|
396
|
+
"""生成 docs/README.md 文件内容"""
|
|
397
|
+
return f'''# {plugin_name} 文档
|
|
398
|
+
|
|
399
|
+
## 功能说明
|
|
400
|
+
|
|
401
|
+
TODO: 描述插件功能
|
|
402
|
+
|
|
403
|
+
## 使用方法
|
|
404
|
+
|
|
405
|
+
TODO: 说明使用方法
|
|
406
|
+
|
|
407
|
+
## API 参考
|
|
408
|
+
|
|
409
|
+
TODO: API 文档
|
|
410
|
+
'''
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _generate_pyproject_file(plugin_name: str, author: str | None, license_type: str) -> str:
|
|
414
|
+
"""生成 pyproject.toml 文件内容"""
|
|
415
|
+
return f'''[project]
|
|
416
|
+
name = "{plugin_name}"
|
|
417
|
+
version = "1.0.0"
|
|
418
|
+
description = "MoFox-Bot 插件"
|
|
419
|
+
authors = [
|
|
420
|
+
{{name = "{author or 'Your Name'}", email = "your.email@example.com"}}
|
|
421
|
+
]
|
|
422
|
+
license = {{text = "{license_type}"}}
|
|
423
|
+
requires-python = ">=3.11"
|
|
424
|
+
|
|
425
|
+
dependencies = []
|
|
426
|
+
'''
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _generate_main_readme_file(plugin_name: str, license_type: str = "GPL-v3.0") -> str:
|
|
430
|
+
"""生成主 README.md 文件内容"""
|
|
431
|
+
return f'''# {plugin_name}
|
|
432
|
+
|
|
433
|
+
MoFox-Bot 插件
|
|
434
|
+
|
|
435
|
+
## 安装
|
|
436
|
+
|
|
437
|
+
将{plugin_name}文件夹放入 `plugins/` 目录中。
|
|
438
|
+
|
|
439
|
+
## 配置
|
|
440
|
+
|
|
441
|
+
编辑 `config/config.toml` 文件进行配置。
|
|
442
|
+
|
|
443
|
+
## 使用
|
|
444
|
+
|
|
445
|
+
TODO: 添加使用说明
|
|
446
|
+
|
|
447
|
+
## 开发
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
# 生成组件
|
|
451
|
+
mpdt generate action MyAction
|
|
452
|
+
|
|
453
|
+
# 运行检查
|
|
454
|
+
mpdt check
|
|
455
|
+
|
|
456
|
+
# 运行测试
|
|
457
|
+
mpdt test
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## 许可证
|
|
461
|
+
|
|
462
|
+
本项目基于 {license_type} 许可证开源,详见 [LICENSE](./LICENSE) 文件。
|
|
463
|
+
'''
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def _to_pascal_case(snake_str: str) -> str:
|
|
467
|
+
"""将 snake_case 转换为 PascalCase"""
|
|
468
|
+
return "".join(word.capitalize() for word in snake_str.split("_"))
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _build_components_tree(template: str) -> dict[str, list[str]] | list[str]:
|
|
472
|
+
"""
|
|
473
|
+
根据模板类型构建组件目录树
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
template: 模板类型
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
组件目录树结构
|
|
480
|
+
"""
|
|
481
|
+
# 基础目录结构
|
|
482
|
+
base_tree = {
|
|
483
|
+
"actions": ["__init__.py"],
|
|
484
|
+
"plus_command": ["__init__.py"],
|
|
485
|
+
"tools": ["__init__.py"],
|
|
486
|
+
"events": ["__init__.py"],
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
# 根据模板类型添加示例文件
|
|
490
|
+
if template == "action":
|
|
491
|
+
base_tree["actions"].append("example_action.py")
|
|
492
|
+
elif template == "tool":
|
|
493
|
+
base_tree["tools"].append("example_tool.py")
|
|
494
|
+
elif template == "plus_command":
|
|
495
|
+
base_tree["plus_command"].append("example_command.py")
|
|
496
|
+
elif template == "adapter":
|
|
497
|
+
base_tree["adapters"] = ["__init__.py", "example_adapter.py"]
|
|
498
|
+
elif template == "full":
|
|
499
|
+
base_tree["actions"].append("example_action.py")
|
|
500
|
+
base_tree["tools"].append("example_tool.py")
|
|
501
|
+
base_tree["plus_command"].append("example_command.py")
|
|
502
|
+
base_tree["events"].append("example_event.py")
|
|
503
|
+
|
|
504
|
+
return base_tree
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def _init_git_repository(plugin_dir: Path, verbose: bool) -> None:
|
|
508
|
+
"""
|
|
509
|
+
初始化 Git 仓库
|
|
510
|
+
|
|
511
|
+
Args:
|
|
512
|
+
plugin_dir: 插件目录
|
|
513
|
+
verbose: 是否详细输出
|
|
514
|
+
"""
|
|
515
|
+
import subprocess
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
# 初始化 Git 仓库
|
|
519
|
+
subprocess.run(
|
|
520
|
+
["git", "init"],
|
|
521
|
+
cwd=plugin_dir,
|
|
522
|
+
check=True,
|
|
523
|
+
capture_output=True,
|
|
524
|
+
text=True,
|
|
525
|
+
encoding='utf-8',
|
|
526
|
+
errors='ignore'
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
# 创建 .gitignore 文件
|
|
530
|
+
gitignore_content = """# Python
|
|
531
|
+
__pycache__/
|
|
532
|
+
*.py[cod]
|
|
533
|
+
*$py.class
|
|
534
|
+
*.so
|
|
535
|
+
.Python
|
|
536
|
+
build/
|
|
537
|
+
develop-eggs/
|
|
538
|
+
dist/
|
|
539
|
+
downloads/
|
|
540
|
+
eggs/
|
|
541
|
+
.eggs/
|
|
542
|
+
lib/
|
|
543
|
+
lib64/
|
|
544
|
+
parts/
|
|
545
|
+
sdist/
|
|
546
|
+
var/
|
|
547
|
+
wheels/
|
|
548
|
+
*.egg-info/
|
|
549
|
+
.installed.cfg
|
|
550
|
+
*.egg
|
|
551
|
+
|
|
552
|
+
# Virtual Environment
|
|
553
|
+
venv/
|
|
554
|
+
ENV/
|
|
555
|
+
env/
|
|
556
|
+
|
|
557
|
+
# IDEs
|
|
558
|
+
.vscode/
|
|
559
|
+
.idea/
|
|
560
|
+
*.swp
|
|
561
|
+
*.swo
|
|
562
|
+
*~
|
|
563
|
+
|
|
564
|
+
# OS
|
|
565
|
+
.DS_Store
|
|
566
|
+
Thumbs.db
|
|
567
|
+
|
|
568
|
+
# Testing
|
|
569
|
+
.pytest_cache/
|
|
570
|
+
.coverage
|
|
571
|
+
htmlcov/
|
|
572
|
+
|
|
573
|
+
# MoFox-Bot specific
|
|
574
|
+
config/local_*.toml
|
|
575
|
+
*.log
|
|
576
|
+
"""
|
|
577
|
+
safe_write_file(plugin_dir / ".gitignore", gitignore_content)
|
|
578
|
+
|
|
579
|
+
# 执行初始提交
|
|
580
|
+
subprocess.run(
|
|
581
|
+
["git", "add", "."],
|
|
582
|
+
cwd=plugin_dir,
|
|
583
|
+
check=True,
|
|
584
|
+
capture_output=True,
|
|
585
|
+
encoding='utf-8',
|
|
586
|
+
errors='ignore'
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
subprocess.run(
|
|
590
|
+
["git", "commit", "-m", "Initial commit"],
|
|
591
|
+
cwd=plugin_dir,
|
|
592
|
+
check=True,
|
|
593
|
+
capture_output=True,
|
|
594
|
+
encoding='utf-8',
|
|
595
|
+
errors='ignore'
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
if verbose:
|
|
599
|
+
console.print("[dim]✓ 初始化 Git 仓库[/dim]")
|
|
600
|
+
print_success("Git 仓库初始化成功")
|
|
601
|
+
|
|
602
|
+
except subprocess.CalledProcessError as e:
|
|
603
|
+
print_error(f"Git 初始化失败: {e}")
|
|
604
|
+
except FileNotFoundError:
|
|
605
|
+
print_error("未找到 Git 命令,请确保已安装 Git")
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def _generate_example_components(
|
|
609
|
+
components_dir: Path,
|
|
610
|
+
plugin_name: str,
|
|
611
|
+
template: str,
|
|
612
|
+
author: str | None,
|
|
613
|
+
verbose: bool,
|
|
614
|
+
) -> None:
|
|
615
|
+
"""
|
|
616
|
+
根据模板类型生成示例组件文件
|
|
617
|
+
|
|
618
|
+
Args:
|
|
619
|
+
components_dir: 组件目录
|
|
620
|
+
plugin_name: 插件名称
|
|
621
|
+
template: 模板类型 (basic, action, tool, plus_command, full, adapter)
|
|
622
|
+
author: 作者
|
|
623
|
+
verbose: 是否详细输出
|
|
624
|
+
"""
|
|
625
|
+
from mpdt.templates import get_component_template, prepare_component_context
|
|
626
|
+
|
|
627
|
+
# 模板类型与组件类型的映射
|
|
628
|
+
template_component_map = {
|
|
629
|
+
"basic": [], # 基础模板不生成示例
|
|
630
|
+
"action": [("action", "example_action", "示例 Action 组件")],
|
|
631
|
+
"tool": [("tool", "example_tool", "示例 Tool 组件")],
|
|
632
|
+
"plus_command": [("plus_command", "example_command", "示例 PlusCommand 组件")],
|
|
633
|
+
"adapter": [("adapter", "example_adapter", "示例 Adapter 组件")],
|
|
634
|
+
"full": [
|
|
635
|
+
("action", "example_action", "示例 Action 组件"),
|
|
636
|
+
("tool", "example_tool", "示例 Tool 组件"),
|
|
637
|
+
("plus_command", "example_command", "示例 PlusCommand 组件"),
|
|
638
|
+
("event", "example_event", "示例 Event 组件"),
|
|
639
|
+
],
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
# 组件类型与目录名的映射
|
|
643
|
+
component_dir_map = {
|
|
644
|
+
"action": "actions",
|
|
645
|
+
"tool": "tools",
|
|
646
|
+
"plus_command": "plus_command",
|
|
647
|
+
"event": "events",
|
|
648
|
+
"adapter": "adapters",
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
components_to_create = template_component_map.get(template, [])
|
|
652
|
+
|
|
653
|
+
for comp_type, comp_name, comp_desc in components_to_create:
|
|
654
|
+
try:
|
|
655
|
+
# 获取模板
|
|
656
|
+
template_str = get_component_template(comp_type)
|
|
657
|
+
|
|
658
|
+
# 准备上下文
|
|
659
|
+
context = prepare_component_context(
|
|
660
|
+
component_type=comp_type,
|
|
661
|
+
component_name=comp_name,
|
|
662
|
+
plugin_name=plugin_name,
|
|
663
|
+
author=author or "",
|
|
664
|
+
description=comp_desc,
|
|
665
|
+
is_async=True,
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
# 渲染模板
|
|
669
|
+
content = template_str.format(**context)
|
|
670
|
+
|
|
671
|
+
# 确定目标目录
|
|
672
|
+
target_dir = components_dir / component_dir_map.get(comp_type, f"{comp_type}s")
|
|
673
|
+
if not target_dir.exists():
|
|
674
|
+
ensure_dir(target_dir)
|
|
675
|
+
safe_write_file(target_dir / "__init__.py", f'"""\n{comp_type.title()} 组件\n"""\n')
|
|
676
|
+
|
|
677
|
+
# 写入文件
|
|
678
|
+
file_path = target_dir / f"{comp_name}.py"
|
|
679
|
+
safe_write_file(file_path, content)
|
|
680
|
+
|
|
681
|
+
if verbose:
|
|
682
|
+
console.print(f"[dim]✓ 生成示例组件: {comp_name}.py[/dim]")
|
|
683
|
+
|
|
684
|
+
except Exception as e:
|
|
685
|
+
if verbose:
|
|
686
|
+
console.print(f"[dim yellow]⚠ 生成组件 {comp_name} 失败: {e}[/dim yellow]")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""开发模式桥接插件
|
|
2
|
+
|
|
3
|
+
这是一个特殊的插件,在开发模式下临时注入到主程序。
|
|
4
|
+
负责文件监控和插件热重载,配置由 mpdt dev 在注入时写入 dev_config.py。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from src.plugin_system.base.plugin_metadata import PluginMetadata
|
|
8
|
+
|
|
9
|
+
__plugin_meta__ = PluginMetadata(
|
|
10
|
+
name="dev_bridge",
|
|
11
|
+
description="开发模式桥接插件,提供文件监控和热重载功能",
|
|
12
|
+
usage="在开发模式下临时注入,监控目标插件文件变化并自动重载。",
|
|
13
|
+
version="1.0.0",
|
|
14
|
+
author="MoFox Team",
|
|
15
|
+
dependencies=[],
|
|
16
|
+
python_dependencies=["watchdog"],
|
|
17
|
+
)
|