此提交包含多项核心功能迭代与问题修复: 1. 新增多操作权限配置,完善权限控制覆盖范围 2. 重构出站流水线适配器参数,优化组件依赖关系 3. 新增渠道账号配置变更事件,支持动态重建传输 worker 4. 实现全链路 trace_id 透传,优化调试追踪能力 5. 新增传输任务最大重启次数配置,避免无限崩溃循环 6. 优化幂等冲突错误体系,重构错误继承与提示信息 7. 增强 bridge_url 安全校验,新增 SSRF 防护与 HTTPS 强制校验 8. 完善 SSE 断线补全逻辑,新增分页与消息数限制 9. 重构 WeChatWoc 插件生命周期与资源管理,优化连接池与缓存清理 10. 修复多处代码逻辑bug,提升系统稳定性与可维护性
262 lines
11 KiB
Python
262 lines
11 KiB
Python
"""插件元数据。
|
||
|
||
定义插件清单相关的枚举、不可变值对象,包括失败策略、凭证策略、插件依赖、
|
||
资源配额、配置字段、渠道清单与插件清单。所有 DTO 均为 ``dataclass(frozen=True)``,
|
||
集合字段使用 tuple,仅依赖标准库与契约层内部类型,用于插件注册、依赖解析、
|
||
资源配额声明与能力边界声明。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
from enum import StrEnum
|
||
from typing import Any, Literal
|
||
|
||
from yuxi.channels.contract.dtos.capability import ChannelCapabilities
|
||
from yuxi.channels.contract.dtos.channel import ChannelType
|
||
from yuxi.channels.contract.dtos.config import ConfigField
|
||
from yuxi.channels.contract.dtos.plugin import EnvVar
|
||
|
||
|
||
class FailurePolicy(StrEnum):
|
||
"""失败策略。
|
||
|
||
标识插件失败时的处理策略,用于宿主决定降级、熔断或隔离。继承
|
||
``str, Enum`` 以支持 JSON 序列化与字符串比较。
|
||
|
||
取值:
|
||
DEGRADE: 降级(走降级路径,保留部分能力)。
|
||
CIRCUIT_BREAK: 熔断(打开熔断器,暂时停止调用)。
|
||
ISOLATE: 隔离(隔离插件,阻止其继续运行)。
|
||
"""
|
||
|
||
DEGRADE = "degrade"
|
||
CIRCUIT_BREAK = "circuit_break"
|
||
ISOLATE = "isolate"
|
||
|
||
|
||
class CredentialType(StrEnum):
|
||
"""凭证类型。
|
||
|
||
标识渠道插件凭证的获取形态,用于 ``WizardService.finalize`` 分支选择
|
||
``CredentialService`` 的 acquire 方法(INV-3 契约显式化)。
|
||
|
||
取值:
|
||
STATIC: 纯静态凭证(如 app_id/app_secret),通过 ``acquireByManual``
|
||
落库。
|
||
DYNAMIC: 纯动态凭证(如 iLink bot_token),通过 ``acquireByQrLogin``
|
||
/ ``acquireByOAuth`` 落库。
|
||
HYBRID: 混合策略(如飞书 app_id/app_secret 静态 + user_access_token
|
||
动态),静态部分 ``acquireByManual``,动态部分可跳过(skippable)。
|
||
"""
|
||
|
||
STATIC = "static"
|
||
DYNAMIC = "dynamic"
|
||
HYBRID = "hybrid"
|
||
|
||
|
||
class AcquireMethod(StrEnum):
|
||
"""凭证获取方法。
|
||
|
||
标识 ``CredentialService`` 应调用的具体 acquire 方法,与
|
||
``CredentialType`` 配合指导凭证落库流程。
|
||
|
||
取值:
|
||
MANUAL: 手动录入(对应 ``acquireByManual``)。
|
||
QR_LOGIN: 扫码登录(对应 ``acquireByQrLogin``)。
|
||
OAUTH: OAuth 授权(对应 ``acquireByOAuth``)。
|
||
WEBHOOK_VERIFY: Webhook 验证(对应 ``acquireByWebhookVerify``)。
|
||
"""
|
||
|
||
MANUAL = "manual"
|
||
QR_LOGIN = "qr_login"
|
||
OAUTH = "oauth"
|
||
WEBHOOK_VERIFY = "webhook_verify"
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PluginDependency:
|
||
"""插件依赖。
|
||
|
||
描述插件对其他插件的依赖关系,包括插件 ID 与版本范围,用于插件加载
|
||
前的依赖解析。
|
||
|
||
字段:
|
||
plugin_id: 依赖的插件 ID。
|
||
version_range: 语义化版本范围表达式(SemVer range)。
|
||
"""
|
||
|
||
plugin_id: str
|
||
version_range: str
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ResourceQuota:
|
||
"""资源配额。
|
||
|
||
描述插件的资源配额限制,包括 CPU、内存、连接数与每秒调用数,用于
|
||
宿主对插件施加资源边界(FR-32 / §8.5)。
|
||
|
||
字段:
|
||
max_cpu: CPU 上限(如 "500m"),可选。
|
||
max_memory: 内存上限(如 "512Mi"),可选。
|
||
max_connections: 最大连接数,可选。
|
||
max_calls_per_sec: 每秒最大调用数,可选。
|
||
"""
|
||
|
||
max_cpu: str | None = None
|
||
max_memory: str | None = None
|
||
max_connections: int | None = None
|
||
max_calls_per_sec: int | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class CredentialStrategy:
|
||
"""凭证获取策略。
|
||
|
||
声明渠道插件的凭证获取策略,指导 ``WizardService.finalize`` 分支调用
|
||
``CredentialService`` 的何种 acquire 方法将凭证落库(INV-3 契约显式化)。
|
||
缺失该声明时(``None``)按 ``type=static`` / ``acquire_method=manual``
|
||
兜底。
|
||
|
||
字段:
|
||
type: 凭证类型(static/dynamic/hybrid)。
|
||
acquire_method: 凭证获取方法(manual/qr_login/oauth/webhook_verify)。
|
||
required_fields: 必需凭证字段名清单(对应 ``config_schema`` 中的字段)。
|
||
optional_fields: 可选凭证字段名清单。
|
||
supports_rotation: 是否支持凭证轮换。
|
||
supports_revocation: 是否支持凭证撤销。
|
||
"""
|
||
|
||
type: CredentialType
|
||
acquire_method: AcquireMethod
|
||
required_fields: tuple[str, ...]
|
||
optional_fields: tuple[str, ...] = ()
|
||
supports_rotation: bool = False
|
||
supports_revocation: bool = False
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ChannelManifest:
|
||
"""渠道清单。
|
||
|
||
描述渠道插件的元数据,包括标识、版本、渠道类型、提供的能力、配置 schema、
|
||
入口模块、依赖、生命周期、兼容性、失败策略、资源配额与能力边界声明,
|
||
用于插件注册与装配。
|
||
|
||
字段:
|
||
id: 全局唯一标识(如 com.yuxi.channels.feishu)。
|
||
name: 人类可读名称。
|
||
version: 语义化版本。
|
||
channel_type: 渠道类型。
|
||
provides: 提供的能力列表。
|
||
entry_module: 插件入口模块路径。
|
||
capabilities: 渠道能力集合(静态声明,必填)。
|
||
config_schema: 配置项 schema(必填)。
|
||
capability_requirements: 能力运行时依赖声明(默认空)。每项为
|
||
``(能力字段名, (所需配置字段名清单))`` 元组,保持 frozen dataclass
|
||
可哈希。用于能力查询页判断"声明支持"与"实际可用"之间的差距。
|
||
depends: 依赖的其他插件(默认空)。
|
||
lifecycle: 支持的生命周期钩子(默认 init/start/stop/unload)。
|
||
compatibility: 兼容性信息(可选)。
|
||
failure_policy: 失败策略(默认 DEGRADE)。
|
||
resource_quota: 资源配额(可选)。
|
||
accessible_ports: 可访问的端口列表(默认空)。
|
||
injectable_pipelines: 可注入的管道列表(默认空)。
|
||
skills: 提供的技能列表(默认空)。
|
||
env_vars: 环境变量声明(默认空)。
|
||
critical: 是否为关键渠道(默认 False)。关键渠道失败时宿主标记为
|
||
``unhealthy``,非关键渠道失败标记为 ``degraded``(FR-31 / FR-35)。
|
||
requires_dm_pairing: 是否要求 DM 安全配对审批(默认 True)。声明
|
||
``False`` 的渠道(如已通过宿主认证体系)跳过 DM 配对审批直接放行
|
||
(FR-31)。插件未注册时框架回退为 ``True``(fail-closed)。
|
||
requires_outbound_delivery: 是否要求出站投递(默认 True)。声明 ``False``
|
||
的渠道无需实现 ``OutboundAdapter``,框架跳过出站投递阶段(FR-31)。
|
||
credential_strategy: 凭证获取策略(默认 ``None`` 表示无动态凭证,按
|
||
``type=static`` / ``acquire_method=manual`` 兜底)。声明该策略后,
|
||
``WizardService.finalize`` 据此分支调用 ``CredentialService`` 的
|
||
acquire 方法完成凭证落库(INV-3 契约显式化)。
|
||
max_message_length: 单条消息最大文本长度(FR19-P0-5 渠道级内容
|
||
长度校验,默认 4096)。出站管道据此对超长内容做截断或分片
|
||
决策,未声明的渠道沿用默认值。本字段为配置型参数(非能力
|
||
声明),故置于 manifest 顶层而非 ``capabilities`` 中。
|
||
supports_credential_cloning: 是否允许在账户克隆时复制凭据字段
|
||
(默认 False,ACC-CLONE)。声明为 False 时 ``include_credentials=True``
|
||
的克隆请求将被 handler 拒绝(``RuleViolationError``),fail-closed
|
||
防止凭据经克隆路径泄露。本字段为安全策略声明(非能力声明),
|
||
故置于 manifest 顶层而非 ``capabilities`` 中。
|
||
icon: 渠道展示图标名(lucide 图标库的 kebab-case 名称,如
|
||
``"message-square"``)。供前端卡片视图、筛选下拉、标签等场景
|
||
渲染渠道图标。缺失时前端回退默认图标。本字段为纯展示元数据
|
||
(非能力声明、非业务规则),由插件作者根据渠道视觉特征选择,
|
||
框架层不做取值校验。
|
||
transport_mode: 传输模式声明(Task 11.3,默认 ``both``)。声明渠道
|
||
偏好的入站传输模式:``pull`` 仅轮询、``stream`` 仅长连接、
|
||
``both`` 两者皆可(优先 Stream)。``TransportManager`` 据此与
|
||
适配器能力选择启动 Puller 或 Stream,避免模式冲突。
|
||
max_restart_attempts: 传输任务异常后的最大重启次数(P0-1,默认
|
||
``None`` 表示无限重启)。声明后 ``TransportManager`` 将其作为
|
||
``TransportConfig.max_restart_attempts`` 的初始默认值,
|
||
``ConfigPort`` 的 ``transport.max_restart_attempts`` 可运行时覆盖。
|
||
避免 StreamWorker 反复崩溃导致无限重启循环。
|
||
"""
|
||
|
||
id: str
|
||
name: str
|
||
version: str
|
||
channel_type: ChannelType
|
||
provides: tuple[str, ...]
|
||
entry_module: str
|
||
capabilities: ChannelCapabilities
|
||
config_schema: tuple[ConfigField, ...]
|
||
capability_requirements: tuple[tuple[str, tuple[str, ...]], ...] = ()
|
||
depends: tuple[PluginDependency, ...] = ()
|
||
lifecycle: tuple[str, ...] = ("init", "start", "stop", "unload")
|
||
compatibility: dict[str, Any] | None = None
|
||
failure_policy: FailurePolicy = FailurePolicy.DEGRADE
|
||
resource_quota: ResourceQuota | None = None
|
||
accessible_ports: tuple[str, ...] = ()
|
||
injectable_pipelines: tuple[str, ...] = ()
|
||
skills: tuple[str, ...] = ()
|
||
env_vars: tuple[EnvVar, ...] = ()
|
||
critical: bool = False
|
||
requires_dm_pairing: bool = True
|
||
requires_outbound_delivery: bool = True
|
||
credential_strategy: CredentialStrategy | None = None
|
||
max_message_length: int = 4096
|
||
supports_credential_cloning: bool = False
|
||
icon: str | None = None
|
||
transport_mode: Literal["pull", "stream", "both"] = "both"
|
||
max_restart_attempts: int | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PluginManifest:
|
||
"""插件清单。
|
||
|
||
描述插件注册到宿主的完整清单,包括渠道清单与插件提供的扩展点(适配器、
|
||
阶段、事件订阅、配置源),由 ``CHANNEL_ENTRY`` 返回给宿主。
|
||
|
||
字段:
|
||
manifest: 渠道清单。
|
||
adapters: 提供的适配器类型列表(默认空)。
|
||
stages: 提供的管道阶段列表(默认空)。
|
||
event_subscriptions: 订阅的事件类型列表(默认空)。
|
||
config_sources: 配置源列表(默认空)。
|
||
installed_at: 文件级安装时间(PLG-INSTALL 端点写入,默认 None
|
||
表示未通过文件级安装流程注册)。
|
||
install_source: 安装来源标识(如文件路径、URL、包名,默认 None)。
|
||
install_version: 安装时的版本号(可能与 manifest.version 不同,
|
||
如安装后未升级,默认 None)。
|
||
"""
|
||
|
||
manifest: ChannelManifest
|
||
adapters: tuple[str, ...] = ()
|
||
stages: tuple[str, ...] = ()
|
||
event_subscriptions: tuple[str, ...] = ()
|
||
config_sources: tuple[str, ...] = ()
|
||
installed_at: datetime | None = None
|
||
install_source: str | None = None
|
||
install_version: str | None = None
|