mofox-plugin-dev-toolkit 0.3.3__tar.gz → 0.3.5__tar.gz
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/mofox_plugin_dev_toolkit.egg-info → mofox_plugin_dev_toolkit-0.3.5}/PKG-INFO +1 -1
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5/mofox_plugin_dev_toolkit.egg-info}/PKG-INFO +1 -1
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/__init__.py +1 -1
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/commands/generate.py +0 -1
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/commands/init.py +0 -1
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/__init__.py +2 -3
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/config_loader.py +11 -11
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/config_manager.py +9 -16
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/file_ops.py +25 -22
- mofox_plugin_dev_toolkit-0.3.5/mpdt/utils/plugin_parser.py +137 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/pyproject.toml +1 -1
- mofox_plugin_dev_toolkit-0.3.3/mpdt/utils/plugin_parser.py +0 -195
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/LICENSE +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/MANIFEST.in +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/README.md +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mofox_plugin_dev_toolkit.egg-info/SOURCES.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mofox_plugin_dev_toolkit.egg-info/dependency_links.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mofox_plugin_dev_toolkit.egg-info/entry_points.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mofox_plugin_dev_toolkit.egg-info/requires.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mofox_plugin_dev_toolkit.egg-info/top_level.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/__main__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/cli.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/commands/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/commands/check.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/commands/dev.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/cleanup_handler.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/dev_config.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/file_watcher.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/plugin.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/action_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/adapter_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/chatter_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/event_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/plus_command_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/prompt_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/router_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/tool_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/code_parser.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/color_printer.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/license_generator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/template_engine.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/auto_fix_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/base.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/component_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/config_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/metadata_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/structure_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/style_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/type_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/setup.cfg +0 -0
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/__init__.py
RENAMED
|
@@ -69,7 +69,6 @@ def prepare_component_context(
|
|
|
69
69
|
plugin_name: str,
|
|
70
70
|
author: str = "",
|
|
71
71
|
description: str = "",
|
|
72
|
-
is_async: bool = False,
|
|
73
72
|
) -> dict[str, str]:
|
|
74
73
|
"""
|
|
75
74
|
准备组件模板上下文
|
|
@@ -116,8 +115,8 @@ def prepare_component_context(
|
|
|
116
115
|
"author": author,
|
|
117
116
|
"description": description or f"{class_name} 组件",
|
|
118
117
|
"date": date,
|
|
119
|
-
"async_keyword": "async "
|
|
120
|
-
"await_keyword": "await "
|
|
118
|
+
"async_keyword": "async " ,
|
|
119
|
+
"await_keyword": "await ",
|
|
121
120
|
"component_type": component_type + "s", # actions, tools, etc.
|
|
122
121
|
"module_name": component_name,
|
|
123
122
|
"method_name": _get_method_name(component_type),
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/config_loader.py
RENAMED
|
@@ -14,7 +14,7 @@ class ConfigLoader:
|
|
|
14
14
|
def __init__(self, config_path: Path | str | None = None):
|
|
15
15
|
"""
|
|
16
16
|
初始化配置加载器
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
Args:
|
|
19
19
|
config_path: 配置文件路径
|
|
20
20
|
"""
|
|
@@ -27,7 +27,7 @@ class ConfigLoader:
|
|
|
27
27
|
def load(self) -> dict[str, Any]:
|
|
28
28
|
"""
|
|
29
29
|
加载配置文件
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
Returns:
|
|
32
32
|
配置字典
|
|
33
33
|
"""
|
|
@@ -40,13 +40,13 @@ class ConfigLoader:
|
|
|
40
40
|
def get(self, key: str, default: Any = None) -> Any:
|
|
41
41
|
"""
|
|
42
42
|
获取配置项
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
支持点号分隔的嵌套键,例如: "mpdt.check.level"
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
Args:
|
|
47
47
|
key: 配置键
|
|
48
48
|
default: 默认值
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
Returns:
|
|
51
51
|
配置值
|
|
52
52
|
"""
|
|
@@ -64,7 +64,7 @@ class ConfigLoader:
|
|
|
64
64
|
def set(self, key: str, value: Any) -> None:
|
|
65
65
|
"""
|
|
66
66
|
设置配置项
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
Args:
|
|
69
69
|
key: 配置键
|
|
70
70
|
value: 配置值
|
|
@@ -82,7 +82,7 @@ class ConfigLoader:
|
|
|
82
82
|
def save(self, path: Path | str | None = None) -> None:
|
|
83
83
|
"""
|
|
84
84
|
保存配置到文件
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
Args:
|
|
87
87
|
path: 保存路径,如果为 None 则使用初始化时的路径
|
|
88
88
|
"""
|
|
@@ -100,14 +100,14 @@ class ConfigLoader:
|
|
|
100
100
|
def load_mpdt_config(project_dir: Path | str = ".") -> ConfigLoader:
|
|
101
101
|
"""
|
|
102
102
|
加载 MPDT 配置文件
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
优先级:
|
|
105
105
|
1. .mpdtrc.toml
|
|
106
106
|
2. pyproject.toml 中的 [tool.mpdt] 部分
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
Args:
|
|
109
109
|
project_dir: 项目目录
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
Returns:
|
|
112
112
|
ConfigLoader 对象
|
|
113
113
|
"""
|
|
@@ -134,7 +134,7 @@ def load_mpdt_config(project_dir: Path | str = ".") -> ConfigLoader:
|
|
|
134
134
|
def get_default_config() -> dict[str, Any]:
|
|
135
135
|
"""
|
|
136
136
|
获取默认配置
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
Returns:
|
|
139
139
|
默认配置字典
|
|
140
140
|
"""
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/config_manager.py
RENAMED
|
@@ -27,7 +27,7 @@ class MPDTConfig:
|
|
|
27
27
|
|
|
28
28
|
def __init__(self, config_path: Path | None = None):
|
|
29
29
|
"""初始化配置管理器
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
Args:
|
|
32
32
|
config_path: 配置文件路径,默认为 ~/.mpdt/config.toml
|
|
33
33
|
"""
|
|
@@ -131,7 +131,7 @@ class MPDTConfig:
|
|
|
131
131
|
|
|
132
132
|
def get_python_command(self) -> list[str]:
|
|
133
133
|
"""获取 Python 启动命令
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
Returns:
|
|
136
136
|
Python 命令列表,例如:
|
|
137
137
|
- ["E:/venv/Scripts/python.exe"]
|
|
@@ -169,7 +169,7 @@ class MPDTConfig:
|
|
|
169
169
|
|
|
170
170
|
def validate(self) -> tuple[bool, list[str]]:
|
|
171
171
|
"""验证配置
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
Returns:
|
|
174
174
|
(是否有效, 错误消息列表)
|
|
175
175
|
"""
|
|
@@ -212,7 +212,7 @@ def get_default_config() -> MPDTConfig:
|
|
|
212
212
|
|
|
213
213
|
def interactive_config() -> MPDTConfig:
|
|
214
214
|
"""交互式配置向导
|
|
215
|
-
|
|
215
|
+
|
|
216
216
|
Returns:
|
|
217
217
|
配置好的 MPDTConfig 实例
|
|
218
218
|
"""
|
|
@@ -223,16 +223,13 @@ def interactive_config() -> MPDTConfig:
|
|
|
223
223
|
console = Console()
|
|
224
224
|
config = MPDTConfig()
|
|
225
225
|
|
|
226
|
-
console.print(Panel.fit(
|
|
227
|
-
"[bold cyan]MPDT 配置向导[/bold cyan]\n\n"
|
|
228
|
-
"让我们配置 MoFox 主程序的路径和虚拟环境"
|
|
229
|
-
))
|
|
226
|
+
console.print(Panel.fit("[bold cyan]MPDT 配置向导[/bold cyan]\n\n让我们配置 MoFox 主程序的路径和虚拟环境"))
|
|
230
227
|
|
|
231
228
|
# 配置 mofox 路径
|
|
232
229
|
while True:
|
|
233
230
|
mofox_path_str = Prompt.ask(
|
|
234
231
|
"\n[bold]请输入 mofox 主程序路径[/bold]",
|
|
235
|
-
default=str(Path.cwd().parent / "mofox") if Path.cwd().parent.name != "mofox" else str(Path.cwd())
|
|
232
|
+
default=str(Path.cwd().parent / "mofox") if Path.cwd().parent.name != "mofox" else str(Path.cwd()),
|
|
236
233
|
)
|
|
237
234
|
mofox_path = Path(mofox_path_str).expanduser().absolute()
|
|
238
235
|
|
|
@@ -255,9 +252,7 @@ def interactive_config() -> MPDTConfig:
|
|
|
255
252
|
# 配置虚拟环境
|
|
256
253
|
console.print("\n[bold]虚拟环境配置[/bold]")
|
|
257
254
|
venv_type_choice = Prompt.ask(
|
|
258
|
-
"请选择虚拟环境类型",
|
|
259
|
-
choices=["venv", "uv", "conda", "poetry", "none"],
|
|
260
|
-
default="venv"
|
|
255
|
+
"请选择虚拟环境类型", choices=["venv", "uv", "conda", "poetry", "none"], default="venv"
|
|
261
256
|
)
|
|
262
257
|
config.venv_type = venv_type_choice
|
|
263
258
|
|
|
@@ -266,16 +261,14 @@ def interactive_config() -> MPDTConfig:
|
|
|
266
261
|
console.print("[cyan]使用 poetry,将在 mofox 目录中执行命令[/cyan]")
|
|
267
262
|
config.venv_path = config.mofox_path
|
|
268
263
|
else:
|
|
264
|
+
assert config.mofox_path is not None
|
|
269
265
|
default_venv_path = str(config.mofox_path.parent / "venv")
|
|
270
266
|
if venv_type_choice == "uv":
|
|
271
267
|
default_venv_path = str(config.mofox_path.parent / ".venv")
|
|
272
268
|
elif venv_type_choice == "conda":
|
|
273
269
|
default_venv_path = str(config.mofox_path.parent / "conda_env")
|
|
274
270
|
|
|
275
|
-
venv_path_str = Prompt.ask(
|
|
276
|
-
f"请输入 {venv_type_choice} 虚拟环境路径",
|
|
277
|
-
default=default_venv_path
|
|
278
|
-
)
|
|
271
|
+
venv_path_str = Prompt.ask(f"请输入 {venv_type_choice} 虚拟环境路径", default=default_venv_path)
|
|
279
272
|
venv_path = Path(venv_path_str).expanduser().absolute()
|
|
280
273
|
|
|
281
274
|
if not venv_path.exists():
|
|
@@ -9,10 +9,10 @@ from pathlib import Path
|
|
|
9
9
|
def ensure_dir(path: Path | str) -> Path:
|
|
10
10
|
"""
|
|
11
11
|
确保目录存在,如果不存在则创建
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
Args:
|
|
14
14
|
path: 目录路径
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
Returns:
|
|
17
17
|
Path 对象
|
|
18
18
|
"""
|
|
@@ -24,15 +24,15 @@ def ensure_dir(path: Path | str) -> Path:
|
|
|
24
24
|
def safe_write_file(path: Path | str, content: str, force: bool = False) -> bool:
|
|
25
25
|
"""
|
|
26
26
|
安全地写入文件
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
Args:
|
|
29
29
|
path: 文件路径
|
|
30
30
|
content: 文件内容
|
|
31
31
|
force: 是否覆盖已存在的文件
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
Returns:
|
|
34
34
|
是否写入成功
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
Raises:
|
|
37
37
|
FileExistsError: 文件已存在且 force=False
|
|
38
38
|
"""
|
|
@@ -52,12 +52,12 @@ def safe_write_file(path: Path | str, content: str, force: bool = False) -> bool
|
|
|
52
52
|
def copy_directory(src: Path | str, dst: Path | str, force: bool = False) -> bool:
|
|
53
53
|
"""
|
|
54
54
|
复制整个目录
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
Args:
|
|
57
57
|
src: 源目录
|
|
58
58
|
dst: 目标目录
|
|
59
59
|
force: 是否覆盖已存在的目录
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
Returns:
|
|
62
62
|
是否复制成功
|
|
63
63
|
"""
|
|
@@ -77,11 +77,11 @@ def copy_directory(src: Path | str, dst: Path | str, force: bool = False) -> boo
|
|
|
77
77
|
def list_python_files(path: Path | str, recursive: bool = True) -> list[Path]:
|
|
78
78
|
"""
|
|
79
79
|
列出目录中的所有 Python 文件
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
Args:
|
|
82
82
|
path: 目录路径
|
|
83
83
|
recursive: 是否递归搜索
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
Returns:
|
|
86
86
|
Python 文件路径列表
|
|
87
87
|
"""
|
|
@@ -96,32 +96,34 @@ def list_python_files(path: Path | str, recursive: bool = True) -> list[Path]:
|
|
|
96
96
|
def validate_plugin_name(name: str) -> bool:
|
|
97
97
|
"""
|
|
98
98
|
验证插件名称是否符合规范
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
规范: 使用小写字母、数字和下划线,以字母开头
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
Args:
|
|
103
103
|
name: 插件名称
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
Returns:
|
|
106
106
|
是否符合规范
|
|
107
107
|
"""
|
|
108
108
|
import re
|
|
109
|
+
|
|
109
110
|
return bool(re.match(r"^[a-z][a-z0-9_]*$", name))
|
|
110
111
|
|
|
111
112
|
|
|
112
113
|
def validate_component_name(name: str) -> bool:
|
|
113
114
|
"""
|
|
114
115
|
验证组件名称是否符合规范
|
|
115
|
-
|
|
116
|
+
|
|
116
117
|
规范: 支持 snake_case 或 PascalCase
|
|
117
|
-
|
|
118
|
+
|
|
118
119
|
Args:
|
|
119
120
|
name: 组件名称
|
|
120
|
-
|
|
121
|
+
|
|
121
122
|
Returns:
|
|
122
123
|
是否符合规范
|
|
123
124
|
"""
|
|
124
125
|
import re
|
|
126
|
+
|
|
125
127
|
# 支持 snake_case 或 PascalCase
|
|
126
128
|
return bool(re.match(r"^[a-zA-Z][a-zA-Z0-9_]*$", name))
|
|
127
129
|
|
|
@@ -129,7 +131,7 @@ def validate_component_name(name: str) -> bool:
|
|
|
129
131
|
def get_git_user_info() -> dict[str, str]:
|
|
130
132
|
"""
|
|
131
133
|
从 git config 获取用户信息
|
|
132
|
-
|
|
134
|
+
|
|
133
135
|
Returns:
|
|
134
136
|
包含 name 和 email 的字典
|
|
135
137
|
"""
|
|
@@ -143,8 +145,8 @@ def get_git_user_info() -> dict[str, str]:
|
|
|
143
145
|
capture_output=True,
|
|
144
146
|
text=True,
|
|
145
147
|
check=False,
|
|
146
|
-
encoding=
|
|
147
|
-
errors=
|
|
148
|
+
encoding="utf-8",
|
|
149
|
+
errors="ignore",
|
|
148
150
|
)
|
|
149
151
|
if name.returncode == 0:
|
|
150
152
|
result["name"] = name.stdout.strip()
|
|
@@ -154,8 +156,8 @@ def get_git_user_info() -> dict[str, str]:
|
|
|
154
156
|
capture_output=True,
|
|
155
157
|
text=True,
|
|
156
158
|
check=False,
|
|
157
|
-
encoding=
|
|
158
|
-
errors=
|
|
159
|
+
encoding="utf-8",
|
|
160
|
+
errors="ignore",
|
|
159
161
|
)
|
|
160
162
|
if email.returncode == 0:
|
|
161
163
|
result["email"] = email.stdout.strip()
|
|
@@ -201,7 +203,8 @@ def to_snake_case(pascal_str: str) -> str:
|
|
|
201
203
|
'test_command_handler'
|
|
202
204
|
"""
|
|
203
205
|
import re
|
|
206
|
+
|
|
204
207
|
# 在大写字母前插入下划线
|
|
205
|
-
s1 = re.sub(
|
|
208
|
+
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", pascal_str)
|
|
206
209
|
# 处理连续大写字母
|
|
207
|
-
return re.sub(
|
|
210
|
+
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
插件名称解析器
|
|
3
|
+
使用统一的 CodeParser 解析插件文件,提取运行时插件名称
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .code_parser import CodeParser
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def extract_plugin_name(plugin_path: Path) -> str | None:
|
|
12
|
+
"""从插件目录提取运行时插件名称
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
plugin_path: 插件目录路径
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
插件名称,如果解析失败返回 None
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> extract_plugin_name(Path("my_awesome_plugin"))
|
|
22
|
+
"awesome_plugin" # 从 plugin.py 中的 plugin_name 属性读取
|
|
23
|
+
"""
|
|
24
|
+
plugin_file = plugin_path / "plugin.py"
|
|
25
|
+
|
|
26
|
+
if not plugin_file.exists():
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
parser = CodeParser.from_file(plugin_file)
|
|
31
|
+
return parser.find_class_attribute(base_class="BasePlugin", attribute_name="plugin_name")
|
|
32
|
+
except Exception:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_plugin_info(plugin_path: Path) -> dict:
|
|
37
|
+
"""获取插件详细信息
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
plugin_path: 插件目录路径
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
包含插件信息的字典:
|
|
44
|
+
{
|
|
45
|
+
"dir_name": "my_awesome_plugin",
|
|
46
|
+
"plugin_name": "awesome_plugin",
|
|
47
|
+
"class_name": "MyAwesomePlugin",
|
|
48
|
+
"path": "/path/to/plugin",
|
|
49
|
+
"has_plugin_file": True,
|
|
50
|
+
"parse_success": True
|
|
51
|
+
}
|
|
52
|
+
"""
|
|
53
|
+
info = {
|
|
54
|
+
"dir_name": plugin_path.name,
|
|
55
|
+
"plugin_name": None,
|
|
56
|
+
"class_name": None,
|
|
57
|
+
"path": str(plugin_path.absolute()),
|
|
58
|
+
"has_plugin_file": False,
|
|
59
|
+
"parse_success": False,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
plugin_file = plugin_path / "plugin.py"
|
|
63
|
+
|
|
64
|
+
if not plugin_file.exists():
|
|
65
|
+
return info
|
|
66
|
+
|
|
67
|
+
info["has_plugin_file"] = True
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
parser = CodeParser.from_file(plugin_file)
|
|
71
|
+
|
|
72
|
+
# 查找 BasePlugin 的子类
|
|
73
|
+
classes = parser.find_class(base_class="BasePlugin")
|
|
74
|
+
|
|
75
|
+
if classes:
|
|
76
|
+
# 获取第一个匹配的类
|
|
77
|
+
cls = classes[0]
|
|
78
|
+
info["class_name"] = cls.name.value
|
|
79
|
+
|
|
80
|
+
# 提取 plugin_name 属性
|
|
81
|
+
plugin_name = parser.find_class_attribute(base_class="BasePlugin", attribute_name="plugin_name")
|
|
82
|
+
|
|
83
|
+
if plugin_name:
|
|
84
|
+
info["plugin_name"] = plugin_name
|
|
85
|
+
info["parse_success"] = True
|
|
86
|
+
|
|
87
|
+
return info
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
info["error"] = str(e)
|
|
91
|
+
return info
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def validate_plugin_structure(plugin_path: Path) -> tuple[bool, list[str]]:
|
|
95
|
+
"""验证插件目录结构
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
plugin_path: 插件目录路径
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
(是否有效, 错误/警告消息列表)
|
|
102
|
+
"""
|
|
103
|
+
messages = []
|
|
104
|
+
|
|
105
|
+
if not plugin_path.is_dir():
|
|
106
|
+
return False, ["路径不是一个目录"]
|
|
107
|
+
|
|
108
|
+
# 检查必需文件
|
|
109
|
+
required_files = {"plugin.py": "插件主文件", "__init__.py": "包初始化文件"}
|
|
110
|
+
|
|
111
|
+
for filename, description in required_files.items():
|
|
112
|
+
file_path = plugin_path / filename
|
|
113
|
+
if not file_path.exists():
|
|
114
|
+
messages.append(f"缺少 {description}: {filename}")
|
|
115
|
+
|
|
116
|
+
# 如果缺少必需文件,直接返回
|
|
117
|
+
if messages:
|
|
118
|
+
return False, messages
|
|
119
|
+
|
|
120
|
+
# 检查 plugin.py 是否可以解析
|
|
121
|
+
plugin_name = extract_plugin_name(plugin_path)
|
|
122
|
+
if not plugin_name:
|
|
123
|
+
messages.append("无法从 plugin.py 中提取 plugin_name")
|
|
124
|
+
messages.append("请确保有一个继承自 BasePlugin 的类,并定义了 plugin_name 属性")
|
|
125
|
+
return False, messages
|
|
126
|
+
|
|
127
|
+
# 检查推荐文件
|
|
128
|
+
recommended_files = {"config.toml": "配置文件", "README.md": "说明文档"}
|
|
129
|
+
|
|
130
|
+
for filename, description in recommended_files.items():
|
|
131
|
+
file_path = plugin_path / filename
|
|
132
|
+
if not file_path.exists():
|
|
133
|
+
messages.append(f"建议添加 {description}: {filename}")
|
|
134
|
+
|
|
135
|
+
# 如果只有建议性消息,仍然返回有效
|
|
136
|
+
has_errors = any("缺少" in msg or "无法" in msg for msg in messages)
|
|
137
|
+
return not has_errors, messages
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
插件名称解析器
|
|
3
|
-
使用 AST 解析插件文件,提取运行时插件名称
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import ast
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def extract_plugin_name(plugin_path: Path) -> str | None:
|
|
11
|
-
"""从插件目录提取运行时插件名称
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
plugin_path: 插件目录路径
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
插件名称,如果解析失败返回 None
|
|
18
|
-
|
|
19
|
-
Example:
|
|
20
|
-
>>> extract_plugin_name(Path("my_awesome_plugin"))
|
|
21
|
-
"awesome_plugin" # 从 plugin.py 中的 plugin_name 属性读取
|
|
22
|
-
"""
|
|
23
|
-
plugin_file = plugin_path / "plugin.py"
|
|
24
|
-
|
|
25
|
-
if not plugin_file.exists():
|
|
26
|
-
return None
|
|
27
|
-
|
|
28
|
-
try:
|
|
29
|
-
with open(plugin_file, encoding="utf-8") as f:
|
|
30
|
-
source = f.read()
|
|
31
|
-
|
|
32
|
-
tree = ast.parse(source)
|
|
33
|
-
|
|
34
|
-
# 查找 BasePlugin 的子类
|
|
35
|
-
for node in ast.walk(tree):
|
|
36
|
-
if isinstance(node, ast.ClassDef):
|
|
37
|
-
# 检查是否继承自 BasePlugin
|
|
38
|
-
is_base_plugin = False
|
|
39
|
-
for base in node.bases:
|
|
40
|
-
if isinstance(base, ast.Name) and base.id == "BasePlugin":
|
|
41
|
-
is_base_plugin = True
|
|
42
|
-
break
|
|
43
|
-
|
|
44
|
-
if not is_base_plugin:
|
|
45
|
-
continue
|
|
46
|
-
|
|
47
|
-
# 查找 plugin_name 属性
|
|
48
|
-
for item in node.body:
|
|
49
|
-
# 处理普通赋值: plugin_name = "xxx"
|
|
50
|
-
if isinstance(item, ast.Assign):
|
|
51
|
-
for target in item.targets:
|
|
52
|
-
if isinstance(target, ast.Name) and target.id == "plugin_name":
|
|
53
|
-
if isinstance(item.value, ast.Constant):
|
|
54
|
-
return item.value.value
|
|
55
|
-
|
|
56
|
-
# 处理带类型注解的赋值: plugin_name: str = "xxx"
|
|
57
|
-
elif isinstance(item, ast.AnnAssign):
|
|
58
|
-
if isinstance(item.target, ast.Name) and item.target.id == "plugin_name":
|
|
59
|
-
if item.value and isinstance(item.value, ast.Constant):
|
|
60
|
-
return item.value.value
|
|
61
|
-
|
|
62
|
-
return None
|
|
63
|
-
|
|
64
|
-
except Exception:
|
|
65
|
-
return None
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def get_plugin_info(plugin_path: Path) -> dict:
|
|
69
|
-
"""获取插件详细信息
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
plugin_path: 插件目录路径
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
包含插件信息的字典:
|
|
76
|
-
{
|
|
77
|
-
"dir_name": "my_awesome_plugin",
|
|
78
|
-
"plugin_name": "awesome_plugin",
|
|
79
|
-
"class_name": "MyAwesomePlugin",
|
|
80
|
-
"path": "/path/to/plugin",
|
|
81
|
-
"has_plugin_file": True,
|
|
82
|
-
"parse_success": True
|
|
83
|
-
}
|
|
84
|
-
"""
|
|
85
|
-
info = {
|
|
86
|
-
"dir_name": plugin_path.name,
|
|
87
|
-
"plugin_name": None,
|
|
88
|
-
"class_name": None,
|
|
89
|
-
"path": str(plugin_path.absolute()),
|
|
90
|
-
"has_plugin_file": False,
|
|
91
|
-
"parse_success": False,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
plugin_file = plugin_path / "plugin.py"
|
|
95
|
-
|
|
96
|
-
if not plugin_file.exists():
|
|
97
|
-
return info
|
|
98
|
-
|
|
99
|
-
info["has_plugin_file"] = True
|
|
100
|
-
|
|
101
|
-
try:
|
|
102
|
-
with open(plugin_file, encoding="utf-8") as f:
|
|
103
|
-
source = f.read()
|
|
104
|
-
|
|
105
|
-
tree = ast.parse(source)
|
|
106
|
-
|
|
107
|
-
# 查找 BasePlugin 的子类
|
|
108
|
-
for node in ast.walk(tree):
|
|
109
|
-
if isinstance(node, ast.ClassDef):
|
|
110
|
-
# 检查是否继承自 BasePlugin
|
|
111
|
-
is_base_plugin = False
|
|
112
|
-
for base in node.bases:
|
|
113
|
-
if isinstance(base, ast.Name) and base.id == "BasePlugin":
|
|
114
|
-
is_base_plugin = True
|
|
115
|
-
break
|
|
116
|
-
|
|
117
|
-
if not is_base_plugin:
|
|
118
|
-
continue
|
|
119
|
-
|
|
120
|
-
info["class_name"] = node.name
|
|
121
|
-
|
|
122
|
-
# 查找 plugin_name 属性
|
|
123
|
-
for item in node.body:
|
|
124
|
-
# 处理普通赋值: plugin_name = "xxx"
|
|
125
|
-
if isinstance(item, ast.Assign):
|
|
126
|
-
for target in item.targets:
|
|
127
|
-
if isinstance(target, ast.Name) and target.id == "plugin_name":
|
|
128
|
-
if isinstance(item.value, ast.Constant):
|
|
129
|
-
info["plugin_name"] = item.value.value
|
|
130
|
-
info["parse_success"] = True
|
|
131
|
-
break
|
|
132
|
-
|
|
133
|
-
# 处理带类型注解的赋值: plugin_name: str = "xxx"
|
|
134
|
-
elif isinstance(item, ast.AnnAssign):
|
|
135
|
-
if isinstance(item.target, ast.Name) and item.target.id == "plugin_name":
|
|
136
|
-
if item.value and isinstance(item.value, ast.Constant):
|
|
137
|
-
info["plugin_name"] = item.value.value
|
|
138
|
-
info["parse_success"] = True
|
|
139
|
-
break
|
|
140
|
-
|
|
141
|
-
# 找到 BasePlugin 子类后跳出
|
|
142
|
-
if info["class_name"]:
|
|
143
|
-
break
|
|
144
|
-
|
|
145
|
-
return info
|
|
146
|
-
|
|
147
|
-
except Exception as e:
|
|
148
|
-
info["error"] = str(e)
|
|
149
|
-
return info
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def validate_plugin_structure(plugin_path: Path) -> tuple[bool, list[str]]:
|
|
153
|
-
"""验证插件目录结构
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
plugin_path: 插件目录路径
|
|
157
|
-
|
|
158
|
-
Returns:
|
|
159
|
-
(是否有效, 错误/警告消息列表)
|
|
160
|
-
"""
|
|
161
|
-
messages = []
|
|
162
|
-
|
|
163
|
-
if not plugin_path.is_dir():
|
|
164
|
-
return False, ["路径不是一个目录"]
|
|
165
|
-
|
|
166
|
-
# 检查必需文件
|
|
167
|
-
required_files = {"plugin.py": "插件主文件", "__init__.py": "包初始化文件"}
|
|
168
|
-
|
|
169
|
-
for filename, description in required_files.items():
|
|
170
|
-
file_path = plugin_path / filename
|
|
171
|
-
if not file_path.exists():
|
|
172
|
-
messages.append(f"缺少 {description}: {filename}")
|
|
173
|
-
|
|
174
|
-
# 如果缺少必需文件,直接返回
|
|
175
|
-
if messages:
|
|
176
|
-
return False, messages
|
|
177
|
-
|
|
178
|
-
# 检查 plugin.py 是否可以解析
|
|
179
|
-
plugin_name = extract_plugin_name(plugin_path)
|
|
180
|
-
if not plugin_name:
|
|
181
|
-
messages.append("无法从 plugin.py 中提取 plugin_name")
|
|
182
|
-
messages.append("请确保有一个继承自 BasePlugin 的类,并定义了 plugin_name 属性")
|
|
183
|
-
return False, messages
|
|
184
|
-
|
|
185
|
-
# 检查推荐文件
|
|
186
|
-
recommended_files = {"config.toml": "配置文件", "README.md": "说明文档"}
|
|
187
|
-
|
|
188
|
-
for filename, description in recommended_files.items():
|
|
189
|
-
file_path = plugin_path / filename
|
|
190
|
-
if not file_path.exists():
|
|
191
|
-
messages.append(f"建议添加 {description}: {filename}")
|
|
192
|
-
|
|
193
|
-
# 如果只有建议性消息,仍然返回有效
|
|
194
|
-
has_errors = any("缺少" in msg or "无法" in msg for msg in messages)
|
|
195
|
-
return not has_errors, messages
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/dev/bridge_plugin/plugin.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/action_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/adapter_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/chatter_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/event_template.py
RENAMED
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/prompt_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/router_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/templates/tool_template.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/color_printer.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/license_generator.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/utils/template_engine.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/style_validator.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.3.3 → mofox_plugin_dev_toolkit-0.3.5}/mpdt/validators/type_validator.py
RENAMED
|
File without changes
|
|
File without changes
|