mcpField
C
create-moviepilot-plugin

create-moviepilot-plugin

github.com/jxxghp开发12.0k56 次安装今天更新
代码仓库

关于 create-moviepilot-plugin

create-moviepilot-plugin 是用于从本地源码创建或修改 MoviePilot 插件的开发技能,面向 MoviePilot 插件开发者。它根据宿主版本指导目录布局、实现方式和验证,并将安装及运行时更改限制在用户授权范围内。

主要功能

  1. 01按版本选择插件目录布局
  2. 02后端与 UI 模式指导
  3. 03本地源码和包元数据流程
  4. 04宿主 API 与扩展点核验
  5. 05范围受限的编辑与验证指导

适用场景

  1. 01为 MoviePilot 宿主创建插件
  2. 02修改现有本地插件
  3. 03选择插件 UI 实现模式

MCP 相关

README 未说明 MCP 接口或 MCP 专用功能。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jxxghp/moviepilot --skill create-moviepilot-plugin

安装后在 Claude Code 中输入/create-moviepilot-plugin确认已加载。

详细介绍

创建 MoviePilot 插件

使用此技能构建或修改 MoviePilot 插件。此类插件可从本地插件源开发,并安装到正在运行的 MoviePilot 实例中。

事实依据

  • 宿主插件契约:app/plugins/__init__.py,尤其是 _PluginBase。
  • 宿主插件发现、本地源同步、安装和重新加载:app/runtime/extensions/plugin_manager.py 和 app/adapters/external/market.py。
  • 宿主插件端点、API 身份验证、静态文件、远程服务和侧边栏导航: app/api/endpoints/plugin.py。
  • 本地开发说明:docs/development-setup.md。
  • 首先根据源代码、版本和目标插件仓库,确定宿主实际使用的代际。MoviePilot V3 使用 plugins.v3/ 和 package.v3.json;V2 使用 plugins.v2/ 和 package.v2.json;旧版 V1 使用 plugins/ 和 package.json。对于 V3 宿主,请勿生成 V2 路径。
  • 在 MoviePilot-Plugins 中或基于其进行开发时,请阅读其 README.md、 docs/Repository_Guide.md 以及目标代际的指南: V3 阅读 docs/Plugin_Development.md,V2 阅读 docs/V2_Plugin_Development.md。 对于特定场景的扩展,请阅读对应的 docs/faq/*.md。
  • 对于 V3,优先使用当前宿主和指南中记录的公开 app.sdk.* 入口点;不要照搬 V2 示例中的旧版内部导入路径。使用模板前,请对照已安装的宿主核实扩展签名。

任务范围

创建或修复插件的请求,授权进行相关的本地源代码修改和验证。应沿用此授权;但这本身并不意味着获准修改宿主配置、安装或重新加载插件、重启或发布。只有在用户提出这些要求或已批准相应范围时,才执行这些步骤,并遵守宿主的确认要求。编辑正在运行且受监视的插件源代码前,请检查自动重新加载设置;此类编辑可能立即影响运行环境,因此必须确保该运行时影响在授权范围内。

代码工具工作流

  • 使用 execute_command(action="run"),通过 rg 配合范围较窄的 glob 模式或路径,定位插件类、扩展点、测试、包条目和目录内容。
  • 编辑前,阅读相关实现及相邻示例。
  • 如果 read_file 报告内容被截断,请缩小 start_line 和 end_line 范围,继续读取,直到检查完所有相关部分。
  • 使用 Python 或 Node.js 依赖 API 前,先根据 requirements、包清单、锁文件、本地包源码以及 .pyi/.d.ts 声明,确定准确的已安装版本或锁定版本。如果这些信息不够,请使用 search_web 搜索官方文档域名,并通过 browse_webpage 阅读对应版本的文档。不要凭记忆猜测 API 签名,也不要混用不同主版本的示例。请直接使用 rg 搜索相关包目录、.venv 或 node_modules,不要不加限制地扫描整个项目。
  • 根据修改范围选择编辑工具。如果一项逻辑改动涉及多个文件、新增文件或删除文件,请使用 apply_patch:提交一个用 *** Begin Patch / *** End Patch 包裹的补丁,并包含 *** Add File:、*** Update File: 和 *** Delete File: 部分;所有上下文和删除行都必须与当前内容完全一致。
  • 单个文件中的局部改动请使用 edit_file。默认情况下,其 old_text 必须精确标识一个位置;应添加周围上下文,而不是启用 replace_all,除非确实要更改所有匹配项。
  • 新建单个独立文件请使用 write_file。要完整重写现有文件,必须设置 overwrite=true;替换已读取的内容前,先调用 read_file(include_metadata=true),并将其 sha256 作为 expected_sha256 传入。
  • 短时验证、Git 和诊断命令请使用 execute_command(action="run")。仅交互式或长时间运行的命令使用 action="start",然后通过返回的会话 ID 继续操作。
  • 阅读结构化命令结果:只有 execution_outcome="succeeded" 且 exit_code=0 才表示命令已完成并通过。命令失败时检查 output,日志被截断时检查 output_file。超时或结果未知并不代表副作用已撤销,不要盲目重试写入操作。
  • 对后台命令,使用 output_until_seq 和 output_until_offset 分别作为 since_seq 和 since_offset 恢复读取。读取部分数据块时从偏移量 0 开始;last_seq 不是已读取位置。以有限时间间隔等待新输出,并逐页读取剩余内容,直到 output_complete=true,确保日志完整。如有 output_lost 缺口,请报告。
  • 写入、启动或终止操作后如出现 output_error,需要在合适的字节数限制下再次读取,而不是重新执行该操作。
  • 不要使用 shell 重定向或内联脚本编辑源文件,也不要借此绕过文件工具的权限错误。
  • 如果插件使用 Vue federation,还需阅读 MoviePilot-Frontend/docs/module-federation-guide.md、MoviePilot-Frontend/docs/federation-troubleshooting.md、MoviePilot-Frontend/src/utils/federationLoader.ts 和 MoviePilot-Frontend/src/pages/plugin-app.vue。
  • 仓库边界:MoviePilot 负责运行时加载、API 注册、事件、服务、数据和权限;MoviePilot-Frontend 负责插件 UI 渲染、联邦加载和侧边栏页面;MoviePilot-Plugins 负责插件源码、图标、包索引和发布元数据。

开始前

  1. 理解用户的请求:插件用途、触发模式、配置、输出界面,以及是否需要调度器、API、命令、工作流操作或 Agent 工具。
  2. 编写任何 UI 代码之前,先执行 UI 模式选择关卡。
    • 如果用户已明确选择 JSON 配置/Vuetify JSON 或 Vue 联邦,则按用户选择执行。
    • 如果插件包含任何 UI 界面,而用户尚未选择模式,请让用户从以下两种模式中选择,并等待其答复后再实现 UI 文件或架构。
    • 不要仅仅因为某种模式看起来更简单,就默认采用该模式。
  3. 创建新插件前,检查现有插件:
    • 本地运行时示例:app/plugins/<plugin>/__init__.py
    • 市场/本地源码候选项:运行实例可用时,通过 operation_id=plugin.market 调用 moviepilot_api。
    • 已安装插件候选项:使用 operation_id=plugin.installed;如果可从本地插件仓库或插件市场元数据匹配到源码,其摘要中会包含 repo_url。
    • Vue 联邦示例:使用对应版本代际的现有插件,以及 MoviePilot-Frontend/examples/plugin-component/。
  4. 确定目标源码路径:
    • 尽可能使用 operation_id=config.system.get 查询 PLUGIN_LOCAL_REPO_PATHS。
    • 如果只配置了一个本地插件仓库,优先使用该路径。
    • 如果配置了多个仓库,则使用用户指定的仓库;否则询问用户要使用哪个仓库。
    • 如果没有配置仓库,则在合适的本地目录中准备源码。如果已获授权进行本地安装/配置,则使用 operation_id=config.system.update 注册该仓库,并设置 setting_key="PLUGIN_LOCAL_REPO_PATHS"、将所选源码路径作为 value,以及设置 operation="replace"。相对路径(如 local-plugins)会相对于 MoviePilot 根目录解析。 将插件写入所选源码目录;除非用户明确要求仅进行运行时实验,否则不要直接写入 app/plugins/。
  5. 选择插件 ID:
    • 类名就是插件 ID,例如 MyNotifier。
    • 目录名为类名的小写形式,例如 mynotifier。
    • 除非用户明确要求修改该插件,否则避免与已安装或市场中的插件发生冲突。
    • 如果插件可能支持克隆,不要将原始插件 ID 硬编码为数据/配置命名空间;请使用 self.__class__.__name__。

UI 模式选择关卡

MoviePilot 插件 UI 恰好有两种实现模式。如果请求包含配置、详情页、仪表板、侧边栏页面或任何其他插件 UI,且未明确指定模式,则必须让用户选择一种。

可以这样简洁地询问:

这个插件 UI 用哪种方式实现?
1. JSON 配置:后端返回 Vuetify JSON,适合普通配置表单、简单详情页和轻量仪表板。
2. 联邦 UI:独立 Vue 远程组件,适合复杂交互、自定义布局、侧栏全页或多页面。

选择规则:

  • JSON 配置 / Vuetify JSON:使用 JSON 组件架构实现 get_form()、get_page() 和 get_dashboard()。无需前端构建,也无需 dist/assets/remoteEntry.js。
  • 联邦 UI / Vue 远程组件:实现 get_render_mode(),通过 Vite 联邦暴露 Vue 组件,将前端资源构建到插件目录中;只有在用户要求侧边栏页面时才使用 get_sidebar_nav()。
  • 如果插件确实没有面向用户的 UI,请说明无需选择 UI 模式,并仅实现请求所需的后端扩展点。
  • 等待期间可以继续进行后端工作,但前提是该工作不会限制或妨碍任一种 UI 模式。

本地源代码布局

对于已验证的 V3 主机,新建本地插件时请使用此布局。对于 V2,请改用 package.v2.json 和 plugins.v2/,并遵循其依赖契约。

<local-plugin-repo>/
├── package.v3.json
└── plugins.v3/
    └── <plugin_id_lower>/
        ├── __init__.py
        ├── pyproject.toml          # only when extra runtime dependencies are necessary
        └── ...                     # helper modules, schemas, static assets

对于 Vue 联邦插件,运行时要求插件目录下包含已构建的远程资源:

plugins.v3/<plugin_id_lower>/
├── __init__.py
├── dist/
│   └── assets/
│       ├── remoteEntry.js
│       └── ...                     # JS/CSS/assets referenced by remoteEntry
├── package.json                    # optional frontend build project metadata
├── vite.config.js                  # optional frontend build config
└── src/                            # optional source, not required at runtime

不要依赖运行时的前端源文件。如果为便于维护而在插件仓库中保留源代码,仍需构建并随插件提供 remoteEntry.js 所需的 dist/assets 文件。

仅当用户明确需要时才使用旧版布局:

<local-plugin-repo>/
├── package.json
└── plugins/
    └── <plugin_id_lower>/
        └── __init__.py

对于应在 V2 上运行的旧版 package.json 条目,请包含 "v2": true。 对于当前的 V3 开发,请使用 package.v3.json 和 plugins.v3/。

软件包元数据

为插件 ID 添加或更新软件包条目。确保软件包版本与类的 plugin_version 保持一致。

{
  "MyNotifier": {
    "name": "通知示例",
    "description": "根据用户配置发送示例通知。",
    "labels": "消息通知",
    "version": "1.0.0",
    "icon": "mynotifier.png",
    "author": "local",
    "level": 1,
    "system_version": ">=3.0.0",
    "history": {
      "v1.0.0": "初始版本"
    }
  }
}

规则:

  • 软件包对象键必须与插件类名一致。
  • version 必须与 plugin_version 一致。
  • 如果插件类具有 plugin_name、plugin_desc、plugin_icon、plugin_author、plugin_label、auth_level 等属性,则 name、description、icon、author、labels 和 level 应与之匹配。
  • history 应记录每个已发布版本的用户可读变更。
  • 如果插件依赖特定 MoviePilot 版本引入的主机功能,请使用 system_version;此类功能包括新的后端 API、辅助函数、事件、Vue 联邦行为、侧边栏导航、仪表盘行为或 Agent 工具。
  • 仅当插件有意通过 GitHub Release 归档进行分发时,才使用 "release": true。
  • 新插件条目通常应追加到软件包索引中,以便在市场中显示为较新的项目。
  • 将 system_version 设为提供所用 API 的实际最低版本;示例并不能证明每项主机功能都已在 3.0.0 中提供。
  • 除非确有需要,否则不要添加依赖。请使用对应主机版本的清单契约(当前 V3 使用 pyproject.toml)。依赖变更需要经过主机安装流程;仅热重载不会安装依赖。
  • 插件依赖会安装到共享的 MoviePilot Python 环境中。除非用户已明确接受兼容性风险,否则不要锁定或降级 MoviePilot 已提供的软件包。

实现框架

实现 _PluginBase 中的所有抽象方法。所有新函数和方法都必须编写中文文档字符串;公共类、公共方法和公共函数是评审的硬性门槛。

from typing import Any, Dict, List, Optional, Tuple

from app.plugins import _PluginBase


class MyNotifier(_PluginBase):
    """通知示例插件。"""

    plugin_name = "通知示例"
    plugin_desc = "根据用户配置发送示例通知。"
    plugin_icon = "mynotifier.png"
    plugin_version = "1.0.0"
    plugin_label = "消息通知"
    plugin_author = "local"
    plugin_config_prefix = "mynotifier_"
    plugin_order = 100
    auth_level = 1

    _enabled = False
    _message = ""

    def init_plugin(self, config: dict = None) -> None:
        """根据插件配置初始化运行状态。"""
        self.stop_service()
        self._enabled = False
        self._message = ""
        if not config:
            return
        self._enabled = bool(config.get("enabled"))
        self._message = str(config.get("message") or "")

    def get_state(self) -> bool:
        """获取插件启用状态。"""
        return self._enabled

    @staticmethod
    def get_command() -> List[Dict[str, Any]]:
        """返回插件远程命令列表。"""
        return []

    def get_api(self) -> List[Dict[str, Any]]:
        """返回插件 API 列表。"""
        return []

    def get_form(self) -> Tuple[Optional[List[dict]], Dict[str, Any]]:
        """返回插件配置表单与默认配置。"""
        return [
            {
                "component": "VForm",
                "content": [
                    {
                        "component": "VSwitch",
                        "props": {
                            "model": "enabled",
                            "label": "启用插件"
                        }
                    },
                    {
                        "component": "VTextField",
                        "props": {
                            "model": "message",
                            "label": "通知内容"
                        }
                    }
                ]
            }
        ], {
            "enabled": False,
            "message": ""
        }

    def get_page(self) -> Optional[List[dict]]:
        """返回插件详情页面。"""
        if not self._enabled:
            return None
        return [
            {
                "component": "VAlert",
                "props": {
                    "type": "info",
                    "text": self._message or "插件已启用"
                }
            }
        ]

    def stop_service(self) -> None:
        """停止插件后台服务并释放资源。"""
        return None

扩展点

仅使用请求的插件实际需要的扩展点:

  • 配置:get_form() 返回 Vuetify 表单架构和默认数据; init_plugin() 读取配置;update_config() 持久化内部变更。
  • 数据:使用 save_data()、get_data()、del_data() 和 get_data_path()。
  • 通知:使用 post_message(),而不是直接调用消息模块。
  • API:从 get_api() 返回路由定义;省略 auth 时,默认身份验证方式为 apikey。Vue 组件 API 通常应使用 auth: "bear",并通过前端传入的 api prop 调用。
  • 命令:从 get_command() 返回斜杠命令定义,并通过 MoviePilot 事件分发。
  • 服务:从 get_service() 返回调度器服务,并始终在 stop_service() 中清理。
  • 一次性或延迟作业(例如“现在运行一次”或“事件发生后 N 秒运行”):调用 app.sdk.scheduler.add_plugin_once_job(plugin_id, job_id, self.method, name, delay_seconds=..., func_kwargs=...),而不是创建私有 BackgroundScheduler。此方法不会重置插件的周期服务;如果存在相同 ID 的待处理作业,则会替换该作业;插件重新加载或卸载时会清除该作业。传入插件实例的绑定方法;如果插件停止后不应再运行待处理作业,请在 stop_service() 中使用 remove_plugin_once_job() 取消。
  • 仪表板:使用 get_dashboard_meta() 和 get_dashboard() 提供主页小组件。
  • 工作流操作:使用 get_actions();操作函数首先接收 ActionContent,并返回 (success, action_content)。
  • Agent 工具:使用 get_agent_tools();每个工具类都必须继承 app.agent.tools.base.MoviePilotTool。
  • 自定义 Vue UI:选用 Vue UI 模式时,实现 get_render_mode()。返回 ("vue", "<compiled-assets-path>"),并将构建后的前端资源放入插件目录。
  • Agent 助手头像。若要替换网页助手的屏幕角色,请在 Vue 插件上实现 get_agent_pets()。参见下方“Agent 助手头像”。

Vue 联邦 UI

若在预检 UI 决策中选用 Vue 联邦,则使用 Vue 联邦。 Vue 插件的后端方法、构建文件和联邦 exposes 必须保持一致。

后端要求:

from typing import Any, Dict, List, Tuple


@staticmethod
def get_render_mode() -> Tuple[str, str]:
    """声明插件使用 Vue 联邦组件渲染。"""
    return "vue", "dist/assets"


def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
    """Vue 模式下返回默认配置模型。"""
    return [], self._current_config()


def get_page(self) -> List[dict]:
    """Vue 模式下详情页由远程 Page 组件渲染。"""
    return []

如果插件需要主布局侧边栏页面,还需实现:

def get_sidebar_nav(self) -> List[Dict[str, Any]]:
    """声明插件在主界面左侧导航栏中的全页入口。"""
    if not self.get_state():
        return []
    return [
        {
            "nav_key": "main",
            "title": "我的插件",
            "icon": "mdi-puzzle",
            "section": "system",
            "permission": "manage",
            "order": 10,
        }
    ]

侧边栏规则:

  • 仅聚合已启用且 get_render_mode() 返回 "vue" 的插件的侧边栏条目。
  • section 必须是 start、discovery、subscribe、organize、system 之一;无效值将回退为 system。
  • permission 可以是 subscribe、discovery、search、manage 或 admin;无效值将被忽略。
  • nav_key 默认为 main,且不得包含 /、?、# 或空格。
  • 允许有多个侧边栏条目;每个条目都需要稳定的 nav_key。

如果插件提供 Agent 助手头像(助手形象),还需实现 get_agent_pets()。参见下方“Agent 助手头像”。

前端联邦要求:

federation({
  name: 'MyPlugin',
  filename: 'remoteEntry.js',
  exposes: {
    './Page': './src/components/Page.vue',
    './Config': './src/components/Config.vue',
    './Dashboard': './src/components/Dashboard.vue',
    './AppPage': './src/components/AppPage.vue',
    './AppPageSettings': './src/components/AppPageSettings.vue',
  },
  shared: {
    vue: { requiredVersion: false, generate: false },
    vuetify: { requiredVersion: false, generate: false, singleton: true },
    'vuetify/styles': { requiredVersion: false, generate: false, singleton: true },
  },
  format: 'esm',
})

构建要求:

  • 将 Vite 的 build.target 设为 esnext,因为联邦使用顶层 await。
  • 使用 cssCodeSplit: true,并尽可能使用作用域样式或组件局部样式。
  • 使用前端项目文档中说明的命令进行构建,然后将 remoteEntry.js 及其引用的所有 JS/CSS/资源文件保留在 dist/assets 下。
  • 不要将前端运行时依赖添加到插件 Python 的 requirements.txt;请将前端依赖保留在前端构建项目中。

组件约定:

  • Page 渲染插件详情对话框,并可发出 action、switch 和 close 事件。
  • Config 渲染插件设置,接收 initialConfig 和 api,并发出 save、close 和 switch 事件。
  • Dashboard 接收 config 和 allowRefresh。
  • AppPage 渲染主布局侧边栏页面,并接收 api、pluginId 和 navKey。
  • 对于侧边栏 nav_key=main,前端依次加载 ./AppPage 和 ./Page。
  • 对于其他任何 nav_key,前端依次加载 ./AppPage{PascalCase(nav_key)}、 ./AppPage 和 ./Page。示例:settings -> AppPageSettings、 my_tool -> AppPageMyTool。
  • 单个 AppPage 可以根据 navKey 分支处理,也可以为特定条目提供单独的 AppPage{PascalCase} 文件。

Agent 助手形象:

def get_agent_pets(self) -> List[Dict[str, Any]]:
    """声明插件提供的 Agent 助手形象。"""
    return [
        {
            "key": "girl",
            "name": "看板娘",
            "mode": "stage",
            "component": "AgentPet",
            "preview": "girl-preview.png",
            "avatar": "girl-avatar.png",
        }
    ]
  • 仅对已启用且 get_render_mode() 返回 "vue" 的插件,通过 GET /api/v1/plugin/agent_pets 聚合声明。
  • 必填字段为 key([a-z0-9_-]{1,32},在插件内唯一)和 name。 可选字段包括 description、mode(默认为 renderer,也可以是 stage)、 component(联邦暴露名称,默认为 AgentPet)、api_version(默认为 1)、 preview、avatar、bubbles(仅用于 stage,可设为 host 或 self)以及 random_actions(仅用于 renderer)。
  • preview 和 avatar 可以是相对于 remoteEntry.js 所在目录(即 get_render_mode() 返回的目录)的路径,也可以是 http(s):// / data: URL。 使用 ("vue", "dist/assets") 时,将 girl-preview.png 放在 dist/assets/remoteEntry.js 旁边,并声明 "girl-preview.png"。写成 "assets/girl-preview.png" 会解析为 dist/assets/assets/girl-preview.png。 相对路径不得包含 ..、\ 或 :。
  • 含有任何无效字段的条目都会被丢弃并发出警告;重复的 key 则保留第一个条目。 一个插件可以声明多个形象,每个形象都可以有自己的 mode 和 component, 并且必须暴露所有被引用的组件。
  • 形象仅负责渲染角色。它不得接管 Agent 面板或发送消息,并且 agent.open({ draft }) 只会填充输入框。
  • 用户可在 Agent 面板标题栏中点击“更换形象”按钮选择形象;管理员默认值为 AI_AGENT_PET(<plugin_id>:<key>)。加载失败、超时超过 8 秒以及运行时错误 都会回退到内置机器人。
  • 请阅读 MoviePilot-Frontend/docs/module-federation-guide.md 的 “Agent 助手形象(AgentPet)”章节(5.11),了解组件 props、 moviepilot:agent 宿主 API、agent.* 事件、renderer 的 action/intent 映射、16KB 的 pet.storage 限制以及 stage 示例。

Vue API 调用:

  • 使用 auth: "bear" 定义面向前端的插件 API。
  • 使用注入的 API 对象调用这些 API,例如 props.api.get(\plugin/${props.pluginId}/history`)`。
  • 不要将 settings.API_TOKEN 传入 Vue 组件用于浏览器端调用。

本地安装和重新加载

仅在授权的安装/运行时范围内执行此阶段。

  1. 在已配置的本地插件仓库中写入文件后,调用 moviepilot_api, 并使用 operation_id=plugin.market 和查询字段 query="<PluginID>"、force_refresh=true,确认本地来源可见。
  2. 使用 operation_id=plugin.install、路径参数 plugin_id="<PluginID>" 和 query.force=true 安装或重新安装。 安装流程会将来源复制到 app/plugins/<plugin_id_lower>/。
  3. 如果启用了 PLUGIN_AUTO_RELOAD 或开发模式,已安装本地插件中的 Python 源代码更改 可以自动同步并重新加载。如果未启用,则在编辑运行时文件后,使用路径参数 plugin_id 调用 operation_id=plugin.reload。
  4. 依赖清单更改时,使用 force=True 重新安装;仅重新加载不会安装新依赖。

验证

  • 重新阅读已更改的文件,确认类名、目录名、包 ID 和包版本保持一致。
  • 确认每个公共类、公共方法和公共函数都有中文文档字符串。
  • 确认每个新编写的函数或方法都有中文文档字符串,即使它是私有辅助代码。
  • 对于 Vue 联邦插件,确认 get_render_mode() 返回 ("vue", "dist/assets") 或实际构建的资源路径,并且 dist/assets/remoteEntry.js 存在。
  • 对于侧边栏插件,确认插件已启用,get_state() 返回 True,get_sidebar_nav() 返回有效条目,并且所有非主导航 nav_key 都有对应的 AppPage expose,或由通用 AppPage 处理。
  • 确认面向前端的 API 路由使用 auth: "bear",且浏览器代码通过提供的 api 属性调用这些路由。
  • 通过 MoviePilot 工具发起外部 HTTP 调用,并避免在测试中进行真实网络调用。
  • 如果插件包含非简单逻辑,使用仓库针对目标代际的引导机制,添加或更新 pytest 原生测试。检查当前测试配置,不要将 V2 后端或命名空间复制到 V3 中。
  • 对已修改的部分运行范围最小的验证。在此仓库中,请遵循 docs/rules/03-commands.md;对于仅包含插件的仓库,请遵循其自身记录的验证命令。
  • 对于插件仓库中的 Python 更改,尽可能使用主机 Python 环境,并至少对已修改的插件文件运行语法编译。
  • 对于 Vue 联邦更改,在可用时运行前端项目记录的类型检查和构建命令,然后确认构建资源已复制到插件目录。

Vue 联邦故障排查

  • GET /api/v1/plugin/remotes?token=moviepilot 应包含该插件,且 URL 以 /plugin/file/<plugin_id_lower>/<dist_path>/remoteEntry.js 结尾。
  • GET /api/v1/plugin/sidebar_nav 应包含已启用的 Vue 插件的侧边栏条目,且这些条目的 nav_key、section 和 permission 均有效。
  • 如果控制台提示 Module name 'vue' does not resolve to a valid URL,请检查联邦 shared 配置,并使用 requiredVersion: false。
  • 如果控制台提示不支持顶层 await,请将 build.target 设置为 esnext。
  • 如果动态导入失败,请检查远程文件请求状态、计算出的 remoteEntry.js 路径,以及已安装的运行时插件目录中是否实际包含构建资源。
  • 如果侧边栏页面空白,请检查当前 nav_key 的 expose 名称解析和回退机制(AppPage{PascalCase} -> AppPage -> Page)。

最终报告

报告以下内容:

  • 插件 ID、源路径,以及已安装时的运行时路径。
  • 主机/插件代际以及已更改的包文件(package.v3.json、 package.v2.json 或旧版 package.json)。
  • 使用的 UI 模式(vuetify JSON 或 vue 联邦);对于 Vue 插件,还需报告 expose 的组件和构建资源路径。
  • 插件是否已安装或重新加载。
  • 运行的验证命令,或未运行验证的原因。

评论

支持 Markdown