ErisPulse 2.8.0.dev0__tar.gz → 2.8.0.dev1__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.8.0.dev0 → erispulse-2.8.0.dev1}/.gitignore +1 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/PKG-INFO +5 -28
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/README.pypi.md +3 -1
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/pyproject.toml +20 -6
- erispulse-2.8.0.dev1/src/ErisPulse/CLI/commands/config.py +208 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/create.py +25 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/init.py +39 -80
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/install.py +68 -93
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/locales/en.py +72 -3
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/locales/ja.py +72 -3
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/locales/ru.py +72 -3
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +72 -3
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +72 -3
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/utils/__init__.py +2 -0
- erispulse-2.8.0.dev1/src/ErisPulse/CLI/utils/config_wizard.py +859 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/adapter.py +520 -141
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/i18n_schema.py +1 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/__init__.py +4 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/base.py +85 -48
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/command.py +330 -97
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/message.py +45 -8
- erispulse-2.8.0.dev1/src/ErisPulse/Core/Event/overrides.py +727 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/session_type.py +45 -1
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/wrapper.py +15 -1
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/__init__.py +6 -1
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/adapter.py +190 -4
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/config.py +109 -28
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/constants.py +103 -16
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/__init__.py +10 -6
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/locales/en.py +34 -12
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/locales/ja.py +34 -12
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/locales/ru.py +34 -12
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/locales/zh_cn.py +34 -12
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/locales/zh_tw.py +34 -12
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/master.py +143 -13
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/module.py +221 -166
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/router.py +68 -4
- erispulse-2.8.0.dev1/src/ErisPulse/Core/scope.py +1171 -0
- erispulse-2.8.0.dev1/src/ErisPulse/Core/text_match.py +195 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/module.py +206 -85
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/__init__.py +2 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/frame_config.py +58 -7
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/plugin_reload.py +1 -1
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/sdk.py +47 -15
- erispulse-2.8.0.dev0/src/ErisPulse/Core/scope.py +0 -537
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/LICENSE +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/base.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/cli.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/doctor.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/language.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/list.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/run.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/self_update.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/types.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/console.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/constants.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/hints.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/utils/display.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/utils/file_watcher.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/CLI/utils/scaffold_text.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/client.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/config_schema.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/converter.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/errors.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/kv_builder.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/module.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/router.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/send_rules.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Bases/websocket.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/message_builder.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/assets/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/assets/error.css +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/assets/error.html +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/assets/root.css +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/assets/root.html +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/client.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/constants.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/lifecycle.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/logger.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/Core/storage.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/finders/adapter.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/finders/module.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/adapter.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/plugin_folder.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/loaders/strict.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/py.typed +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/config_schema.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/context.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/diagnostics.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/hints.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/memory.py +0 -0
- {erispulse-2.8.0.dev0 → erispulse-2.8.0.dev1}/src/ErisPulse/runtime/tasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 2.8.0.
|
|
3
|
+
Version: 2.8.0.dev1
|
|
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
|
|
@@ -10,27 +10,7 @@ Project-URL: Bug Tracker, https://github.com/ErisPulse/ErisPulse/blob/main/docs/
|
|
|
10
10
|
Project-URL: Changelog, https://github.com/ErisPulse/ErisPulse/blob/main/CHANGELOG.md
|
|
11
11
|
Author: ErisDev
|
|
12
12
|
Maintainer-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>
|
|
13
|
-
License: MIT
|
|
14
|
-
|
|
15
|
-
Copyright (c) 2025-2026 ErisPulse
|
|
16
|
-
|
|
17
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
19
|
-
in the Software without restriction, including without limitation the rights
|
|
20
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
22
|
-
furnished to do so, subject to the following conditions:
|
|
23
|
-
|
|
24
|
-
The above copyright notice and this permission notice shall be included in all
|
|
25
|
-
copies or substantial portions of the Software.
|
|
26
|
-
|
|
27
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
-
SOFTWARE.
|
|
13
|
+
License-Expression: MIT
|
|
34
14
|
License-File: LICENSE
|
|
35
15
|
Keywords: async,asyncio,bot,bot-framework,chatbot,email,extensible,framework,instant-messaging,kook,matrix,modular,multi-platform,onebot,onebot12,qq,robot,sdk,telegram,yunhu
|
|
36
16
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -54,11 +34,6 @@ Requires-Dist: rich>=13.0.0
|
|
|
54
34
|
Requires-Dist: toml>=0.10.2
|
|
55
35
|
Requires-Dist: uvicorn>=0.30.0
|
|
56
36
|
Requires-Dist: websockets>=12.0
|
|
57
|
-
Provides-Extra: dev
|
|
58
|
-
Requires-Dist: basedpyright>=1.20.0; extra == 'dev'
|
|
59
|
-
Requires-Dist: pyclean>=3.6.0; extra == 'dev'
|
|
60
|
-
Requires-Dist: ruff>=0.1.7; extra == 'dev'
|
|
61
|
-
Requires-Dist: typing-extensions>=4.7.0; (python_version < '3.11') and extra == 'dev'
|
|
62
37
|
Provides-Extra: test
|
|
63
38
|
Requires-Dist: httpx>=0.24.0; extra == 'test'
|
|
64
39
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
|
|
@@ -99,7 +74,9 @@ It ships with a flexible plugin system, hot reload, a visual Dashboard, an AI Bu
|
|
|
99
74
|
- **Event-driven architecture** — Clear event model based on OneBot12, making message handling intuitive and consistent across platforms.
|
|
100
75
|
- **Cross-platform compatibility** — Write handlers once, deploy to 15+ platforms without rewriting business logic.
|
|
101
76
|
- **Modular design** — Flexible plugin system with hot-plug module management and per-module configuration.
|
|
102
|
-
- **Hot reload** —
|
|
77
|
+
- **Hot reload** — Local plugins reload on save; any module (including PyPI-installed ones) hot-reloads via a single `sdk.reload_module()` call — no process restart, even after pip upgrades.
|
|
78
|
+
- **Unified Event Overrides** — Override message triggers, command params and user permissions for *every* event type with a single config — no module code changes, takes effect instantly.
|
|
79
|
+
- **Scope Control** — Three-dimensional scope (module availability / event admission / outbound limits) with runtime-adjustable fine-grained control.
|
|
103
80
|
- **Chainable SendDSL** — Express `@mention`, reply, retry, timeout, progress hook and bulk send as a single fluent chain.
|
|
104
81
|
- **Complete toolchain** — `epsdk` CLI, visual Dashboard, AI Builder and Module Market included out of the box.
|
|
105
82
|
|
|
@@ -28,7 +28,9 @@ It ships with a flexible plugin system, hot reload, a visual Dashboard, an AI Bu
|
|
|
28
28
|
- **Event-driven architecture** — Clear event model based on OneBot12, making message handling intuitive and consistent across platforms.
|
|
29
29
|
- **Cross-platform compatibility** — Write handlers once, deploy to 15+ platforms without rewriting business logic.
|
|
30
30
|
- **Modular design** — Flexible plugin system with hot-plug module management and per-module configuration.
|
|
31
|
-
- **Hot reload** —
|
|
31
|
+
- **Hot reload** — Local plugins reload on save; any module (including PyPI-installed ones) hot-reloads via a single `sdk.reload_module()` call — no process restart, even after pip upgrades.
|
|
32
|
+
- **Unified Event Overrides** — Override message triggers, command params and user permissions for *every* event type with a single config — no module code changes, takes effect instantly.
|
|
33
|
+
- **Scope Control** — Three-dimensional scope (module availability / event admission / outbound limits) with runtime-adjustable fine-grained control.
|
|
32
34
|
- **Chainable SendDSL** — Express `@mention`, reply, retry, timeout, progress hook and bulk send as a single fluent chain.
|
|
33
35
|
- **Complete toolchain** — `epsdk` CLI, visual Dashboard, AI Builder and Module Market included out of the box.
|
|
34
36
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling>=1.26"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ErisPulse"
|
|
7
|
-
version = "2.8.0-dev.
|
|
7
|
+
version = "2.8.0-dev.1"
|
|
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"
|
|
11
|
-
license =
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
12
13
|
|
|
13
14
|
authors = [
|
|
14
15
|
{ name = "ErisDev" }
|
|
@@ -87,11 +88,14 @@ test = [
|
|
|
87
88
|
"pytest-benchmark>=4.0.0",
|
|
88
89
|
"httpx>=0.24.0",
|
|
89
90
|
]
|
|
91
|
+
|
|
92
|
+
[dependency-groups]
|
|
90
93
|
dev = [
|
|
91
94
|
"pyclean>=3.6.0",
|
|
92
95
|
"ruff>=0.1.7",
|
|
93
96
|
"basedpyright>=1.20.0",
|
|
94
|
-
"typing_extensions>=4.7.0; python_version < '3.11'"
|
|
97
|
+
"typing_extensions>=4.7.0; python_version < '3.11'",
|
|
98
|
+
]
|
|
95
99
|
|
|
96
100
|
[project.scripts]
|
|
97
101
|
ep-init = "ErisPulse.__init__:init_sync"
|
|
@@ -118,7 +122,7 @@ include = [
|
|
|
118
122
|
target-version = "py310"
|
|
119
123
|
line-length = 120
|
|
120
124
|
|
|
121
|
-
include = ["src/**/*.py"]
|
|
125
|
+
include = ["src/**/*.py", "tests/**/*.py"]
|
|
122
126
|
|
|
123
127
|
extend-exclude = [
|
|
124
128
|
"__pycache__",
|
|
@@ -161,6 +165,7 @@ ignore = [
|
|
|
161
165
|
"PLR0912", # too-many-branches
|
|
162
166
|
"PLR0913", # too-many-arguments
|
|
163
167
|
"PLR0915", # too-many-statements
|
|
168
|
+
"PLR0917", # too-many-positional-arguments —— 同 PLR0913,既有公共 API 签名不可破坏
|
|
164
169
|
"PLR2004", # magic-value-comparison —— 数值阈值判定过于严格
|
|
165
170
|
"PLR1714", # repeated-equality-comparison
|
|
166
171
|
"PLW0602", # global-variable-not-assigned
|
|
@@ -169,6 +174,7 @@ ignore = [
|
|
|
169
174
|
"PERF203", # try-except-in-loop —— 异步轮询/重试场景的常见且必要写法,性能影响极小
|
|
170
175
|
"RUF012", # mutable-class-default —— 类属性容器(handlers/事件表)是项目既有 API
|
|
171
176
|
"RUF005", # collection-literal-concatenation —— `list + list` 有时比 unpack 更清晰
|
|
177
|
+
"RUF022", # unsorted-dunder-all —— __all__ 常按语义分组并附加注释,强制排序破坏可读性
|
|
172
178
|
"RET504", # unnecessary-assign —— “先 assign 再 return”有时更易调试
|
|
173
179
|
|
|
174
180
|
# 风格偏好(不强加于现有代码)
|
|
@@ -194,7 +200,15 @@ ignore = [
|
|
|
194
200
|
|
|
195
201
|
[tool.ruff.lint.per-file-ignores]
|
|
196
202
|
"__init__.py" = ["F401", "PLC0415", "I001"] # 导出式 __init__ 经常按需导入
|
|
197
|
-
"tests/**/*.py" = [
|
|
203
|
+
"tests/**/*.py" = [
|
|
204
|
+
# 断言/夹具相关(测试代码天然模式)
|
|
205
|
+
"S101", "PLR2004", "SLF001", "ARG", "ANN", "PTH",
|
|
206
|
+
"E712", "B011", "B017", "B018", "RUF043",
|
|
207
|
+
# 测试内常见的宽松写法
|
|
208
|
+
"E702", "E402", "F841", "RUF059", "PLW0108",
|
|
209
|
+
"UP031", "N814", "PERF401", "PLR0917", "RUF015",
|
|
210
|
+
"B007", "PIE810", "PLR0124", "PLW1510", "SIM118",
|
|
211
|
+
]
|
|
198
212
|
|
|
199
213
|
[tool.ruff.lint.pep8-naming]
|
|
200
214
|
# 装饰器方法、classmethod 的首参数名允许保留
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config 命令实现
|
|
3
|
+
|
|
4
|
+
交互式配置适配器/模块(schema 驱动向导,含适配器多账户管理)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from argparse import ArgumentParser
|
|
9
|
+
|
|
10
|
+
from rich.box import SIMPLE
|
|
11
|
+
from rich.prompt import Prompt
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from ..base import Command
|
|
15
|
+
from ..console import console
|
|
16
|
+
from ..i18n import i18n
|
|
17
|
+
from ..utils import config_wizard
|
|
18
|
+
from ..utils.config_wizard import (
|
|
19
|
+
STATUS_INCOMPLETE,
|
|
20
|
+
STATUS_NONE,
|
|
21
|
+
STATUS_OK,
|
|
22
|
+
STATUS_UNCONFIGURED,
|
|
23
|
+
)
|
|
24
|
+
from ..utils.display import section_header
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ConfigCommand(Command):
|
|
28
|
+
"""
|
|
29
|
+
config 命令
|
|
30
|
+
|
|
31
|
+
交互式配置适配器/模块;适配器支持多账户(bot 账户)管理
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
name = "config"
|
|
35
|
+
description = i18n.t("cli.config.description")
|
|
36
|
+
aliases = ["cfg", "conf"]
|
|
37
|
+
|
|
38
|
+
def add_arguments(self, parser: ArgumentParser):
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"name",
|
|
41
|
+
nargs="?",
|
|
42
|
+
help=i18n.t("cli.config.name_help"),
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--list",
|
|
46
|
+
"-l",
|
|
47
|
+
action="store_true",
|
|
48
|
+
help=i18n.t("cli.config.list_help"),
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--json",
|
|
52
|
+
action="store_true",
|
|
53
|
+
help=i18n.t("cli.config.json_help"),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def execute(self, args):
|
|
57
|
+
from ErisPulse import config
|
|
58
|
+
|
|
59
|
+
targets = config_wizard.load_config_targets()
|
|
60
|
+
if not targets:
|
|
61
|
+
console.print(f"[dim] {i18n.t('cli.config.no_targets')}[/]")
|
|
62
|
+
console.print(f"[dim] {i18n.t('cli.config.install_hint')}[/]")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
if getattr(args, "json", False):
|
|
66
|
+
self._print_status_json(targets, config)
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if args.name:
|
|
70
|
+
self._run_named(targets, args.name, config)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
self._print_status_table(targets, config)
|
|
74
|
+
|
|
75
|
+
if args.list:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
if not config_wizard.is_interactive():
|
|
79
|
+
console.print(f"[dim] {i18n.t('cli.config.non_interactive_hint')}[/]")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
self._interactive_select(targets, config)
|
|
83
|
+
|
|
84
|
+
def _print_status_json(self, targets, config):
|
|
85
|
+
"""
|
|
86
|
+
以 JSON 输出全部目标及其配置状态(供 CI / 脚本消费)
|
|
87
|
+
|
|
88
|
+
:param targets: ConfigTarget 列表
|
|
89
|
+
:param config: ConfigManager 实例
|
|
90
|
+
"""
|
|
91
|
+
result = {}
|
|
92
|
+
for target in sorted(targets, key=lambda t: (t.kind != "adapter", t.name.lower())):
|
|
93
|
+
status, errors = config_wizard.get_target_status(target, config)
|
|
94
|
+
result[target.name] = {
|
|
95
|
+
"kind": target.kind,
|
|
96
|
+
"config_key": target.config_key,
|
|
97
|
+
"status": status,
|
|
98
|
+
"errors": errors,
|
|
99
|
+
}
|
|
100
|
+
console.print_json(json.dumps(result, ensure_ascii=False, indent=2))
|
|
101
|
+
|
|
102
|
+
def _run_named(self, targets, name: str, config):
|
|
103
|
+
"""
|
|
104
|
+
按名称定位目标并直接进入向导
|
|
105
|
+
|
|
106
|
+
:param targets: ConfigTarget 列表
|
|
107
|
+
:param name: 目标名(适配器平台名/模块名,或适配器配置键)
|
|
108
|
+
:param config: ConfigManager 实例
|
|
109
|
+
"""
|
|
110
|
+
for target in targets:
|
|
111
|
+
if target.name == name or target.config_key == name:
|
|
112
|
+
if not target.configurable:
|
|
113
|
+
console.print(f"[warning] {i18n.t('cli.config.no_declaration', name=target.name)}[/]")
|
|
114
|
+
return
|
|
115
|
+
config_wizard.run_wizard(target, config)
|
|
116
|
+
return
|
|
117
|
+
console.print(f"[error] {i18n.t('cli.config.not_found', name=name)}[/]")
|
|
118
|
+
|
|
119
|
+
def _status_text(self, status: str) -> str:
|
|
120
|
+
"""
|
|
121
|
+
将状态常量渲染为带颜色的显示文本
|
|
122
|
+
|
|
123
|
+
:param status: get_target_status 返回的状态常量
|
|
124
|
+
:return: rich 标记的状态文本
|
|
125
|
+
"""
|
|
126
|
+
mapping = {
|
|
127
|
+
STATUS_OK: ("cli.config.status_ok", "green"),
|
|
128
|
+
STATUS_INCOMPLETE: ("cli.config.status_incomplete", "yellow"),
|
|
129
|
+
STATUS_UNCONFIGURED: ("cli.config.status_unconfigured", "cyan"),
|
|
130
|
+
STATUS_NONE: ("cli.config.status_none", "dim"),
|
|
131
|
+
}
|
|
132
|
+
key, style = mapping.get(status, ("cli.config.status_none", "dim"))
|
|
133
|
+
return f"[{style}]{i18n.t(key)}[/]"
|
|
134
|
+
|
|
135
|
+
def _print_status_table(self, targets, config):
|
|
136
|
+
"""
|
|
137
|
+
打印全部目标及其配置状态表
|
|
138
|
+
|
|
139
|
+
:param targets: ConfigTarget 列表
|
|
140
|
+
:param config: ConfigManager 实例
|
|
141
|
+
"""
|
|
142
|
+
table = Table(box=SIMPLE, show_lines=False, header_style="bold", pad_edge=False)
|
|
143
|
+
table.add_column(i18n.t("cli.config.header_kind"), width=8)
|
|
144
|
+
table.add_column(i18n.t("cli.config.header_name"), min_width=12)
|
|
145
|
+
table.add_column(i18n.t("cli.config.header_status"), width=10)
|
|
146
|
+
table.add_column(i18n.t("cli.config.header_detail"))
|
|
147
|
+
|
|
148
|
+
for target in sorted(targets, key=lambda t: (t.kind != "adapter", t.name.lower())):
|
|
149
|
+
status, errors = config_wizard.get_target_status(target, config)
|
|
150
|
+
detail = ";".join(errors[:3])
|
|
151
|
+
if len(errors) > 3:
|
|
152
|
+
detail += f" (+{len(errors) - 3})"
|
|
153
|
+
kind_style = "adapter" if target.kind == "adapter" else "module"
|
|
154
|
+
table.add_row(
|
|
155
|
+
f"[{kind_style}]{target.kind_label}[/]",
|
|
156
|
+
target.name,
|
|
157
|
+
self._status_text(status),
|
|
158
|
+
detail,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
console.print(table)
|
|
162
|
+
|
|
163
|
+
def _interactive_select(self, targets, config):
|
|
164
|
+
"""
|
|
165
|
+
交互式选择目标并进入向导(可连续配置多个)
|
|
166
|
+
|
|
167
|
+
:param targets: ConfigTarget 列表
|
|
168
|
+
:param config: ConfigManager 实例
|
|
169
|
+
"""
|
|
170
|
+
selectable = [t for t in targets if t.configurable]
|
|
171
|
+
if not selectable:
|
|
172
|
+
console.print(f"[dim] {i18n.t('cli.config.no_configurable')}[/]")
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
selectable.sort(key=lambda t: (t.kind != "adapter", t.name.lower()))
|
|
176
|
+
|
|
177
|
+
while True:
|
|
178
|
+
section_header(i18n.t("cli.config.select_section"))
|
|
179
|
+
for idx, target in enumerate(selectable, 1):
|
|
180
|
+
status, _ = config_wizard.get_target_status(target, config)
|
|
181
|
+
kind_style = "adapter" if target.kind == "adapter" else "module"
|
|
182
|
+
console.print(f" [bold]{idx}.[/] [{kind_style}]{target.name}[/] {self._status_text(status)}")
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
raw = Prompt.ask(
|
|
186
|
+
f" {i18n.t('cli.config.select_prompt')}",
|
|
187
|
+
default="",
|
|
188
|
+
show_default=False,
|
|
189
|
+
)
|
|
190
|
+
except (EOFError, KeyboardInterrupt):
|
|
191
|
+
break
|
|
192
|
+
|
|
193
|
+
raw = raw.strip()
|
|
194
|
+
if not raw:
|
|
195
|
+
break
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
idx = int(raw)
|
|
199
|
+
except ValueError:
|
|
200
|
+
console.print(f"[warning] {i18n.t('cli.config.invalid_index', idx=raw)}[/]")
|
|
201
|
+
continue
|
|
202
|
+
|
|
203
|
+
if not 1 <= idx <= len(selectable):
|
|
204
|
+
console.print(f"[warning] {i18n.t('cli.config.invalid_index', idx=idx)}[/]")
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
config_wizard.run_wizard(selectable[idx - 1], config)
|
|
208
|
+
# 向导结束后回到选择菜单(状态重新刷新),支持连续配置多个目标
|
|
@@ -58,6 +58,13 @@ dependencies = [
|
|
|
58
58
|
|
|
59
59
|
[project.entry-points]
|
|
60
60
|
"erispulse.module" = {{ "{name}" = "{name}:Main" }}
|
|
61
|
+
|
|
62
|
+
[build-system]
|
|
63
|
+
requires = ["hatchling"]
|
|
64
|
+
build-backend = "hatchling.build"
|
|
65
|
+
|
|
66
|
+
[tool.hatch.build.targets.wheel]
|
|
67
|
+
packages = ["{name}"]
|
|
61
68
|
"""
|
|
62
69
|
|
|
63
70
|
_MODULE_INIT = """from .Core import Main
|
|
@@ -192,6 +199,12 @@ class Main(BaseModule):
|
|
|
192
199
|
self.logger.info(\"{text[module.log.config_updated]}\")
|
|
193
200
|
|
|
194
201
|
async def _register_commands(self):
|
|
202
|
+
# 命令权限(可选):permission 为调用函数,返回 True 才执行命令;
|
|
203
|
+
# master=True 限定框架主人(仅开发者默认值,用户可在统一覆写系统
|
|
204
|
+
# ErisPulse.event.overrides.command.<module>.<cmd>.master = true/false 覆写收紧或放开);
|
|
205
|
+
# 跨命令的用户黑白名单用覆写系统 ACL
|
|
206
|
+
# (ErisPulse.event.overrides.acl 或 overrides.acl.set(name, allow/deny),命令名支持 glob);
|
|
207
|
+
# 模块级可用性 / 事件准入 / 出站限制由作用域 scope 管理(用户可控)
|
|
195
208
|
@command(\"hello\", help=i18n.t(\"module.{name}.command.hello.help\"))
|
|
196
209
|
async def hello_command(event: Event):
|
|
197
210
|
await event.reply(i18n.t(\"module.{name}.command.hello.reply\"))
|
|
@@ -205,6 +218,11 @@ class Main(BaseModule):
|
|
|
205
218
|
async def group_message_handler(event: Event):
|
|
206
219
|
pass
|
|
207
220
|
|
|
221
|
+
# 消息装饰器支持 pattern(glob 通配符)/ regex(正则),不匹配则不触发
|
|
222
|
+
@message.on_message(pattern=\"签到*\")
|
|
223
|
+
async def signin_handler(event: Event):
|
|
224
|
+
await event.reply(\"签到成功\")
|
|
225
|
+
|
|
208
226
|
@notice.on_friend_add()
|
|
209
227
|
async def friend_add_handler(event: Event):
|
|
210
228
|
self.logger.info((\"{text[module.log.friend_add]}\").format(nickname=event.get_user_nickname()))
|
|
@@ -227,6 +245,13 @@ dependencies = [
|
|
|
227
245
|
|
|
228
246
|
[project.entry-points]
|
|
229
247
|
"erispulse.adapter" = {{ "{entry_key}" = "{name}:{name}" }}
|
|
248
|
+
|
|
249
|
+
[build-system]
|
|
250
|
+
requires = ["hatchling"]
|
|
251
|
+
build-backend = "hatchling.build"
|
|
252
|
+
|
|
253
|
+
[tool.hatch.build.targets.wheel]
|
|
254
|
+
packages = ["{name}"]
|
|
230
255
|
"""
|
|
231
256
|
|
|
232
257
|
_ADAPTER_INIT = """from .Core import {name}
|
|
@@ -44,20 +44,14 @@ class InitCommand(Command):
|
|
|
44
44
|
|
|
45
45
|
def add_arguments(self, parser: ArgumentParser):
|
|
46
46
|
parser.add_argument("--project-name", "-n", help=i18n.t("cli.init.name_help"))
|
|
47
|
-
parser.add_argument(
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
parser.add_argument(
|
|
51
|
-
"--force", "-f", action="store_true", help=i18n.t("cli.init.force_help")
|
|
52
|
-
)
|
|
47
|
+
parser.add_argument("--quick", "-q", action="store_true", help=i18n.t("cli.init.quick_help"))
|
|
48
|
+
parser.add_argument("--force", "-f", action="store_true", help=i18n.t("cli.init.force_help"))
|
|
53
49
|
parser.add_argument(
|
|
54
50
|
"--here",
|
|
55
51
|
action="store_true",
|
|
56
52
|
help=i18n.t("cli.init.here_help"),
|
|
57
53
|
)
|
|
58
|
-
parser.add_argument(
|
|
59
|
-
"--no-uv", action="store_true", help=i18n.t("cli.init.nouv_help")
|
|
60
|
-
)
|
|
54
|
+
parser.add_argument("--no-uv", action="store_true", help=i18n.t("cli.init.nouv_help"))
|
|
61
55
|
|
|
62
56
|
def execute(self, args):
|
|
63
57
|
self.no_uv = getattr(args, "no_uv", False)
|
|
@@ -107,19 +101,13 @@ class InitCommand(Command):
|
|
|
107
101
|
|
|
108
102
|
if project_path.exists():
|
|
109
103
|
if project_path.is_dir():
|
|
110
|
-
console.print(
|
|
111
|
-
f"[warning] {i18n.t('cli.init.dir_exists', name=project_name)}[/]"
|
|
112
|
-
)
|
|
104
|
+
console.print(f"[warning] {i18n.t('cli.init.dir_exists', name=project_name)}[/]")
|
|
113
105
|
else:
|
|
114
|
-
console.print(
|
|
115
|
-
f"[error] {i18n.t('cli.init.file_exists_not_dir', name=project_name)}[/]"
|
|
116
|
-
)
|
|
106
|
+
console.print(f"[error] {i18n.t('cli.init.file_exists_not_dir', name=project_name)}[/]")
|
|
117
107
|
return False
|
|
118
108
|
else:
|
|
119
109
|
project_path.mkdir()
|
|
120
|
-
console.print(
|
|
121
|
-
f"[success] {i18n.t('cli.init.created_dir', name=project_name)}[/]"
|
|
122
|
-
)
|
|
110
|
+
console.print(f"[success] {i18n.t('cli.init.created_dir', name=project_name)}[/]")
|
|
123
111
|
|
|
124
112
|
try:
|
|
125
113
|
for dir_name in ["config", "logs"]:
|
|
@@ -131,8 +119,7 @@ class InitCommand(Command):
|
|
|
131
119
|
ssl_file = ssl_dir / ssl_name
|
|
132
120
|
if not ssl_file.exists():
|
|
133
121
|
ssl_file.write_text(
|
|
134
|
-
"# 将你的证书/密钥 PEM
|
|
135
|
-
"或在配置中改用 ssl_cert/ssl_key 内联填写\n",
|
|
122
|
+
"# 将你的证书/密钥 PEM 内容粘贴到本文件,或在配置中改用 ssl_cert/ssl_key 内联填写\n",
|
|
136
123
|
encoding="utf-8",
|
|
137
124
|
)
|
|
138
125
|
|
|
@@ -168,29 +155,19 @@ class InitCommand(Command):
|
|
|
168
155
|
f.write('if __name__ == "__main__":\n')
|
|
169
156
|
f.write(" asyncio.run(main())\n")
|
|
170
157
|
|
|
171
|
-
console.print(
|
|
172
|
-
f"[success] {i18n.t('cli.init.display_success', name=display_name)}[/]"
|
|
173
|
-
)
|
|
158
|
+
console.print(f"[success] {i18n.t('cli.init.display_success', name=display_name)}[/]")
|
|
174
159
|
console.print()
|
|
175
160
|
console.print(Text(i18n.t("cli.create.next_steps"), style="bold"))
|
|
176
161
|
if in_current_dir:
|
|
177
|
-
console.print(
|
|
178
|
-
f" · {i18n.t('cli.init.edit_config', path='config/config.toml')}"
|
|
179
|
-
)
|
|
162
|
+
console.print(f" · {i18n.t('cli.init.edit_config', path='config/config.toml')}")
|
|
180
163
|
console.print(f" · {i18n.t('cli.init.run_direct')}")
|
|
181
164
|
else:
|
|
182
|
-
console.print(
|
|
183
|
-
|
|
184
|
-
)
|
|
185
|
-
console.print(
|
|
186
|
-
f" · {i18n.t('cli.init.cd_and_run', dir=display_name)}"
|
|
187
|
-
)
|
|
165
|
+
console.print(f" · {i18n.t('cli.init.edit_config', path=f'{display_name}/config/config.toml')}")
|
|
166
|
+
console.print(f" · {i18n.t('cli.init.cd_and_run', dir=display_name)}")
|
|
188
167
|
return True
|
|
189
168
|
|
|
190
169
|
except Exception as e:
|
|
191
|
-
console.print(
|
|
192
|
-
f"[error] {i18n.t('cli.init.init_project_failed', error=e)}[/]"
|
|
193
|
-
)
|
|
170
|
+
console.print(f"[error] {i18n.t('cli.init.init_project_failed', error=e)}[/]")
|
|
194
171
|
return False
|
|
195
172
|
|
|
196
173
|
@staticmethod
|
|
@@ -335,9 +312,7 @@ class InitCommand(Command):
|
|
|
335
312
|
if adapters:
|
|
336
313
|
return adapters
|
|
337
314
|
except Exception as e:
|
|
338
|
-
console.print(
|
|
339
|
-
f"[warning] {i18n.t('cli.init.fetch_remote_failed', error=e)}[/]"
|
|
340
|
-
)
|
|
315
|
+
console.print(f"[warning] {i18n.t('cli.init.fetch_remote_failed', error=e)}[/]")
|
|
341
316
|
|
|
342
317
|
return {
|
|
343
318
|
"yunhu": i18n.t("cli.init.adapter_desc_yunhu"),
|
|
@@ -346,9 +321,7 @@ class InitCommand(Command):
|
|
|
346
321
|
"email": i18n.t("cli.init.adapter_desc_email"),
|
|
347
322
|
}
|
|
348
323
|
|
|
349
|
-
def _interactive_init(
|
|
350
|
-
self, project_name: str | None = None, force: bool = False, here: bool = False
|
|
351
|
-
) -> bool:
|
|
324
|
+
def _interactive_init(self, project_name: str | None = None, force: bool = False, here: bool = False) -> bool:
|
|
352
325
|
"""
|
|
353
326
|
交互式初始化项目,引导用户配置项目位置及基本参数
|
|
354
327
|
|
|
@@ -368,9 +341,7 @@ class InitCommand(Command):
|
|
|
368
341
|
f" [bold]2.[/] {i18n.t('cli.init.location_option_new')} [dim]— {i18n.t('cli.init.location_desc_new')}[/]"
|
|
369
342
|
)
|
|
370
343
|
console.print()
|
|
371
|
-
location_choice = IntPrompt.ask(
|
|
372
|
-
i18n.t("cli.create.select_prompt"), default=2, choices=["1", "2"]
|
|
373
|
-
)
|
|
344
|
+
location_choice = IntPrompt.ask(i18n.t("cli.create.select_prompt"), default=2, choices=["1", "2"])
|
|
374
345
|
console.print()
|
|
375
346
|
in_current_dir = location_choice == 1
|
|
376
347
|
|
|
@@ -423,26 +394,20 @@ class InitCommand(Command):
|
|
|
423
394
|
config.setConfig("ErisPulse.logger.level", new_level.upper())
|
|
424
395
|
|
|
425
396
|
current_host = config.getConfig("ErisPulse.server.host", "0.0.0.0")
|
|
426
|
-
console.print(
|
|
427
|
-
f" {i18n.t('cli.init.listen_host')} [dim]({current_host})[/]"
|
|
428
|
-
)
|
|
397
|
+
console.print(f" {i18n.t('cli.init.listen_host')} [dim]({current_host})[/]")
|
|
429
398
|
new_host = _input(">")
|
|
430
399
|
if new_host:
|
|
431
400
|
config.setConfig("ErisPulse.server.host", new_host)
|
|
432
401
|
|
|
433
402
|
current_port = str(config.getConfig("ErisPulse.server.port", 8000))
|
|
434
|
-
console.print(
|
|
435
|
-
f" {i18n.t('cli.init.listen_port')} [dim]({current_port})[/]"
|
|
436
|
-
)
|
|
403
|
+
console.print(f" {i18n.t('cli.init.listen_port')} [dim]({current_port})[/]")
|
|
437
404
|
new_port = _input(">")
|
|
438
405
|
while new_port:
|
|
439
406
|
try:
|
|
440
407
|
config.setConfig("ErisPulse.server.port", int(new_port))
|
|
441
408
|
break
|
|
442
409
|
except ValueError:
|
|
443
|
-
console.print(
|
|
444
|
-
f"[warning] {i18n.t('cli.init.invalid_port', port=new_port)}[/]"
|
|
445
|
-
)
|
|
410
|
+
console.print(f"[warning] {i18n.t('cli.init.invalid_port', port=new_port)}[/]")
|
|
446
411
|
new_port = _input(">")
|
|
447
412
|
|
|
448
413
|
if Confirm.ask(
|
|
@@ -466,19 +431,13 @@ class InitCommand(Command):
|
|
|
466
431
|
"""
|
|
467
432
|
from ErisPulse import config
|
|
468
433
|
|
|
469
|
-
with console.status(
|
|
470
|
-
f"[bold green]{i18n.t('cli.init.fetching_adapters')}...", spinner="dots"
|
|
471
|
-
):
|
|
434
|
+
with console.status(f"[bold green]{i18n.t('cli.init.fetching_adapters')}...", spinner="dots"):
|
|
472
435
|
try:
|
|
473
436
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
474
|
-
future = executor.submit(
|
|
475
|
-
asyncio.run, self._fetch_available_adapters()
|
|
476
|
-
)
|
|
437
|
+
future = executor.submit(asyncio.run, self._fetch_available_adapters())
|
|
477
438
|
adapters = future.result(timeout=10)
|
|
478
439
|
except Exception as e:
|
|
479
|
-
console.print(
|
|
480
|
-
f"[error] {i18n.t('cli.init.fetch_adapters_failed', error=e)}[/]"
|
|
481
|
-
)
|
|
440
|
+
console.print(f"[error] {i18n.t('cli.init.fetch_adapters_failed', error=e)}[/]")
|
|
482
441
|
return
|
|
483
442
|
|
|
484
443
|
if not adapters:
|
|
@@ -510,30 +469,30 @@ class InitCommand(Command):
|
|
|
510
469
|
enabled.append(name)
|
|
511
470
|
config.setConfig(f"ErisPulse.adapters.status.{name}", True)
|
|
512
471
|
else:
|
|
513
|
-
console.print(
|
|
514
|
-
f"[warning] {i18n.t('cli.init.invalid_index', idx=idx)}[/]"
|
|
515
|
-
)
|
|
472
|
+
console.print(f"[warning] {i18n.t('cli.init.invalid_index', idx=idx)}[/]")
|
|
516
473
|
|
|
517
474
|
for name, _ in adapter_list:
|
|
518
475
|
if name not in enabled:
|
|
519
476
|
config.setConfig(f"ErisPulse.adapters.status.{name}", False)
|
|
520
477
|
|
|
521
|
-
console.print(
|
|
522
|
-
f"[dim] {i18n.t('cli.init.adapters_enabled', count=len(enabled))}[/]"
|
|
523
|
-
)
|
|
478
|
+
console.print(f"[dim] {i18n.t('cli.init.adapters_enabled', count=len(enabled))}[/]")
|
|
524
479
|
|
|
525
|
-
if enabled and Confirm.ask(
|
|
526
|
-
f" [cyan]{i18n.t('cli.init.install_selected_prompt')}[/]", default=True
|
|
527
|
-
):
|
|
480
|
+
if enabled and Confirm.ask(f" [cyan]{i18n.t('cli.init.install_selected_prompt')}[/]", default=True):
|
|
528
481
|
self._install_adapters(enabled, adapters)
|
|
529
482
|
|
|
530
483
|
def _install_adapters(self, adapter_names, adapters_info):
|
|
531
484
|
"""
|
|
532
485
|
安装选中的适配器
|
|
533
486
|
|
|
487
|
+
安装成功后衔接交互式配置向导(写入新项目的 config.toml)
|
|
488
|
+
|
|
534
489
|
:param adapter_names: [list] 适配器简称列表
|
|
535
490
|
:param adapters_info: [dict] 适配器信息
|
|
536
491
|
"""
|
|
492
|
+
from ErisPulse import config
|
|
493
|
+
|
|
494
|
+
from ..utils import config_wizard
|
|
495
|
+
|
|
537
496
|
pkg_manager = PackageManager()
|
|
538
497
|
pkg_manager.no_uv = getattr(self, "no_uv", False)
|
|
539
498
|
for adapter_name in adapter_names:
|
|
@@ -542,14 +501,10 @@ class InitCommand(Command):
|
|
|
542
501
|
remote_packages = pkg_manager._cache.get("remote_packages", {})
|
|
543
502
|
if not remote_packages:
|
|
544
503
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
545
|
-
future = executor.submit(
|
|
546
|
-
asyncio.run, pkg_manager.get_remote_packages()
|
|
547
|
-
)
|
|
504
|
+
future = executor.submit(asyncio.run, pkg_manager.get_remote_packages())
|
|
548
505
|
remote_packages = future.result(timeout=10)
|
|
549
506
|
if adapter_name in remote_packages.get("adapters", {}):
|
|
550
|
-
package_name = remote_packages["adapters"][adapter_name].get(
|
|
551
|
-
"package"
|
|
552
|
-
)
|
|
507
|
+
package_name = remote_packages["adapters"][adapter_name].get("package")
|
|
553
508
|
except Exception:
|
|
554
509
|
pass
|
|
555
510
|
|
|
@@ -562,6 +517,10 @@ class InitCommand(Command):
|
|
|
562
517
|
success = pkg_manager.install_package([package_name])
|
|
563
518
|
|
|
564
519
|
if not success:
|
|
565
|
-
console.print(
|
|
566
|
-
|
|
567
|
-
|
|
520
|
+
console.print(f"[error] {i18n.t('cli.init.adapter_install_failed', name=adapter_name)}[/]")
|
|
521
|
+
elif config_wizard.is_interactive():
|
|
522
|
+
# 安装成功后衔接配置向导,写入已重定向到新项目的 config.toml
|
|
523
|
+
try:
|
|
524
|
+
config_wizard.post_install_configure([package_name], config)
|
|
525
|
+
except Exception as e:
|
|
526
|
+
console.print(f"[warning] {i18n.t('cli.config.post_install_failed', error=e)}[/]")
|