mofox-plugin-dev-toolkit 0.3.5__tar.gz → 0.4.3__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.5/mofox_plugin_dev_toolkit.egg-info → mofox_plugin_dev_toolkit-0.4.3}/PKG-INFO +1 -1
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3/mofox_plugin_dev_toolkit.egg-info}/PKG-INFO +1 -1
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mofox_plugin_dev_toolkit.egg-info/SOURCES.txt +5 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/__init__.py +1 -1
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/cli.py +22 -5
- mofox_plugin_dev_toolkit-0.4.3/mpdt/commands/__init__.py +3 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/commands/build.py +300 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/commands/generate.py +278 -63
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/commands/init.py +310 -132
- mofox_plugin_dev_toolkit-0.4.3/mpdt/dev/bridge_plugin/__init__.py +7 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/dev/bridge_plugin/cleanup_handler.py +6 -7
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/dev/bridge_plugin/file_watcher.py +1 -1
- mofox_plugin_dev_toolkit-0.4.3/mpdt/dev/bridge_plugin/manifest.json +16 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/dev/bridge_plugin/plugin.py +29 -59
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/templates/__init__.py +18 -21
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/action_template.py +64 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/adapter_template.py +127 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/chatter_template.py +115 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/collection_template.py +104 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/config_template.py +57 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/event_template.py +101 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/plus_command_template.py +92 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/router_template.py +114 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/service_template.py +105 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/templates/tool_template.py +90 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/code_parser.py +94 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/validators/auto_fix_validator.py +266 -150
- mofox_plugin_dev_toolkit-0.4.3/mpdt/validators/component_validator.py +1026 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/validators/config_validator.py +320 -0
- mofox_plugin_dev_toolkit-0.4.3/mpdt/validators/metadata_validator.py +169 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/validators/structure_validator.py +1 -1
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/pyproject.toml +1 -1
- mofox_plugin_dev_toolkit-0.3.5/mpdt/commands/__init__.py +0 -9
- mofox_plugin_dev_toolkit-0.3.5/mpdt/dev/bridge_plugin/__init__.py +0 -17
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/action_template.py +0 -102
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/adapter_template.py +0 -129
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/chatter_template.py +0 -103
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/event_template.py +0 -116
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/plus_command_template.py +0 -150
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/router_template.py +0 -175
- mofox_plugin_dev_toolkit-0.3.5/mpdt/templates/tool_template.py +0 -98
- mofox_plugin_dev_toolkit-0.3.5/mpdt/validators/component_validator.py +0 -842
- mofox_plugin_dev_toolkit-0.3.5/mpdt/validators/config_validator.py +0 -119
- mofox_plugin_dev_toolkit-0.3.5/mpdt/validators/metadata_validator.py +0 -107
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/LICENSE +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/MANIFEST.in +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/README.md +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mofox_plugin_dev_toolkit.egg-info/dependency_links.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mofox_plugin_dev_toolkit.egg-info/entry_points.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mofox_plugin_dev_toolkit.egg-info/requires.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mofox_plugin_dev_toolkit.egg-info/top_level.txt +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/__main__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/commands/check.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/commands/dev.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/dev/bridge_plugin/dev_config.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/templates/prompt_template.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/color_printer.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/config_loader.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/config_manager.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/file_ops.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/license_generator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/plugin_parser.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/utils/template_engine.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/validators/__init__.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/validators/base.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/validators/style_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/mpdt/validators/type_validator.py +0 -0
- {mofox_plugin_dev_toolkit-0.3.5 → mofox_plugin_dev_toolkit-0.4.3}/setup.cfg +0 -0
|
@@ -12,6 +12,7 @@ mpdt/__init__.py
|
|
|
12
12
|
mpdt/__main__.py
|
|
13
13
|
mpdt/cli.py
|
|
14
14
|
mpdt/commands/__init__.py
|
|
15
|
+
mpdt/commands/build.py
|
|
15
16
|
mpdt/commands/check.py
|
|
16
17
|
mpdt/commands/dev.py
|
|
17
18
|
mpdt/commands/generate.py
|
|
@@ -20,15 +21,19 @@ mpdt/dev/bridge_plugin/__init__.py
|
|
|
20
21
|
mpdt/dev/bridge_plugin/cleanup_handler.py
|
|
21
22
|
mpdt/dev/bridge_plugin/dev_config.py
|
|
22
23
|
mpdt/dev/bridge_plugin/file_watcher.py
|
|
24
|
+
mpdt/dev/bridge_plugin/manifest.json
|
|
23
25
|
mpdt/dev/bridge_plugin/plugin.py
|
|
24
26
|
mpdt/templates/__init__.py
|
|
25
27
|
mpdt/templates/action_template.py
|
|
26
28
|
mpdt/templates/adapter_template.py
|
|
27
29
|
mpdt/templates/chatter_template.py
|
|
30
|
+
mpdt/templates/collection_template.py
|
|
31
|
+
mpdt/templates/config_template.py
|
|
28
32
|
mpdt/templates/event_template.py
|
|
29
33
|
mpdt/templates/plus_command_template.py
|
|
30
34
|
mpdt/templates/prompt_template.py
|
|
31
35
|
mpdt/templates/router_template.py
|
|
36
|
+
mpdt/templates/service_template.py
|
|
32
37
|
mpdt/templates/tool_template.py
|
|
33
38
|
mpdt/utils/__init__.py
|
|
34
39
|
mpdt/utils/code_parser.py
|
|
@@ -67,7 +67,7 @@ def init(ctx: click.Context, plugin_name: str | None, template: str, author: str
|
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
@cli.command()
|
|
70
|
-
@click.argument("component_type", type=click.Choice(["action", "tool", "event", "adapter", "
|
|
70
|
+
@click.argument("component_type", type=click.Choice(["action", "tool", "event", "adapter", "plus-command", "router", "chatter", "service", "config"]), required=False)
|
|
71
71
|
@click.argument("component_name", required=False)
|
|
72
72
|
@click.option("--description", "-d", help="组件描述")
|
|
73
73
|
@click.option("--output", "-o", type=click.Path(), help="输出目录")
|
|
@@ -135,14 +135,31 @@ def check(ctx: click.Context, path: str, level: str, fix: bool, report: str, out
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
@cli.command()
|
|
138
|
+
@click.argument("plugin_path", type=click.Path(), required=False, default=".")
|
|
138
139
|
@click.option("--output", "-o", type=click.Path(), default="dist", help="输出目录")
|
|
139
140
|
@click.option("--with-docs", is_flag=True, help="包含文档")
|
|
140
|
-
@click.option("--format", type=click.Choice(["
|
|
141
|
+
@click.option("--format", "fmt", type=click.Choice(["mfp", "zip"]), default="mfp", help="构建格式(mfp 为推荐格式)")
|
|
141
142
|
@click.option("--bump", type=click.Choice(["major", "minor", "patch"]), help="自动升级版本号")
|
|
142
143
|
@click.pass_context
|
|
143
|
-
def build(ctx: click.Context, output: str, with_docs: bool,
|
|
144
|
-
"""
|
|
145
|
-
|
|
144
|
+
def build(ctx: click.Context, plugin_path: str, output: str, with_docs: bool, fmt: str, bump: str | None) -> None:
|
|
145
|
+
"""构建并打包插件为 .mfp 文件
|
|
146
|
+
|
|
147
|
+
PLUGIN_PATH 为插件根目录(含 manifest.json),默认为当前目录。
|
|
148
|
+
"""
|
|
149
|
+
from mpdt.commands.build import build_plugin
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
build_plugin(
|
|
153
|
+
plugin_path=plugin_path,
|
|
154
|
+
output_dir=output,
|
|
155
|
+
with_docs=with_docs,
|
|
156
|
+
fmt=fmt,
|
|
157
|
+
bump=bump,
|
|
158
|
+
verbose=ctx.obj["verbose"],
|
|
159
|
+
)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
console.print(f"[bold red]❌ 构建失败: {e}[/bold red]")
|
|
162
|
+
raise click.Abort()
|
|
146
163
|
|
|
147
164
|
|
|
148
165
|
@cli.command()
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""
|
|
2
|
+
构建命令实现
|
|
3
|
+
|
|
4
|
+
将插件目录打包为 .mfp 文件(本质为 ZIP 压缩包)。
|
|
5
|
+
loader.py 支持从 .mfp 直接加载插件,与文件夹加载行为一致。
|
|
6
|
+
|
|
7
|
+
流程:
|
|
8
|
+
1. 验证插件目录及 manifest.json
|
|
9
|
+
2. 可选地自动升级版本号(major / minor / patch)
|
|
10
|
+
3. 收集需打包的文件(排除缓存、版本控制等)
|
|
11
|
+
4. 写入 .mfp(ZIP)或 .zip 文件
|
|
12
|
+
5. 打印构建摘要
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import re
|
|
19
|
+
import zipfile
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from rich.panel import Panel
|
|
23
|
+
from rich.table import Table
|
|
24
|
+
|
|
25
|
+
from mpdt.utils.color_printer import (
|
|
26
|
+
console,
|
|
27
|
+
print_error,
|
|
28
|
+
print_step,
|
|
29
|
+
print_success,
|
|
30
|
+
print_warning,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# 构建时默认排除的文件/目录名称(精确匹配)
|
|
34
|
+
_EXCLUDE_NAMES: set[str] = {
|
|
35
|
+
"__pycache__",
|
|
36
|
+
".git",
|
|
37
|
+
".gitignore",
|
|
38
|
+
".gitattributes",
|
|
39
|
+
".github",
|
|
40
|
+
".mypy_cache",
|
|
41
|
+
".ruff_cache",
|
|
42
|
+
".pytest_cache",
|
|
43
|
+
".venv",
|
|
44
|
+
"venv",
|
|
45
|
+
".env",
|
|
46
|
+
"node_modules",
|
|
47
|
+
"dist",
|
|
48
|
+
".DS_Store",
|
|
49
|
+
"Thumbs.db",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# 排除的文件扩展名
|
|
53
|
+
_EXCLUDE_EXTS: set[str] = {
|
|
54
|
+
".pyc",
|
|
55
|
+
".pyo",
|
|
56
|
+
".pyd",
|
|
57
|
+
".egg-info",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# 文档相关目录/文件名
|
|
61
|
+
_DOC_NAMES: set[str] = {
|
|
62
|
+
"docs",
|
|
63
|
+
"doc",
|
|
64
|
+
"README.md",
|
|
65
|
+
"README.rst",
|
|
66
|
+
"CHANGELOG.md",
|
|
67
|
+
"CHANGELOG.rst",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _is_excluded(path: Path, with_docs: bool) -> bool:
|
|
72
|
+
"""判断路径是否应被排除。"""
|
|
73
|
+
name = path.name
|
|
74
|
+
if name in _EXCLUDE_NAMES:
|
|
75
|
+
return True
|
|
76
|
+
if path.suffix in _EXCLUDE_EXTS:
|
|
77
|
+
return True
|
|
78
|
+
# 以 . 开头的隐藏文件/目录
|
|
79
|
+
if name.startswith("."):
|
|
80
|
+
return True
|
|
81
|
+
# 文档文件(若不包含文档)
|
|
82
|
+
if not with_docs and name in _DOC_NAMES:
|
|
83
|
+
return True
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _collect_files(plugin_dir: Path, with_docs: bool) -> list[Path]:
|
|
88
|
+
"""递归收集插件目录中需要打包的文件列表(相对路径)。"""
|
|
89
|
+
files: list[Path] = []
|
|
90
|
+
|
|
91
|
+
for item in sorted(plugin_dir.rglob("*")):
|
|
92
|
+
# 检查路径上的每个部分是否被排除
|
|
93
|
+
relative = item.relative_to(plugin_dir)
|
|
94
|
+
parts = relative.parts
|
|
95
|
+
|
|
96
|
+
excluded = False
|
|
97
|
+
for part in parts:
|
|
98
|
+
part_path = Path(part)
|
|
99
|
+
if _is_excluded(part_path, with_docs):
|
|
100
|
+
excluded = True
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if excluded:
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
if item.is_file():
|
|
107
|
+
files.append(item)
|
|
108
|
+
|
|
109
|
+
return files
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _bump_version(version: str, bump: str) -> str:
|
|
113
|
+
"""按规则升级版本号(语义化版本 major.minor.patch)。
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
version: 当前版本字符串,例如 "1.2.3"
|
|
117
|
+
bump: 升级类型,"major" / "minor" / "patch"
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
升级后的版本字符串
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ValueError: 版本格式不合法
|
|
124
|
+
"""
|
|
125
|
+
match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)(.*)", version.strip())
|
|
126
|
+
if not match:
|
|
127
|
+
raise ValueError(f"版本号格式不合法: '{version}',应为 major.minor.patch")
|
|
128
|
+
|
|
129
|
+
major, minor, patch, suffix = int(match.group(1)), int(match.group(2)), int(match.group(3)), match.group(4)
|
|
130
|
+
|
|
131
|
+
if bump == "major":
|
|
132
|
+
major += 1
|
|
133
|
+
minor = 0
|
|
134
|
+
patch = 0
|
|
135
|
+
elif bump == "minor":
|
|
136
|
+
minor += 1
|
|
137
|
+
patch = 0
|
|
138
|
+
elif bump == "patch":
|
|
139
|
+
patch += 1
|
|
140
|
+
|
|
141
|
+
return f"{major}.{minor}.{patch}{suffix}"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _load_manifest(plugin_dir: Path) -> dict | None:
|
|
145
|
+
"""读取并解析 manifest.json,返回字典;失败返回 None。"""
|
|
146
|
+
manifest_path = plugin_dir / "manifest.json"
|
|
147
|
+
if not manifest_path.exists():
|
|
148
|
+
print_error(f"manifest.json 不存在: {manifest_path}")
|
|
149
|
+
return None
|
|
150
|
+
try:
|
|
151
|
+
with open(manifest_path, encoding="utf-8") as f:
|
|
152
|
+
return json.load(f)
|
|
153
|
+
except json.JSONDecodeError as e:
|
|
154
|
+
print_error(f"manifest.json 解析失败: {e}")
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _save_manifest(plugin_dir: Path, manifest: dict) -> None:
|
|
159
|
+
"""将 manifest 字典写回 manifest.json。"""
|
|
160
|
+
manifest_path = plugin_dir / "manifest.json"
|
|
161
|
+
with open(manifest_path, "w", encoding="utf-8") as f:
|
|
162
|
+
json.dump(manifest, f, ensure_ascii=False, indent=4)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def build_plugin(
|
|
166
|
+
plugin_path: str = ".",
|
|
167
|
+
output_dir: str = "dist",
|
|
168
|
+
with_docs: bool = False,
|
|
169
|
+
fmt: str = "mfp",
|
|
170
|
+
bump: str | None = None,
|
|
171
|
+
verbose: bool = False,
|
|
172
|
+
) -> None:
|
|
173
|
+
"""构建并打包插件为 .mfp / .zip 文件。
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
plugin_path: 插件根目录(包含 manifest.json)
|
|
177
|
+
output_dir: 输出目录,默认 dist/
|
|
178
|
+
with_docs: 是否将文档文件打入包中
|
|
179
|
+
fmt: 输出格式,"mfp"(推荐) 或 "zip"
|
|
180
|
+
bump: 自动升级版本,"major" / "minor" / "patch" / None
|
|
181
|
+
verbose: 是否显示详细信息
|
|
182
|
+
"""
|
|
183
|
+
plugin_dir = Path(plugin_path).resolve()
|
|
184
|
+
|
|
185
|
+
# ── 1. 基本验证 ──────────────────────────────────────────────────────────
|
|
186
|
+
if not plugin_dir.exists():
|
|
187
|
+
print_error(f"插件路径不存在: {plugin_dir}")
|
|
188
|
+
return
|
|
189
|
+
if not plugin_dir.is_dir():
|
|
190
|
+
print_error(f"插件路径不是目录: {plugin_dir}")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
console.print(Panel.fit(f"📦 构建插件: [cyan]{plugin_dir.name}[/cyan]", border_style="blue"))
|
|
194
|
+
|
|
195
|
+
manifest = _load_manifest(plugin_dir)
|
|
196
|
+
if manifest is None:
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
required = ["name", "version", "description", "author", "entry_point"]
|
|
200
|
+
for field in required:
|
|
201
|
+
if field not in manifest:
|
|
202
|
+
print_error(f"manifest.json 缺少必需字段: '{field}'")
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
plugin_name: str = manifest["name"]
|
|
206
|
+
plugin_version: str = manifest["version"]
|
|
207
|
+
|
|
208
|
+
# ── 2. 版本升级 ──────────────────────────────────────────────────────────
|
|
209
|
+
if bump:
|
|
210
|
+
try:
|
|
211
|
+
new_version = _bump_version(plugin_version, bump)
|
|
212
|
+
except ValueError as e:
|
|
213
|
+
print_error(str(e))
|
|
214
|
+
return
|
|
215
|
+
print_step(f"版本升级: [yellow]{plugin_version}[/yellow] → [green]{new_version}[/green]")
|
|
216
|
+
manifest["version"] = new_version
|
|
217
|
+
_save_manifest(plugin_dir, manifest)
|
|
218
|
+
plugin_version = new_version
|
|
219
|
+
|
|
220
|
+
# ── 3. 验证入口文件 ───────────────────────────────────────────────────────
|
|
221
|
+
entry_point = manifest.get("entry_point", "plugin.py")
|
|
222
|
+
entry_file = plugin_dir / entry_point
|
|
223
|
+
if not entry_file.exists():
|
|
224
|
+
print_warning(f"入口文件不存在: {entry_point}(仍将继续构建)")
|
|
225
|
+
|
|
226
|
+
# ── 4. 收集文件 ───────────────────────────────────────────────────────────
|
|
227
|
+
print_step("收集文件...")
|
|
228
|
+
files = _collect_files(plugin_dir, with_docs)
|
|
229
|
+
|
|
230
|
+
if not files:
|
|
231
|
+
print_warning("未找到任何需要打包的文件")
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
if verbose:
|
|
235
|
+
for f in files:
|
|
236
|
+
rel = f.relative_to(plugin_dir)
|
|
237
|
+
console.print(f" [dim]+ {rel}[/dim]")
|
|
238
|
+
|
|
239
|
+
# ── 5. 确定输出路径 ───────────────────────────────────────────────────────
|
|
240
|
+
# output_dir 可以是绝对路径或相对于插件目录
|
|
241
|
+
out_path = Path(output_dir)
|
|
242
|
+
if not out_path.is_absolute():
|
|
243
|
+
out_path = plugin_dir / output_dir
|
|
244
|
+
|
|
245
|
+
out_path.mkdir(parents=True, exist_ok=True)
|
|
246
|
+
|
|
247
|
+
suffix = ".mfp" if fmt == "mfp" else ".zip"
|
|
248
|
+
archive_name = f"{plugin_name}-{plugin_version}{suffix}"
|
|
249
|
+
archive_path = out_path / archive_name
|
|
250
|
+
|
|
251
|
+
if archive_path.exists():
|
|
252
|
+
print_warning(f"目标文件已存在,将覆盖: {archive_path}")
|
|
253
|
+
|
|
254
|
+
# ── 6. 压缩打包 ───────────────────────────────────────────────────────────
|
|
255
|
+
print_step(f"正在写入 {suffix} 包...")
|
|
256
|
+
total_bytes = 0
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
260
|
+
for file_path in files:
|
|
261
|
+
arcname = file_path.relative_to(plugin_dir)
|
|
262
|
+
zf.write(file_path, arcname)
|
|
263
|
+
total_bytes += file_path.stat().st_size
|
|
264
|
+
if verbose:
|
|
265
|
+
console.print(f" [dim] → {arcname}[/dim]")
|
|
266
|
+
except Exception as e:
|
|
267
|
+
print_error(f"打包失败: {e}")
|
|
268
|
+
# 清理不完整的文件
|
|
269
|
+
if archive_path.exists():
|
|
270
|
+
archive_path.unlink()
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
# ── 7. 摘要 ──────────────────────────────────────────────────────────────
|
|
274
|
+
archive_size = archive_path.stat().st_size
|
|
275
|
+
|
|
276
|
+
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
277
|
+
table.add_column(style="cyan")
|
|
278
|
+
table.add_column(style="white")
|
|
279
|
+
table.add_row("插件名称", plugin_name)
|
|
280
|
+
table.add_row("版本", plugin_version)
|
|
281
|
+
table.add_row("作者", manifest.get("author", "-"))
|
|
282
|
+
table.add_row("入口文件", entry_point)
|
|
283
|
+
table.add_row("打包文件数", str(len(files)))
|
|
284
|
+
table.add_row("原始大小", _format_size(total_bytes))
|
|
285
|
+
table.add_row("包大小", _format_size(archive_size))
|
|
286
|
+
table.add_row("输出路径", str(archive_path))
|
|
287
|
+
|
|
288
|
+
console.print()
|
|
289
|
+
console.print(table)
|
|
290
|
+
console.print()
|
|
291
|
+
print_success(f"构建完成: {archive_path.name}")
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _format_size(size: int) -> str:
|
|
295
|
+
"""将字节数格式化为人类可读字符串。"""
|
|
296
|
+
for unit in ("B", "KB", "MB", "GB"):
|
|
297
|
+
if size < 1024:
|
|
298
|
+
return f"{size:.1f} {unit}"
|
|
299
|
+
size //= 1024
|
|
300
|
+
return f"{size:.1f} TB"
|