---
title: "插件系统"
description: "Kasumi 插件清单、运行时注册、命令归属和扩展边界"
---


> 理解 Kasumi 插件清单、运行时注册、命令归属和前后端扩展边界。

## 模块边界

Kasumi 插件是宿主侧的原生命令模块，不是把任意 JavaScript 注入 WebView 的机制。插件只
声明命令、权限和参数校验器，命令仍由 `KSMRouter` 统一接收、检查 allowlist 并返回协议
响应。

| 层级 | 事实源 | 作用 |
| --- | --- | --- |
| 协议清单 | `protocol/plugins.json` | 内置插件 ID、版本、说明和命令归属 |
| 生成契约 | `KSM_PLUGINS`、`KSMPluginRegistry` | 前端、Windows、Android 的插件常量 |
| 运行时注册 | `KSMPlugin` | 宿主创建插件实例并注册到窗口路由器 |
| 前端调用 | `KSM.invoke` 或模块 API | 通过命令名进入统一消息通道 |

插件不能直接访问 WebView DOM、不能使用 `addJavascriptInterface`，也不能绕过
`KSMRouter` 自行拼接响应。`fs`、`dialog` 和 `sql` 是默认插件；其他平台能力仍由核心
路由实现。

## 清单结构

`protocol/plugins.json` 只描述内置插件。每个命令必须且只能属于一个插件：

```json
{
  "plugins": {
    "example": {
      "name": "Example",
      "version": "1.0.0",
      "description": "Example host commands.",
      "commands": ["example.ping"]
    }
  }
}
```

新增内置命令时先修改 `commands.json`，再把命令加入唯一插件，运行
`python tools/validate_protocol.py` 和 `python tools/codegen.py`。生成器会同步前端、
C++ 和 Kotlin 契约，禁止手写平行插件清单。

## Windows 注册

Windows 插件实现 `KSMPlugin`。`Id`、`Commands` 和 `Permissions` 必须稳定；自定义命令
不在协议 schema 中时，`ValidateArgs` 负责拒绝非法参数。

```cpp
#include <kasumi/plugins/ksm_plugin.h>

class EchoPlugin final : public KSMPlugin {
public:
    const std::string& Id() const override {
        static const std::string id = "com.example.echo";
        return id;
    }

    const std::vector<std::string>& Commands() const override {
        static const std::vector<std::string> commands = {
            "com.example.echo.ping"
        };
        return commands;
    }

    const std::vector<std::string>& Permissions() const override {
        static const std::vector<std::string> permissions = {
            "com.example.echo"
        };
        return permissions;
    }

    bool ValidateArgs(
        const std::string& command,
        const nlohmann::json& args
    ) const override {
        return command == "com.example.echo.ping" && args.is_object();
    }

    nlohmann::json Handle(
        const std::string& command,
        const nlohmann::json& args,
        const KSMPluginContext& context
    ) override {
        if (
            command != "com.example.echo.ping" ||
            !context.has_permission("com.example.echo")
        ) {
            throw KSMHostException(
                "PERMISSION_DENIED",
                "The plugin was not authorized."
            );
        }
        return nlohmann::json{{"pong", true}};
    }
};
```

把实例传给 `KSMWebView2Host` 的 `plugins` 参数。默认 `fs`、`dialog`、`sql` 可以用同 ID
插件替换，但替换插件必须完整声明该 ID 在 `protocol/plugins.json` 中的全部命令；其他
生成插件 ID 仍是保留值。

## Android 注册

Android 插件实现 `KSMPlugin`，通过 `KSMWebViewHost` 构造参数注册：

```kotlin
import org.json.JSONObject

class EchoPlugin : KSMPlugin {
    override val id = "com.example.echo"
    override val commands = setOf("com.example.echo.ping")
    override val permissions = setOf("com.example.echo")

    override fun handle(
        command: String,
        args: JSONObject,
        context: KSMPluginContext
    ): Any {
        if (!context.hasPermission("com.example.echo")) {
            throw KSMHostException(
                "PERMISSION_DENIED",
                "The plugin was not authorized."
            )
        }
        return JSONObject().put("pong", true)
    }
}
```

插件实例必须存活到 WebView 宿主销毁；命令执行不得阻塞主线程。需要 I/O 的插件应把工作
放到宿主已有的后台执行链路，或自行管理可取消任务，并在宿主关闭后抑制迟到响应。

## 命令与权限

| 规则 | 行为 |
| --- | --- |
| ID 冲突 | 注册表拒绝重复插件 ID |
| 命令冲突 | 注册表拒绝重复命令 |
| 保留 ID | 除 `fs`、`dialog`、`sql` 外的生成插件 ID 不可替换 |
| 替换范围 | 替换内置插件必须完整声明该插件的生成命令 |
| 生成命令 | 自定义插件不得抢占 `protocol/commands.json` 中的命令 |
| 未知命令 | `UNKNOWN_COMMAND` |
| allowlist 外命令 | `NOT_SUPPORTED` |
| 参数错误 | 插件 `ValidateArgs` 返回 `false`，路由器返回 `INVALID_ARGS` |

自定义插件声明权限后仍需在 `Handle` 中检查业务授权。插件权限会进入
`capabilities.query` 的权限集合，但敏感资源、凭据和用户数据必须由插件实现自己的
授权策略。

## 示例

```ts
const response = await KSM.invoke('com.example.echo.ping', {});
console.info(response);
```

插件命令可以直接使用通用 `KSM.invoke`，也可以由独立 npm 包封装成业务 API。前端封装只
负责参数形状和 Promise 生命周期，不能替代宿主校验。

SQL 不是单纯示例插件：它的驱动注册、SQLite 路径规则和远程数据库接入见
[SQL 插件](/docs/kasumi/plugins/sql)。

## 常见问题

| 现象 | 优先检查 |
| --- | --- |
| `UNKNOWN_COMMAND` | 插件是否注册到当前窗口宿主 |
| `NOT_SUPPORTED` | 命令是否在应用 allowlist，平台是否支持 |
| `INVALID_ARGS` | 插件是否实现 `ValidateArgs`，schema 是否同步生成 |
| `PERMISSION_DENIED` | `Handle` 内是否错误拒绝权限，或业务授权是否缺失 |
| 能力列表缺少插件 | 插件是否在 `KSMWebView2Host` / `KSMWebViewHost` 创建前注册 |
| 关闭窗口后仍有副作用 | 插件是否持有未取消的后台任务 |
