mofox-plugin-dev-toolkit 0.5.1__tar.gz → 0.5.2__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.5.1/mofox_plugin_dev_toolkit.egg-info → mofox_plugin_dev_toolkit-0.5.2}/PKG-INFO +2 -1
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2/mofox_plugin_dev_toolkit.egg-info}/PKG-INFO +2 -1
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mofox_plugin_dev_toolkit.egg-info/requires.txt +1 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/build.py +3 -3
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/init.py +2 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/market.py +3 -3
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/market/manifest.py +58 -2
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/manifest_metadata.py +62 -10
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/metadata_validator.py +2 -2
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/pyproject.toml +2 -1
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/LICENSE +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/MANIFEST.in +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/README.md +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mofox_plugin_dev_toolkit.egg-info/SOURCES.txt +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mofox_plugin_dev_toolkit.egg-info/dependency_links.txt +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mofox_plugin_dev_toolkit.egg-info/entry_points.txt +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mofox_plugin_dev_toolkit.egg-info/top_level.txt +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/__main__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/cli.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/check.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/dev.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/commands/generate.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/cleanup_handler.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/dev_config.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/file_watcher.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/manifest.json +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/plugin.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/market/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/market/client.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/market/config.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/market/git.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/market/github.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/action_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/adapter_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/chatter_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/collection_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/config_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/event_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/plus_command_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/prompt_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/router_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/service_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/tool_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/code_parser.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/color_printer.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/config_loader.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/config_manager.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/file_ops.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/license_generator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/plugin_parser.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/template_engine.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/auto_fix_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/base.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/component_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/config_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/structure_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/style_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/type_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mofox-plugin-dev-toolkit
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: 开发工具集,用于快速创建、开发和测试 MoFox-Bot 插件
|
|
5
5
|
Author-email: MoFox-Studio <wwwww95915@qq.com>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -30,6 +30,7 @@ Requires-Dist: watchdog>=3.0.0
|
|
|
30
30
|
Requires-Dist: websockets>=12.0
|
|
31
31
|
Requires-Dist: libcst>=1.8.6
|
|
32
32
|
Requires-Dist: aiohttp>=3.9.0
|
|
33
|
+
Requires-Dist: pillow>=11.2.0
|
|
33
34
|
Requires-Dist: uvicorn>=0.24.0
|
|
34
35
|
Requires-Dist: fastapi>=0.104.0
|
|
35
36
|
Requires-Dist: ruff>=0.1.6
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mofox-plugin-dev-toolkit
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: 开发工具集,用于快速创建、开发和测试 MoFox-Bot 插件
|
|
5
5
|
Author-email: MoFox-Studio <wwwww95915@qq.com>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -30,6 +30,7 @@ Requires-Dist: watchdog>=3.0.0
|
|
|
30
30
|
Requires-Dist: websockets>=12.0
|
|
31
31
|
Requires-Dist: libcst>=1.8.6
|
|
32
32
|
Requires-Dist: aiohttp>=3.9.0
|
|
33
|
+
Requires-Dist: pillow>=11.2.0
|
|
33
34
|
Requires-Dist: uvicorn>=0.24.0
|
|
34
35
|
Requires-Dist: fastapi>=0.104.0
|
|
35
36
|
Requires-Dist: ruff>=0.1.6
|
|
@@ -181,14 +181,14 @@ def _save_manifest(plugin_dir: Path, manifest: dict) -> None:
|
|
|
181
181
|
json.dump(manifest, f, ensure_ascii=False, indent=4)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def _validate_manifest_metadata(manifest: dict) -> None:
|
|
184
|
+
def _validate_manifest_metadata(manifest: dict, plugin_dir: Path) -> None:
|
|
185
185
|
"""验证打包所需的 manifest 元数据。"""
|
|
186
186
|
required = ["name", "version", "description", "author", "entry_point"]
|
|
187
187
|
for field in required:
|
|
188
188
|
if field not in manifest:
|
|
189
189
|
raise ValueError(f"manifest.json 缺少必需字段: '{field}'")
|
|
190
190
|
|
|
191
|
-
errors = metadata_errors(manifest)
|
|
191
|
+
errors = metadata_errors(manifest, plugin_dir)
|
|
192
192
|
if errors:
|
|
193
193
|
raise ValueError(errors[0])
|
|
194
194
|
|
|
@@ -279,7 +279,7 @@ def build_package(
|
|
|
279
279
|
|
|
280
280
|
manifest = ensure_manifest_metadata_interactive(plugin_dir, manifest)
|
|
281
281
|
|
|
282
|
-
_validate_manifest_metadata(manifest)
|
|
282
|
+
_validate_manifest_metadata(manifest, plugin_dir)
|
|
283
283
|
|
|
284
284
|
plugin_name: str = manifest["name"]
|
|
285
285
|
plugin_version: str = manifest["version"]
|
|
@@ -369,9 +369,11 @@ def _generate_manifest_file(
|
|
|
369
369
|
|
|
370
370
|
manifest = {
|
|
371
371
|
"name": plugin_name,
|
|
372
|
+
"display_name": plugin_name,
|
|
372
373
|
"version": "1.0.0",
|
|
373
374
|
"description": description or f"{plugin_name} 插件",
|
|
374
375
|
"author": author or "Your Name",
|
|
376
|
+
"icon": "icon.png",
|
|
375
377
|
"categories": categories or ["tool"],
|
|
376
378
|
"tags": tags or [plugin_name],
|
|
377
379
|
"dependencies": {"plugins": [], "components": []},
|
|
@@ -51,7 +51,7 @@ def market_register(plugin_path: str = ".", market_url: str | None = None, token
|
|
|
51
51
|
|
|
52
52
|
async def run() -> None:
|
|
53
53
|
manifest = await _load_manifest_for_market(plugin_path)
|
|
54
|
-
payload = plugin_payload(manifest, repository_url=repository_url)
|
|
54
|
+
payload = plugin_payload(manifest, repository_url=repository_url, plugin_dir=plugin_path)
|
|
55
55
|
result = await _client(market_url, token).register_plugin(payload)
|
|
56
56
|
_print_ok(f"Plugin registered: {result['plugin_id']} ({result['status']})")
|
|
57
57
|
|
|
@@ -63,7 +63,7 @@ def market_update(plugin_path: str = ".", market_url: str | None = None, token:
|
|
|
63
63
|
|
|
64
64
|
async def run() -> None:
|
|
65
65
|
manifest = await _load_manifest_for_market(plugin_path)
|
|
66
|
-
payload = plugin_payload(manifest, repository_url=repository_url)
|
|
66
|
+
payload = plugin_payload(manifest, repository_url=repository_url, plugin_dir=plugin_path)
|
|
67
67
|
plugin_id = payload.pop("plugin_id")
|
|
68
68
|
result = await _client(market_url, token).update_plugin(plugin_id, payload)
|
|
69
69
|
_print_ok(f"Plugin updated: {result['plugin_id']} ({result['status']})")
|
|
@@ -232,7 +232,7 @@ def market_publish(
|
|
|
232
232
|
asset_url = str(asset.get("browser_download_url") or asset_download_url_for(plugin_id, version, package.package_path.name))
|
|
233
233
|
|
|
234
234
|
market = _client(market_url, token or github_api_token)
|
|
235
|
-
plugin_registration_payload = plugin_payload(manifest, repository_url=repo_html_url)
|
|
235
|
+
plugin_registration_payload = plugin_payload(manifest, repository_url=repo_html_url, plugin_dir=plugin_dir)
|
|
236
236
|
try:
|
|
237
237
|
plugin = await market.register_plugin(plugin_registration_payload)
|
|
238
238
|
_print_ok(f"Plugin registered: {plugin['plugin_id']} ({plugin['status']})")
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import base64
|
|
6
|
+
import io
|
|
5
7
|
import json
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import Any
|
|
8
10
|
|
|
11
|
+
from PIL import Image, ImageOps
|
|
12
|
+
|
|
9
13
|
|
|
10
14
|
def load_manifest(plugin_path: str = ".") -> dict[str, Any]:
|
|
11
15
|
"""Load manifest.json from a plugin directory."""
|
|
@@ -20,16 +24,24 @@ def load_manifest(plugin_path: str = ".") -> dict[str, Any]:
|
|
|
20
24
|
return data
|
|
21
25
|
|
|
22
26
|
|
|
23
|
-
def plugin_payload(
|
|
27
|
+
def plugin_payload(
|
|
28
|
+
manifest: dict[str, Any],
|
|
29
|
+
repository_url: str | None = None,
|
|
30
|
+
plugin_dir: str | Path | None = None,
|
|
31
|
+
) -> dict[str, Any]:
|
|
24
32
|
"""Convert MPDT manifest.json to market plugin registration payload."""
|
|
25
33
|
|
|
26
34
|
plugin_id = str(manifest.get("name") or "").strip()
|
|
27
35
|
if not plugin_id:
|
|
28
36
|
raise ValueError("manifest.json 缺少 name 字段")
|
|
37
|
+
display_name = str(manifest.get("display_name") or "").strip()
|
|
38
|
+
if not display_name:
|
|
39
|
+
raise ValueError("manifest.json 缺少 display_name 字段")
|
|
29
40
|
repo = repository_url or manifest.get("repository_url") or f"https://github.com/MoFox-Studio/{plugin_id}"
|
|
41
|
+
resolved_plugin_dir = Path(plugin_dir).resolve() if plugin_dir is not None else None
|
|
30
42
|
return {
|
|
31
43
|
"plugin_id": plugin_id,
|
|
32
|
-
"display_name":
|
|
44
|
+
"display_name": display_name,
|
|
33
45
|
"summary": str(manifest.get("summary") or manifest.get("description") or f"{plugin_id} 插件"),
|
|
34
46
|
"description": str(manifest.get("description") or ""),
|
|
35
47
|
"homepage": manifest.get("homepage") or repo,
|
|
@@ -38,6 +50,9 @@ def plugin_payload(manifest: dict[str, Any], repository_url: str | None = None)
|
|
|
38
50
|
"categories": list(manifest.get("categories") or []),
|
|
39
51
|
"tags": list(manifest.get("tags") or []),
|
|
40
52
|
"maintainers": _maintainers(manifest),
|
|
53
|
+
"plugin_dependencies": list(((manifest.get("dependencies") or {}).get("plugins") or [])),
|
|
54
|
+
"icon_png_base64": _icon_png_base64(manifest, resolved_plugin_dir),
|
|
55
|
+
"readme_markdown": _readme_markdown(resolved_plugin_dir),
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
|
|
@@ -100,3 +115,44 @@ def _maintainers(manifest: dict[str, Any]) -> list[str]:
|
|
|
100
115
|
return [str(item) for item in maintainers]
|
|
101
116
|
author = str(manifest.get("author") or "mock-author").strip()
|
|
102
117
|
return [author or "mock-author"]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _icon_png_base64(manifest: dict[str, Any], plugin_dir: Path | None) -> str | None:
|
|
121
|
+
"""Return a normalized base64 PNG icon for upload, if configured."""
|
|
122
|
+
|
|
123
|
+
raw_icon = str(manifest.get("icon") or "").strip()
|
|
124
|
+
if not raw_icon:
|
|
125
|
+
return None
|
|
126
|
+
if plugin_dir is None:
|
|
127
|
+
raise ValueError("manifest.json 包含 icon 字段,但当前上下文缺少插件目录")
|
|
128
|
+
icon_path = (plugin_dir / raw_icon).resolve() if not Path(raw_icon).is_absolute() else Path(raw_icon)
|
|
129
|
+
if not icon_path.exists() or not icon_path.is_file():
|
|
130
|
+
raise ValueError(f"manifest.json 的 icon 指向的文件不存在: {icon_path}")
|
|
131
|
+
if icon_path.suffix.lower() != ".png":
|
|
132
|
+
raise ValueError("manifest.json 的 icon 必须指向 PNG 文件")
|
|
133
|
+
|
|
134
|
+
with Image.open(icon_path) as image:
|
|
135
|
+
if (image.format or "").upper() != "PNG":
|
|
136
|
+
raise ValueError("manifest.json 的 icon 必须是有效的 PNG 图片")
|
|
137
|
+
working = ImageOps.contain(image.convert("RGBA"), (512, 512), method=Image.Resampling.LANCZOS)
|
|
138
|
+
|
|
139
|
+
canvas = Image.new("RGBA", (512, 512), (0, 0, 0, 0))
|
|
140
|
+
offset = ((512 - working.width) // 2, (512 - working.height) // 2)
|
|
141
|
+
canvas.paste(working, offset, working)
|
|
142
|
+
normalized = canvas.convert("P", palette=Image.Palette.ADAPTIVE, colors=256)
|
|
143
|
+
|
|
144
|
+
buffer = io.BytesIO()
|
|
145
|
+
normalized.save(buffer, format="PNG", optimize=True)
|
|
146
|
+
return base64.b64encode(buffer.getvalue()).decode("ascii")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _readme_markdown(plugin_dir: Path | None) -> str | None:
|
|
150
|
+
"""Load README.md contents for market detail rendering when present."""
|
|
151
|
+
|
|
152
|
+
if plugin_dir is None:
|
|
153
|
+
return None
|
|
154
|
+
readme_path = plugin_dir / "README.md"
|
|
155
|
+
if not readme_path.exists() or not readme_path.is_file():
|
|
156
|
+
return None
|
|
157
|
+
content = readme_path.read_text(encoding="utf-8").strip()
|
|
158
|
+
return content or None
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/manifest_metadata.py
RENAMED
|
@@ -15,6 +15,15 @@ from .color_printer import print_success, print_warning
|
|
|
15
15
|
ALLOWED_CATEGORIES = ("tool", "chat", "fun", "information", "moderation")
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def get_display_name(manifest: dict[str, Any]) -> str:
|
|
19
|
+
"""Return normalized display_name from manifest."""
|
|
20
|
+
|
|
21
|
+
value = manifest.get("display_name")
|
|
22
|
+
if not isinstance(value, str):
|
|
23
|
+
return ""
|
|
24
|
+
return value.strip()
|
|
25
|
+
|
|
26
|
+
|
|
18
27
|
def get_categories(manifest: dict[str, Any]) -> list[str]:
|
|
19
28
|
"""Return normalized categories from manifest."""
|
|
20
29
|
value = manifest.get("categories")
|
|
@@ -60,14 +69,19 @@ def tags_are_valid(manifest: dict[str, Any]) -> bool:
|
|
|
60
69
|
return bool(get_tags(manifest))
|
|
61
70
|
|
|
62
71
|
|
|
63
|
-
def metadata_errors(manifest: dict[str, Any]) -> list[str]:
|
|
72
|
+
def metadata_errors(manifest: dict[str, Any], plugin_dir: Path | None = None) -> list[str]:
|
|
64
73
|
"""Return validation errors for required manifest metadata."""
|
|
65
74
|
errors: list[str] = []
|
|
75
|
+
if not get_display_name(manifest):
|
|
76
|
+
errors.append("manifest.json 的 display_name 必须是非空字符串")
|
|
66
77
|
if not categories_are_valid(manifest):
|
|
67
78
|
allowed = ", ".join(ALLOWED_CATEGORIES)
|
|
68
79
|
errors.append(f"manifest.json 的 categories 必须是只包含一个值的数组,且取值只能是: {allowed}")
|
|
69
80
|
if not tags_are_valid(manifest):
|
|
70
81
|
errors.append("manifest.json 的 tags 必须是至少包含一个非空字符串的数组")
|
|
82
|
+
icon_error = _icon_error(manifest, plugin_dir)
|
|
83
|
+
if icon_error:
|
|
84
|
+
errors.append(icon_error)
|
|
71
85
|
return errors
|
|
72
86
|
|
|
73
87
|
|
|
@@ -99,13 +113,22 @@ def _save_manifest(plugin_dir: Path, manifest: dict[str, Any]) -> None:
|
|
|
99
113
|
json.dump(manifest, f, ensure_ascii=False, indent=4)
|
|
100
114
|
|
|
101
115
|
|
|
102
|
-
def prompt_manifest_metadata(existing_manifest: dict[str, Any] | None = None) -> dict[str,
|
|
116
|
+
def prompt_manifest_metadata(existing_manifest: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
103
117
|
"""Collect required manifest metadata from the console."""
|
|
104
118
|
manifest = existing_manifest or {}
|
|
119
|
+
display_name = get_display_name(manifest) or str(manifest.get("name") or "").strip()
|
|
105
120
|
existing_categories = get_categories(manifest)
|
|
106
121
|
default_category = existing_categories[0] if existing_categories else ALLOWED_CATEGORIES[0]
|
|
107
122
|
existing_tags = ", ".join(get_tags(manifest))
|
|
108
123
|
|
|
124
|
+
resolved_display_name = questionary.text(
|
|
125
|
+
"填写插件展示名 display_name:",
|
|
126
|
+
default=display_name,
|
|
127
|
+
validate=lambda value: bool(str(value).strip()) or "display_name 不能为空",
|
|
128
|
+
).ask()
|
|
129
|
+
if resolved_display_name is None:
|
|
130
|
+
raise ValueError("已取消填写 display_name")
|
|
131
|
+
|
|
109
132
|
category = questionary.select(
|
|
110
133
|
"选择插件分类 categories(只能选一个):",
|
|
111
134
|
choices=[questionary.Choice(item, value=item) for item in ALLOWED_CATEGORIES],
|
|
@@ -123,18 +146,28 @@ def prompt_manifest_metadata(existing_manifest: dict[str, Any] | None = None) ->
|
|
|
123
146
|
raise ValueError("已取消填写 tags")
|
|
124
147
|
|
|
125
148
|
return {
|
|
149
|
+
"display_name": str(resolved_display_name).strip(),
|
|
126
150
|
"categories": [category],
|
|
127
151
|
"tags": normalize_tags(tags_text),
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
|
|
131
|
-
async def prompt_manifest_metadata_async(existing_manifest: dict[str, Any] | None = None) -> dict[str,
|
|
155
|
+
async def prompt_manifest_metadata_async(existing_manifest: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
132
156
|
"""Collect required manifest metadata from the console inside an event loop."""
|
|
133
157
|
manifest = existing_manifest or {}
|
|
158
|
+
display_name = get_display_name(manifest) or str(manifest.get("name") or "").strip()
|
|
134
159
|
existing_categories = get_categories(manifest)
|
|
135
160
|
default_category = existing_categories[0] if existing_categories else ALLOWED_CATEGORIES[0]
|
|
136
161
|
existing_tags = ", ".join(get_tags(manifest))
|
|
137
162
|
|
|
163
|
+
resolved_display_name = await questionary.text(
|
|
164
|
+
"填写插件展示名 display_name:",
|
|
165
|
+
default=display_name,
|
|
166
|
+
validate=lambda value: bool(str(value).strip()) or "display_name 不能为空",
|
|
167
|
+
).ask_async()
|
|
168
|
+
if resolved_display_name is None:
|
|
169
|
+
raise ValueError("已取消填写 display_name")
|
|
170
|
+
|
|
138
171
|
category = await questionary.select(
|
|
139
172
|
"选择插件分类 categories(只能选一个):",
|
|
140
173
|
choices=[questionary.Choice(item, value=item) for item in ALLOWED_CATEGORIES],
|
|
@@ -152,6 +185,7 @@ async def prompt_manifest_metadata_async(existing_manifest: dict[str, Any] | Non
|
|
|
152
185
|
raise ValueError("已取消填写 tags")
|
|
153
186
|
|
|
154
187
|
return {
|
|
188
|
+
"display_name": str(resolved_display_name).strip(),
|
|
155
189
|
"categories": [category],
|
|
156
190
|
"tags": normalize_tags(tags_text),
|
|
157
191
|
}
|
|
@@ -159,14 +193,14 @@ async def prompt_manifest_metadata_async(existing_manifest: dict[str, Any] | Non
|
|
|
159
193
|
|
|
160
194
|
def ensure_manifest_metadata_interactive(plugin_dir: Path, manifest: dict[str, Any]) -> dict[str, Any]:
|
|
161
195
|
"""Prompt for missing manifest metadata and persist it when running in a TTY."""
|
|
162
|
-
errors = metadata_errors(manifest)
|
|
196
|
+
errors = metadata_errors(manifest, plugin_dir)
|
|
163
197
|
if not errors:
|
|
164
198
|
return _finalize_manifest_metadata(manifest)
|
|
165
199
|
|
|
166
200
|
if not _is_interactive_terminal() or _has_running_event_loop():
|
|
167
201
|
return manifest
|
|
168
202
|
|
|
169
|
-
print_warning("检测到 manifest.json
|
|
203
|
+
print_warning("检测到 manifest.json 缺少或无效的市场元数据,进入交互补全...")
|
|
170
204
|
for error in errors:
|
|
171
205
|
print_warning(error)
|
|
172
206
|
|
|
@@ -174,20 +208,20 @@ def ensure_manifest_metadata_interactive(plugin_dir: Path, manifest: dict[str, A
|
|
|
174
208
|
_finalize_manifest_metadata(manifest)
|
|
175
209
|
_save_manifest(plugin_dir, manifest)
|
|
176
210
|
|
|
177
|
-
print_success("已更新 manifest.json
|
|
211
|
+
print_success("已更新 manifest.json 中的市场元数据")
|
|
178
212
|
return manifest
|
|
179
213
|
|
|
180
214
|
|
|
181
215
|
async def ensure_manifest_metadata_interactive_async(plugin_dir: Path, manifest: dict[str, Any]) -> dict[str, Any]:
|
|
182
216
|
"""Async prompt for missing manifest metadata and persist it when running in a TTY."""
|
|
183
|
-
errors = metadata_errors(manifest)
|
|
217
|
+
errors = metadata_errors(manifest, plugin_dir)
|
|
184
218
|
if not errors:
|
|
185
219
|
return _finalize_manifest_metadata(manifest)
|
|
186
220
|
|
|
187
221
|
if not _is_interactive_terminal():
|
|
188
222
|
return manifest
|
|
189
223
|
|
|
190
|
-
print_warning("检测到 manifest.json
|
|
224
|
+
print_warning("检测到 manifest.json 缺少或无效的市场元数据,进入交互补全...")
|
|
191
225
|
for error in errors:
|
|
192
226
|
print_warning(error)
|
|
193
227
|
|
|
@@ -195,5 +229,23 @@ async def ensure_manifest_metadata_interactive_async(plugin_dir: Path, manifest:
|
|
|
195
229
|
_finalize_manifest_metadata(manifest)
|
|
196
230
|
_save_manifest(plugin_dir, manifest)
|
|
197
231
|
|
|
198
|
-
print_success("已更新 manifest.json
|
|
199
|
-
return manifest
|
|
232
|
+
print_success("已更新 manifest.json 中的市场元数据")
|
|
233
|
+
return manifest
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _icon_error(manifest: dict[str, Any], plugin_dir: Path | None) -> str | None:
|
|
237
|
+
"""Return a validation error for the optional icon field, if any."""
|
|
238
|
+
|
|
239
|
+
icon = manifest.get("icon")
|
|
240
|
+
if icon is None or str(icon).strip() == "":
|
|
241
|
+
return None
|
|
242
|
+
if not isinstance(icon, str):
|
|
243
|
+
return "manifest.json 的 icon 必须是字符串路径"
|
|
244
|
+
if plugin_dir is None:
|
|
245
|
+
return None
|
|
246
|
+
icon_path = plugin_dir / icon
|
|
247
|
+
if not icon_path.exists() or not icon_path.is_file():
|
|
248
|
+
return f"manifest.json 的 icon 指向的文件不存在: {icon}"
|
|
249
|
+
if icon_path.suffix.lower() != ".png":
|
|
250
|
+
return "manifest.json 的 icon 必须指向 .png 文件"
|
|
251
|
+
return None
|
|
@@ -72,11 +72,11 @@ class MetadataValidator(BaseValidator):
|
|
|
72
72
|
else:
|
|
73
73
|
self.result.add_info("所有必需的元数据字段都已提供")
|
|
74
74
|
|
|
75
|
-
for error in metadata_errors(manifest_data):
|
|
75
|
+
for error in metadata_errors(manifest_data, self.plugin_path):
|
|
76
76
|
self.result.add_error(
|
|
77
77
|
error,
|
|
78
78
|
file_path="manifest.json",
|
|
79
|
-
suggestion='请填写
|
|
79
|
+
suggestion='请填写 display_name,并补全 categories/tags;如果配置了 icon,请确保它指向有效的 PNG 文件',
|
|
80
80
|
)
|
|
81
81
|
|
|
82
82
|
# 检查推荐字段
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mofox-plugin-dev-toolkit"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "开发工具集,用于快速创建、开发和测试 MoFox-Bot 插件"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "MoFox-Studio", email = "wwwww95915@qq.com"}
|
|
@@ -35,6 +35,7 @@ dependencies = [
|
|
|
35
35
|
"websockets>=12.0",
|
|
36
36
|
"libcst>=1.8.6",
|
|
37
37
|
"aiohttp>=3.9.0",
|
|
38
|
+
"pillow>=11.2.0",
|
|
38
39
|
"uvicorn>=0.24.0",
|
|
39
40
|
"fastapi>=0.104.0",
|
|
40
41
|
"ruff>=0.1.6",
|
|
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
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/dev/bridge_plugin/plugin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/__init__.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/action_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/adapter_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/chatter_template.py
RENAMED
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/config_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/event_template.py
RENAMED
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/prompt_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/router_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/service_template.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/templates/tool_template.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/color_printer.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/config_loader.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/config_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/license_generator.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/plugin_parser.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/utils/template_engine.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/style_validator.py
RENAMED
|
File without changes
|
{mofox_plugin_dev_toolkit-0.5.1 → mofox_plugin_dev_toolkit-0.5.2}/mpdt/validators/type_validator.py
RENAMED
|
File without changes
|
|
File without changes
|