插件开发套件
包含统一 SDK、三种起步模板和软算法工具完整源码,可自行构建安装包。
正在准备工作区,请稍候
跟着可运行的示例,从第一次安装开始。框架负责权限、存储与执行,你专注于业务。
包含统一 SDK、三种起步模板和软算法工具完整源码,可自行构建安装包。
先选一种插件形态,再准备工具。完成后你会得到一个可以构建、安装和修改的项目。
能在终端运行 Node.js,并找到模板项目。
只给用户操作,选择 ui;只提供 AI 工具,选择 ai;既有界面又有 AI 工具,选择 full。下面全程以 full 文本统计插件为例。三个模板都已经能运行,不需要从空白项目开始。
点击上方「开发套件」下载按钮,解压到你自己的开发目录。保留 packages 目录结构。使用 VS Code、Cursor 或其他编辑器打开整个解压目录,而不是只打开一个文件。
需要 Node.js 20 或以上版本、npm,以及已登录的 LIMINA 桌面客户端。终端位于刚才解压的目录。
示例 / 终端node --version
npm --version两条命令都返回版本号。若提示 command not found,请先从 nodejs.org 安装 Node.js LTS 后重新打开终端。
示例 / 终端packages/
plugin-sdk/ 官方 SDK 与校验器
plugin-starters/ 起步模板(新插件从这里开始)
ui/ 纯人工界面
ai/ 纯 AI 工具
full/ UI + AI(本教程使用)
plugin-examples/ 完整示例(参考实现与构建方式)
soft-crypto/ 官方插件 Demo(独立参考)不需要修改 plugin-sdk;业务代码写在你选择的模板里。
框架负责安装、权限、实例、隔离执行与存储;开发者负责界面和业务逻辑。当前 SDK 0.7.0 对应插件契约 v2,新插件按 v2 开发。开发套件以源码交付,尚未发布到 npm 公共仓库。
cd packages/plugin-starters/full npm install npm run build npm run check
在桌面客户端「插件 → 已安装」点击 +,导入生成的 starter_full.limina-plugin.zip。修改代码后重新构建,增加版本号并通过同一个「安装或更新插件」按钮导入。同 ID 直接替换,校验失败时旧版保留。
开发套件还包含 packages/plugin-examples/soft-crypto 官方国密源码。软算法构建另需 Python 3;在该目录执行 npm ci 和 npm run build(先在 packages/plugin-sdk 执行 npm ci)。菜单与会话使用各自的界面,依赖会离线打包。
| 层 | 职责 |
|---|---|
| 宿主框架 | 账号会话隔离、安装更新卸载、参数校验、实例版本、事务存储、工具桥和沙箱。 |
| UI SDK | 统一注入 window.limina,提供 execute、onState、copy、download、storage,含超时及订阅取消。开发包提供类型声明和 getClient()。 |
| 构建 CLI | 打包清单、Worker 与 HTML,构建和安装复用同一校验器。check 验证契约,不代替业务测试。 |
| 开发者 | 业务算法、界面、输入输出、模型字段白名单与依赖许可证。 |
plugin.json 构建配置:manifest / code / views / licenses / output manifest.json 插件清单与能力声明 operations.js Worker 业务入口 execute(input) standalone.html 菜单工作台 session.html 会话项目侧栏
基础模板不要求 UI 框架。使用 Vue、React、TypeScript 或 npm 依赖时,先构建成内联 HTML 与单个经典 Worker 脚本,再交给 CLI;CLI 不编译这些语言,不提供运行时模块加载。软算法实例展示 esbuild 用法。
/// <reference types="@limina/plugin-sdk" />
// 中文:构建型 UI 可导入客户端。
// English: Bundled UI can import the client.
// import { getClient } from '@limina/plugin-sdk'
const stop = limina.onState(instance => render(instance.result));
// 中文:组件卸载时调用 stop()。
// English: Call stop() on component teardown.纯 HTML 直接使用 limina,不要加载 bridge.js。SDK 由宿主注入,插件不能选择身份或任意实例。SDK 与插件各自版本管理,contract_version:2 是契约边界。当前不提供热重载、远程执行、自动数据迁移。
| 身份 | 入口 | 默认边界 |
|---|---|---|
| standalone | 菜单人工工作台 | 菜单实例 |
| session | 会话侧栏人工操作 | 当前会话实例 |
| ai | 会话工具桥 | 当前会话获授权工具 |
窗口位置不改变身份:从会话弹出独立窗口仍是 session。身份由宿主指定,不可通过 UI 消息或工具参数伪造。AI 不能操作菜单实例。业务工具改变实例后,插件通过 onState 自行更新界面;显式声明 ui_control:1 后,可通过独立的 local.<id>.ui 工具操作会话工作台。
{
"format":"limina.plugin.v2",
"manifest":{
"id":"example", "title":"示例", "version":"1.0.0", "contract_version":2,
"permissions":[],
"ui":{
"standalone":{"entry":"workbench","operations":["calculate"]},
"session":{"entry":"card","operations":["calculate"]}
},
"ai":[{"operation":"calculate","description":"计算内容"}],
"operations":{"calculate":{
"callers":["standalone","session","ai"],
"input_schema":{"type":"object","properties":{},"additionalProperties":false},
"model_fields":["output"]
}}
},
"views":{"workbench":"工作台 HTML","card":"会话卡 HTML"},
"code":"async function execute(input){return {result:{output:'完成'}}}"
}两套 UI 可完全不同,也可使用同一 entry。仅 UI 省略 ai;仅 AI 省略 ui/views。会话界面仅用于展示 AI 结果时,将 session.operations 设为 [],人工执行会被宿主拒绝。只供人工的操作不写入 ai;人工和 AI 共用则同时授权。每包最多 2 MB,账号本机最多 32 个 AI 工具;ui_control 额外占用一个工具名额。
limina.onState(instance => render(instance.result));
await limina.execute('calculate', {input:'abc'});
await limina.copy('用户点击后复制的结果');
const saved = await limina.storage.get('preferences');
await limina.storage.set('preferences', {algorithm:'SHA-256'}, saved.revision);SDK 绑定当前插件、实例和入口,不接受任意实例 ID。$clear 仅允许有人工操作权限的 UI 调用。错误须展示;冲突需重新读取,不自动覆盖。
execute({operation, arguments, state, storage, context}) 返回 {result, storage?}。context 包含宿主指定的 actor、taskId。人工操作可读当前 state;AI 默认不接收旧 state,操作显式声明 state_read:true 才允许。model_fields 为 result 顶层字段白名单,只有这些字段进入模型,开发者须避免把秘密混入允许字段。v2 不持久化原始操作参数。
input_schema 由 AJV 验证,Worker 默认 10 秒,可通过每个操作的 timeout_ms 声明 1000–110000 毫秒,输入由 input_schema 与传输边界约束,Worker 返回结构由宿主校验,不对模板附加固定字节上限。只能使用包内代码;不支持运行时 import、require 或外部脚本。依赖需提前打包,如本例使用 esbuild 打包 Noble 算法。
短期实例默认一小时过期。命名数据区独立持久保存偏好、备注等 JSON,容量由浏览器存储配额管理。按设备、站点、账号、后端和插件隔离,session 区额外按会话隔离。没有跨设备同步。若业务按用户要求保存密钥,应明确告知并限制为人工可读;这不是系统钥匙串。
"storage": {
"preferences":{"scope":"standalone","readers":["standalone"],"writers":["standalone"]},
"session_data":{"scope":"session","readers":["session","ai"],"writers":["session","ai"]},
"shared":{"scope":"shared","readers":["standalone","session"],"writers":["standalone"]}
}standalone 区禁止授权会话和 AI;session 区禁止授权菜单;shared 仅在显式授权身份间共享,不跨账号或插件。除了数据区 readers/writers,操作或 UI 入口还须声明 storage:{read:[区名],write:[区名]}。两层权限同时满足才可访问。AI 若需共享数据必须两层都明确授权,不会自动开放。
代码的 storage 仅含当前操作可读的数据。返回 storage:{session_data:新值} 写回可写区;实例和数据在同一事务提交,检查全部读写版本,冲突则全部取消。UI storage.set 必须传读取到的 revision,值为 null 可清空。
重启保留持久数据;同 ID 更新保留相同数据契约的持久数据,清除旧实例。修改存储契约时拒绝直接更新,需要先由插件提供导出流程再卸载重装;当前没有自动 schema 迁移或通用导入导出 UI。卸载删除插件、本机实例和持久数据,聊天历史保留。清理客户端网站数据也会移除数据。
UI 位于不透明来源 sandbox iframe,工具位于限时 Worker。CSP 禁止网络、外部资源、表单和嵌套页面;不开放宿主 DOM、直接本地存储、原生 IPC、Shell、任意文件。离线算法插件的 permissions 为空;加密机通信可声明 network.hsm,并在操作中指定 host: "hsm",仅通过已授权节点执行受控收发。插件仍是用户主动信任的代码,白名单不能阻止作者主动把私有值写入允许字段。
会话注册 limina.bridge.v1;服务端仅转发调用并接收允许结果。每次执行核对账号、插件、会话、安装代次、身份与 revision。关闭客户端不会转为服务器执行。
开发验收须覆盖:三种身份允许与拒绝、伪造实例 ID、跨账号和会话、数据区显式授权、并发冲突、更新卸载、模型字段、主题与窄侧栏、键盘、错误反馈、减少动态效果。不能只验证菜单按钮能点击。
构建默认生成 .limina-plugin.zip,客户直接导入,不需要解压。ZIP 中分别保存 plugin.json 包入口、manifest.json 权限清单、views/ 界面、tools/worker.js 工具代码和 LICENSES.txt。源码包用于学习和构建,不能当作安装包。
压缩前后均不超过 2 MiB,最多 64 个文件和目录;拒绝路径穿越、加密文件、符号链接和重名文件。旧版 JSON 安装包继续兼容。界面资源和工具依赖需要在构建阶段打包,运行时不加载外部文件或网络依赖。
人工点击后调用 await limina.download('result.txt', text, 'utf8');二进制使用 Base64 和 base64 参数。导出使用全局下载列表、进度、取消与 Mac 保存回执,最多 256 KB。不上传数据,AI Worker 无权调用。
Call download from a human click. UTF-8 or Base64 data uses global downloads and native save receipts, up to 256 KB. UI only; no upload.
Worker 可使用 crypto.getRandomValues 与 crypto.subtle。subtle 缺失时宿主提供受限本机桥,密钥仅本轮有效;每次最多 128 次调用、8 并发、32 密钥,仍受当前操作声明的运行时限限制(默认 10 秒)。不开放网络、存储或宿主凭证。
Opaque Workers use a bounded local crypto bridge when needed. Execution-scoped keys, 128 calls, 8 concurrency, 32 keys and the operation’s declared deadline (10 seconds by default). No host credentials, network or storage.
SDK 0.3.0:普通数据区容量由浏览器存储配额管理;kind: "records" 保存独立记录,单条最多 256 KB。scope: "context" 分隔工作台与每个会话。AI 业务操作可获追加权限,不读取或删除历史;显式授权的 UI 控制可以使用界面已有的历史操作;人工界面需同时声明数据区和入口读写权限。
SDK 0.3.0: ordinary areas use the browser storage quota; records collections hold entries up to 256 KB each. Context scope isolates the workbench and each session. Business tools can append authorized records. Explicit UI control can use the workspace history handlers. Human views require both area and entry grants.
"history": {
"kind": "records", "scope": "context",
"readers": ["standalone", "session"],
"writers": ["standalone", "session", "ai"]
}// 中文:操作返回的记录与结果一起原子提交,失败不留下半条记录。
// English: Records and results commit atomically; failures leave no partial entry.
return {result: {output}, records: [{area: "history", title: "文本处理", value: {input, output}}]};
// 中文:人工界面先读取索引,再按 ID 读取完整内容。
// English: Human UI reads metadata first, then the complete entry by ID.
const page = await limina.storage.list("history", {limit: 20});
const record = await limina.storage.read("history", page.items[0].id);
// 中文:删除指定记录;null 表示清空当前上下文,界面应先确认。
// English: Delete one record; null clears the current context after UI confirmation.
await limina.storage.remove("history", record.id);list 返回 items、total、next,最多 50 条;将 next 作为下一页 before。read 返回元数据和 value;remove 返回 removed。不要把密钥放进标题或 model_fields。历史在兼容更新后保留;卸载会删除。
list returns items, total and next (up to 50 entries); pass next as before for pagination. read includes value; remove returns removed. Never include keys in titles or model_fields. Compatible updates preserve history; uninstall removes it.
所有插件均可按操作设置 timeout_ms,无需修改宿主。它属于 manifest.operations 的操作定义,不是工具调用参数。CLI 与客户端安装使用同一校验器:只接受 1000–110000 的整数,省略时默认 10000 毫秒。人工、会话侧栏、独立窗口和 AI 共用运行规则。
{
"operations": {
"analyze": {
"callers": ["standalone", "session", "ai"],
"timeout_ms": 60000,
"input_schema": {
"type": "object",
"properties": {"text": {"type": "string", "maxLength": 10000}},
"required": ["text"], "additionalProperties": false
},
"model_fields": ["characters", "lines"]
}
}
}示例允许最多 60 秒执行;AI 自动等待 70 秒,UI 等待 75 秒,预留通信和结果保存时间。到时清理 Worker 和 crypto 桥,不保存失败结果。更改清单并重新安装插件后生效。网络、系统权限、输入输出大小限制保持不变。
Any plugin can declare a per-operation timeout in its installed manifest. Valid range: 1000–110000 integer milliseconds; default: 10000. All callers share the same execution limit. The host adds 10 seconds for AI transport and another 5 seconds for UI waiting. Update and reinstall the manifest to change the budget. Timeouts terminate isolated execution without saving a result; network and system permissions are unchanged.
声明 permissions: ["network.hsm"],操作指定 host: "hsm"。人工与 AI 共用封闭 input_schema。宿主自动绑定当前客户端的本机节点,直接 connect / exchange / receive / disconnect;连接按账号、插件和会话隔离。网络请求有实际副作用,不自动重发。插件不能获得任意 Fetch、Shell 或文件访问。
TCP、TLS、GMTLS,支持双向 PFX 或 PEM 证书、0–4 字节大端长度头、0–127 字节消息头、Hex / UTF-8 / GB18030 / UTF-16BE / Base64。Hex 混合发送支持 # 注释、@ UTF-8、$ GB18030、* UTF-16BE;发送前可核对完整报文。GMTLS 需要执行节点安装 Tongsuo,安装说明位于开发套件的 HSM_SETUP.md。
证书和私钥由用户显式提供,经平台转发到本机节点;不会写入插件历史或 AI 结果。业务工具结果不返回密钥、证书和日志;UI 控制须另外标记敏感控件与展示区域,不能用业务工具白名单代替 UI 敏感信息保护。连接空闲 10 分钟释放,TLS 默认校验证书。
业务工具 local.<id>.<operation> 直接执行业务,不模拟点击。宿主提交结果并通知 onState;切换面板、回填输入和展示结果由插件实现。起步模板只展示 result JSON,软算法的完整回填是插件参考实现。
在 v2 清单顶层声明 "ui_control": 1 并提供 ui.session,宿主自动登记 local.<id>.ui。先 inspect,再按返回的 view、revision、target 执行 read、set、activate、upload 或 key。set 只改输入,activate 复用人工按钮处理器;ui 是保留操作名。
异步处理器须返回完整 Promise。导入文件用 limina.ui.bindFile,自定义控件用 limina.ui.registerAdapter。敏感字段及展示区域标记 data-ui-sensitive;model_fields 只过滤业务结果,不限制 UI 可读内容。coverage 不是全部业务已验收的证明。
UI 控制优先复用当前会话已移出的工作台。业务成功不等于界面渲染完成,has_ui/presented_at 不是渲染回执;UI 的 applied 不等于设备业务成功,须检查 effects 和结果快照。冲突、人工交错或超时后先 inspect,不盲目重试。
完整回填示例、草稿冲突策略与验收清单位于开发套件 packages/plugin-sdk/UI_SYNC.md。