ErisPulse 2.9.0.dev1__tar.gz → 2.9.0.dev2__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.9.0.dev1 → erispulse-2.9.0.dev2}/.gitignore +6 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/PKG-INFO +2 -1
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/pyproject.toml +3 -1
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/cli.py +12 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/doctor.py +47 -1
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/init.py +11 -7
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/install.py +10 -6
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/list.py +40 -6
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/run.py +20 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/self_update.py +5 -4
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/upgrade.py +9 -3
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/constants.py +5 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/locales/en.py +13 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/locales/ja.py +13 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/locales/ru.py +13 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +13 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +13 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/utils/config_wizard.py +7 -6
- erispulse-2.9.0.dev2/src/ErisPulse/CLI/utils/interactive.py +127 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/utils/package_manager.py +9 -6
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/kv_builder.py +4 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/__init__.py +1 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/adapter.py +67 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/config.py +214 -1
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/constants.py +4 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/locales/en.py +11 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/locales/ja.py +11 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/locales/ru.py +11 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/locales/zh_cn.py +11 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/locales/zh_tw.py +11 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/logger.py +1 -1
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/module.py +42 -3
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/router.py +184 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/module.py +168 -8
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/__init__.py +1 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/sdk.py +27 -2
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/LICENSE +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/README.pypi.md +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/base.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/config.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/create.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/language.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/types.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/commands/version.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/console.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/hints.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/utils/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/utils/display.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/CLI/utils/scaffold_text.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/adapter.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/api_dsl.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/client.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/config_schema.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/converter.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/errors.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/i18n_schema.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/model.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/module.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/router.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/send_rules.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/sql_base.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Bases/websocket.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/base.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/command.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/command_args.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/conversation.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/governance.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/interaction.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/message.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/message_builder.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/overrides.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/session_type.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/throttle.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/trace.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/Event/wrapper.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/assets/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/assets/error.css +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/assets/error.html +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/assets/root.css +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/assets/root.html +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/client.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/di.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/constants.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/lifecycle.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/master.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/ownership.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/scope.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/shadow.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/storage/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/storage/mysql.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/storage/postgres.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/storage/sqlite.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/text_match.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/Core/transcript.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/finders/adapter.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/finders/module.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/adapter.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/plugin_folder.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/loaders/strict.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/py.typed +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/config_schema.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/context.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/diagnostics.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/example_config.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/file_watcher.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/frame_config.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/hints.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/memory.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/owner_cleanup.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/plugin_reload.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/proactive_gc.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/sdk_initializer.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/tasks.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/troubleshoot.py +0 -0
- {erispulse-2.9.0.dev1 → erispulse-2.9.0.dev2}/src/ErisPulse/runtime/version.py +0 -0
|
@@ -122,7 +122,7 @@ ipython_config.py
|
|
|
122
122
|
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
123
123
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
124
124
|
# commonly ignored for libraries.
|
|
125
|
-
|
|
125
|
+
uv.lock
|
|
126
126
|
|
|
127
127
|
# poetry
|
|
128
128
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
@@ -239,4 +239,8 @@ pyvenv.cfg
|
|
|
239
239
|
# 但保留源中文文档
|
|
240
240
|
!docs/zh-CN/
|
|
241
241
|
|
|
242
|
-
.zcode/
|
|
242
|
+
.zcode/
|
|
243
|
+
|
|
244
|
+
.github/assets/ErisPulseFilm1.mp4
|
|
245
|
+
.github/assets/score1.wav
|
|
246
|
+
.github/assets/score2.wav
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 2.9.0-dev.
|
|
3
|
+
Version: 2.9.0-dev.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
|
|
@@ -38,6 +38,7 @@ Requires-Dist: uvicorn>=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'
|
|
41
|
+
Requires-Dist: cryptography>=41.0.0; extra == 'mysql'
|
|
41
42
|
Provides-Extra: postgres
|
|
42
43
|
Requires-Dist: asyncpg>=0.29.0; extra == 'postgres'
|
|
43
44
|
Provides-Extra: test
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ErisPulse"
|
|
7
|
-
version = "2.9.0-dev.
|
|
7
|
+
version = "2.9.0-dev.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"
|
|
@@ -84,6 +84,8 @@ Changelog = "https://github.com/ErisPulse/ErisPulse/blob/main/CHANGELOG.md"
|
|
|
84
84
|
# 存储后端可选驱动:pip install ErisPulse[mysql] / ErisPulse[postgres]
|
|
85
85
|
mysql = [
|
|
86
86
|
"aiomysql>=0.2.0",
|
|
87
|
+
# MySQL 8 默认 caching_sha2_password 认证要求(缺失时连接池初始化失败)
|
|
88
|
+
"cryptography>=41.0.0",
|
|
87
89
|
]
|
|
88
90
|
postgres = [
|
|
89
91
|
"asyncpg>=0.29.0",
|
|
@@ -305,6 +305,11 @@ class CLI:
|
|
|
305
305
|
args, unknown = self.parser.parse_known_args()
|
|
306
306
|
args._unknown_args = unknown
|
|
307
307
|
|
|
308
|
+
# --yes/-y:assume-yes 模式,由统一交互封装(utils/interactive)消费
|
|
309
|
+
from .utils.interactive import set_assume_yes
|
|
310
|
+
|
|
311
|
+
set_assume_yes(bool(getattr(args, "yes", False)))
|
|
312
|
+
|
|
308
313
|
# --no-color:禁用 Rich 控制台着色(CI / 日志采集场景)
|
|
309
314
|
if getattr(args, "no_color", False):
|
|
310
315
|
console.no_color = True
|
|
@@ -375,6 +380,13 @@ class CLI:
|
|
|
375
380
|
console.print(f"\n[warning]{i18n.t('cli.run.user_interrupted')}[/]")
|
|
376
381
|
sys.exit(1)
|
|
377
382
|
except Exception as e:
|
|
383
|
+
from .utils.interactive import NonInteractiveError
|
|
384
|
+
|
|
385
|
+
if isinstance(e, NonInteractiveError):
|
|
386
|
+
# 非交互环境遇到无法自动应答的提示:明确报错而非 EOF 崩溃
|
|
387
|
+
console.print(f"[error]{e}[/]")
|
|
388
|
+
console.print(f"[hint]{i18n.t('cli.interactive.non_tty_hint')}[/]")
|
|
389
|
+
sys.exit(1)
|
|
378
390
|
console.print(f"[error]{i18n.t('cli.run.exec_error', error=e)}[/]")
|
|
379
391
|
# 场景化友好提示:根据异常类型给出下一步建议
|
|
380
392
|
from .hints import suggest_for_exception
|
|
@@ -95,7 +95,7 @@ class DoctorCommand(Command):
|
|
|
95
95
|
pip_note = (
|
|
96
96
|
i18n.t("cli.doctor.venv_pip_ok")
|
|
97
97
|
if venv_pip.exists()
|
|
98
|
-
else "
|
|
98
|
+
else i18n.t("cli.doctor.venv_pip_uv")
|
|
99
99
|
)
|
|
100
100
|
rows.append(
|
|
101
101
|
(
|
|
@@ -133,6 +133,52 @@ class DoctorCommand(Command):
|
|
|
133
133
|
)
|
|
134
134
|
failed = True
|
|
135
135
|
|
|
136
|
+
# 4.5 组件配置健康(配置文件存在 ≠ 必填项齐全:复用配置向导的
|
|
137
|
+
# schema 状态检测,"装好了跑不起来"多因适配器必填项缺失)
|
|
138
|
+
try:
|
|
139
|
+
from ..utils.config_wizard import (
|
|
140
|
+
STATUS_INCOMPLETE,
|
|
141
|
+
get_target_status,
|
|
142
|
+
load_config_targets,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
incomplete: list[str] = []
|
|
146
|
+
for target in load_config_targets():
|
|
147
|
+
status, _errors = get_target_status(target)
|
|
148
|
+
# 仅"待完善"(开了但必填项缺失)判 FAIL;完全未配置属
|
|
149
|
+
# 用户尚未开始配置,不算故障
|
|
150
|
+
if status == STATUS_INCOMPLETE:
|
|
151
|
+
incomplete.append(target.name)
|
|
152
|
+
if incomplete:
|
|
153
|
+
failed = True
|
|
154
|
+
rows.append(
|
|
155
|
+
(
|
|
156
|
+
self._status(False),
|
|
157
|
+
i18n.t("cli.doctor.component_config"),
|
|
158
|
+
i18n.t(
|
|
159
|
+
"cli.doctor.component_config_incomplete",
|
|
160
|
+
names=", ".join(incomplete),
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
rows.append(
|
|
166
|
+
(
|
|
167
|
+
self._status(True),
|
|
168
|
+
i18n.t("cli.doctor.component_config"),
|
|
169
|
+
i18n.t("cli.doctor.component_config_ok"),
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
except Exception:
|
|
173
|
+
# 发现失败(如依赖缺失)不阻塞其余诊断项
|
|
174
|
+
rows.append(
|
|
175
|
+
(
|
|
176
|
+
self._status(True),
|
|
177
|
+
i18n.t("cli.doctor.component_config"),
|
|
178
|
+
i18n.t("cli.doctor.component_config_skipped"),
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
136
182
|
# 5. PyPI 连通性
|
|
137
183
|
try:
|
|
138
184
|
remote = asyncio.run(pm.get_remote_packages())
|
|
@@ -11,7 +11,6 @@ import sys
|
|
|
11
11
|
from argparse import ArgumentParser
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
from rich.prompt import Confirm, IntPrompt
|
|
15
14
|
from rich.text import Text
|
|
16
15
|
|
|
17
16
|
from ..base import Command
|
|
@@ -19,6 +18,7 @@ from ..console import console
|
|
|
19
18
|
from ..i18n import i18n
|
|
20
19
|
from ..utils import PackageManager
|
|
21
20
|
from ..utils.display import _input, prompt_validated, section_header
|
|
21
|
+
from ..utils.interactive import confirm, select
|
|
22
22
|
from ..utils.package_manager import (
|
|
23
23
|
append_pyproject_dependencies,
|
|
24
24
|
create_project_venv,
|
|
@@ -432,7 +432,11 @@ class InitCommand(Command):
|
|
|
432
432
|
f" [bold]2.[/] {i18n.t('cli.init.location_option_new')} [dim]— {i18n.t('cli.init.location_desc_new')}[/]"
|
|
433
433
|
)
|
|
434
434
|
console.print()
|
|
435
|
-
location_choice =
|
|
435
|
+
location_choice = int(
|
|
436
|
+
select(
|
|
437
|
+
i18n.t("cli.create.select_prompt"), choices=["1", "2"], default="2"
|
|
438
|
+
)
|
|
439
|
+
)
|
|
436
440
|
console.print()
|
|
437
441
|
in_current_dir = location_choice == 1
|
|
438
442
|
|
|
@@ -458,7 +462,7 @@ class InitCommand(Command):
|
|
|
458
462
|
project_name = p.name
|
|
459
463
|
project_path = (Path(target_dir) if target_dir else Path()) / project_name
|
|
460
464
|
if project_path.exists() and not force:
|
|
461
|
-
if not
|
|
465
|
+
if not confirm(
|
|
462
466
|
f" [cyan]{i18n.t('cli.init.dir_overwrite_prompt', name=project_name)}[/]",
|
|
463
467
|
default=False,
|
|
464
468
|
):
|
|
@@ -467,10 +471,10 @@ class InitCommand(Command):
|
|
|
467
471
|
|
|
468
472
|
# 虚拟环境与 git 仓库问询
|
|
469
473
|
if not getattr(self, "no_venv", False):
|
|
470
|
-
create_venv =
|
|
474
|
+
create_venv = confirm(
|
|
471
475
|
f" [cyan]{i18n.t('cli.init.venv_prompt')}[/]", default=True
|
|
472
476
|
)
|
|
473
|
-
git_init =
|
|
477
|
+
git_init = confirm(
|
|
474
478
|
f" [cyan]{i18n.t('cli.init.git_prompt')}[/]", default=False
|
|
475
479
|
)
|
|
476
480
|
|
|
@@ -521,7 +525,7 @@ class InitCommand(Command):
|
|
|
521
525
|
console.print(f"[warning] {i18n.t('cli.init.invalid_port', port=new_port)}[/]")
|
|
522
526
|
new_port = _input(">")
|
|
523
527
|
|
|
524
|
-
if
|
|
528
|
+
if confirm(
|
|
525
529
|
f"\n [cyan]{i18n.t('cli.init.configure_adapters_prompt')}[/]",
|
|
526
530
|
default=True,
|
|
527
531
|
):
|
|
@@ -587,7 +591,7 @@ class InitCommand(Command):
|
|
|
587
591
|
|
|
588
592
|
console.print(f"[dim] {i18n.t('cli.init.adapters_enabled', count=len(enabled))}[/]")
|
|
589
593
|
|
|
590
|
-
if enabled and
|
|
594
|
+
if enabled and confirm(f" [cyan]{i18n.t('cli.init.install_selected_prompt')}[/]", default=True):
|
|
591
595
|
self._install_adapters(enabled, adapters)
|
|
592
596
|
|
|
593
597
|
def _install_adapters(self, adapter_names, adapters_info):
|
|
@@ -8,7 +8,7 @@ import asyncio
|
|
|
8
8
|
import sys
|
|
9
9
|
from argparse import ArgumentParser
|
|
10
10
|
|
|
11
|
-
from rich.prompt import
|
|
11
|
+
from rich.prompt import Prompt
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
|
|
14
14
|
from ..base import Command
|
|
@@ -16,6 +16,7 @@ from ..console import console
|
|
|
16
16
|
from ..i18n import i18n
|
|
17
17
|
from ..utils import PackageManager, config_wizard
|
|
18
18
|
from ..utils.display import interactive_select_table
|
|
19
|
+
from ..utils.interactive import confirm
|
|
19
20
|
from ..utils.package_manager import (
|
|
20
21
|
resolve_target_python,
|
|
21
22
|
warn_if_uv_isolated,
|
|
@@ -251,6 +252,7 @@ class InstallCommand(Command):
|
|
|
251
252
|
if success:
|
|
252
253
|
# 非交互路径不进入向导,仅提示后续配置入口
|
|
253
254
|
console.print(f"[dim] {i18n.t('cli.install.configure_hint')}[/]")
|
|
255
|
+
console.print(f"[hint] {i18n.t('cli.install.done_hint')}[/]")
|
|
254
256
|
|
|
255
257
|
if not success:
|
|
256
258
|
sys.exit(1)
|
|
@@ -294,7 +296,7 @@ class InstallCommand(Command):
|
|
|
294
296
|
elif choice == "4":
|
|
295
297
|
self._install_custom(upgrade, pre)
|
|
296
298
|
|
|
297
|
-
if not
|
|
299
|
+
if not confirm(f"\n [cyan]{i18n.t('cli.install.continue_install')}[/]", default=False):
|
|
298
300
|
break
|
|
299
301
|
|
|
300
302
|
def _install_adapters(self, remote_packages: dict, upgrade: bool, pre: bool):
|
|
@@ -341,7 +343,7 @@ class InstallCommand(Command):
|
|
|
341
343
|
|
|
342
344
|
selected_names = [name for name, _ in selected]
|
|
343
345
|
console.print(f"\n [dim]{i18n.t('cli.install.selected', selected=', '.join(selected_names))}[/]")
|
|
344
|
-
if
|
|
346
|
+
if confirm(
|
|
345
347
|
f" [cyan]{i18n.t('cli.install.confirm_adapters', count=len(selected_names))}[/]",
|
|
346
348
|
default=True,
|
|
347
349
|
):
|
|
@@ -390,7 +392,7 @@ class InstallCommand(Command):
|
|
|
390
392
|
|
|
391
393
|
selected_names = [name for name, _ in selected]
|
|
392
394
|
console.print(f"\n [dim]{i18n.t('cli.install.selected', selected=', '.join(selected_names))}[/]")
|
|
393
|
-
if
|
|
395
|
+
if confirm(
|
|
394
396
|
f" [cyan]{i18n.t('cli.install.confirm_modules', count=len(selected_names))}[/]",
|
|
395
397
|
default=True,
|
|
396
398
|
):
|
|
@@ -495,7 +497,7 @@ class InstallCommand(Command):
|
|
|
495
497
|
except ValueError:
|
|
496
498
|
continue
|
|
497
499
|
|
|
498
|
-
if selected and
|
|
500
|
+
if selected and confirm(
|
|
499
501
|
f" [cyan]{i18n.t('cli.install.confirm_search_install', count=len(selected))}[/]",
|
|
500
502
|
default=True,
|
|
501
503
|
):
|
|
@@ -514,7 +516,7 @@ class InstallCommand(Command):
|
|
|
514
516
|
if package_name.lower() == "q":
|
|
515
517
|
return
|
|
516
518
|
if package_name:
|
|
517
|
-
if
|
|
519
|
+
if confirm(
|
|
518
520
|
f" [cyan]{i18n.t('cli.install.confirm_custom_install', package_name=package_name)}[/]",
|
|
519
521
|
default=True,
|
|
520
522
|
):
|
|
@@ -534,6 +536,7 @@ class InstallCommand(Command):
|
|
|
534
536
|
"""
|
|
535
537
|
if not config_wizard.is_interactive():
|
|
536
538
|
console.print(f"[dim] {i18n.t('cli.install.configure_hint')}[/]")
|
|
539
|
+
console.print(f"[hint] {i18n.t('cli.install.done_hint')}[/]")
|
|
537
540
|
return
|
|
538
541
|
|
|
539
542
|
resolved = []
|
|
@@ -545,3 +548,4 @@ class InstallCommand(Command):
|
|
|
545
548
|
config_wizard.post_install_configure(resolved)
|
|
546
549
|
except Exception as e:
|
|
547
550
|
console.print(f"[warning] {i18n.t('cli.config.post_install_failed', error=e)}[/]")
|
|
551
|
+
console.print(f"[hint] {i18n.t('cli.install.done_hint')}[/]")
|
|
@@ -56,6 +56,7 @@ class ListCommand(Command):
|
|
|
56
56
|
|
|
57
57
|
# 仅在需要时一次性拉取远程索引,避免逐包重复 asyncio.run / 网络请求
|
|
58
58
|
remote_packages = None
|
|
59
|
+
remote_unavailable = False
|
|
59
60
|
if outdated_only:
|
|
60
61
|
try:
|
|
61
62
|
remote_packages = asyncio.run(
|
|
@@ -63,15 +64,29 @@ class ListCommand(Command):
|
|
|
63
64
|
)
|
|
64
65
|
except Exception:
|
|
65
66
|
remote_packages = None
|
|
67
|
+
# 索引拉取失败(异常或双源均空):区分"全是最新"与"无法检查"
|
|
68
|
+
remote_unavailable = remote_packages is None or not (
|
|
69
|
+
remote_packages.get("modules") or remote_packages.get("adapters")
|
|
70
|
+
)
|
|
66
71
|
|
|
67
72
|
if pkg_type == "all":
|
|
68
|
-
self._print_installed_packages(
|
|
69
|
-
|
|
73
|
+
self._print_installed_packages(
|
|
74
|
+
"modules", outdated_only, remote_packages, remote_unavailable
|
|
75
|
+
)
|
|
76
|
+
self._print_installed_packages(
|
|
77
|
+
"adapters", outdated_only, remote_packages, remote_unavailable
|
|
78
|
+
)
|
|
70
79
|
else:
|
|
71
|
-
self._print_installed_packages(
|
|
80
|
+
self._print_installed_packages(
|
|
81
|
+
pkg_type, outdated_only, remote_packages, remote_unavailable
|
|
82
|
+
)
|
|
72
83
|
|
|
73
84
|
def _print_installed_packages(
|
|
74
|
-
self,
|
|
85
|
+
self,
|
|
86
|
+
pkg_type: str,
|
|
87
|
+
outdated_only: bool = False,
|
|
88
|
+
remote_packages: dict | None = None,
|
|
89
|
+
remote_unavailable: bool = False,
|
|
75
90
|
):
|
|
76
91
|
"""
|
|
77
92
|
以表格形式打印已安装的模块或适配器
|
|
@@ -79,6 +94,7 @@ class ListCommand(Command):
|
|
|
79
94
|
:param pkg_type: [str] 组件类型 (modules 或 adapters)
|
|
80
95
|
:param outdated_only: [bool] 是否仅显示可升级的包 (默认: False)
|
|
81
96
|
:param remote_packages: [Optional[dict]] 预取的远程索引,避免逐包重复拉取 (默认: None)
|
|
97
|
+
:param remote_unavailable: [bool] 远程索引是否不可达(空结果区分文案用,默认: False)
|
|
82
98
|
"""
|
|
83
99
|
installed = self.package_manager.get_installed_packages()
|
|
84
100
|
|
|
@@ -121,6 +137,10 @@ class ListCommand(Command):
|
|
|
121
137
|
)
|
|
122
138
|
# 展示模块注册的脚本入口
|
|
123
139
|
self._print_package_scripts(installed["modules"])
|
|
140
|
+
elif outdated_only and remote_unavailable:
|
|
141
|
+
console.print(f"[dim] {i18n.t('cli.list.outdated_unavailable')}[/]")
|
|
142
|
+
elif outdated_only:
|
|
143
|
+
console.print(f"[dim] {i18n.t('cli.list.no_outdated')}[/]")
|
|
124
144
|
else:
|
|
125
145
|
console.print(f"[dim] {i18n.t('cli.list.no_modules')}[/]")
|
|
126
146
|
console.print(f"[dim] {i18n.t('cli.list.empty_hint')}[/]")
|
|
@@ -134,6 +154,7 @@ class ListCommand(Command):
|
|
|
134
154
|
)
|
|
135
155
|
table.add_column(i18n.t("cli.list.header_package"), min_width=20)
|
|
136
156
|
table.add_column(i18n.t("cli.list.header_version"), width=10)
|
|
157
|
+
table.add_column(i18n.t("cli.list.header_status"), width=8)
|
|
137
158
|
table.add_column(i18n.t("cli.list.header_desc"))
|
|
138
159
|
|
|
139
160
|
count = 0
|
|
@@ -142,10 +163,16 @@ class ListCommand(Command):
|
|
|
142
163
|
info["package"], info["version"], remote_packages
|
|
143
164
|
):
|
|
144
165
|
continue
|
|
166
|
+
status = (
|
|
167
|
+
f"[green]{i18n.t('cli.list.status_enabled')}[/]"
|
|
168
|
+
if info.get("enabled", True)
|
|
169
|
+
else f"[yellow]{i18n.t('cli.list.status_disabled')}[/]"
|
|
170
|
+
)
|
|
145
171
|
table.add_row(
|
|
146
172
|
name,
|
|
147
173
|
info["package"],
|
|
148
174
|
info["version"],
|
|
175
|
+
status,
|
|
149
176
|
info["summary"],
|
|
150
177
|
)
|
|
151
178
|
count += 1
|
|
@@ -155,6 +182,10 @@ class ListCommand(Command):
|
|
|
155
182
|
console.print(
|
|
156
183
|
f"[dim] {i18n.t('cli.list.count_adapters', count=count)}[/]"
|
|
157
184
|
)
|
|
185
|
+
elif outdated_only and remote_unavailable:
|
|
186
|
+
console.print(f"[dim] {i18n.t('cli.list.outdated_unavailable')}[/]")
|
|
187
|
+
elif outdated_only:
|
|
188
|
+
console.print(f"[dim] {i18n.t('cli.list.no_outdated')}[/]")
|
|
158
189
|
else:
|
|
159
190
|
console.print(f"[dim] {i18n.t('cli.list.no_adapters')}[/]")
|
|
160
191
|
console.print(f"[dim] {i18n.t('cli.list.empty_hint')}[/]")
|
|
@@ -182,12 +213,15 @@ class ListCommand(Command):
|
|
|
182
213
|
"""
|
|
183
214
|
if remote_packages is None:
|
|
184
215
|
remote_packages = asyncio.run(self.package_manager.get_remote_packages())
|
|
216
|
+
# 仅"远端更新"才算可升级:本地 dev/git 安装版本比远端新时不再误报
|
|
217
|
+
from ...runtime.version import compare_versions
|
|
218
|
+
|
|
185
219
|
for module_info in remote_packages["modules"].values():
|
|
186
220
|
if module_info["package"] == package_name:
|
|
187
|
-
return module_info["version"]
|
|
221
|
+
return compare_versions(module_info["version"], current_version) > 0
|
|
188
222
|
for adapter_info in remote_packages["adapters"].values():
|
|
189
223
|
if adapter_info["package"] == package_name:
|
|
190
|
-
return adapter_info["version"]
|
|
224
|
+
return compare_versions(adapter_info["version"], current_version) > 0
|
|
191
225
|
return False
|
|
192
226
|
|
|
193
227
|
def _print_package_scripts(self, packages: dict) -> None:
|
|
@@ -24,6 +24,7 @@ from ..constants import (
|
|
|
24
24
|
HARD_RESTART_EXIT_CODE,
|
|
25
25
|
RUN_CRASH_BACKOFF_MAX_SECS,
|
|
26
26
|
RUN_CRASH_BACKOFF_PER_CRASH_SECS,
|
|
27
|
+
RUN_PERSISTENT_CRASH_HINT_THRESHOLD,
|
|
27
28
|
RUN_RESTART_PAUSE_SECS,
|
|
28
29
|
)
|
|
29
30
|
from ..i18n import i18n
|
|
@@ -205,6 +206,11 @@ class RunCommand(Command):
|
|
|
205
206
|
]
|
|
206
207
|
|
|
207
208
|
crash_count = 0
|
|
209
|
+
# 连续同退出码崩溃计数:达到阈值输出一次排查指引(确定性故障
|
|
210
|
+
# 退避重试无意义,与其反复失败不如引导留现场)
|
|
211
|
+
consecutive_same_code = 0
|
|
212
|
+
last_crash_code: int | None = None
|
|
213
|
+
crash_hint_shown = False
|
|
208
214
|
process = None
|
|
209
215
|
try:
|
|
210
216
|
while True:
|
|
@@ -229,6 +235,20 @@ class RunCommand(Command):
|
|
|
229
235
|
# 非硬重启/非正常退出码:模块/适配器内部错误导致子进程异常终止
|
|
230
236
|
# 不退出主进程,等待后自动重试
|
|
231
237
|
crash_count += 1
|
|
238
|
+
if process.returncode == last_crash_code:
|
|
239
|
+
consecutive_same_code += 1
|
|
240
|
+
else:
|
|
241
|
+
consecutive_same_code = 1
|
|
242
|
+
last_crash_code = process.returncode
|
|
243
|
+
if (
|
|
244
|
+
not crash_hint_shown
|
|
245
|
+
and RUN_PERSISTENT_CRASH_HINT_THRESHOLD > 0
|
|
246
|
+
and consecutive_same_code >= RUN_PERSISTENT_CRASH_HINT_THRESHOLD
|
|
247
|
+
):
|
|
248
|
+
crash_hint_shown = True
|
|
249
|
+
console.print(
|
|
250
|
+
f"[hint]{i18n.t('cli.run.persistent_crash_hint', count=consecutive_same_code, code=process.returncode)}[/]"
|
|
251
|
+
)
|
|
232
252
|
backoff = min(RUN_CRASH_BACKOFF_MAX_SECS, RUN_CRASH_BACKOFF_PER_CRASH_SECS * crash_count)
|
|
233
253
|
console.print(
|
|
234
254
|
f"[warning]{i18n.t('cli.run.process_crashed', code=process.returncode)}[/]"
|
|
@@ -9,7 +9,7 @@ import sys
|
|
|
9
9
|
from argparse import ArgumentParser
|
|
10
10
|
|
|
11
11
|
from rich.box import SIMPLE
|
|
12
|
-
from rich.prompt import
|
|
12
|
+
from rich.prompt import Prompt
|
|
13
13
|
from rich.table import Table
|
|
14
14
|
from rich.text import Text
|
|
15
15
|
|
|
@@ -18,6 +18,7 @@ from ..console import console
|
|
|
18
18
|
from ..i18n import i18n
|
|
19
19
|
from ..utils import PackageManager
|
|
20
20
|
from ..utils.display import _input, section_header
|
|
21
|
+
from ..utils.interactive import confirm
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class SelfUpdateCommand(Command):
|
|
@@ -82,7 +83,7 @@ class SelfUpdateCommand(Command):
|
|
|
82
83
|
)
|
|
83
84
|
sys.exit(0)
|
|
84
85
|
elif not args.force:
|
|
85
|
-
if not
|
|
86
|
+
if not confirm(
|
|
86
87
|
i18n.t(
|
|
87
88
|
"cli.self_update.confirm_update",
|
|
88
89
|
current=current_version,
|
|
@@ -114,7 +115,7 @@ class SelfUpdateCommand(Command):
|
|
|
114
115
|
console.print(
|
|
115
116
|
f"[warning]{i18n.t('cli.self_update.version_not_found', version=specified_version)}[/]"
|
|
116
117
|
)
|
|
117
|
-
if not
|
|
118
|
+
if not confirm(
|
|
118
119
|
i18n.t("cli.self_update.confirm_continue"), default=False
|
|
119
120
|
):
|
|
120
121
|
return None
|
|
@@ -197,7 +198,7 @@ class SelfUpdateCommand(Command):
|
|
|
197
198
|
console.print(
|
|
198
199
|
f"[warning]{i18n.t('cli.self_update.version_not_found', version=target_version)}[/]"
|
|
199
200
|
)
|
|
200
|
-
if not
|
|
201
|
+
if not confirm(
|
|
201
202
|
i18n.t("cli.self_update.confirm_continue"), default=False
|
|
202
203
|
):
|
|
203
204
|
return None
|
|
@@ -7,11 +7,11 @@ Upgrade 命令实现
|
|
|
7
7
|
import sys
|
|
8
8
|
from argparse import ArgumentParser
|
|
9
9
|
|
|
10
|
-
from rich.prompt import Confirm
|
|
11
|
-
|
|
12
10
|
from ..base import Command
|
|
11
|
+
from ..console import console
|
|
13
12
|
from ..i18n import i18n
|
|
14
13
|
from ..utils import PackageManager
|
|
14
|
+
from ..utils.interactive import confirm
|
|
15
15
|
from ..utils.package_manager import resolve_target_python, warn_if_uv_isolated
|
|
16
16
|
|
|
17
17
|
|
|
@@ -50,15 +50,21 @@ class UpgradeCommand(Command):
|
|
|
50
50
|
def execute(self, args):
|
|
51
51
|
warn_if_uv_isolated()
|
|
52
52
|
self.package_manager.no_uv = getattr(args, "no_uv", False)
|
|
53
|
+
upgraded = False
|
|
53
54
|
if args.package:
|
|
54
55
|
# 升级指定包
|
|
55
56
|
success = self.package_manager.upgrade_package(args.package, pre=args.pre)
|
|
56
57
|
if not success:
|
|
57
58
|
sys.exit(1)
|
|
59
|
+
upgraded = True
|
|
58
60
|
# 升级所有包
|
|
59
|
-
elif args.force or
|
|
61
|
+
elif args.force or confirm(
|
|
60
62
|
i18n.t("cli.upgrade.confirm_all"), default=False
|
|
61
63
|
):
|
|
62
64
|
success = self.package_manager.upgrade_all()
|
|
63
65
|
if not success:
|
|
64
66
|
sys.exit(1)
|
|
67
|
+
upgraded = True
|
|
68
|
+
if upgraded:
|
|
69
|
+
# 生效方式提示:升级只更新磁盘代码,运行中的进程需重启或热重载
|
|
70
|
+
console.print(f"[hint]{i18n.t('cli.upgrade.done_hint')}[/]")
|
|
@@ -50,12 +50,17 @@ PYPI_PACKAGE_JSON_URL_TEMPLATE: str = "https://pypi.org/pypi/{package}/json"
|
|
|
50
50
|
RUN_CRASH_BACKOFF_MAX_SECS: float = 60.0
|
|
51
51
|
RUN_CRASH_BACKOFF_PER_CRASH_SECS: float = 3.0
|
|
52
52
|
RUN_RESTART_PAUSE_SECS: float = 0.5
|
|
53
|
+
# 连续同退出码崩溃达到该次数后,输出一次排查指引(疑似确定性故障,
|
|
54
|
+
# 退避重试无意义)。使用位置: CLI/commands/run.py -> 监督循环;
|
|
55
|
+
# 修改影响: 数值越小越早提示,越大越保守(0/负数禁用提示)。
|
|
56
|
+
RUN_PERSISTENT_CRASH_HINT_THRESHOLD: int = 3
|
|
53
57
|
|
|
54
58
|
__all__ = [
|
|
55
59
|
"ADAPTER_ENTRY_POINT_GROUP",
|
|
56
60
|
"RUN_CRASH_BACKOFF_MAX_SECS",
|
|
57
61
|
"RUN_CRASH_BACKOFF_PER_CRASH_SECS",
|
|
58
62
|
"RUN_RESTART_PAUSE_SECS",
|
|
63
|
+
"RUN_PERSISTENT_CRASH_HINT_THRESHOLD",
|
|
59
64
|
"ENV_SUPERVISED",
|
|
60
65
|
"HARD_RESTART_EXIT_CODE",
|
|
61
66
|
"MODULE_ENTRY_POINT_GROUP",
|
|
@@ -280,10 +280,13 @@ TRANSLATIONS = {
|
|
|
280
280
|
"cli.install.custom_prompt": "Enter package name to install (or q to go back)",
|
|
281
281
|
"cli.install.confirm_custom_install": "Confirm install {package_name}?",
|
|
282
282
|
"cli.install.configure_hint": "Run epsdk config to set up credentials/params of newly installed components",
|
|
283
|
+
"cli.install.done_hint": "Installed. Restart the running instance or call sdk.reload_all_modules() to apply",
|
|
283
284
|
# ==================== list command ====================
|
|
284
285
|
"cli.list.description": "List installed components",
|
|
285
286
|
"cli.list.type_help": "List type (default: all)",
|
|
286
287
|
"cli.list.outdated_help": "Show upgradable packages only",
|
|
288
|
+
"cli.list.no_outdated": "All components are up to date",
|
|
289
|
+
"cli.list.outdated_unavailable": "Component index unreachable; cannot check for updates (network issue or index source temporarily unavailable)",
|
|
287
290
|
"cli.list.header_module": "Module Name",
|
|
288
291
|
"cli.list.header_package": "Package",
|
|
289
292
|
"cli.list.header_version": "Version",
|
|
@@ -373,6 +376,7 @@ TRANSLATIONS = {
|
|
|
373
376
|
"cli.run.process_crashed": "Process crashed (exit code {code}), fix and save to restart",
|
|
374
377
|
"cli.run.process_exited": "Child process exited; run finished",
|
|
375
378
|
"cli.run.subprocess_crashed_retry": "Subprocess crashed, will auto-retry in {seconds}s...",
|
|
379
|
+
"cli.run.persistent_crash_hint": "Crashed {count} consecutive times with the same exit code ({code}) — likely a persistent failure; retrying is futile. Run epsdk doctor to check the environment; set ErisPulse.logger.log_dir = \"logs\" in config.toml to keep crash logs; use --verbose for full stack traces",
|
|
376
380
|
"cli.run.terminating_child": "Terminating the bot subprocess to release ports and resources...",
|
|
377
381
|
# ==================== uninstall command ====================
|
|
378
382
|
"cli.uninstall.description": "Uninstall module/adapter packages",
|
|
@@ -394,6 +398,10 @@ TRANSLATIONS = {
|
|
|
394
398
|
"cli.upgrade.pre_help": "Include pre-release versions",
|
|
395
399
|
"cli.upgrade.no_uv_help": "Disable uv, force upgrade via pip",
|
|
396
400
|
"cli.upgrade.confirm_all": "Are you sure you want to upgrade all ErisPulse components?",
|
|
401
|
+
"cli.upgrade.done_hint": "Upgraded. Restart the running instance or call sdk.reload_module(name, full=True) to apply",
|
|
402
|
+
# ==================== interactive unified prompt helpers ====================
|
|
403
|
+
"cli.interactive.non_tty": "Non-interactive environment detected (stdin is not a TTY); cannot answer interactive prompts",
|
|
404
|
+
"cli.interactive.non_tty_hint": "Use --yes/-y to auto-confirm all interactive prompts, or run inside a terminal (TTY)",
|
|
397
405
|
# ==================== language command ====================
|
|
398
406
|
"cli.language.description": "View / switch CLI display language",
|
|
399
407
|
"cli.language.lang_help": "Target language code (e.g. zh-CN, en, ja, ru)",
|
|
@@ -509,6 +517,11 @@ TRANSLATIONS = {
|
|
|
509
517
|
"cli.doctor.venv": "project virtual environment",
|
|
510
518
|
"cli.doctor.venv_missing": "not detected (current interpreter will be used)",
|
|
511
519
|
"cli.doctor.venv_pip_ok": "pip available",
|
|
520
|
+
"cli.doctor.venv_pip_uv": "uv (pip unavailable, uv backend as fallback)",
|
|
521
|
+
"cli.doctor.component_config": "Component config health",
|
|
522
|
+
"cli.doctor.component_config_ok": "All ready (nothing pending)",
|
|
523
|
+
"cli.doctor.component_config_incomplete": "Pending: {names} (run epsdk config <name> to fill required fields)",
|
|
524
|
+
"cli.doctor.component_config_skipped": "Component discovery failed; this check was skipped",
|
|
512
525
|
"cli.doctor.config": "Config file",
|
|
513
526
|
"cli.doctor.config_missing": "not found",
|
|
514
527
|
"cli.doctor.pypi": "PyPI reachability",
|
|
@@ -280,10 +280,13 @@ TRANSLATIONS = {
|
|
|
280
280
|
"cli.install.custom_prompt": "インストールするパッケージ名を入力(q で戻る)",
|
|
281
281
|
"cli.install.confirm_custom_install": "{package_name} をインストールしますか?",
|
|
282
282
|
"cli.install.configure_hint": "epsdk config を実行して新規インストールしたコンポーネントの認証情報/パラメーターを設定してください",
|
|
283
|
+
"cli.install.done_hint": "インストール完了。実行中のインスタンスは再起動するか sdk.reload_all_modules() でホットリロードすると反映されます",
|
|
283
284
|
# ==================== list コマンド ====================
|
|
284
285
|
"cli.list.description": "インストール済みコンポーネントを一覧表示",
|
|
285
286
|
"cli.list.type_help": "一覧タイプ (デフォルト: all)",
|
|
286
287
|
"cli.list.outdated_help": "アップグレード可能なパッケージのみ表示",
|
|
288
|
+
"cli.list.no_outdated": "すべてのコンポーネントは最新です",
|
|
289
|
+
"cli.list.outdated_unavailable": "コンポーネント索引に到達できず、更新を確認できません(ネットワーク問題または索引ソースの一時的な障害)",
|
|
287
290
|
"cli.list.header_module": "モジュール名",
|
|
288
291
|
"cli.list.header_package": "パッケージ",
|
|
289
292
|
"cli.list.header_version": "バージョン",
|
|
@@ -373,6 +376,7 @@ TRANSLATIONS = {
|
|
|
373
376
|
"cli.run.process_crashed": "プロセスが異常終了しました(終了コード {code})。修正して保存すると自動再起動",
|
|
374
377
|
"cli.run.process_exited": "サブプロセスが終了しました。実行を終了します",
|
|
375
378
|
"cli.run.subprocess_crashed_retry": "サブプロセスが異常終了しました。{seconds} 秒後に自動再試行します...",
|
|
379
|
+
"cli.run.persistent_crash_hint": "同じ終了コード({code})で {count} 回連続クラッシュ——恒久的な障害の可能性が高く、再試行は無意味です。epsdk doctor で環境を確認するか、config.toml に ErisPulse.logger.log_dir = \"logs\" を設定してクラッシュ時のログを残し、--verbose で完全なスタックトレースを確認してください",
|
|
376
380
|
"cli.run.terminating_child": "ポートなどのリソースを解放するため、ボットの子プロセスを終了しています...",
|
|
377
381
|
# ==================== uninstall コマンド ====================
|
|
378
382
|
"cli.uninstall.description": "モジュール/アダプターパッケージをアンインストール",
|
|
@@ -394,6 +398,10 @@ TRANSLATIONS = {
|
|
|
394
398
|
"cli.upgrade.pre_help": "プレリリース版を含める",
|
|
395
399
|
"cli.upgrade.no_uv_help": "uv を無効にし、pip でのアップグレードを強制",
|
|
396
400
|
"cli.upgrade.confirm_all": "すべての ErisPulse コンポーネントをアップグレードしますか?",
|
|
401
|
+
"cli.upgrade.done_hint": "アップグレード完了。実行中のインスタンスは再起動するか sdk.reload_module(name, full=True) でホットリロードすると反映されます",
|
|
402
|
+
# ==================== interactive 統一プロンプトヘルパー ====================
|
|
403
|
+
"cli.interactive.non_tty": "非対話環境を検出しました(stdin が TTY ではありません)。対話プロンプトに応答できません",
|
|
404
|
+
"cli.interactive.non_tty_hint": "--yes/-y ですべての対話プロンプトを自動確認するか、ターミナル(TTY)内で実行してください",
|
|
397
405
|
# ==================== language コマンド ====================
|
|
398
406
|
"cli.language.description": "CLI 表示言語の確認 / 切り替え",
|
|
399
407
|
"cli.language.lang_help": "対象言語コード (例: zh-CN, en, ja, ru)",
|
|
@@ -509,6 +517,11 @@ TRANSLATIONS = {
|
|
|
509
517
|
"cli.doctor.venv": "プロジェクト仮想環境",
|
|
510
518
|
"cli.doctor.venv_missing": "未検出(現在のインタープリターを使用)",
|
|
511
519
|
"cli.doctor.venv_pip_ok": "pip 利用可能",
|
|
520
|
+
"cli.doctor.venv_pip_uv": "uv(pip 利用不可、uv バックエンドで代替)",
|
|
521
|
+
"cli.doctor.component_config": "コンポーネント設定の健全性",
|
|
522
|
+
"cli.doctor.component_config_ok": "すべて準備完了(未完了項目なし)",
|
|
523
|
+
"cli.doctor.component_config_incomplete": "要対応: {names}(epsdk config <名称> で必須項目を入力してください)",
|
|
524
|
+
"cli.doctor.component_config_skipped": "コンポーネント検出に失敗したため、このチェックをスキップしました",
|
|
512
525
|
"cli.doctor.config": "設定ファイル",
|
|
513
526
|
"cli.doctor.config_missing": "見つかりません",
|
|
514
527
|
"cli.doctor.pypi": "PyPI 接続",
|