系统能力补全规格
定义真实托盘、全局快捷键、更新安装、单实例、通知权限与开发者工具。
定义真实托盘、全局快捷键、签名更新安装与回滚、单实例、通知权限和开发者工具的目标边界。
| 状态项 | 说明 |
|---|---|
当前 26.11.5 | 当前 26.11.5 基线的托盘没有真实图标和菜单,全局快捷键默认返回 false,更新器无远端源时返回不可用,单实例默认 primary: true;appInstance、updater、globalShortcut、tray 存在占位行为。 |
目标规格(未发布) | 规格 / 待实现:tray.configure 与 tray.action/click、真实 globalShortcut.triggered、updater.install 与 updater.progress/status、真实单实例、通知运行时权限与受控 app.openDevtools;占位成功全部移除。 |
实施阶段 | P4;依赖 P0 协议与生成契约、P3 媒体会话和 P5 签名产物。 |
验收标准 | 托盘图标/菜单/点击、系统热键、单实例互斥、签名更新安装与回滚、通知权限和 devtools 都有真实状态与事件;无未实现能力返回成功。 |
系统能力只提供操作系统级入口和状态事件。菜单内容、热键语义、更新策略和业务动作留在应用配置或扩展层。图标、更新包、安装包签名、系统权限与窗口消息属于宿主服务层,前端守卫不能代替原生校验。
| 层级 | 真实入口 | 责任 |
|---|---|---|
| 协议 | protocol/commands.json、protocol/permissions.schema.json、protocol/plugins.json、protocol/errors.json | 目标命令、事件、权限、平台矩阵与 UPDATE_VERIFICATION_FAILED。 |
| 配置 | protocol/kasumi.config.schema.json、packages/kasumi-cli/src/config.mjs、cli/kasumi_config.py | 图标、签名、更新源、debug 策略、命令白名单与 origin 白名单。 |
| 前端 | frontend/src/api/tray.ts、globalShortcut.ts、updater.ts、notification.ts、frontend/src/bridge/ksm.ts | 参数守卫、能力查询、事件订阅与 64 KiB 消息边界。 |
| Windows | windows/src/plugins/ksm_plugin.h、ksm_plugin_registry.cpp、windows/src/bridge/ksm_router.cpp、windows/src/platform/ksm_windows_host_services.cpp、ksm_webview2_host.cpp、windows/vcpkg.json | Shell_NotifyIconW、菜单消息泵、RegisterHotKey、签名校验、原子安装与回滚、devtools 策略。 |
| Android | KSMNotificationPlugin.kt、KSMNotification.kt、KSMHostServices.kt、KSMRouter.kt、AndroidManifest.xml | POST_NOTIFICATIONS 运行时权限、通知通道、系统下载与安装边界、deeplink 与单实例启动模式。 |
| 产物 | version.json、.ksm/release/、`pnpm ksm package --installer nsis | msi` |
| 能力 | 当前 26.11.5 基线 | 目标规格 / 待实现 |
|---|---|---|
| 托盘 | tray.show/hide 只判断窗口句柄;没有真实图标、菜单、点击或菜单动作事件。 | tray.configure 使用 icons.tray 配置资源,创建 Shell_NotifyIconW 图标和菜单;tray.click、tray.action 投递菜单 ID、窗口 ID 与动作。 |
| 全局快捷键 | globalShortcut.register/unregister 默认返回 false;没有触发事件。 | 真实 RegisterHotKey 注册、冲突检测、窗口销毁注销;globalShortcut.triggered 投递规范化的快捷键 ID。 |
| 更新器 | updater.check/download 默认无远端源;没有校验、安装、进度、状态或回滚。 | updater.install 验证签名与校验和,原子替换并保留回滚点;updater.progress、updater.status 报告阶段。 |
| 单实例 | app.singleInstance.state/focus 默认放行,不阻止第二进程。 | Windows 命名互斥量与 Android 启动模式真实生效;第二实例参数进入 app.secondInstance.received。 |
| 通知权限 | notification.show 已有 Android 运行时权限流程,但没有点击与动作事件。 | 保留 permission.request('notification.show');媒体通知与系统动作共用权限状态,未授权返回 PERMISSION_DENIED。 |
| 开发者工具 | 生产 WebView 禁止调试,没有受控前端入口。 | app.openDevtools 仅在显式 debug 配置下可用,生产配置返回 NOT_SUPPORTED。 |
| 占位成功 | 托盘、热键、更新器、单实例可能返回无副作用的成功值。 | 成功字段必须对应真实系统状态;未实现时返回 false 或 NOT_SUPPORTED,不得伪造。 |
| 目标命令与事件 | 关键约束 |
|---|---|
tray.configure | { icon?, tooltip?, menu? };菜单项使用稳定 actionId,图标为配置资源或 app:// 虚拟路径。 |
tray.action / tray.click | { actionId?, windowId };只投递给已订阅窗口。 |
globalShortcut.triggered | { shortcut, actionId };注册冲突返回 BUSY 或 PERMISSION_DENIED,不覆盖已有热键。 |
updater.install | { version };只安装已下载且通过签名与校验和验证的包;失败保留旧版本。 |
updater.progress / updater.status | { phase, bytes?, totalBytes? } / { state, version };进度事件节流,状态边界去重。 |
app.openDevtools | 只在 debug 配置下返回 { opened: true };生产构建不注册。 |
真实入口是 kasumi-bridge 的 KSM 类及 frontend/src/api/ 下的 tray、globalShortcut、updater、notification 包装。protocol/kasumi.config.schema.json 已声明 icons.tray、signing 与平台策略字段;Node CLI 与 Python CLI 负责校验。
以下示例描述目标接口,尚未在 26.11.5 发布:
import { KSM, KSMWebMessageTransport } from 'kasumi-bridge';
KSM.initialize(new KSMWebMessageTransport());
const capabilities = await KSM.capabilities();
const trayPermission = await KSM.permission.request('tray');
if (!trayPermission.granted || !capabilities.commands.includes('tray.configure')) {
throw new Error('The system tray is unavailable.');
}
const offAction = KSM.on<{ actionId: string }>(
'tray.action',
(payload) => console.info(payload.actionId)
);
const offShortcut = KSM.on<{ shortcut: string }>(
'globalShortcut.triggered',
(payload) => console.info(payload.shortcut)
);
const offStatus = KSM.on<{ state: string; version: string }>(
'updater.status',
(payload) => console.info(payload.state, payload.version)
);
await KSM.subscribe('tray.action');
await KSM.subscribe('globalShortcut.triggered');
await KSM.subscribe('updater.status');
await KSM.invoke('tray.configure', {
tooltip: 'Kasumi',
menu: [
{ actionId: 'show', label: '显示' },
{ actionId: 'playPause', label: '播放或暂停' },
{ actionId: 'quit', label: '退出' }
]
});
await KSM.invoke('globalShortcut.register', {
shortcut: 'Ctrl+Shift+K'
});
const update = await KSM.invoke<{ available: boolean; version: string | null },
Record<string, never>>('updater.check', {});
if (update.available && update.version) {
await KSM.invoke('updater.install', { version: update.version });
}
offAction();
offShortcut();
offStatus();
await KSM.unsubscribe('tray.action');
await KSM.unsubscribe('globalShortcut.triggered');
await KSM.unsubscribe('updater.status');updater.install 先验证发布资产的签名、校验和、版本与平台,再把临时包原子替换到安装位置。签名或校验失败返回 UPDATE_VERIFICATION_FAILED 并删除临时包;安装失败恢复回滚点,旧版本保持可启动。Android 不静默侧载未签名包;系统安装边界由宿主策略提供。
| 平台 | 实现边界 | 行为 |
|---|---|---|
| Windows | windows/src/plugins/、windows/src/platform/ksm_windows_host_services.cpp、ksm_webview2_host.cpp、windows/vcpkg.json、windows/CMakeLists.txt | Shell_NotifyIconW 创建真实图标与菜单,窗口消息泵映射菜单 ID;RegisterHotKey 与注销走同一窗口所有权;命名互斥量实现单实例;更新包签名校验、原子替换、回滚和 NSIS/MSI 资产校验留在服务层;生产 AreDevToolsEnabled=false。 |
| Android | KSMNotificationPlugin.kt、KSMNotification.kt、KSMHostServices.kt、KSMRouter.kt、android/kasumi/src/main/AndroidManifest.xml | POST_NOTIFICATIONS 运行时权限与通知通道;媒体通知、深链和单实例启动模式由系统服务处理;系统下载与安装边界遵循签名策略;app.openDevtools 只在受控 debug 构建启用。 |
Windows 托盘与热键事件按 windowId 投递;Android 不支持的系统命令返回 NOT_SUPPORTED。两端 capabilities.query 只列出真实注册的服务。
shown、快捷键 registered、更新器 downloaded、单实例 primary 与 devtools opened 必须反映真实系统状态。app:// 虚拟路径或配置资源。addJavascriptInterface,不直接调用宿主私有系统对象,不在前端写平台分支。icons.tray、签名证书与更新源硬编码进业务代码。| 错误码 | 触发条件 | 期望行为 |
|---|---|---|
INVALID_ARGS | 菜单项、快捷键、版本、图标虚拟路径或通知参数非法。 | 前端守卫与宿主校验同时拒绝。 |
PERMISSION_DENIED | 通知、托盘、热键、更新或命令白名单未授权。 | 原生层在系统调用前拒绝。 |
NOT_SUPPORTED | Android 不支持的 Windows 系统能力或生产环境禁用 devtools。 | 能力查询排除相关命令。 |
NOT_FOUND | 图标、更新版本、菜单项或回滚点不存在。 | 不改变现有系统状态。 |
IO_ERROR | 托盘创建、热键注册、通知通道或更新文件读写失败。 | 清理已获取资源并返回可操作诊断。 |
CANCELLED / TIMEOUT | 下载、安装、窗口销毁或请求取消。 | 取消底层任务,抑制迟到事件。 |
BUSY | 热键冲突、已有更新任务或菜单请求并发。 | 保持当前状态,不覆盖已有注册。 |
UPDATE_VERIFICATION_FAILED | 签名、校验和、版本或平台不匹配。 | 删除临时包,保留旧版本与回滚点。 |
DEEP_LINK_INVALID | 更新通知或单实例参数中的深链非法。 | 拒绝导航与事件投递。 |
MEDIA_UNSUPPORTED | 托盘或通知图标无法解析。 | 使用默认资源或返回结构化原因,不伪造成功。 |
| 现象 | 优先检查 |
|---|---|
| 托盘显示成功但无图标 | icons.tray、Shell_NotifyIconW、资源句柄、窗口消息泵与销毁清理。 |
| 菜单点击无事件 | action ID 映射、windowId 订阅表、事件形状、迟到响应抑制。 |
快捷键返回 false | 注册冲突、格式、真实宿主服务、窗口所有权和注销时机。 |
| 更新安装后无法启动 | 签名、校验和、原子替换、回滚点、平台资产与版本元数据。 |
UPDATE_VERIFICATION_FAILED 反复出现 | Release 资产校验和、签名证书链、更新源 origin、下载是否截断。 |
| Android 通知不显示 | permission.request('notification.show')、系统开关、通知通道与前台服务类型声明。 |
| 第二实例仍可运行 | Windows 互斥量、Android 启动模式、app.secondInstance.received 转发。 |
| Release 与 npm 不一致 | version.json、平台版本头、manifests、Release 资产与 registry latest。 |
pnpm validate:protocol、pnpm codegen、pnpm typecheck、pnpm test:contract、pnpm test:packages、pnpm test:tarballs 通过;目标命令、事件、权限和平台矩阵无漂移。UPDATE_VERIFICATION_FAILED 后旧版本仍可启动;所有占位成功清零;capabilities.query 不列出未注册服务。addJavascriptInterface,不在 JSON 中传递超过 64 KiB 的载荷,不引入 Rust/Node 中间 runtime,不写前端平台分支;不覆盖 26.11.5。
评论