插件系统
查看 MarkdownKasumi 插件清单、运行时注册、命令归属和扩展边界
理解 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 只描述内置插件。每个命令必须且只能属于一个插件:
{
"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 插件实现 KSMPlugin。Id、Commands 和 Permissions 必须稳定;自定义命令
不在协议 schema 中时,ValidateArgs 负责拒绝非法参数。
#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 插件实现 KSMPlugin,通过 KSMWebViewHost 构造参数注册:
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 的权限集合,但敏感资源、凭据和用户数据必须由插件实现自己的
授权策略。
const response = await KSM.invoke('com.example.echo.ping', {});
console.info(response);插件命令可以直接使用通用 KSM.invoke,也可以由独立 npm 包封装成业务 API。前端封装只
负责参数形状和 Promise 生命周期,不能替代宿主校验。
SQL 不是单纯示例插件:它的驱动注册、SQLite 路径规则和远程数据库接入见 SQL 插件。
| 现象 | 优先检查 |
|---|---|
UNKNOWN_COMMAND | 插件是否注册到当前窗口宿主 |
NOT_SUPPORTED | 命令是否在应用 allowlist,平台是否支持 |
INVALID_ARGS | 插件是否实现 ValidateArgs,schema 是否同步生成 |
PERMISSION_DENIED | Handle 内是否错误拒绝权限,或业务授权是否缺失 |
| 能力列表缺少插件 | 插件是否在 KSMWebView2Host / KSMWebViewHost 创建前注册 |
| 关闭窗口后仍有副作用 | 插件是否持有未取消的后台任务 |
评论