ErisPulse 2.10.0.dev0__tar.gz → 2.10.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.
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/PKG-INFO +4 -3
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/pyproject.toml +9 -3
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/base.py +3 -1
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/cli.py +62 -3
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/config.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/create.py +309 -24
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/doctor.py +127 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/init.py +75 -13
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/install.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/language.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/list.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/list_remote.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/run.py +144 -9
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/self_update.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/types.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/uninstall.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/upgrade.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/version.py +1 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/constants.py +1 -1
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/locales/en.py +39 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/locales/ja.py +39 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/locales/ru.py +39 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +39 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +39 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/utils/config_wizard.py +7 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/utils/package_manager.py +7 -6
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/utils/scaffold_text.py +18 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/adapter.py +1 -1
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/config_schema.py +68 -7
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/converter.py +11 -2
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/module.py +3 -1
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/base.py +28 -4
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/command.py +104 -50
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/interaction.py +48 -16
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/message.py +12 -4
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/message_builder.py +7 -1
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/overrides.py +7 -3
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/wrapper.py +116 -33
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/assets/error.css +20 -10
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/assets/root.css +23 -13
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/config.py +282 -104
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/constants.py +15 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/locales/en.py +10 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/locales/ja.py +10 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/locales/ru.py +10 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/locales/zh_cn.py +10 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/locales/zh_tw.py +10 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/module.py +97 -13
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/router.py +41 -20
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/adapter.py +81 -3
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/plugin_folder.py +203 -7
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/example_config.py +5 -1
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/sdk_initializer.py +17 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/.gitignore +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/LICENSE +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/README.pypi.md +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/console.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/hints.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/utils/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/utils/display.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/CLI/utils/interactive.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/api_dsl.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/client.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/errors.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/i18n_schema.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/kv_builder.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/model.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/router.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/send_rules.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/sql_base.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Bases/websocket.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/command_args.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/conversation.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/governance.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/session_type.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/throttle.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/Event/trace.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/adapter.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/assets/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/assets/error.html +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/assets/root.html +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/client.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/connections.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/di.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/constants.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/lifecycle.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/logger.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/master.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/ownership.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/scope.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/shadow.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/storage/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/storage/mysql.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/storage/postgres.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/storage/sqlite.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/text_match.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/Core/transcript.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/finders/adapter.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/finders/module.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/module.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/loaders/strict.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/py.typed +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/__init__.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/config_schema.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/context.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/diagnostics.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/file_watcher.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/frame_config.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/hints.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/memory.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/owner_cleanup.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/plugin_reload.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/proactive_gc.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/tasks.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/troubleshoot.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/runtime/version.py +0 -0
- {erispulse-2.10.0.dev0 → erispulse-2.10.2}/src/ErisPulse/sdk.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 2.10.
|
|
3
|
+
Version: 2.10.2
|
|
4
4
|
Summary: Event-driven modular async bot framework — write once, deploy to any platform | 事件驱动的模块化异步机器人框架,一次编写,多平台部署
|
|
5
5
|
Project-URL: Homepage, https://github.com/ErisPulse/ErisPulse
|
|
6
6
|
Project-URL: Documentation, https://www.erisdev.com#docs
|
|
@@ -33,8 +33,8 @@ Requires-Dist: aiohttp>=3.9.0
|
|
|
33
33
|
Requires-Dist: aiosqlite>=0.20.0
|
|
34
34
|
Requires-Dist: fastapi>=0.116.1
|
|
35
35
|
Requires-Dist: rich>=13.0.0
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist: uvicorn
|
|
36
|
+
Requires-Dist: tomlclass<0.4,>=0.3.2
|
|
37
|
+
Requires-Dist: uvicorn<1.0,>=0.35.0
|
|
38
38
|
Requires-Dist: websockets>=12.0
|
|
39
39
|
Provides-Extra: mysql
|
|
40
40
|
Requires-Dist: aiomysql>=0.2.0; extra == 'mysql'
|
|
@@ -49,6 +49,7 @@ Requires-Dist: pytest-mock>=3.10.0; extra == 'test'
|
|
|
49
49
|
Requires-Dist: pytest-timeout>=2.1.0; extra == 'test'
|
|
50
50
|
Requires-Dist: pytest-xdist>=3.0.0; extra == 'test'
|
|
51
51
|
Requires-Dist: pytest>=7.0.0; extra == 'test'
|
|
52
|
+
Requires-Dist: tomli>=2.0.0; (python_version < '3.11') and extra == 'test'
|
|
52
53
|
Description-Content-Type: text/markdown
|
|
53
54
|
|
|
54
55
|
# ErisPulse
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ErisPulse"
|
|
7
|
-
version = "2.10.
|
|
7
|
+
version = "2.10.2"
|
|
8
8
|
description = "Event-driven modular async bot framework — write once, deploy to any platform | 事件驱动的模块化异步机器人框架,一次编写,多平台部署"
|
|
9
9
|
readme = "README.pypi.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -62,12 +62,17 @@ classifiers = [
|
|
|
62
62
|
|
|
63
63
|
dependencies = [
|
|
64
64
|
"rich>=13.0.0",
|
|
65
|
-
"
|
|
65
|
+
"tomlclass>=0.3.2,<0.4",
|
|
66
|
+
|
|
66
67
|
# network 相关
|
|
67
68
|
"websockets>=12.0",
|
|
68
69
|
"aiohttp>=3.9.0",
|
|
69
70
|
"fastapi>=0.116.1",
|
|
70
|
-
|
|
71
|
+
|
|
72
|
+
# uvicorn的内嵌启动依赖私有接口 Server._serve()(0.34.0 引入,用于跳过 serve() 的信号捕获),
|
|
73
|
+
# 上限锁定 0.x 线;启动期按能力探测,接口缺失时仅告警并回退公开 serve()
|
|
74
|
+
"uvicorn>=0.35.0,<1.0",
|
|
75
|
+
|
|
71
76
|
# storage 相关(默认 SQLite 后端异步驱动,开箱即用)
|
|
72
77
|
"aiosqlite>=0.20.0",
|
|
73
78
|
]
|
|
@@ -98,6 +103,7 @@ test = [
|
|
|
98
103
|
"pytest-timeout>=2.1.0",
|
|
99
104
|
"pytest-mock>=3.10.0",
|
|
100
105
|
"httpx>=0.24.0",
|
|
106
|
+
"tomli>=2.0.0; python_version < '3.11'",
|
|
101
107
|
]
|
|
102
108
|
|
|
103
109
|
[dependency-groups]
|
|
@@ -18,13 +18,15 @@ class Command(ABC):
|
|
|
18
18
|
1. 每个命令类必须实现 add_arguments 和 execute 方法
|
|
19
19
|
2. name 和 description 为类属性,必须在子类中定义
|
|
20
20
|
3. aliases 为可选类属性,用于定义命令的简化形式
|
|
21
|
-
4.
|
|
21
|
+
4. category 为可选类属性,决定主帮助分组表中的归类
|
|
22
|
+
5. execute 方法接收解析后的 args 对象
|
|
22
23
|
{!--< /tips >!--}
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
26
|
name: str = "" # 命令名称
|
|
26
27
|
description: str = "" # 命令描述
|
|
27
28
|
aliases: list = [] # 命令别名(简化形式,如 "install" -> ["i", "add"])
|
|
29
|
+
category: str = "" # 主帮助分组类别标识(project/components/config/misc;空串归入 misc)
|
|
28
30
|
|
|
29
31
|
@abstractmethod
|
|
30
32
|
def add_arguments(self, parser: ArgumentParser):
|
|
@@ -168,6 +168,65 @@ class CLI:
|
|
|
168
168
|
)
|
|
169
169
|
)
|
|
170
170
|
|
|
171
|
+
# 主帮助分组表展示顺序(未列出的类别按注册顺序附在末尾)
|
|
172
|
+
_HELP_CATEGORY_ORDER = ("project", "components", "config", "misc")
|
|
173
|
+
|
|
174
|
+
def _print_grouped_help(self) -> None:
|
|
175
|
+
"""
|
|
176
|
+
按类别分组打印主帮助(Rich 表格渲染)
|
|
177
|
+
|
|
178
|
+
取代 argparse 默认的子命令平铺列表:命令按 category 归类展示
|
|
179
|
+
(名称 + 别名 + 描述),全局选项从 ``parser._actions`` 实时遍历
|
|
180
|
+
生成避免与实际参数漂移。子命令级 ``--help`` 保持 argparse 原样。
|
|
181
|
+
"""
|
|
182
|
+
from rich.table import Table
|
|
183
|
+
|
|
184
|
+
if self.parser.description:
|
|
185
|
+
console.print(f"[bold]{self.parser.description}[/]\n")
|
|
186
|
+
console.print(f"[dim]{self.parser.format_usage().strip()}[/]\n")
|
|
187
|
+
|
|
188
|
+
groups: dict[str, list[Command]] = {}
|
|
189
|
+
for command in self.registry.get_all():
|
|
190
|
+
groups.setdefault(command.category or "misc", []).append(command)
|
|
191
|
+
|
|
192
|
+
ordered = list(self._HELP_CATEGORY_ORDER) + [
|
|
193
|
+
cat for cat in groups if cat not in self._HELP_CATEGORY_ORDER
|
|
194
|
+
]
|
|
195
|
+
table = Table(show_header=False, box=None, pad_edge=False, expand=True)
|
|
196
|
+
table.add_column(min_width=18)
|
|
197
|
+
table.add_column(ratio=1)
|
|
198
|
+
for cat in ordered:
|
|
199
|
+
commands = groups.get(cat)
|
|
200
|
+
if not commands:
|
|
201
|
+
continue
|
|
202
|
+
table.add_row(
|
|
203
|
+
f"[title]{i18n.t(f'cli.help.category.{cat}')}[/]", ""
|
|
204
|
+
)
|
|
205
|
+
for command in sorted(commands, key=lambda c: c.name):
|
|
206
|
+
aliases = getattr(command, "aliases", None) or []
|
|
207
|
+
name_text = command.name + (
|
|
208
|
+
f" [dim]({', '.join(aliases)})[/]" if aliases else ""
|
|
209
|
+
)
|
|
210
|
+
table.add_row(f" [cmd]{name_text}[/]", command.description)
|
|
211
|
+
console.print(table)
|
|
212
|
+
|
|
213
|
+
option_lines: list[tuple[str, str]] = []
|
|
214
|
+
for action in self.parser._actions:
|
|
215
|
+
if not action.option_strings:
|
|
216
|
+
continue
|
|
217
|
+
option_lines.append(
|
|
218
|
+
(", ".join(action.option_strings), action.help or "")
|
|
219
|
+
)
|
|
220
|
+
if option_lines:
|
|
221
|
+
width = max(len(opts) for opts, _ in option_lines)
|
|
222
|
+
console.print(f"\n[title]{i18n.t('cli.parser.optionals_title')}[/]")
|
|
223
|
+
for opts, help_text in option_lines:
|
|
224
|
+
console.print(f" [cmd]{opts.ljust(width)}[/] {help_text}")
|
|
225
|
+
|
|
226
|
+
console.print(
|
|
227
|
+
f"\n[dim]{i18n.t('cli.run.quickstart.run_help_hint')}[/]"
|
|
228
|
+
)
|
|
229
|
+
|
|
171
230
|
def _print_quickstart(self) -> None:
|
|
172
231
|
"""
|
|
173
232
|
打印 Quick Start 面板
|
|
@@ -248,7 +307,7 @@ class CLI:
|
|
|
248
307
|
console.print(
|
|
249
308
|
f"[error]{i18n.t('cli.run.unknown_command', command=cmd)}[/]"
|
|
250
309
|
)
|
|
251
|
-
self.
|
|
310
|
+
self._print_grouped_help()
|
|
252
311
|
sys.exit(1)
|
|
253
312
|
|
|
254
313
|
def _maybe_show_language_hint(self) -> None:
|
|
@@ -336,7 +395,7 @@ class CLI:
|
|
|
336
395
|
f"[warning]{i18n.t('cli.run.unknown_args', args=' '.join(unknown))}[/]"
|
|
337
396
|
)
|
|
338
397
|
self._print_quickstart()
|
|
339
|
-
self.
|
|
398
|
+
self._print_grouped_help()
|
|
340
399
|
return
|
|
341
400
|
|
|
342
401
|
# 将别名解析为规范命令名
|
|
@@ -373,7 +432,7 @@ class CLI:
|
|
|
373
432
|
console.print(
|
|
374
433
|
f"[error]{i18n.t('cli.run.unknown_command', command=args.command)}[/]"
|
|
375
434
|
)
|
|
376
|
-
self.
|
|
435
|
+
self._print_grouped_help()
|
|
377
436
|
sys.exit(1)
|
|
378
437
|
|
|
379
438
|
except KeyboardInterrupt:
|
|
@@ -5,6 +5,7 @@ Create 命令实现
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import datetime
|
|
8
|
+
import shutil
|
|
8
9
|
import sys
|
|
9
10
|
from argparse import ArgumentParser
|
|
10
11
|
from pathlib import Path
|
|
@@ -42,10 +43,10 @@ SOFTWARE.
|
|
|
42
43
|
"""
|
|
43
44
|
|
|
44
45
|
# 生成模块/适配器模板的最低 SDK 地板(写入模板 min_sdk_version 字段)。
|
|
45
|
-
#
|
|
46
|
-
#
|
|
46
|
+
# 模板示例使用根导入契约(2.10 特性)与 args=/options=(2.9 特性);pip 默认不解析
|
|
47
|
+
# 预发布版本,该地板不得高于已发布的正式版本,否则生成的组件会因依赖装不上或
|
|
47
48
|
# 加载器按 PEP 440 拒载(与 CLI/constants.py 的 INIT_ERISPULSE_DEPENDENCY 同步升版)。
|
|
48
|
-
_TEMPLATE_MIN_SDK_VERSION = "2.
|
|
49
|
+
_TEMPLATE_MIN_SDK_VERSION = "2.10.0"
|
|
49
50
|
|
|
50
51
|
_MODULE_PYPROJECT = """[project]
|
|
51
52
|
name = "ErisPulse-{name}"
|
|
@@ -76,6 +77,33 @@ packages = ["{name}"]
|
|
|
76
77
|
_MODULE_INIT = """from .Core import Main
|
|
77
78
|
"""
|
|
78
79
|
|
|
80
|
+
# pack 转换用:entry-point 组与类名按插件类型动态生成
|
|
81
|
+
_PACK_PYPROJECT = """[project]
|
|
82
|
+
name = "{pypi_name}"
|
|
83
|
+
version = "1.0.0"
|
|
84
|
+
description = "{description}"
|
|
85
|
+
readme = "README.md"
|
|
86
|
+
requires-python = ">=3.10"
|
|
87
|
+
license = {{ file = "LICENSE" }}
|
|
88
|
+
authors = [ {{ name = "{author}", email = "{email}" }} ]
|
|
89
|
+
|
|
90
|
+
dependencies = [
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
[project.urls]
|
|
94
|
+
"homepage" = "{homepage}"
|
|
95
|
+
|
|
96
|
+
[project.entry-points]
|
|
97
|
+
"{group}" = {{ "{entry_key}" = "{name}:{class_name}" }}
|
|
98
|
+
|
|
99
|
+
[build-system]
|
|
100
|
+
requires = ["hatchling"]
|
|
101
|
+
build-backend = "hatchling.build"
|
|
102
|
+
|
|
103
|
+
[tool.hatch.build.targets.wheel]
|
|
104
|
+
packages = ["{name}"]
|
|
105
|
+
"""
|
|
106
|
+
|
|
79
107
|
_MODULE_CORE = """from dataclasses import dataclass, field
|
|
80
108
|
|
|
81
109
|
from ErisPulse import (
|
|
@@ -107,12 +135,6 @@ class Main(BaseModule):
|
|
|
107
135
|
:ivar advanced_threshold: 高级阈值(docstring :ivar: 声明即为字段描述,无需 metadata)
|
|
108
136
|
\"\"\"
|
|
109
137
|
|
|
110
|
-
enabled: bool = field(
|
|
111
|
-
default=True,
|
|
112
|
-
metadata={{
|
|
113
|
-
\"description\": {{\"i18n\": \"module.{name}.enabled\", \"default\": \"Enable module\"}},
|
|
114
|
-
}},
|
|
115
|
-
)
|
|
116
138
|
# example 字段:默认不写入 config.toml(仅记录在 config.full.example,用户按需启用)
|
|
117
139
|
advanced_threshold: int = field(
|
|
118
140
|
default=100,
|
|
@@ -132,15 +154,6 @@ class Main(BaseModule):
|
|
|
132
154
|
ru=\"Модуль {name}\",
|
|
133
155
|
zh_TW=\"{name} 模組\",
|
|
134
156
|
)
|
|
135
|
-
enabled: I18nKey = I18nKey(
|
|
136
|
-
key=\"module.{name}.enabled\",
|
|
137
|
-
default=\"Enable module\",
|
|
138
|
-
zh_CN=\"是否启用模块\",
|
|
139
|
-
en=\"Enable module\",
|
|
140
|
-
ja=\"モジュールを有効にする\",
|
|
141
|
-
ru=\"Включить модуль\",
|
|
142
|
-
zh_TW=\"啟用模組\",
|
|
143
|
-
)
|
|
144
157
|
hello_help: I18nKey = I18nKey(
|
|
145
158
|
key=\"module.{name}.command.hello.help\",
|
|
146
159
|
default=\"Send a greeting message\",
|
|
@@ -222,8 +235,13 @@ class Main(BaseModule):
|
|
|
222
235
|
group=\"default\",
|
|
223
236
|
tags=[\"{name}\"],
|
|
224
237
|
# 对外服务白名单(可选):声明后其他模块可经 sdk.module.call() 调用这些方法;
|
|
225
|
-
# 缺省时公开方法全开放(限制由用户通过 scope.actions
|
|
226
|
-
# services
|
|
238
|
+
# 缺省时公开方法全开放(限制由用户通过 scope.actions 配置)。
|
|
239
|
+
# dict 形态可声明 description 与 parameters 参数提示(供 sdk.module.services()
|
|
240
|
+
# 服务目录 / AI 智能体消费);未声明的参数自动取方法 docstring 的 :param: 行
|
|
241
|
+
# services=[
|
|
242
|
+
# "get_welcome_message",
|
|
243
|
+
# {{"name": "render", "description": "渲染", "parameters": {{"html": "HTML 文本"}}}},
|
|
244
|
+
# ],
|
|
227
245
|
)
|
|
228
246
|
|
|
229
247
|
@staticmethod
|
|
@@ -576,6 +594,7 @@ class {name}(BaseAdapter):
|
|
|
576
594
|
return self.make_error(message=str(e))
|
|
577
595
|
"""
|
|
578
596
|
|
|
597
|
+
|
|
579
598
|
_ADAPTER_CONVERTER = """\"\"\"
|
|
580
599
|
{text[adapter.converter_doc]}
|
|
581
600
|
\"\"\"
|
|
@@ -775,12 +794,13 @@ class CreateCommand(Command):
|
|
|
775
794
|
name = "create"
|
|
776
795
|
description = i18n.t("cli.create.description")
|
|
777
796
|
aliases = ["c", "new"]
|
|
797
|
+
category = "project"
|
|
778
798
|
|
|
779
799
|
def add_arguments(self, parser: ArgumentParser):
|
|
780
800
|
parser.add_argument(
|
|
781
801
|
"create_type",
|
|
782
802
|
nargs="?",
|
|
783
|
-
choices=["module", "adapter"],
|
|
803
|
+
choices=["module", "adapter", "pack"],
|
|
784
804
|
help=i18n.t("cli.create.type_help"),
|
|
785
805
|
)
|
|
786
806
|
parser.add_argument("--name", "-n", help=i18n.t("cli.create.name_help"))
|
|
@@ -806,7 +826,7 @@ class CreateCommand(Command):
|
|
|
806
826
|
"--local",
|
|
807
827
|
action="store_true",
|
|
808
828
|
default=False,
|
|
809
|
-
help=i18n.t("cli.create.
|
|
829
|
+
help=i18n.t("cli.create.local_help"),
|
|
810
830
|
)
|
|
811
831
|
|
|
812
832
|
def execute(self, args):
|
|
@@ -814,6 +834,11 @@ class CreateCommand(Command):
|
|
|
814
834
|
if not create_type:
|
|
815
835
|
create_type = self._interactive_select_type()
|
|
816
836
|
|
|
837
|
+
# pack:本地插件 → 可发布项目(无需组件名,直接进入转换流程)
|
|
838
|
+
if create_type == "pack":
|
|
839
|
+
self._create_pack(args)
|
|
840
|
+
return
|
|
841
|
+
|
|
817
842
|
default_name = "MyModule" if create_type == "module" else "MyAdapter"
|
|
818
843
|
name = args.name
|
|
819
844
|
# 名称校验:非法时保留输入并重新提示,而非直接退出
|
|
@@ -843,12 +868,15 @@ class CreateCommand(Command):
|
|
|
843
868
|
console.print(
|
|
844
869
|
f" [bold]2.[/] Adapter [dim]{i18n.t('cli.create.select_type_adapter')}[/]"
|
|
845
870
|
)
|
|
871
|
+
console.print(
|
|
872
|
+
f" [bold]3.[/] Pack [dim]{i18n.t('cli.create.select_type_pack')}[/]"
|
|
873
|
+
)
|
|
846
874
|
console.print()
|
|
847
875
|
choice = IntPrompt.ask(
|
|
848
|
-
i18n.t("cli.create.select_prompt"), default=1, choices=["1", "2"]
|
|
876
|
+
i18n.t("cli.create.select_prompt"), default=1, choices=["1", "2", "3"]
|
|
849
877
|
)
|
|
850
878
|
console.print()
|
|
851
|
-
return "module"
|
|
879
|
+
return ("module", "adapter", "pack")[choice - 1]
|
|
852
880
|
|
|
853
881
|
def _ask_missing(
|
|
854
882
|
self, args, field_name: str, prompt_text: str, default: str = ""
|
|
@@ -867,6 +895,184 @@ class CreateCommand(Command):
|
|
|
867
895
|
val = Prompt.ask(f" {prompt_text}", default=default)
|
|
868
896
|
return val
|
|
869
897
|
|
|
898
|
+
def _create_pack(self, args):
|
|
899
|
+
"""
|
|
900
|
+
将本地插件(plugins/ 目录)转换为可发布的标准项目结构
|
|
901
|
+
|
|
902
|
+
交互式选择插件目录中的插件,识别类型(模块/适配器)后输入包元信息,
|
|
903
|
+
输出含 pyproject.toml(entry-point 按插件类型生成)与插件源码的完整
|
|
904
|
+
项目目录,可直接 ``pip install -e .`` 或打包发布。
|
|
905
|
+
|
|
906
|
+
:param args: [Any] 解析后的命令参数对象
|
|
907
|
+
:return: [None] 无返回值
|
|
908
|
+
"""
|
|
909
|
+
from ErisPulse.loaders.plugin_folder import PluginFolderLoader
|
|
910
|
+
|
|
911
|
+
# 1. 收集插件目录中的候选插件
|
|
912
|
+
loader = PluginFolderLoader()
|
|
913
|
+
candidates: list[tuple[str, Path]] = []
|
|
914
|
+
for base in loader.get_plugins_dirs():
|
|
915
|
+
if not base.is_dir():
|
|
916
|
+
continue
|
|
917
|
+
for entry in sorted(base.iterdir()):
|
|
918
|
+
plugin_name = PluginFolderLoader._plugin_name_of(entry)
|
|
919
|
+
if plugin_name is not None:
|
|
920
|
+
candidates.append((plugin_name, entry))
|
|
921
|
+
|
|
922
|
+
if not candidates:
|
|
923
|
+
console.print(f"[error] {i18n.t('cli.create.pack_no_plugins')}")
|
|
924
|
+
sys.exit(1)
|
|
925
|
+
|
|
926
|
+
# 2. 选择插件:--name 直选,否则交互编号选择
|
|
927
|
+
plugin_name = getattr(args, "name", None)
|
|
928
|
+
selected = next((c for c in candidates if c[0] == plugin_name), None)
|
|
929
|
+
if selected is None:
|
|
930
|
+
if plugin_name:
|
|
931
|
+
console.print(
|
|
932
|
+
f"[error] {i18n.t('cli.create.pack_not_found', name=plugin_name)}"
|
|
933
|
+
)
|
|
934
|
+
sys.exit(1)
|
|
935
|
+
console.print(Text(i18n.t("cli.create.pack_select_title"), style="bold"))
|
|
936
|
+
for i, (name, path) in enumerate(candidates, start=1):
|
|
937
|
+
console.print(f" [bold]{i}.[/] {name} [dim]{path}[/]")
|
|
938
|
+
console.print()
|
|
939
|
+
choice = IntPrompt.ask(
|
|
940
|
+
i18n.t("cli.create.pack_select_prompt"),
|
|
941
|
+
default=1,
|
|
942
|
+
choices=[str(i) for i in range(1, len(candidates) + 1)],
|
|
943
|
+
)
|
|
944
|
+
selected = candidates[choice - 1]
|
|
945
|
+
console.print()
|
|
946
|
+
|
|
947
|
+
plugin_name, plugin_path = selected
|
|
948
|
+
|
|
949
|
+
# 3. 导入插件并识别类型
|
|
950
|
+
try:
|
|
951
|
+
module_obj = loader._import_plugin(plugin_name, plugin_path)
|
|
952
|
+
except Exception as e:
|
|
953
|
+
console.print(f"[error] {i18n.t('cli.create.failed', error=e)}")
|
|
954
|
+
sys.exit(1)
|
|
955
|
+
module_class = PluginFolderLoader._find_module_class(module_obj)
|
|
956
|
+
adapter_class = PluginFolderLoader._find_adapter_class(module_obj)
|
|
957
|
+
if module_class is not None:
|
|
958
|
+
class_name = module_class.__name__
|
|
959
|
+
group = "erispulse.module"
|
|
960
|
+
kind_label = "module"
|
|
961
|
+
elif adapter_class is not None:
|
|
962
|
+
class_name = adapter_class.__name__
|
|
963
|
+
group = "erispulse.adapter"
|
|
964
|
+
kind_label = "adapter"
|
|
965
|
+
else:
|
|
966
|
+
console.print(
|
|
967
|
+
f"[error] {i18n.t('cli.create.pack_unknown_kind', name=plugin_name)}"
|
|
968
|
+
)
|
|
969
|
+
sys.exit(1)
|
|
970
|
+
console.print(
|
|
971
|
+
f"[success] {i18n.t('cli.create.pack_detected', name=plugin_name, kind=kind_label, cls=class_name)}"
|
|
972
|
+
)
|
|
973
|
+
|
|
974
|
+
# 4. 元信息(项目名默认插件名,经组件名校验)
|
|
975
|
+
project_name = prompt_validated(
|
|
976
|
+
i18n.t("cli.create.pack_project_prompt"),
|
|
977
|
+
default=plugin_name,
|
|
978
|
+
validate=_validate_name,
|
|
979
|
+
error_msg=i18n.t("cli.create.name_error"),
|
|
980
|
+
)
|
|
981
|
+
description = self._ask_missing(
|
|
982
|
+
args,
|
|
983
|
+
"description",
|
|
984
|
+
i18n.t("cli.create.pack_desc_prompt"),
|
|
985
|
+
i18n.t("cli.create.pack_desc_placeholder", name=project_name),
|
|
986
|
+
)
|
|
987
|
+
author = self._ask_missing(args, "author", i18n.t("cli.create.author_prompt"), "yourname")
|
|
988
|
+
email = self._ask_missing(args, "email", i18n.t("cli.create.email_prompt"), "your@mail.com")
|
|
989
|
+
homepage = self._ask_missing(
|
|
990
|
+
args,
|
|
991
|
+
"homepage",
|
|
992
|
+
i18n.t("cli.create.homepage_prompt"),
|
|
993
|
+
f"https://github.com/{author}/{project_name}",
|
|
994
|
+
)
|
|
995
|
+
entry_key = _camel_to_snake(project_name)
|
|
996
|
+
|
|
997
|
+
output = Path(args.output)
|
|
998
|
+
project_dir = output / project_name
|
|
999
|
+
if project_dir.exists() and not args.force:
|
|
1000
|
+
console.print(
|
|
1001
|
+
f"[error] {i18n.t('cli.create.dir_exists', dir=str(project_dir))}"
|
|
1002
|
+
)
|
|
1003
|
+
sys.exit(1)
|
|
1004
|
+
|
|
1005
|
+
# 5. 生成项目:源码拷贝 + pyproject(entry-point 按类型)+ LICENSE/README
|
|
1006
|
+
try:
|
|
1007
|
+
pkg_dir = project_dir / project_name
|
|
1008
|
+
pkg_dir.mkdir(parents=True, exist_ok=True)
|
|
1009
|
+
|
|
1010
|
+
if plugin_path.is_file():
|
|
1011
|
+
# 单文件插件 → 包内模块 + __init__ 导出
|
|
1012
|
+
(pkg_dir / "__init__.py").write_text(
|
|
1013
|
+
f"from .{plugin_path.stem} import {class_name}\n",
|
|
1014
|
+
encoding="utf-8",
|
|
1015
|
+
)
|
|
1016
|
+
shutil.copy2(plugin_path, pkg_dir / plugin_path.name)
|
|
1017
|
+
else:
|
|
1018
|
+
shutil.copytree(
|
|
1019
|
+
plugin_path,
|
|
1020
|
+
pkg_dir,
|
|
1021
|
+
dirs_exist_ok=True,
|
|
1022
|
+
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
(project_dir / "pyproject.toml").write_text(
|
|
1026
|
+
_PACK_PYPROJECT.format(
|
|
1027
|
+
name=project_name,
|
|
1028
|
+
pypi_name=f"ErisPulse-{project_name}",
|
|
1029
|
+
description=description,
|
|
1030
|
+
author=author,
|
|
1031
|
+
email=email,
|
|
1032
|
+
homepage=homepage,
|
|
1033
|
+
group=group,
|
|
1034
|
+
entry_key=entry_key,
|
|
1035
|
+
class_name=class_name,
|
|
1036
|
+
),
|
|
1037
|
+
encoding="utf-8",
|
|
1038
|
+
)
|
|
1039
|
+
(project_dir / "LICENSE").write_text(
|
|
1040
|
+
_LICENSE_TEMPLATE.format(
|
|
1041
|
+
year=str(datetime.datetime.now().year), author=author
|
|
1042
|
+
),
|
|
1043
|
+
encoding="utf-8",
|
|
1044
|
+
)
|
|
1045
|
+
readme_tpl = _README_MODULE if module_class is not None else _README_ADAPTER
|
|
1046
|
+
readme_kwargs = {"name": project_name, "description": description}
|
|
1047
|
+
if module_class is None:
|
|
1048
|
+
readme_kwargs["name_snake"] = entry_key
|
|
1049
|
+
(project_dir / "README.md").write_text(
|
|
1050
|
+
readme_tpl.format(**readme_kwargs),
|
|
1051
|
+
encoding="utf-8",
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
console.print()
|
|
1055
|
+
console.print(
|
|
1056
|
+
f"[success] {i18n.t('cli.create.pack_created', name=plugin_name, dir=str(project_dir))}[/]"
|
|
1057
|
+
)
|
|
1058
|
+
console.print()
|
|
1059
|
+
console.print(Text(i18n.t("cli.create.project_structure"), style="bold"))
|
|
1060
|
+
console.print(f" {project_name}/")
|
|
1061
|
+
console.print(" ├── pyproject.toml")
|
|
1062
|
+
console.print(" ├── LICENSE")
|
|
1063
|
+
console.print(" ├── README.md")
|
|
1064
|
+
console.print(f" └── {project_name}/ [dim]({plugin_path})[/]")
|
|
1065
|
+
console.print()
|
|
1066
|
+
console.print(Text(i18n.t("cli.create.next_steps"), style="bold"))
|
|
1067
|
+
console.print(f" · {i18n.t('cli.create.cd_to', dir=project_name)}")
|
|
1068
|
+
console.print(f" · {i18n.t('cli.create.install_dev')}")
|
|
1069
|
+
console.print(f" · {i18n.t('cli.create.run_test')}")
|
|
1070
|
+
console.print(f" · {i18n.t('cli.create.pack_remove_hint', name=plugin_name)}")
|
|
1071
|
+
|
|
1072
|
+
except Exception as e:
|
|
1073
|
+
console.print(f"[error] {i18n.t('cli.create.failed', error=e)}")
|
|
1074
|
+
sys.exit(1)
|
|
1075
|
+
|
|
870
1076
|
def _create_module(self, args, name: str):
|
|
871
1077
|
"""
|
|
872
1078
|
创建 Module 项目脚手架
|
|
@@ -1014,6 +1220,80 @@ class CreateCommand(Command):
|
|
|
1014
1220
|
console.print(f"[error] {i18n.t('cli.create.failed', error=e)}")
|
|
1015
1221
|
sys.exit(1)
|
|
1016
1222
|
|
|
1223
|
+
def _create_adapter_local(self, args, name: str):
|
|
1224
|
+
"""
|
|
1225
|
+
创建本地适配器插件(--local):生成 plugins/ 目录结构,免打包安装
|
|
1226
|
+
|
|
1227
|
+
:param args: [Any] 解析后的命令参数对象
|
|
1228
|
+
:param name: [str] 适配器名称
|
|
1229
|
+
:return: [None] 无返回值
|
|
1230
|
+
"""
|
|
1231
|
+
output = Path(args.output)
|
|
1232
|
+
plugins_dir = output / "plugins"
|
|
1233
|
+
plugin_dir = plugins_dir / name
|
|
1234
|
+
|
|
1235
|
+
if plugin_dir.exists() and not args.force:
|
|
1236
|
+
console.print(
|
|
1237
|
+
f"[error] {i18n.t('cli.create.dir_exists', dir=str(plugin_dir))}"
|
|
1238
|
+
)
|
|
1239
|
+
sys.exit(1)
|
|
1240
|
+
|
|
1241
|
+
try:
|
|
1242
|
+
plugin_dir.mkdir(parents=True, exist_ok=True)
|
|
1243
|
+
|
|
1244
|
+
converter_name = _to_converter_name(name)
|
|
1245
|
+
entry_key = _camel_to_snake(name)
|
|
1246
|
+
|
|
1247
|
+
(plugin_dir / "__init__.py").write_text(
|
|
1248
|
+
_ADAPTER_INIT.format(name=name, converter_name=converter_name),
|
|
1249
|
+
encoding="utf-8",
|
|
1250
|
+
)
|
|
1251
|
+
(plugin_dir / "Core.py").write_text(
|
|
1252
|
+
_ADAPTER_CORE.format(
|
|
1253
|
+
name=name,
|
|
1254
|
+
converter_name=converter_name,
|
|
1255
|
+
entry_key=entry_key,
|
|
1256
|
+
text=_scaffold_text(name),
|
|
1257
|
+
min_sdk=_TEMPLATE_MIN_SDK_VERSION,
|
|
1258
|
+
),
|
|
1259
|
+
encoding="utf-8",
|
|
1260
|
+
)
|
|
1261
|
+
(plugin_dir / "Converter.py").write_text(
|
|
1262
|
+
_ADAPTER_CONVERTER.format(
|
|
1263
|
+
name=name,
|
|
1264
|
+
converter_name=converter_name,
|
|
1265
|
+
entry_key=entry_key,
|
|
1266
|
+
text=_scaffold_text(name),
|
|
1267
|
+
),
|
|
1268
|
+
encoding="utf-8",
|
|
1269
|
+
)
|
|
1270
|
+
|
|
1271
|
+
console.print()
|
|
1272
|
+
console.print(
|
|
1273
|
+
f"[success] {i18n.t('cli.create.adapter_created', name=name)}[/]"
|
|
1274
|
+
)
|
|
1275
|
+
console.print()
|
|
1276
|
+
console.print(Text(i18n.t("cli.create.local_structure"), style="bold"))
|
|
1277
|
+
console.print(" plugins/")
|
|
1278
|
+
console.print(f" └── {name}/")
|
|
1279
|
+
console.print(" ├── __init__.py")
|
|
1280
|
+
console.print(" ├── Core.py")
|
|
1281
|
+
console.print(" └── Converter.py")
|
|
1282
|
+
console.print()
|
|
1283
|
+
console.print(Text(i18n.t("cli.create.next_steps"), style="bold"))
|
|
1284
|
+
console.print(f" · {i18n.t('cli.create.cd_to', dir=str(plugin_dir))}")
|
|
1285
|
+
console.print(
|
|
1286
|
+
f" · {i18n.t('cli.create.edit_adapter', file=str(plugin_dir) + '/Core.py')}"
|
|
1287
|
+
)
|
|
1288
|
+
console.print(
|
|
1289
|
+
f" · {i18n.t('cli.create.edit_converter', file=str(plugin_dir) + '/Converter.py')}"
|
|
1290
|
+
)
|
|
1291
|
+
console.print(f" · {i18n.t('cli.create.local_tip')}")
|
|
1292
|
+
|
|
1293
|
+
except Exception as e:
|
|
1294
|
+
console.print(f"[error] {i18n.t('cli.create.failed', error=e)}")
|
|
1295
|
+
sys.exit(1)
|
|
1296
|
+
|
|
1017
1297
|
def _create_adapter(self, args, name: str):
|
|
1018
1298
|
"""
|
|
1019
1299
|
创建 Adapter 项目脚手架
|
|
@@ -1022,6 +1302,11 @@ class CreateCommand(Command):
|
|
|
1022
1302
|
:param name: [str] 适配器名称
|
|
1023
1303
|
:return: [None] 无返回值
|
|
1024
1304
|
"""
|
|
1305
|
+
# --local:创建本地适配器插件(plugins/ 目录结构,免打包安装)
|
|
1306
|
+
if getattr(args, "local", False):
|
|
1307
|
+
self._create_adapter_local(args, name)
|
|
1308
|
+
return
|
|
1309
|
+
|
|
1025
1310
|
description = self._ask_missing(
|
|
1026
1311
|
args,
|
|
1027
1312
|
"description",
|