Skip to content

插件 API 完整参考

插件脚本通过全局对象 window.__PLUGIN_API__ 与启动器交互。本文档列出全部可调用方法、签名、权限要求与示例。

调用方式

js
const __PLUGIN_API__ = window.__PLUGIN_API__

// ① 通用 call 方式(绝大多数方法)
const data = await __PLUGIN_API__.call('getSettings')

// ② 专用快捷方式(仅以下 3 个)
await __PLUGIN_API__.registerMethod('name', fn)      // 注册方法
await __PLUGIN_API__.callPlugin('id', 'method', ...) // 调用其他插件方法
await __PLUGIN_API__.proxyFetchStream(req, handlers) // 流式请求

权限机制

每个 API 方法对应一个权限。调用时启动器会检查插件 manifest 的 permissions 是否包含该权限,否则报错:

Permission denied: requires <权限id>

完整权限表

权限 ID中文风险
instance:read读取实例列表普通
instance:write创建/修改/删除实例警告
account:read读取账号列表普通
license:read读取许可证信息普通
config:read读取启动器配置普通
config:write修改启动器配置警告
cache:access读写插件缓存普通
endpoint:discover获取后端 API 端点普通
page:list获取页面列表普通
network:fetch发送 HTTP 请求警告
network:cors_proxyCORS 代理请求警告
network:websocketWebSocket 连接警告
network:proxy修改代理设置警告
ui:inject_sidebar注入侧边栏菜单普通
ui:inject_settings注入设置页普通
ui:picture_in_picture画中画窗口警告
ui:sub_window独立子窗口警告
ui:context_menu注入右键菜单普通
ui:toast应用内通知普通
ui:navigate跳转页面普通
system:info读取系统和启动器信息普通
system:notification发送系统通知普通
clipboard:read读取剪贴板警告
clipboard:write写入剪贴板警告
wasm:execute执行 WASM 模块警告
plugin:install安装/卸载/更新插件危险
resource:read读取游戏资源文件普通
resource:write写入游戏资源文件警告
java:manage管理 Java 运行时警告
game:process启停游戏进程警告
game:log检测游戏日志普通
connector:host启停联机警告
connector:scan扫描局域网联机普通
shell:execute执行系统命令危险
filesystem:read读取文件系统警告
filesystem:write写入文件系统危险

TIP

「风险」用于安装详情弹窗的视觉提示:普通=蓝、警告=黄、危险=红。声明权限时遵循最小权限原则,只声明你真正用到的。

方法参考

getSettings — 读取插件配置

读取插件自己的 settings.json

js
const settings = await __PLUGIN_API__.call('getSettings')
  • 权限:config:read
  • 返回:Record<string, unknown>(无文件时返回 {}
  • 存储位置:{数据目录}/plugins/{插件id}/settings.json

setSettings — 写入插件配置

按 key 合并写入配置(合并而非覆盖整个文件)。

js
await __PLUGIN_API__.call('setSettings', 'theme', 'dark')
  • 权限:config:write
  • 参数:(key: string, value: any),value 可为任意 JSON 值
  • 并发写入由后端加锁保护

setCache / getCache — 插件缓存

按 key 读写插件自己的缓存文件,支持 TTL 过期。

js
// 写缓存,TTL 1 小时(3600 秒)
await __PLUGIN_API__.call('setCache', 'modelList', { items: [...] }, 3600)

// 读缓存(不存在或过期返回 null)
const cached = await __PLUGIN_API__.call('getCache', 'modelList')
  • 权限:cache:access
  • 存储位置:{数据目录}/plugins/{插件id}/cache.json
  • 内部结构 { key: { v: 值, e: 过期时间戳|null } },无需插件关心

callBackend — 调用启动器后端 API

直接请求启动器后端(http://localhost:5000/api/...),绕过 CORS 限制。

js
const instances = await __PLUGIN_API__.call('callBackend', '/instance')

const result = await __PLUGIN_API__.call('callBackend', '/resource-download/start', {
  instanceId: 'xxx', url: 'https://...', fileName: 'mod.jar', category: 'mods'
})
  • 权限:network:fetch
  • 参数:(endpoint: string, data?: any)
    • data → POST;无 data → GET
    • endpoint/ 开头(如 /instance),不含 /api 前缀
  • 返回:后端 JSON 响应;非 2xx 抛错
  • 可用端点清单:见启动器后端 OpenAPI(开发模式 /openapi/v1.json),常用如 /instance/resources/search/settings

在沙箱中打开外部链接

L2 沙箱(sandbox="allow-scripts",无 allow-popups)里 window.open(url, '_blank') 会被拦截。要打开外部链接,应通过后端端点:

js
await __PLUGIN_API__.call('callBackend', '/system/open-url', { url: 'https://example.com' })

该端点使用系统默认浏览器打开 http/https 链接(仅接受这两种协议,其余返回 400)。

proxyFetch — CORS 代理请求

经启动器后端转发请求任意外部 URL,绕开浏览器 CORS 限制,且带 SSRF 防护。

js
const res = await __PLUGIN_API__.call('proxyFetch', {
  url: 'https://api.example.com/data',
  method: 'GET',
  headers: { 'Accept': 'application/json' },
  timeoutMs: 15000
})
if (res.status === 200) {
  const data = JSON.parse(res.body)   // 文本响应
}
  • 权限:network:cors_proxy
  • 请求 ProxyRequest
    ts
    {
      url: string                // 必填,http/https
      method?: string            // 默认 GET
      headers?: Record<string, string>
      body?: string              // POST body(字符串)
      timeoutMs?: number         // 默认 15000,范围 1000–60000
    }
  • 响应 ProxyResponse
    ts
    {
      status: number
      headers: Record<string, string>
      body?: string | null       // 文本响应
      bodyBase64?: string | null // 二进制响应
    }
  • SSRF 防护:仅 http/https;禁止内网/保留地址(localhost、127.x、10.x、172.16-31.x、192.168.x、169.254.x 等),返回 PROXY_PRIVATE_ADDRESS(400)

proxyFetchStream — 流式代理请求

消费 SSE 流(如 AI 对话逐字输出),逐块回调。

js
await __PLUGIN_API__.proxyFetchStream(
  {
    url: 'https://api.deepseek.com/v1/chat/completions',
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + key },
    body: JSON.stringify({ model, messages, stream: true }),
    timeoutMs: 120000,
    signal: abortController.signal    // 可选,中断流
  },
  {
    onChunk(chunk) {
      // chunk 为 SSE data: 行内容(已去 data: 前缀)
      const json = JSON.parse(chunk)
      const delta = json.choices?.[0]?.delta?.content
      if (delta) appendText(delta)
    },
    onError(err) { console.error(err) }
  }
)
  • 权限:network:cors_proxy
  • 后端自动带 stream: true 转发上游流
  • signal(AbortSignal)用于中断;req.signal 经 postMessage 前自动剥离,改走 __plugin_api_abort 消息
  • ⚠️ 已知限制proxyFetchStream 返回的 Promise 在流结束时 resolve,但 chunk 回调可能在 resolve 之后仍触发,建议以 onError / 流内 [DONE] 标记判断完成

registerMethod — 注册插件方法

将当前插件的方法注册到全局注册表,供其他插件通过 callPlugin 调用。

js
__PLUGIN_API__.registerMethod('renderMarkdown', function (md) {
  return marked.parse(md || '')
})

// 支持异步方法
__PLUGIN_API__.registerMethod('fetchTranslation', async function (text) {
  const res = await fetch('https://api.example.com/translate?text=' + encodeURIComponent(text))
  return res.json()
})
  • 权限:config:write
  • 参数:(method: string, fn: Function),fn 返回值为值或 Promise
  • 插件停用时自动注销所有方法
  • 详细见 插件依赖与互调用

callPlugin — 调用其他插件方法

调用目标插件已注册的方法。

js
try {
  const html = await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'renderMarkdown', '**加粗**')
  document.getElementById('out').innerHTML = html
} catch (e) {
  console.error(e.message)   // 目标未安装/未激活/未注册都会 reject
}
  • 权限:network:fetch
  • 参数:(pluginId: string, method: string, ...args)
  • 目标未安装 / 未激活 / 方法未注册 → reject

