mofox-plugin-dev-toolkit 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mofox_plugin_dev_toolkit-0.2.1.dist-info/METADATA +409 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/RECORD +43 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/WHEEL +5 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/entry_points.txt +2 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/licenses/LICENSE +674 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/top_level.txt +1 -0
- mpdt/__init__.py +15 -0
- mpdt/__main__.py +8 -0
- mpdt/cli.py +314 -0
- mpdt/commands/__init__.py +9 -0
- mpdt/commands/check.py +316 -0
- mpdt/commands/dev.py +550 -0
- mpdt/commands/generate.py +366 -0
- mpdt/commands/init.py +487 -0
- mpdt/dev/bridge_plugin/__init__.py +17 -0
- mpdt/dev/bridge_plugin/discovery_server.py +126 -0
- mpdt/dev/bridge_plugin/plugin.py +258 -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/color_printer.py +99 -0
- mpdt/utils/config_loader.py +171 -0
- mpdt/utils/config_manager.py +297 -0
- mpdt/utils/file_ops.py +203 -0
- mpdt/utils/license_generator.py +980 -0
- mpdt/utils/plugin_parser.py +196 -0
- mpdt/utils/template_engine.py +112 -0
- mpdt/validators/__init__.py +26 -0
- mpdt/validators/auto_fix_validator.py +182 -0
- mpdt/validators/base.py +121 -0
- mpdt/validators/component_validator.py +415 -0
- mpdt/validators/config_validator.py +173 -0
- mpdt/validators/metadata_validator.py +125 -0
- mpdt/validators/structure_validator.py +70 -0
- mpdt/validators/style_validator.py +125 -0
- mpdt/validators/type_validator.py +223 -0
mpdt/commands/init.py
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
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_examples: bool = False,
|
|
28
|
+
with_docs: bool = False,
|
|
29
|
+
output_dir: str | None = None,
|
|
30
|
+
init_git: bool | None = None,
|
|
31
|
+
verbose: bool = False,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""
|
|
34
|
+
初始化新插件
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
plugin_name: 插件名称
|
|
38
|
+
template: 模板类型
|
|
39
|
+
author: 作者名称
|
|
40
|
+
license_type: 开源协议
|
|
41
|
+
with_examples: 是否包含示例
|
|
42
|
+
with_docs: 是否创建文档
|
|
43
|
+
output_dir: 输出目录
|
|
44
|
+
init_git: 是否初始化 Git 仓库 (None 表示交互式询问)
|
|
45
|
+
verbose: 是否详细输出
|
|
46
|
+
"""
|
|
47
|
+
print_step("开始初始化插件...")
|
|
48
|
+
|
|
49
|
+
# 交互式获取插件信息
|
|
50
|
+
if not plugin_name:
|
|
51
|
+
plugin_info = _interactive_init()
|
|
52
|
+
plugin_name = plugin_info["plugin_name"]
|
|
53
|
+
template = plugin_info["template"]
|
|
54
|
+
author = plugin_info.get("author")
|
|
55
|
+
license_type = plugin_info["license"]
|
|
56
|
+
with_examples = plugin_info.get("with_examples", False)
|
|
57
|
+
with_docs = plugin_info.get("with_docs", False)
|
|
58
|
+
init_git = plugin_info.get("init_git", False)
|
|
59
|
+
|
|
60
|
+
# 此时 plugin_name 必定不为 None
|
|
61
|
+
assert plugin_name is not None
|
|
62
|
+
|
|
63
|
+
# 验证插件名称
|
|
64
|
+
if not validate_plugin_name(plugin_name):
|
|
65
|
+
print_error("插件名称无效!必须使用小写字母、数字和下划线,以字母开头")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# 确定输出目录
|
|
69
|
+
if output_dir:
|
|
70
|
+
base_dir = Path(output_dir)
|
|
71
|
+
else:
|
|
72
|
+
base_dir = Path.cwd()
|
|
73
|
+
|
|
74
|
+
plugin_dir = base_dir / plugin_name
|
|
75
|
+
|
|
76
|
+
# 检查目录是否已存在
|
|
77
|
+
if plugin_dir.exists():
|
|
78
|
+
print_error(f"目录已存在: {plugin_dir}")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
# 创建插件结构
|
|
82
|
+
_create_plugin_structure(
|
|
83
|
+
plugin_dir=plugin_dir,
|
|
84
|
+
plugin_name=plugin_name,
|
|
85
|
+
template=template,
|
|
86
|
+
author=author,
|
|
87
|
+
license_type=license_type,
|
|
88
|
+
with_examples=with_examples,
|
|
89
|
+
with_docs=with_docs,
|
|
90
|
+
verbose=verbose,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# 初始化 Git 仓库
|
|
94
|
+
if init_git is None:
|
|
95
|
+
# 如果未指定,则询问用户
|
|
96
|
+
init_git = questionary.confirm(
|
|
97
|
+
"是否初始化 Git 仓库?",
|
|
98
|
+
default=True,
|
|
99
|
+
).ask()
|
|
100
|
+
|
|
101
|
+
if init_git:
|
|
102
|
+
_init_git_repository(plugin_dir, verbose)
|
|
103
|
+
|
|
104
|
+
# 打印成功信息
|
|
105
|
+
print_success("插件创建成功!")
|
|
106
|
+
print_tree(
|
|
107
|
+
plugin_name,
|
|
108
|
+
{
|
|
109
|
+
".gitignore":None,
|
|
110
|
+
"__init__.py": None,
|
|
111
|
+
"plugin.py": None,
|
|
112
|
+
"config": ["config.toml"],
|
|
113
|
+
"components": ["actions", "commands", "tools", "events"],
|
|
114
|
+
"utils": ["__init__.py"],
|
|
115
|
+
"docs": ["README.md"] if with_docs else [],
|
|
116
|
+
"pyproject.toml": None,
|
|
117
|
+
"requirements.txt": None,
|
|
118
|
+
"README.md": None,
|
|
119
|
+
"LICENSE": None,
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# 打印下一步指引
|
|
124
|
+
next_steps = f"""
|
|
125
|
+
1. cd {plugin_name}
|
|
126
|
+
2. mpdt generate action MyAction # 创建 Action 组件
|
|
127
|
+
3. mpdt dev # 启动开发模式
|
|
128
|
+
4. mpdt check # 运行检查
|
|
129
|
+
"""
|
|
130
|
+
print_panel("📝 下一步", next_steps, style="cyan")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _interactive_init() -> dict[str, Any]:
|
|
134
|
+
"""交互式初始化"""
|
|
135
|
+
console.print("\n[bold cyan]🚀 欢迎使用 MPDT 插件初始化向导[/bold cyan]\n")
|
|
136
|
+
|
|
137
|
+
git_info = get_git_user_info()
|
|
138
|
+
|
|
139
|
+
answers = questionary.form(
|
|
140
|
+
plugin_name=questionary.text(
|
|
141
|
+
"插件名称 (使用下划线命名):",
|
|
142
|
+
validate=lambda x: validate_plugin_name(x) or "插件名称格式无效",
|
|
143
|
+
),
|
|
144
|
+
display_name=questionary.text(
|
|
145
|
+
"显示名称 (用户可见):",
|
|
146
|
+
),
|
|
147
|
+
description=questionary.text(
|
|
148
|
+
"插件描述:",
|
|
149
|
+
),
|
|
150
|
+
template=questionary.select(
|
|
151
|
+
"选择插件模板:",
|
|
152
|
+
choices=[
|
|
153
|
+
questionary.Choice("基础插件", value="basic"),
|
|
154
|
+
questionary.Choice("Action 插件", value="action"),
|
|
155
|
+
questionary.Choice("Tool 插件", value="tool"),
|
|
156
|
+
questionary.Choice("Plus_Command 插件", value="plus_command"),
|
|
157
|
+
questionary.Choice("完整插件", value="full"),
|
|
158
|
+
questionary.Choice("Adapter 插件", value="adapter"),
|
|
159
|
+
],
|
|
160
|
+
),
|
|
161
|
+
author=questionary.text(
|
|
162
|
+
"作者名称:",
|
|
163
|
+
default=git_info.get("name", ""),
|
|
164
|
+
),
|
|
165
|
+
license=questionary.select(
|
|
166
|
+
"选择开源协议:",
|
|
167
|
+
choices=["GPL-v3.0", "MIT", "Apache-2.0", "BSD-3-Clause"],
|
|
168
|
+
),
|
|
169
|
+
with_examples=questionary.confirm(
|
|
170
|
+
"包含示例代码?",
|
|
171
|
+
default=True,
|
|
172
|
+
),
|
|
173
|
+
with_docs=questionary.confirm(
|
|
174
|
+
"创建文档文件?",
|
|
175
|
+
default=True,
|
|
176
|
+
),
|
|
177
|
+
init_git=questionary.confirm(
|
|
178
|
+
"初始化 Git 仓库?",
|
|
179
|
+
default=True,
|
|
180
|
+
),
|
|
181
|
+
).ask()
|
|
182
|
+
|
|
183
|
+
return answers
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _create_plugin_structure(
|
|
187
|
+
plugin_dir: Path,
|
|
188
|
+
plugin_name: str,
|
|
189
|
+
template: str,
|
|
190
|
+
author: str | None,
|
|
191
|
+
license_type: str,
|
|
192
|
+
with_examples: bool,
|
|
193
|
+
with_docs: bool,
|
|
194
|
+
verbose: bool,
|
|
195
|
+
) -> None:
|
|
196
|
+
"""创建插件目录结构"""
|
|
197
|
+
|
|
198
|
+
# 创建主目录
|
|
199
|
+
ensure_dir(plugin_dir)
|
|
200
|
+
|
|
201
|
+
# 创建 __init__.py
|
|
202
|
+
init_content = _generate_init_file(plugin_name, author, license_type)
|
|
203
|
+
safe_write_file(plugin_dir / "__init__.py", init_content)
|
|
204
|
+
|
|
205
|
+
# 创建 plugin.py
|
|
206
|
+
plugin_content = _generate_plugin_file(plugin_name, template)
|
|
207
|
+
safe_write_file(plugin_dir / "plugin.py", plugin_content)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# 创建 components 目录
|
|
211
|
+
components_dir = ensure_dir(plugin_dir / "components")
|
|
212
|
+
safe_write_file(components_dir / "__init__.py", '"""\n组件模块\n"""\n')
|
|
213
|
+
|
|
214
|
+
for comp_type in ["actions", "plus_command", "tools", "events"]:
|
|
215
|
+
comp_dir = ensure_dir(components_dir / comp_type)
|
|
216
|
+
safe_write_file(comp_dir / "__init__.py", f'"""\n{comp_type.title()} 组件\n"""\n')
|
|
217
|
+
|
|
218
|
+
# 创建 utils 目录
|
|
219
|
+
utils_dir = ensure_dir(plugin_dir / "utils")
|
|
220
|
+
safe_write_file(utils_dir / "__init__.py", '"""\n工具函数\n"""\n')
|
|
221
|
+
|
|
222
|
+
# 创建文档目录
|
|
223
|
+
if with_docs:
|
|
224
|
+
docs_dir = ensure_dir(plugin_dir / "docs")
|
|
225
|
+
safe_write_file(docs_dir / "README.md", _generate_readme_file(plugin_name))
|
|
226
|
+
|
|
227
|
+
# 创建 pyproject.toml
|
|
228
|
+
pyproject_content = _generate_pyproject_file(plugin_name, author, license_type)
|
|
229
|
+
safe_write_file(plugin_dir / "pyproject.toml", pyproject_content)
|
|
230
|
+
|
|
231
|
+
# 创建 requirements.txt
|
|
232
|
+
safe_write_file(plugin_dir / "requirements.txt", "# 插件依赖列表\n")
|
|
233
|
+
|
|
234
|
+
# 创建 README.md
|
|
235
|
+
readme_content = _generate_main_readme_file(plugin_name, license_type)
|
|
236
|
+
safe_write_file(plugin_dir / "README.md", readme_content)
|
|
237
|
+
|
|
238
|
+
# 创建 LICENSE 文件
|
|
239
|
+
license_content = get_license_text(license_type, author or "")
|
|
240
|
+
safe_write_file(plugin_dir / "LICENSE", license_content)
|
|
241
|
+
if verbose:
|
|
242
|
+
console.print(f"[dim]✓ 生成许可证文件: {license_type}[/dim]")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _generate_init_file(plugin_name: str, author: str | None, license_type: str) -> str:
|
|
246
|
+
"""生成 __init__.py 文件内容"""
|
|
247
|
+
from mpdt.utils.template_engine import prepare_common_context
|
|
248
|
+
|
|
249
|
+
context = prepare_common_context(
|
|
250
|
+
plugin_name=plugin_name,
|
|
251
|
+
author=author or "",
|
|
252
|
+
license=license_type,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
return f'''"""
|
|
256
|
+
{plugin_name} - MoFox-Bot Plugin
|
|
257
|
+
|
|
258
|
+
Author: {context['author']}
|
|
259
|
+
License: {context['license']}
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
from src.plugin_system.base.plugin_metadata import PluginMetadata
|
|
263
|
+
|
|
264
|
+
__plugin_meta__ = PluginMetadata(
|
|
265
|
+
name="{plugin_name}",
|
|
266
|
+
description="插件描述",
|
|
267
|
+
usage="该插件提供 XXX 功能",
|
|
268
|
+
version="1.0.0",
|
|
269
|
+
author="{context['author']}",
|
|
270
|
+
license="{context['license']}",
|
|
271
|
+
repository_url="https://github.com/{context['author']}/{plugin_name}",
|
|
272
|
+
keywords=[],
|
|
273
|
+
categories=[],
|
|
274
|
+
extra={{"is_built_in": False}},
|
|
275
|
+
)
|
|
276
|
+
'''
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _generate_plugin_file(plugin_name: str, template: str) -> str:
|
|
280
|
+
"""生成 plugin.py 文件内容"""
|
|
281
|
+
return f'''"""
|
|
282
|
+
{plugin_name} 插件主类
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
from src.common.logger import get_logger
|
|
286
|
+
from src.plugin_system import BasePlugin, ComponentInfo, register_plugin
|
|
287
|
+
|
|
288
|
+
logger = get_logger("{plugin_name}")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@register_plugin
|
|
292
|
+
class {_to_pascal_case(plugin_name)}Plugin(BasePlugin):
|
|
293
|
+
"""
|
|
294
|
+
{plugin_name} 插件
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
plugin_name: str = "{plugin_name}"
|
|
298
|
+
enable_plugin: bool = True
|
|
299
|
+
dependencies: list[str] = []
|
|
300
|
+
config_file_name: str = "config.toml"
|
|
301
|
+
config_schema: dict = {{}}
|
|
302
|
+
|
|
303
|
+
def get_plugin_components(self) -> list[tuple[ComponentInfo, type]]:
|
|
304
|
+
"""
|
|
305
|
+
获取插件包含的组件列表
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
组件信息和组件类的列表
|
|
309
|
+
"""
|
|
310
|
+
components = []
|
|
311
|
+
|
|
312
|
+
# TODO: 在这里添加你的组件
|
|
313
|
+
|
|
314
|
+
return components
|
|
315
|
+
'''
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _generate_readme_file(plugin_name: str) -> str:
|
|
319
|
+
"""生成 docs/README.md 文件内容"""
|
|
320
|
+
return f'''# {plugin_name} 文档
|
|
321
|
+
|
|
322
|
+
## 功能说明
|
|
323
|
+
|
|
324
|
+
TODO: 描述插件功能
|
|
325
|
+
|
|
326
|
+
## 使用方法
|
|
327
|
+
|
|
328
|
+
TODO: 说明使用方法
|
|
329
|
+
|
|
330
|
+
## API 参考
|
|
331
|
+
|
|
332
|
+
TODO: API 文档
|
|
333
|
+
'''
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _generate_pyproject_file(plugin_name: str, author: str | None, license_type: str) -> str:
|
|
337
|
+
"""生成 pyproject.toml 文件内容"""
|
|
338
|
+
return f'''[project]
|
|
339
|
+
name = "{plugin_name}"
|
|
340
|
+
version = "1.0.0"
|
|
341
|
+
description = "MoFox-Bot 插件"
|
|
342
|
+
authors = [
|
|
343
|
+
{{name = "{author or 'Your Name'}", email = "your.email@example.com"}}
|
|
344
|
+
]
|
|
345
|
+
license = {{text = "{license_type}"}}
|
|
346
|
+
requires-python = ">=3.11"
|
|
347
|
+
|
|
348
|
+
dependencies = []
|
|
349
|
+
'''
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _generate_main_readme_file(plugin_name: str, license_type: str = "GPL-v3.0") -> str:
|
|
353
|
+
"""生成主 README.md 文件内容"""
|
|
354
|
+
return f'''# {plugin_name}
|
|
355
|
+
|
|
356
|
+
MoFox-Bot 插件
|
|
357
|
+
|
|
358
|
+
## 安装
|
|
359
|
+
|
|
360
|
+
将插件目录放入 `plugins/` 目录中。
|
|
361
|
+
|
|
362
|
+
## 配置
|
|
363
|
+
|
|
364
|
+
编辑 `config/config.toml` 文件进行配置。
|
|
365
|
+
|
|
366
|
+
## 使用
|
|
367
|
+
|
|
368
|
+
TODO: 添加使用说明
|
|
369
|
+
|
|
370
|
+
## 开发
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
# 生成组件
|
|
374
|
+
mpdt generate action MyAction
|
|
375
|
+
|
|
376
|
+
# 运行检查
|
|
377
|
+
mpdt check
|
|
378
|
+
|
|
379
|
+
# 运行测试
|
|
380
|
+
mpdt test
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## 许可证
|
|
384
|
+
|
|
385
|
+
本项目基于 {license_type} 许可证开源,详见 [LICENSE](./LICENSE) 文件。
|
|
386
|
+
'''
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _to_pascal_case(snake_str: str) -> str:
|
|
390
|
+
"""将 snake_case 转换为 PascalCase"""
|
|
391
|
+
return "".join(word.capitalize() for word in snake_str.split("_"))
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _init_git_repository(plugin_dir: Path, verbose: bool) -> None:
|
|
395
|
+
"""
|
|
396
|
+
初始化 Git 仓库
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
plugin_dir: 插件目录
|
|
400
|
+
verbose: 是否详细输出
|
|
401
|
+
"""
|
|
402
|
+
import subprocess
|
|
403
|
+
|
|
404
|
+
try:
|
|
405
|
+
# 初始化 Git 仓库
|
|
406
|
+
subprocess.run(
|
|
407
|
+
["git", "init"],
|
|
408
|
+
cwd=plugin_dir,
|
|
409
|
+
check=True,
|
|
410
|
+
capture_output=True,
|
|
411
|
+
text=True,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# 创建 .gitignore 文件
|
|
415
|
+
gitignore_content = """# Python
|
|
416
|
+
__pycache__/
|
|
417
|
+
*.py[cod]
|
|
418
|
+
*$py.class
|
|
419
|
+
*.so
|
|
420
|
+
.Python
|
|
421
|
+
build/
|
|
422
|
+
develop-eggs/
|
|
423
|
+
dist/
|
|
424
|
+
downloads/
|
|
425
|
+
eggs/
|
|
426
|
+
.eggs/
|
|
427
|
+
lib/
|
|
428
|
+
lib64/
|
|
429
|
+
parts/
|
|
430
|
+
sdist/
|
|
431
|
+
var/
|
|
432
|
+
wheels/
|
|
433
|
+
*.egg-info/
|
|
434
|
+
.installed.cfg
|
|
435
|
+
*.egg
|
|
436
|
+
|
|
437
|
+
# Virtual Environment
|
|
438
|
+
venv/
|
|
439
|
+
ENV/
|
|
440
|
+
env/
|
|
441
|
+
|
|
442
|
+
# IDEs
|
|
443
|
+
.vscode/
|
|
444
|
+
.idea/
|
|
445
|
+
*.swp
|
|
446
|
+
*.swo
|
|
447
|
+
*~
|
|
448
|
+
|
|
449
|
+
# OS
|
|
450
|
+
.DS_Store
|
|
451
|
+
Thumbs.db
|
|
452
|
+
|
|
453
|
+
# Testing
|
|
454
|
+
.pytest_cache/
|
|
455
|
+
.coverage
|
|
456
|
+
htmlcov/
|
|
457
|
+
|
|
458
|
+
# MoFox-Bot specific
|
|
459
|
+
config/local_*.toml
|
|
460
|
+
*.log
|
|
461
|
+
"""
|
|
462
|
+
safe_write_file(plugin_dir / ".gitignore", gitignore_content)
|
|
463
|
+
|
|
464
|
+
# 执行初始提交
|
|
465
|
+
subprocess.run(
|
|
466
|
+
["git", "add", "."],
|
|
467
|
+
cwd=plugin_dir,
|
|
468
|
+
check=True,
|
|
469
|
+
capture_output=True,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
subprocess.run(
|
|
473
|
+
["git", "commit", "-m", "Initial commit"],
|
|
474
|
+
cwd=plugin_dir,
|
|
475
|
+
check=True,
|
|
476
|
+
capture_output=True,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
if verbose:
|
|
480
|
+
console.print("[dim]✓ 初始化 Git 仓库[/dim]")
|
|
481
|
+
print_success("Git 仓库初始化成功")
|
|
482
|
+
|
|
483
|
+
except subprocess.CalledProcessError as e:
|
|
484
|
+
print_error(f"Git 初始化失败: {e}")
|
|
485
|
+
except FileNotFoundError:
|
|
486
|
+
print_error("未找到 Git 命令,请确保已安装 Git")
|
|
487
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""开发模式桥接插件
|
|
2
|
+
|
|
3
|
+
这是一个特殊的插件,在开发模式下临时注入到主程序。
|
|
4
|
+
通过 WebSocket 与 mpdt dev 通信,提供插件重载等功能。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from src.plugin_system.base.plugin_metadata import PluginMetadata
|
|
8
|
+
|
|
9
|
+
__plugin_meta__ = PluginMetadata(
|
|
10
|
+
name="dev_bridge",
|
|
11
|
+
description="开发模式桥接插件,提供 WebSocket 热重载接口",
|
|
12
|
+
usage="在开发模式下临时注入,提供热重载和调试桥接接口。",
|
|
13
|
+
version="1.0.0",
|
|
14
|
+
author="MoFox Team",
|
|
15
|
+
dependencies=[],
|
|
16
|
+
python_dependencies=[],
|
|
17
|
+
)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
开发模式发现服务器
|
|
3
|
+
固定端口 12318,用于 mpdt dev 获取主程序的动态端口
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import uvicorn
|
|
10
|
+
from fastapi import FastAPI
|
|
11
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from src.common.logger import get_logger
|
|
16
|
+
logger = get_logger("dev_discovery")
|
|
17
|
+
except ImportError:
|
|
18
|
+
import logging
|
|
19
|
+
logger = logging.getLogger("dev_discovery")
|
|
20
|
+
|
|
21
|
+
# 发现服务器固定端口
|
|
22
|
+
DISCOVERY_PORT = 12318
|
|
23
|
+
|
|
24
|
+
# 全局变量
|
|
25
|
+
_server_instance: Optional[uvicorn.Server] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ServerInfo(BaseModel):
|
|
29
|
+
"""主程序服务器信息"""
|
|
30
|
+
host: str
|
|
31
|
+
port: int
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_discovery_app(main_host: str, main_port: int) -> FastAPI:
|
|
35
|
+
"""创建发现服务的 FastAPI 应用"""
|
|
36
|
+
app = FastAPI(
|
|
37
|
+
title="MoFox Dev Discovery",
|
|
38
|
+
description="开发模式服务发现",
|
|
39
|
+
version="1.0.0"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# 添加 CORS 中间件
|
|
43
|
+
app.add_middleware(
|
|
44
|
+
CORSMiddleware,
|
|
45
|
+
allow_origins=["*"],
|
|
46
|
+
allow_credentials=True,
|
|
47
|
+
allow_methods=["*"],
|
|
48
|
+
allow_headers=["*"],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@app.get("/api/health")
|
|
52
|
+
async def health_check():
|
|
53
|
+
"""健康检查"""
|
|
54
|
+
return {
|
|
55
|
+
"status": "ok",
|
|
56
|
+
"service": "MoFox Dev Discovery"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@app.get("/api/server-info", response_model=ServerInfo)
|
|
60
|
+
async def get_server_info():
|
|
61
|
+
"""获取主程序服务器信息
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
主程序的 host 和 port
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
GET http://127.0.0.1:12318/api/server-info
|
|
68
|
+
→ {"host": "127.0.0.1", "port": 8000}
|
|
69
|
+
|
|
70
|
+
WebSocket: ws://127.0.0.1:8000/plugin-api/dev_bridge/dev_bridge_router/ws
|
|
71
|
+
"""
|
|
72
|
+
return ServerInfo(host=main_host, port=main_port)
|
|
73
|
+
|
|
74
|
+
return app
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def start_discovery_server(
|
|
78
|
+
main_host: str,
|
|
79
|
+
main_port: int,
|
|
80
|
+
discovery_host: str = "127.0.0.1"
|
|
81
|
+
) -> None:
|
|
82
|
+
"""启动发现服务器
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
main_host: 主程序的 host
|
|
86
|
+
main_port: 主程序的 port
|
|
87
|
+
discovery_host: 发现服务器绑定的 host
|
|
88
|
+
"""
|
|
89
|
+
global _server_instance
|
|
90
|
+
|
|
91
|
+
if _server_instance is not None:
|
|
92
|
+
logger.warning("发现服务器已经在运行")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
app = create_discovery_app(main_host, main_port)
|
|
96
|
+
|
|
97
|
+
config = uvicorn.Config(
|
|
98
|
+
app,
|
|
99
|
+
host=discovery_host,
|
|
100
|
+
port=DISCOVERY_PORT,
|
|
101
|
+
log_level="error", # 减少日志输出
|
|
102
|
+
access_log=False
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
_server_instance = uvicorn.Server(config)
|
|
106
|
+
|
|
107
|
+
logger.info(f"发现服务器启动在 http://{discovery_host}:{DISCOVERY_PORT}")
|
|
108
|
+
logger.info(f"主程序地址: http://{main_host}:{main_port}")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
await _server_instance.serve()
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.error(f"发现服务器运行出错: {e}")
|
|
114
|
+
_server_instance = None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
async def stop_discovery_server() -> None:
|
|
118
|
+
"""停止发现服务器"""
|
|
119
|
+
global _server_instance
|
|
120
|
+
|
|
121
|
+
if _server_instance is None:
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
logger.info("正在停止发现服务器...")
|
|
125
|
+
_server_instance.should_exit = True
|
|
126
|
+
_server_instance = None
|