mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-16 11:33:59 +08:00
201 lines
6.2 KiB
Python
201 lines
6.2 KiB
Python
"""工具策略例外与参数级策略解析。"""
|
||
|
||
from typing import Any, Mapping
|
||
|
||
from app.agent.policy.contracts import (
|
||
ActionEffect,
|
||
ActionPolicy,
|
||
ConfirmationMode,
|
||
MigrationState,
|
||
PrincipalRole,
|
||
RecoveryMode,
|
||
ResultSensitivity,
|
||
)
|
||
|
||
|
||
# 这些非管理员读取在运行时解析为强制 SAFE_READ;管理员门禁仍沿用原有授权事实源。
|
||
SAFE_READ_TOOL_NAMES = frozenset(
|
||
{
|
||
"list_slash_commands",
|
||
"query_installed_plugins",
|
||
"query_personas",
|
||
"query_schedulers",
|
||
"query_workflows",
|
||
}
|
||
)
|
||
|
||
|
||
# 该清单只校验固定工具 inventory;未命中的固定或动态工具同样默认 LEGACY_SHADOW。
|
||
BUILTIN_LEGACY_SHADOW_INVENTORY = frozenset(
|
||
{
|
||
"add_custom_filter_rule",
|
||
"add_download_tasks",
|
||
"add_rule_group",
|
||
"add_subscribe",
|
||
"ask_user_choice",
|
||
"browse_webpage",
|
||
"create_agent_task",
|
||
"delete_agent_task",
|
||
"delete_custom_filter_rule",
|
||
"delete_download_history",
|
||
"delete_download_tasks",
|
||
"delete_rule_group",
|
||
"delete_subscribe",
|
||
"delete_transfer_history",
|
||
"edit_file",
|
||
"execute_command",
|
||
"get_recommendations",
|
||
"get_search_results",
|
||
"install_plugin",
|
||
"list_directory",
|
||
"query_agent_tasks",
|
||
"query_builtin_filter_rules",
|
||
"query_custom_filter_rules",
|
||
"query_custom_identifiers",
|
||
"query_directory_settings",
|
||
"query_doctor_report",
|
||
"query_download_tasks",
|
||
"query_downloaders",
|
||
"query_episode_schedule",
|
||
"query_library_exists",
|
||
"query_library_latest",
|
||
"query_market_plugins",
|
||
"query_media_detail",
|
||
"query_plugin_capabilities",
|
||
"query_plugin_config",
|
||
"query_plugin_data",
|
||
"query_popular_subscribes",
|
||
"query_rule_groups",
|
||
"query_site_userdata",
|
||
"query_sites",
|
||
"query_subscribe_history",
|
||
"query_subscribe_shares",
|
||
"query_subscribes",
|
||
"query_system_settings",
|
||
"query_transfer_history",
|
||
"read_file",
|
||
"recognize_captcha",
|
||
"recognize_media",
|
||
"reload_plugin",
|
||
"run_agent_task",
|
||
"run_scheduler",
|
||
"run_slash_command",
|
||
"run_workflow",
|
||
"scrape_metadata",
|
||
"search_media",
|
||
"search_person",
|
||
"search_person_credits",
|
||
"search_subscribe",
|
||
"search_torrents",
|
||
"search_web",
|
||
"send_local_file",
|
||
"send_message",
|
||
"send_voice_message",
|
||
"switch_persona",
|
||
"test_site",
|
||
"transfer_file",
|
||
"uninstall_plugin",
|
||
"update_agent_task",
|
||
"update_custom_filter_rule",
|
||
"update_custom_identifiers",
|
||
"update_download_tasks",
|
||
"update_persona_definition",
|
||
"update_plugin_config",
|
||
"update_rule_group",
|
||
"update_site",
|
||
"update_site_cookie",
|
||
"update_subscribe",
|
||
"update_system_settings",
|
||
"write_file",
|
||
}
|
||
)
|
||
|
||
|
||
class ToolPolicyRegistry:
|
||
"""解析固定和动态工具的宿主策略。"""
|
||
|
||
def __init__(
|
||
self,
|
||
*,
|
||
safe_read_tool_names: frozenset[str] = SAFE_READ_TOOL_NAMES,
|
||
builtin_legacy_shadow_inventory: frozenset[str] = (
|
||
BUILTIN_LEGACY_SHADOW_INVENTORY
|
||
),
|
||
) -> None:
|
||
"""建立 SAFE_READ 例外与固定工具 inventory。"""
|
||
overlap = safe_read_tool_names & builtin_legacy_shadow_inventory
|
||
if overlap:
|
||
raise ValueError(f"工具策略 inventory 存在重复项: {sorted(overlap)}")
|
||
self._safe_read_tool_names = safe_read_tool_names
|
||
self._builtin_legacy_shadow_inventory = builtin_legacy_shadow_inventory
|
||
|
||
@property
|
||
def builtin_tool_inventory(self) -> set[str]:
|
||
"""返回用于测试校验的固定工具 inventory。"""
|
||
return set(
|
||
self._safe_read_tool_names | self._builtin_legacy_shadow_inventory
|
||
)
|
||
|
||
def resolve(
|
||
self,
|
||
*,
|
||
tool_name: str,
|
||
arguments: Mapping[str, Any],
|
||
requires_admin: bool,
|
||
) -> ActionPolicy:
|
||
"""根据工具名和宿主权限声明解析当前参数级策略。"""
|
||
required_role = (
|
||
PrincipalRole.SYSTEM_ADMIN if requires_admin else PrincipalRole.USER
|
||
)
|
||
if (
|
||
tool_name == "query_system_settings"
|
||
and arguments.get("show_secrets") is True
|
||
):
|
||
return ActionPolicy(
|
||
effect=ActionEffect.SENSITIVE_READ,
|
||
required_role=PrincipalRole.SYSTEM_ADMIN,
|
||
confirmation=ConfirmationMode.REQUIRED,
|
||
recovery=RecoveryMode.NONE,
|
||
result_sensitivity=ResultSensitivity.SECRET,
|
||
migration_state=MigrationState.ENFORCED,
|
||
policy_version="p1-g2a2-v1",
|
||
machine_allowed=True,
|
||
background_allowed=False,
|
||
subagent_allowed=False,
|
||
)
|
||
if tool_name in self._safe_read_tool_names:
|
||
return ActionPolicy(
|
||
effect=ActionEffect.SAFE_READ,
|
||
required_role=required_role,
|
||
confirmation=ConfirmationMode.NONE,
|
||
recovery=RecoveryMode.NONE,
|
||
result_sensitivity=ResultSensitivity.NORMAL,
|
||
# 角色门禁仍由既有授权事实源判断,管理员读取保持兼容观测。
|
||
migration_state=(
|
||
MigrationState.LEGACY_SHADOW
|
||
if requires_admin
|
||
else MigrationState.ENFORCED
|
||
),
|
||
)
|
||
|
||
# 除明确例外外,固定和动态工具都保持现有能力,但不得被视为安全读取。
|
||
return ActionPolicy(
|
||
effect=ActionEffect.UNKNOWN,
|
||
required_role=required_role,
|
||
confirmation=ConfirmationMode.REQUIRED,
|
||
recovery=RecoveryMode.MANUAL_ONLY,
|
||
result_sensitivity=ResultSensitivity.UNKNOWN,
|
||
migration_state=MigrationState.LEGACY_SHADOW,
|
||
)
|
||
|
||
|
||
DEFAULT_TOOL_POLICY_REGISTRY = ToolPolicyRegistry()
|
||
|
||
|
||
__all__ = [
|
||
"BUILTIN_LEGACY_SHADOW_INVENTORY",
|
||
"DEFAULT_TOOL_POLICY_REGISTRY",
|
||
"SAFE_READ_TOOL_NAMES",
|
||
"ToolPolicyRegistry",
|
||
]
|