callWasm — 调用 WASM 插件导出函数

调用 L3 WASM 插件导出的函数(on_load / on_unload / 自定义导出)。经启动器 Rust 网关(wasmtime)执行。

js
const result = await __PLUGIN_API__.callWasm('dev.example.wasmplugin', 'on_load')
  • 权限:wasm:execute
  • 参数:(pluginId: string, exportName?: string)exportName 缺省 on_load
  • 返回:{ ok: true, result: ... } 或错误
  • 详细见 WASM 插件

listWasmPlugins — 列出已加载的 WASM 插件

js
const ids = await __PLUGIN_API__.listWasmPlugins()   // ['dev.example.wasmplugin', ...]
  • 权限:wasm:execute

跳转到启动器内部路由。

js
await __PLUGIN_API__.call('navigate', '/settings')
  • 权限:config:read
  • 注意:应使用启动器内部路由,勿用外部 URL

showToast — 应用内通知

弹出一条 toast 提示。

js
await __PLUGIN_API__.call('showToast', '操作成功', 'success')
  • 权限:ui:toast
  • 参数:(message: string, type?: 'info' | 'error' | 'success'),默认 info

overlay.create — 创建悬浮窗

创建一个可拖拽的独立悬浮窗,返回悬浮窗 id。

js
const overlayId = await __PLUGIN_API__.call('overlay.create', {
  title: '我的悬浮窗',
  html: '<div class="p-card">你好</div>',
  x: 120, y: 80,
  width: 320, height: 240,
  minimizable: true,
  resizable: true
})
  • 权限:ui:sub_window
  • 返回:悬浮窗 id(string)
  • 详细见 悬浮窗开发

overlay.show / hide / destroy — 悬浮窗控制

js
await __PLUGIN_API__.call('overlay.show', overlayId)    // 显示(含最小化恢复)
await __PLUGIN_API__.call('overlay.hide', overlayId)    // 隐藏(最小化)
await __PLUGIN_API__.call('overlay.destroy', overlayId) // 销毁
  • 权限:ui:sub_window

overlay.setHtml — 更新悬浮窗内容

js
await __PLUGIN_API__.call('overlay.setHtml', overlayId, '<p>新内容</p>')
  • 权限:ui:sub_window

overlay.setPosition — 移动悬浮窗

js
await __PLUGIN_API__.call('overlay.setPosition', overlayId, 300, 200)
  • 权限:ui:sub_window

错误处理

所有 API 调用失败会 reject,错误信息形如:

js
try {
  await __PLUGIN_API__.call('getSettings')
} catch (e) {
  console.error(e.message)
}

常见错误:

  • Permission denied: requires xxx — manifest 权限未包含
  • Backend error: 404callBackend 端点不存在
  • Proxy failed: 400proxyFetch 参数错误(含 SSRF 拦截)
  • 插件 xxx 未提供方法 yyy(可能未安装或未激活)callPlugin 目标不可用