本文档只描述当前仓库已经落地的真实能力,用于帮助维护者和人工 reviewer 在执行插件同步、启用、禁用、安装、卸载相关操作后,快速确认宿主侧元数据是否保持一致。
当前仓库仍然以第一期源码插件底座为主,但 apps/lina-plugins/ 下已经同时提供:
plugin-demo-source:源码插件样例plugin-demo-dynamic:动态插件样例
动态插件完整能力尚未交付,但宿主已经先补齐了后续阶段会复用的元数据表:
sys_pluginsys_plugin_releasesys_plugin_migrationsys_plugin_resource_refsys_plugin_node_state
这意味着,即使二三期 dynamic wasm、多节点热更新等能力还没有完成,reviewer 也已经可以通过数据库直接看到:
- 宿主当前识别到的插件注册状态
- 宿主认为当前生效的是哪个插件版本
- 插件安装/卸载迁移的执行记录与抽象执行键
- 宿主从插件目录中发现了哪些资源类型,以及对应的数量摘要与抽象标识
- 当前节点对插件状态的本地观测结果
源码插件通过 apps/lina-plugins/<plugin-id>/plugin.yaml 被宿主发现。
当前实际行为如下:
- 宿主扫描插件目录。
- 宿主校验
plugin.yaml、go.mod、backend/plugin.go、SQL 命名规则以及前端页面/Slot目录约定。 - 宿主同步
sys_plugin插件注册表;sys_plugin.version与release_id只表示当前生效版本。 - 宿主将当前清单基础字段和资源数量摘要写入
sys_plugin_release;如果源码目录发现了更高版本,则新 release 先保持prepared,不会自动切到当前生效版本。 - 宿主将目录发现到的资源类型与抽象 owner 信息写入
sys_plugin_resource_ref。 - 宿主将当前节点的插件状态投影写入
sys_plugin_node_state。
当前约束如下:
- 源码插件已经支持显式安装、卸载与升级治理;版本变化必须在开发阶段通过受支持的插件工作区更新流程处理。
- 如果某个已安装源码插件发现了更高版本但尚未完成显式处理,宿主启动会直接失败,并提示需要处理的插件。
- 源码插件禁用后,只会隐藏路由、菜单、页面和
Slot,不会删除历史业务数据。 - 当前仓库至少保留
plugin-demo-source与plugin-demo-dynamic两个样例目录,分别用于 source / dynamic 两种接入模式的 review。 - 当前源码样例
plugin-demo-source只保留一个左侧菜单页,不再额外演示登录页、工作台、CRUD 或右上角Slot扩展。
动态插件的完整执行模型仍在后续阶段,但宿主服务层已经先补齐了一部分基础记录能力。
当前已经落地的行为:
- 宿主已提供
POST /api/v1/plugins/dynamic/package上传入口,用于把 dynamic wasm 产物写入plugin.dynamic.storagePath。 - 运维也可以手工将
.wasm文件复制到plugin.dynamic.storagePath,再通过插件管理页执行同步识别。 - 宿主会扫描
plugin.dynamic.storagePath/*.wasm,并验证wasm文件头、自定义节和 ABI 版本。 - 若
wasm内嵌了lina.plugin.frontend.assets,宿主会直接从.wasm构建内存中的只读前端资源 bundle,而不是先提取到工作区目录。 - 当动态插件处于“已安装 + 已启用”状态时,宿主会通过
/plugin-assets/<plugin-id>/<version>/...对外公开这些静态资源。 - 当插件菜单的
path指向/plugin-assets/<plugin-id>/<version>/...时,宿主会继续复用现有菜单体系并按sys_menu.is_frame解释前端接入模式:is_frame = 0:在宿主主内容区以内嵌iframe方式打开托管页面。is_frame = 1:点击菜单后直接在新标签页打开托管页面。
- 当插件菜单额外声明
component = system/plugin/dynamic-page且query_param.pluginAccessMode = embedded-mount时,宿主会改走“宿主内嵌挂载”模式,并在dynamic-page壳内部动态导入对应的托管 ESM 入口。 - 若
wasm内嵌了lina.plugin.install.sql/lina.plugin.uninstall.sql,宿主会优先执行这些嵌入 SQL,并把执行结果记录到sys_plugin_migration。 - 当运行时
wasm未声明嵌入 SQL 时,安装/卸载生命周期仍会回退到目录约定 SQL。 - 当动态插件准备启用时,宿主会再次校验插件菜单引用的托管资源是否已经能从
.wasm中解析出来;若菜单指向不存在的/plugin-assets/...资源,或者宿主内嵌挂载入口未满足最小 ESM 契约,则启用会被拒绝。 - 安装完成后会同步当前版本快照、资源引用和节点状态。
- 卸载完成后会清理当前版本的资源引用、对应的内存前端资源缓存,并刷新节点状态。
当前尚未落地的行为:
- 真正的运行时装载、热升级、回滚和多节点代际切换
当前可以通过以下方式上传 dynamic wasm 插件包:
curl -X POST "http://127.0.0.1:8080/api/v1/plugins/dynamic/package" \
-H "Authorization: Bearer <token>" \
-F "file=@./plugin-demo-dynamic.wasm" \
-F "overwriteSupport=0"当前约束如下:
- 只能上传
.wasm文件。 - 宿主会从上传包中解析插件 ID,因此不接受额外手工指定插件 ID。
- 宿主会把上传产物规范化写入
plugin.dynamic.storagePath/<plugin-id>.wasm。 - 若同 ID 动态插件文件已存在,只有在
overwriteSupport=1且该插件尚未安装时才允许覆盖原文件。 - 若目标插件已经处于已安装状态,上传更高版本时会把新版本作为待切换 release 暂存;后续仍需通过 install 或 reconcile 流程完成正式升级。
- 若
storagePath下存在多个嵌入相同 plugin ID 的.wasm文件,宿主会在同步阶段直接报冲突错误,要求先人工清理。
当前仓库中的动态样例插件不再提交编译后的 .wasm 文件,而是统一通过通用命令生成:
make wasm
make wasm p=plugin-demo-dynamic运维与 review 时需要注意:
- 运行
make dev或make build前后,宿主都会复用同一个通用构建入口 - 如需单独验证某个动态样例目录,优先使用
make wasm p=<plugin-id> - 通用构建入口当前由根级
hack/tools/build-wasm/独立 Go 工具负责,该工具自身维护go.mod,不依赖主服务模块 - 标准仓库构建流程会把动态插件产物和 guest runtime 中间
wasm统一收敛到仓库根temp/output/,不再回写apps/lina-plugins/*/temp/
如果动态插件声明了 lina.plugin.frontend.assets,宿主会在启动预热或首次访问时直接从 plugin.dynamic.storagePath 中的 .wasm 解析这些资源到内存 bundle,并通过下面的稳定地址对外提供:
http://127.0.0.1:8080/plugin-assets/<plugin-id>/<version>/<relative-path>
运维和 review 时需要注意:
- 资源路径中的
<version>必须与当前插件版本一致。 - 插件未安装、未启用、已禁用或已卸载时,该 URL 应返回不可访问。
- 服务重启后,宿主会优先预热“已安装 + 已启用”的 runtime 资源;若预热失败,请求链路仍会按需从
.wasm懒加载。 - 若要走菜单驱动访问,插件菜单 SQL 需要把
path直接写成/plugin-assets/<plugin-id>/<version>/...。 is_frame = 0与is_frame = 1当前分别对应iframe与新标签页两种访问模式,review 时应同时核对菜单记录和最终页面行为。- 若要走宿主内嵌挂载,菜单还必须声明:
component = system/plugin/dynamic-pagequery_param至少包含{"pluginAccessMode":"embedded-mount"}
- 宿主会把原始托管资源地址透传为
embeddedSrc查询参数,并在dynamic-page壳中按最小 ESM 契约导入该入口。 - 当前 ESM 契约至少要求入口导出
mount(context);unmount(context)、update(context)为可选能力。 - 若菜单选择宿主内嵌挂载模式,则入口文件扩展名当前必须为
.js或.mjs。 - 前端资源缓存只是宿主运行态优化,真正的治理依据仍应以
plugin.dynamic.storagePath下的.wasm、sys_plugin_release、sys_plugin_resource_ref和当前插件状态为准。
建议人工 review 按下面顺序核对。
核对 sys_plugin:
plugin_id、name、version、type是否与源码插件清单或 dynamic wasm 嵌入清单一致installed、status是否与本次操作预期一致- 对源码插件,
manifest_path应指向正确的plugin.yaml - 对动态插件,
manifest_path当前允许为空,因为宿主以.wasm嵌入清单为真相源
建议查询:
SELECT plugin_id, version, type, installed, status, manifest_path, installed_at, enabled_at, disabled_at
FROM sys_plugin
ORDER BY plugin_id;核对 sys_plugin_release:
release_version是否与当前生效版本一致runtime_kind对动态插件应为wasmstatus是否符合当前生命周期动作(如active、installed、uninstalled)manifest_snapshot中应包含基础清单字段和资源数量摘要,而不是具体文件路径- 若为动态插件,
manifest_snapshot中应额外包含runtimeKind、runtimeAbiVersion、runtimeFrontendAssetCount、runtimeSqlAssetCount manifest_snapshot中的installSqlCount、uninstallSqlCount、frontendPageCount、frontendSlotCount是否与目录实际情况一致- 若动态插件声明了嵌入 SQL,则
installSqlCount/uninstallSqlCount当前表示宿主最终会执行的 SQL 资源数量,不再局限于目录扫描结果
建议查询:
SELECT plugin_id, release_version, type, status, manifest_path, package_path, checksum
FROM sys_plugin_release
ORDER BY plugin_id, release_version;在动态插件安装/卸载链路中,核对 sys_plugin_migration:
phase只能是install或uninstallmigration_key应为抽象执行键,例如install-step-001status = succeeded表示执行成功- 如果 SQL 内容变更,
checksum也应随之变化
建议查询:
SELECT plugin_id, release_id, phase, migration_key, execution_order, status, error_message, executed_at
FROM sys_plugin_migration
ORDER BY plugin_id, release_id, phase, execution_order;核对 sys_plugin_resource_ref:
- 每个发现到的资源都应有稳定的
resource_type与resource_key resource_path当前应保持为空字符串,或仅作为宿主抽象定位补充信息,不允许保存具体前端/SQL 文件路径remark应描述数量摘要或宿主识别结论,便于 review 快速判断发现结果- 当前源码插件快照至少应覆盖:
manifestbackend_entryinstall_sqluninstall_sqlfrontend_pagefrontend_slot
- 当前动态插件快照至少还应覆盖:
runtime_wasmruntime_frontend(当运行时产物声明了前端静态资源时)
建议查询:
SELECT plugin_id, release_id, resource_type, resource_key, resource_path, owner_type, owner_key, remark
FROM sys_plugin_resource_ref
ORDER BY plugin_id, release_id, resource_type, resource_key;核对 sys_plugin_node_state:
node_key应为当前宿主节点主机名release_id应指向当前节点观测到的发布记录desired_state与current_state应与installed/enabled组合保持一致generation不应回退;当前实现至少与release_id对齐或更大
当前映射规则:
installed = 0->uninstalledinstalled = 1且enabled = 0->installedinstalled = 1且enabled = 1->enabled
建议查询:
SELECT plugin_id, release_id, node_key, desired_state, current_state, generation, last_heartbeat_at, error_message
FROM sys_plugin_node_state
ORDER BY plugin_id, node_key;因为当前插件设计明确要求 plugin.yaml 保持最小化。页面、Slot、SQL 等信息应由真实目录结构推导,而不是在清单里重复维护一份配置,避免双真相源。
因为人工 review 不能只依赖磁盘上的当前文件,还需要一个“宿主在某次同步时到底看到了什么”的持久化快照。但当前快照只保存基础清单字段和数量摘要,不保存具体 SQL 文件路径或前端源码路径,避免把框架实现细节硬编码进治理表。
因为后续多节点阶段一定需要宿主持久化节点侧观测结果。现在先把表结构补齐,可以避免后续继续改底层元数据模型,也能让当前人工 review 立即获得一个节点视角的检查入口。
- 宿主当前还没有把“源码插件目录被删除”自动回收进注册表同步逻辑中。
- 宿主当前还没有把插件菜单 SQL 进一步解析成
menu_key级别的宿主资源引用记录。 - 动态插件的真实产物上传、装载隔离和热升级仍未实现。
- 仓库当前不提供插件打包脚本;这是有意保留的约束,避免出现与真实实现脱节的辅助脚本。
如果人工 reviewer 只想判断“除第三期外,当前基础工作是否已全部完成”,应使用以下口径:
- 不应再把低 ROI 的模板/打包脚本缺失视为当前基础能力阻塞项。
- 不应把依赖第三期热升级/代际切换的验收项误计入当前基础收尾缺口。
- 当前仍然真正阻塞基础收尾的,主要是 runtime
wasm的真实执行与隔离能力,以及与之对应的失败隔离/权限治理验收闭环。