diff --git a/app/api/endpoints/agent.py b/app/api/endpoints/agent.py index 979cd20a7..1fb0595f8 100644 --- a/app/api/endpoints/agent.py +++ b/app/api/endpoints/agent.py @@ -42,7 +42,7 @@ from app.agent.runtime_loader import ( get_running_agent_manager, ) from app.chain.message import MessageChain -from app.command import Command +from app.application.commands import get_command, get_commands from app.runtime.config import global_vars from app.runtime.events import Event, EventManager from app.api.principal import ApiPrincipal @@ -1548,7 +1548,7 @@ def _build_web_agent_command_items() -> list[dict]: :return: 按分类和命令名排序的命令列表 """ - commands = Command().get_commands() or {} + commands = get_commands() or {} items = [] for command, data in commands.items(): if not command.startswith("/"): @@ -1591,7 +1591,7 @@ def _get_web_agent_unknown_command_message(text: str) -> Optional[str]: command = _extract_web_agent_slash_command(text) if not command: return None - if Command().get(command): + if get_command(command): return None return f"命令不存在:{command}" diff --git a/app/api/endpoints/site.py b/app/api/endpoints/site.py index 08c932a12..126fb3d28 100644 --- a/app/api/endpoints/site.py +++ b/app/api/endpoints/site.py @@ -21,7 +21,7 @@ from app.application.site.query import SiteQueryService from app.api.endpoints.plugin import register_plugin_api from app.chain.site import SiteChain from app.chain.torrents import TorrentsChain -from app.command import Command +from app.application.commands import init_commands from app.application.plugin.runtime import get_plugin_manager as PluginManager from app.adapters.web.security.access import verify_token from app.api.principal import ApiPrincipal @@ -569,7 +569,7 @@ def auth_site( # 认证成功后,重新初始化插件 PluginManager().init_config() Scheduler().init_plugin_jobs() - Command().init_commands() + init_commands() register_plugin_api() return _SchemaResponse(success=status, message=msg) diff --git a/docs/refactor/backend-architecture-next-stage.md b/docs/refactor/backend-architecture-next-stage.md index ce1ab7ea6..2d459579c 100644 --- a/docs/refactor/backend-architecture-next-stage.md +++ b/docs/refactor/backend-architecture-next-stage.md @@ -91,6 +91,8 @@ 插件运行时也采用相同边界:factory 的动态路由投影和 Scheduler 插件任务统一延迟调用 `app.application.plugin.runtime`,只有 startup 组合根与 `app.sdk.plugins` 兼容面允许直接引用 concrete `PluginManager`,不改变 V1/V2/V3 插件加载与自由响应 API。 + Command 的 API 消费点也已完成原计划迁移:WebAgent 查询和站点认证后的刷新统一使用 + `app.application.commands`,只有 startup 组合根注册 concrete Command;门面保留原命令对象与插件命令语义。 ### P2:中长期可演进性债务 diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index 647a88086..914b78c56 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -14,7 +14,7 @@ "workflow_to_db": [] }, "edge_count": 6500, - "edge_sha256": "d143f65c278b6eb1887240583c9ecce7b17a8cf7931a22b4f4cd39f269ac676c", + "edge_sha256": "c27b649d471e40e8580426bdee28fdd927be03b7868a1b28bd8d9374c10be280", "edges": [ "app -> app.runtime", "app -> app.runtime.compat", @@ -1657,6 +1657,7 @@ "app.api.endpoints.agent -> app.api.principal", "app.api.endpoints.agent -> app.api.response", "app.api.endpoints.agent -> app.application", + "app.api.endpoints.agent -> app.application.commands", "app.api.endpoints.agent -> app.application.configuration", "app.api.endpoints.agent -> app.application.messaging", "app.api.endpoints.agent -> app.application.messaging.agent", @@ -1666,7 +1667,6 @@ "app.api.endpoints.agent -> app.application.security.user", "app.api.endpoints.agent -> app.chain", "app.api.endpoints.agent -> app.chain.message", - "app.api.endpoints.agent -> app.command", "app.api.endpoints.agent -> app.runtime", "app.api.endpoints.agent -> app.runtime.config", "app.api.endpoints.agent -> app.runtime.events", @@ -2158,6 +2158,7 @@ "app.api.endpoints.site -> app.api.principal", "app.api.endpoints.site -> app.api.response", "app.api.endpoints.site -> app.application", + "app.api.endpoints.site -> app.application.commands", "app.api.endpoints.site -> app.application.configuration", "app.api.endpoints.site -> app.application.plugin", "app.api.endpoints.site -> app.application.plugin.runtime", @@ -2168,7 +2169,6 @@ "app.api.endpoints.site -> app.chain", "app.api.endpoints.site -> app.chain.site", "app.api.endpoints.site -> app.chain.torrents", - "app.api.endpoints.site -> app.command", "app.api.endpoints.site -> app.domain", "app.api.endpoints.site -> app.domain.site", "app.api.endpoints.site -> app.runtime", diff --git a/tests/test_architecture_dependencies.py b/tests/test_architecture_dependencies.py index 6e316460d..167229be7 100644 --- a/tests/test_architecture_dependencies.py +++ b/tests/test_architecture_dependencies.py @@ -894,6 +894,21 @@ def test_runtime_consumers_use_plugin_application_facade(): assert violations == {} +def test_runtime_consumers_use_command_application_facade(): + """Command concrete 实现只允许 startup 组合根直接依赖。""" + allowed = { + "app.startup.initializers.command", + "app.startup.initializers.modules", + } + violations = { + module_name: dependencies & {"app.command"} + for module_name, dependencies in _build_module_graph().items() + if module_name not in allowed and "app.command" in dependencies + } + + assert violations == {} + + def test_api_does_not_import_factory(): """装配器(factory)只允许 app.main 使用,HTTP 端点不得回引。""" violations: dict[str, set[str]] = {} diff --git a/tests/test_web_agent_stream.py b/tests/test_web_agent_stream.py index 101ef7a07..fbed1abb4 100644 --- a/tests/test_web_agent_stream.py +++ b/tests/test_web_agent_stream.py @@ -290,14 +290,12 @@ def test_build_web_agent_input_attachments_marks_kinds(): def test_build_web_agent_command_items_returns_slash_commands(): """WebAgent 命令建议应返回可展示的斜杠命令。""" with patch( - "app.api.endpoints.agent.Command", - return_value=SimpleNamespace( - get_commands=lambda: { - "/sites": {"description": "管理站点", "category": "站点"}, - "hidden": {"description": "忽略", "category": "其他"}, - "/hidden": {"description": "隐藏", "category": "其他", "show": False}, - } - ), + "app.api.endpoints.agent.get_commands", + return_value={ + "/sites": {"description": "管理站点", "category": "站点"}, + "hidden": {"description": "忽略", "category": "其他"}, + "/hidden": {"description": "隐藏", "category": "其他", "show": False}, + }, ): commands = _build_web_agent_command_items() @@ -344,8 +342,8 @@ def test_web_agent_stream_returns_error_for_unknown_command(): user = SimpleNamespace(id=1, name="admin", is_superuser=True) with patch( - "app.api.endpoints.agent.Command", - return_value=SimpleNamespace(get=lambda _: {}), + "app.api.endpoints.agent.get_command", + return_value=None, ), patch("app.api.endpoints.agent.MessageChain.handle_message") as handle_message: response = asyncio.run(web_agent_stream(payload, request, user)) body = "".join(asyncio.run(_collect_streaming_response(response)))