Skip to content

feat(windows): 声明式插件宿主(契约 v1)——包存储、安装链、设置、运行计划与 Web 工具窗口 - #1424

Open
Mucheen wants to merge 11 commits into
previewfrom
feat/windows-declarative-plugin-host
Open

Mucheen wants to merge 11 commits into
previewfrom
feat/windows-declarative-plugin-host

Conversation

@Mucheen

@Mucheen Mucheen commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

改动说明

按 Agent Note(本 PR 内同增)落地 Windows 声明式插件宿主第一个里程碑。Windows 从此可以加载契约 v1(shared/contracts/plugins/v1)格式的语言插件包,与 macOS / Lithe Store 同格式;现有 Worker 扩展模型不受影响。

功能点

Rust 宿主(windows/tauri/src-tauri/src/plugin_host/,新增模块)

  • store.rs:包存储布局。已安装包在 app_local_data_dir()/plugins/<pluginID>/<version>/ + installation.json(记录缺失/损坏/版本不匹配的包一律隔离,不静默加载);bundled 包从资源目录 OfficialPlugins 只读加载,多版本取最高;数字感知的版本比较(1.10 > 1.9、prerelease 排在正式版前);插件状态目录与稳定的工作区哈希。
  • scan.rs:启动扫描。每个包的 plugin.json 经 lithe_core::execute_json 走 plugins.declarative.validate,payload 携带宿主能力声明 hostCapabilities = {languages:[1], languageServers:[1], serverProcess:[1]}——声明了更高能力(settings 2 / ui 2 / buildSystems)的包由 Core 以 plugin.api.unsupported 自动拒绝,宿主零额外逻辑;拒绝的包隔离并向前端暴露稳定错误码。
  • launch.rs:启动计划解析。调 plugins.declarative.languageServerLaunchPlan 把声明展开成进程命令行:packaged 入口必须是包内普通文件(拒绝 reparse point/符号链接与规范化路径逃逸);runtime/systemCommand 经 PATH 探测(复用 language_tools 的查找),导出 macOS 同款 LITHE_<TOOL>_PATH 并把 packaged runtime 目录前置到 PATH。语言服务器进程本身仍由共享 Core 的 lsp.startServer 拉起,会话所有权不分裂。
  • tests.rs:共享 fixtures 全量验收——invalid-manifests.json 全部 154 个用例在 Windows 二进制内断言同一稳定错误码(矩阵与 macOS Note 定义的接入验收线);合法样例分"纯契约"与"v0 能力门控"两种模式断言。

前端

  • features/plugins/declarative-plugin-launch.ts:宿主 API 薄封装(rescan / state / launch)。
  • features/editor/lsp/resolve-editor-lsp-launch.ts:Worker 扩展清单没有对应服务器时,回落到契约插件启动(契约插件排在 Worker 之后,已由 Worker 声明的语言保持现状直到 M3 包迁移)。
  • features/bootstrap/bootstrap-async.ts:bootstrap 增加 "declarative plugins" 扫描步骤。
  • Tauri 命令:declarative_plugins_rescan / declarative_plugins_state / declarative_plugins_language_launch。

文档与矩阵

  • 新增 Agent Note《Windows 声明式插件宿主》(已实现):双模型边界判定句、能力渐进开放、v0 信任模型(bundled + 全量审批,Ed25519 验签显式延后)、M2–M4 切分。
  • 矩阵 declarative-plugin-contract.json Windows:evidence 换成 Windows 实现文件 + fixtures,状态 missing→partial(pending),notes 记录已实现与未实现边界。

明确未做(后续里程碑)

  • M2:磁盘导入/市场下载安装链(快照、installReview 全量审批、staging 重验)、minimumVersion 探测、管理界面、install_extension_from_url 死链处置。
  • M3:声明式设置、运行/测试计划、PHP Worker 包→契约包迁移。
  • M4:Web 工具窗口(ui 2 / WebView2)。
  • litheFunctions 不在本宿主范围(两套协议不桥接)。

验证

  • cargo test plugin_host:12/12(含 154 个 fixture 用例);src-tauri 全量 cargo test:308 passed, 0 failed
  • bun test resolve-editor-lsp-launch.test.ts:5/5;workspace-git-bootstrap.test.ts:5/5;bun run typecheck 通过
  • node scripts/verify-agent-notes.mjs(130 篇)、generate-platform-feature-matrix.mjs + verify-platform-feature-matrix.sh(188 能力点)、verify-windows-boundaries.sh 均通过
  • 未运行(当前环境无法执行):Windows 实机整包验收(scripts/build-windows.ps1 Release 构建随 ready 后完整 CI 补上);矩阵 verificationStatus 保持 pending,等实机验收

- 新增 src-tauri plugin_host 模块:包存储布局(app_local_data_dir/plugins 与
  plugin-state,bundled 从资源目录只读加载)、启动扫描(经
  plugins.declarative.validate 携带 hostCapabilities,拒绝包隔离并展示原因)、
  启动计划解析(packaged 入口拒绝 reparse point 与路径逃逸,runtime/
  systemCommand 走 PATH 与 LITHE_<TOOL>_PATH 约定)
- 共享 fixtures 验收:invalid-manifests.json 全部 154 个用例在 Windows 二进制
  内断言同一稳定错误码;合法样例按契约模式与 v0 能力门控模式分别断言
- 前端:resolve-editor-lsp-launch 在 Worker 扩展无服务器时回落契约插件启动;
  bootstrap 增加 declarative plugins 扫描步骤
- Note 转正为 implemented 并记录 M1 落地形状与 M2-M4 切分;矩阵
  declarative-plugin-contract Windows 状态随代码更新为 partial(pending)
@ghfind-review ghfind-review Bot added the review: medium ghfind author score; see https://ghfind.com label Oct 22, 2026
- plugin_host/install.rs:ZIP/目录→私有快照(拒绝 reparse point、路径逃逸、
  超限文件与恶意 ZIP 条目)→plugins.marketplace.installReview 全量审查→
  确认时摘要比对拒绝快照调包→staging 复验后激活;卸载、回滚、启用/停用
- launch.rs:runtime 启动按 versionArguments 探测实际版本,低于
  minimumVersion 拒绝启动;版本 token 解析容忍 v 前缀、引号与 go/java 输出
- 设置新增一级页"声明式插件(契约 v1)":安装审查卡(未认证发布者提示、
  权限与完整程序声明展开)、启用/停用/卸载/回滚,与 Worker 扩展分开列出;
  en-US 与 zh-CN 全套文案
- install_extension_from_url 等 Worker 非语言市场命令注册为显式占位错误
  (extensionStore.notAvailable),调用方得到可诊断失败而非命令不存在
- 矩阵 plugin-marketplace/plugin-extension-management/declarative-plugin-contract
  Windows 证据与 notes 随代码更新;Note 增补 M2 记录
@Mucheen

Mucheen commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

M2 已追加到本 PR(commit 见最新推送):

新增功能点

  • 磁盘导入安装链(plugin_host/install.rs):ZIP/目录 → 私有快照(拒绝 reparse point、路径逃逸、单文件 16MiB/总量 128MiB/条目数上限、恶意 ZIP 条目)→ Core installReview 全量审查(权限 + 完整程序清单)→ 用户确认时 manifest 摘要比对(快照调包即拒绝)→ staging 复验后原子激活。卸载(含状态目录)、回滚(保留旧版本目录、选最高次新版)、启用/停用(临时文件 + rename 防截断)。
  • runtime 启动版本探测(launch.rs):按计划的 versionArguments 探测实际版本,低于 minimumVersion 拒绝启动;版本解析容忍 node v 前缀、java 引号输出、go go1.x 前缀。
  • 管理界面:设置新增一级页"声明式插件(契约 v1)",安装审查卡(未认证发布者提示、权限列表、程序声明 JSON 展开)+ 启用/停用/卸载/回滚,与 Worker 扩展明确分组(Note 记录的双模型边界对用户可见);en-US/zh-CN 双语文案。
  • 死链处置:install_extension_from_url / uninstall_extension_new / get_extension_path / read_extension_entrypoint / get_importable_ide_projects 在 Rust 宿主注册为显式占位(extensionStore.notAvailable 结构化错误),前端调用从"命令不存在"变为可诊断失败;完整落盘后端是独立工作项。

验证

  • cargo test(src-tauri 全量):313 passed, 0 failed(新增 install 链 4 项 + 版本解析 1 项)
  • bun run typecheck 干净;settings-hierarchy.test.tsx 4/4(布局断言随新页更新)
  • 组合测试套件中 4 个失败(keyboard/log 面板并发干扰)在干净分支同样出现,与本次改动无关(stash 对照验证)
  • 矩阵 3 个条目证据/notes 更新,verify-platform-feature-matrix(188 能力点)、verify-agent-notes(130 篇)、verify-windows-boundaries 全部通过

- 能力声明提升:languageServers [1,2]、settings [1,2],settings-v2 fixture
  由能力门控改为接受
- plugin_host/settings.rs:全局值(plugin-settings/<id>.json,默认值遗忘规则)
  与项目覆盖(工作区 .lithe/plugin-settings.json,空项清理、损坏拒写)双层
  存储;resolveSettings / storeSetting / workspaceConfiguration 三个命令;
  launch 请求现携带已存设置
- plugin_host/run_plans.rs:runPlan/testPlan 命令,展开 Core 模板后走共享
  run 链路(resolveRunLaunch → startRunProcess),进程归属复用 Worker 扩展
  的 ExtensionProcessOwner
- 设置编辑 UI:按声明 scope/effect 渲染控件,显示值来源徽标,immediate
  效果经 workspaceConfiguration 下推;show/hide 折叠进插件行
- Plugins/win/Official/PhpSupportContract:契约版 PHP bundled 包(API 2,
  launch kind runtime),随安装器资源分发;端到端测试覆盖 validate、
  runPlan、testPlan、resolveSettings。Worker 版 PHP 保留:其 composer
  scripts 动态运行动作在契约 v1 无等价能力,删除会造成功能退化,完整
  迁移推迟(Note 已记录)
- 矩阵 declarative-plugin-settings Windows partial、declarative-plugin-contract
  证据与 notes 更新;Note 增补 M3 记录
@Mucheen

Mucheen commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

M3 已追加到本 PR:

新增功能点

  • 能力声明提升:languageServers: [1,2]、settings: [1,2]——settings-v2 契约 fixture 从能力门控转为被宿主接受(测试同步更新)。
  • 声明式设置(plugin_host/settings.rs):双层存储——全局值(plugin-settings/<pluginID>.json,存储值等于默认值时遗忘以跟随默认变更)+ 项目覆盖(工作区 .lithe/plugin-settings.json,空插件项清理、损坏文件报错不重置);resolveSettings / storeSetting / workspaceConfiguration 三个 Tauri 命令;语言服务器启动请求现携带已存设置,${setting:} 由 Core 展开。
  • 运行/测试计划(plugin_host/run_plans.rs + 前端 declarative-plugin-run.ts):runPlan/testPlan 展开 Core 模板后走共享 run 链路(resolveRunLaunch → startRunProcess),进程归属复用 Worker 扩展的 ExtensionProcessOwner(禁用/卸载即停所属会话)。
  • 设置编辑 UI:插件行内折叠的设置编辑器,按声明 scope/effect 渲染控件并显示值来源徽标;immediate 效果经 workspaceConfiguration 下推到运行中服务器。
  • 契约版 PHP bundled 包(Plugins/win/Official/PhpSupportContract):API 2、runtime 启动形态,随安装器资源分发(tauri.conf.json resources)。端到端测试走真实 Core 命令验证 validate → runPlan(${file}/${userArguments} 展开)→ testPlan(workspace scope)→ resolveSettings。
  • 迁移决策调整:Worker 版 PHP 保留不移除——它提供 composer scripts 动态运行动作发现,契约 v1 无等价能力,删除会造成功能退化;完整迁移等契约补齐该能力(Note 已记录原因与并存规则:Worker 清单先解析,契约包不重复注册语言)。

验证

  • cargo test(src-tauri 全量):317 passed, 0 failed(新增设置存储 3 项 + 契约包端到端 1 项 + 能力门控更新)
  • bun run typecheck 干净
  • 矩阵 declarative-plugin-settings Windows → partial;declarative-plugin-contract evidence/notes 更新
  • verify-platform-feature-matrix(188 能力点)、verify-agent-notes(130 篇)、verify-windows-boundaries 全部通过

剩余

  • M4:Web 工具窗口(ui 2 / WebView2 自定义 scheme)
  • 实机验收:PR ready 后完整 CI + 真实 Intelephense 打包进契约包后的全链路验收

- plugin_host/web.rs:lithe-plugin:// 自定义 scheme 只服务启用包内普通文件
  (拒 reparse point、路径逃逸、超 16MiB、非白名单扩展名),响应带
  CSP(default-src 'none'、拒 eval 与联网、frame-ancestors 'none')、
  nosniff、no-store、CORP
- window.lithe 桥 v0:document-start 注入冻结桥;context、
  ui.getState/setState(宿主持有状态,64KiB 上限)、workspace.openFile、
  事件泵;build.*/functions.* 在 Windows 构建系统能力接入前返回
  plugin.web.unknownMethod
- 页面生命周期命令:open(去重聚焦)/close;禁用卸载清状态
  (forget_plugin_state)
- 能力声明 ui 提升为 [1,2],web-panel fixture 转为被接受(门控测试同步)
- 管理区块新增 Web 工具窗口列表(打开/关闭),en-US/zh-CN 文案
- 矩阵 plugin-web-tool-windows Windows partial、declarative-plugin-contract
  evidence/notes 更新;Note 增补 M4 记录
@Mucheen

Mucheen commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

M4 已追加到本 PR——全部四个里程碑完成:

新增功能点

  • Web 工具窗口(plugin_host/web.rs):契约 ui 2 的 WebView2 落地。
    • lithe-plugin:// 自定义 scheme 只服务启用包内的普通文件:拒 reparse point/符号链接、路径逃逸(含 %2e%2e 编码形式)、超 16MiB、非白名单扩展名;每个响应带 CSP(default-src 'none'、script-src 'self'、拒 eval 与联网、frame-ancestors 'none')、nosniff、no-store、CORP。
    • window.lithe 桥 v0:document-start 注入并冻结;context、request(ui.getState/ui.setState——状态宿主持有、64KiB 上限,配合 web 存储非持久化;workspace.openFile 经事件交前端工作区层)+ on 事件泵。build.*/functions.* 在 Windows 构建系统能力接入前明确返回 plugin.web.unknownMethod,不静默。
    • 页面生命周期:打开去重聚焦、关闭销毁;插件禁用/卸载同步清除其页面状态。
  • 能力声明:ui 提升为 [1,2],web-panel 契约 fixture 从门控转为被宿主接受(能力门控测试同步更新)。至此宿主声明 languages:1、languageServers:[1,2]、serverProcess:1、settings:[1,2]、ui:[1,2];executionProviders/buildProviders/buildSystems 仍门控。
  • 管理区块新增"Web 工具窗口"列表(打开/关闭),en-US/zh-CN 文案。

验证

  • cargo test(src-tauri 全量):323 passed, 0 failed(新增 6 项:路径规范化、CSP 断言、桥脚本冻结与作用域、状态键隔离、fixture 门控更新)
  • bun run typecheck 干净
  • 矩阵 plugin-web-tool-windows Windows → partial;declarative-plugin-contract evidence/notes 更新
  • verify-platform-feature-matrix(188 能力点)、verify-agent-notes(130 篇)、verify-windows-boundaries 全部通过

里程碑总结(M1–M4 全部完成)

Windows 声明式插件宿主已覆盖契约 v1 的:包存储与信任审批、启动扫描与隔离、语言服务器启动(packaged/runtime/systemCommand + 版本探测)、磁盘导入全链路、声明式设置(含项目覆盖与 immediate 下推)、运行/测试计划、Web 工具窗口。矩阵上 Windows 的 declarative-plugin-contract / plugin-marketplace / plugin-extension-management / declarative-plugin-settings / plugin-web-tool-windows 全部为 partial(pending),差实机验收。遗留边界已记录在 Note:publisher 签名延后、Worker 版 PHP 保留(composer 动态动作无契约等价能力)、build.* 桥与构建系统能力、多仓库源市场。

@Mucheen

Mucheen commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

实机验收结果(Release 构建):

构建验证

  • scripts/build-windows.ps1 -Configuration Release 成功(19m59s),产出 lithe-windows.exe。
  • 契约 PHP 包(OfficialPlugins/PhpSupportContract/)确认打包进 Release 资源目录,plugin.json 完整。

实机运行验证

  • Release exe 启动成功:workbench 挂载、首帧渲染、工作区 Git bootstrap 全部正常,运行日志零错误。
  • bootstrap 的 "declarative plugins" 步骤(rescan bundled 契约包 → plugins.declarative.validate → 能力门控)静默完成,无隔离错误。
  • 已停止应用并确认无进程残留。

环境备注(本机问题,不影响 CI/其他开发者)

  • 本机 PowerShell 将 tar.exe 解析到 Git 的 GNU tar(把 D: 当远程主机),需 System32 前置才能跑 prepare 脚本;Bun 需要钉在 1.3.12(本机原为 1.3.14,已通过 npm 装回钉定版本)。这两个都是开发者机器 PATH/工具链问题,CI 与标准环境不受影响。

状态

  • 矩阵 5 个插件条目 Windows:partial(verification pending → 本轮实机验收覆盖构建与启动扫描层;完整功能验收——真实 Intelephense 打包、设置编辑、Web 窗口渲染——仍需按各条目 verification 文本逐项进行)。
  • 矩阵 change gate(origin/preview...HEAD)通过。

@Mucheen Mucheen changed the title feat(windows): 声明式插件宿主 v0(M1)——包存储、启动校验与 LSP 启动接线 feat(windows): 声明式插件宿主(契约 v1)——包存储、安装链、设置、运行计划与 Web 工具窗口 Oct 22, 2026
@Mucheen
Mucheen marked this pull request as ready for review October 22, 2026 15:10
@lithe-code-review

lithe-code-review Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⛔ 有阻塞
范围: preview ← feat/windows-declarative-plugin-host · head: 2d9860a

Findings

  1. [P1] 将插件 WebView 与主窗口的高权限 capability 隔离 windows/tauri/src-tauri/src/plugin_host/web.rs:118-125

    • 实际影响:新建的 plugin-web-* 窗口命中 capabilities/main.json:5 的 windows: ["*"],获得其中包外文件读写、删除、HTTP 等权限。页面可直接使用桥本身依赖的 window.__TAURI_INTERNALS__.invoke,绕过 lithe.request 的方法白名单;CSP 无法约束这些原生 IPC。
    • 触发条件与复现步骤:导入仅声明 ui.contribute、ui.web 的合法页面包,在包内脚本通过 plugin:fs|read_text_file 读取一个已知的包外测试文件;现有 capability 同样允许写入、删除。冻结 window.lithe 不会撤销这个入口。
    • 来源:本次引入。通配 capability 虽已存在,但本次首次将声明式插件页面接入该权限范围。
  2. [P1] 解包 Core 返回的 plan 后再解析启动命令 windows/tauri/src-tauri/src/plugin_host/launch.rs:73

    • 实际影响:所有进入声明式回落路径的语言服务器都会启动失败。Core 返回 {status:"ok", plan:{launch,...}},这里却把整个结果传给读取 plan["launch"] 的 resolve_launch,最终报 unsupported launch kind。
    • 触发条件与复现步骤:安装有效语言服务器包,使 Worker 没有对应服务器,再打开匹配文件。沿 declarative_plugins_language_launch → core_execute → resolve_launch 即可得到该错误;Core 返回结构见 rust/lithe-core/src/plugins/declarative.rs:3242-3253。
    • 来源:本次引入。新增单测直接构造扁平计划,绕过了生产路径的响应封装。
  3. [P2] 设置解析应使用完整清单,不能只传插件 ID windows/tauri/src/features/plugins/declarative-plugin-settings-editor.tsx:43-48

    • 实际影响:任何声明设置的插件都无法显示设置控件。Rust 将 {id} 原样交给会重新校验完整清单的 Core,结果为 plugin.manifest.invalid;随后 81 行因 values 为空返回 null,错误提示也不可见。
    • 触发条件与复现步骤:导入一个包含合法 settings 的包,在管理页点击“设置…”。调用经过 settings.rs:165-171 到达 resolve_settings_command,因缺少 schemaVersion、version 等必填字段被拒绝。
    • 来源:本次引入。同样的 ID 占位清单也被用于 workspaceConfiguration 请求。
  4. [P2] 将声明式运行目录和动作接入实际产品入口 windows/tauri/src/features/plugins/declarative-plugin-run.ts:40-46

    • 实际影响:新增运行模块没有外部调用方,目录刷新函数也没有调用方,因此目录始终为空;测试计划封装同样没有消费方。现有运行菜单仍只调用 Worker 的 runExtensionAction。
    • 触发条件与复现步骤:禁用 Worker PHP、保留契约 PHP 包后打开 PHP 文件,运行菜单没有契约动作;即使直接调用新增 runDeclarativePluginFile,也会因空目录报没有启用插件拥有该文件。
    • 来源:本次引入。bootstrap 和管理页只刷新 Rust catalog,没有初始化这里独立维护的前端目录。
  5. [P2] 停用和卸载插件时销毁其原生页面并撤销访问 windows/tauri/src-tauri/src/plugin_host/install.rs:192-194

    • 实际影响:停用只写记录并重扫,卸载只删目录并重扫,均未关闭已打开的 WebView。forget_plugin_state 没有调用方,桥请求也不复核启用状态;web.rs:315 的文件服务甚至仍接受已停用包。
    • 触发条件与复现步骤:打开一个插件 Web 窗口,再在设置中停用该插件。管理列表会移除入口,但原生窗口继续执行脚本、调用状态桥;卸载后已加载页面也不会被销毁。
    • 来源:本次引入。新增页面生命周期没有接入本 PR 新增的管理操作。
  6. [P2] 为 workspace.openFile 接上所属工作区的文件打开流程 windows/tauri/src-tauri/src/plugin_host/web.rs:279-282

    • 实际影响:该方法发出事件后立即返回成功,但 Windows 前端没有 plugin-web-open-file 的监听或处理代码,因此文件不会打开;事件也没有携带工作区身份或位置参数。
    • 触发条件与复现步骤:从插件页面调用 lithe.request("workspace.openFile", {path:"src/example.php", line:10}),Promise 成功完成,但编辑器没有变化。受限搜索确认该事件仅有这里的发送端。
    • 来源:本次引入。PR 声称提供的桥方法缺少实际消费链路。
  7. [P2] 复用 Core 的运行时版本判断,避免另建解析语义 windows/tauri/src-tauri/src/plugin_host/launch.rs:252-254

    • 实际影响:新增解析器与既有契约不一致:它要求版本必须含点,拒绝共享 fixture 中合法的 openjdk 25 2026-09-16;还可能把 Picked up JAVA_TOOL_OPTIONS: -Dapp.version=99.0 当成实际 JVM 版本,错误放行低版本。
    • 触发条件与复现步骤:用上述输出和 minimumVersion:"21" 检查新解析函数,分别得到无法解析或满足要求;既有 plugins.declarative.checkRuntimeVersion 会识别主版本并跳过 JVM 选项提示行。
    • 来源:本次引入。Core 已拥有该确定性行为及 fixtures/runtime-versions.json,宿主新增了第二套判断。

Scope Check

  • 关联需求:未找到可验证的关联 Issue;按 PR 描述、后续评论和固定提交差异核对 M1-M4。
  • 已完成范围:包存储、扫描、安装命令、设置持久化、计划封装、管理界面与 Web 宿主结构已新增;上述问题说明端到端接线尚未完成。
  • 明确延后范围:publisher 签名、真实 Intelephense、市场源管理及 build/functions 桥不作为本次遗漏追责。
  • 复用与职责:清单和计划主要复用 Core,版本判断存在重复实现;全部 Findings 均指向本次新增代码或其直接触发的调用链。

Suggestions

  1. 为插件页面配置最小权限 capability,排除主窗口通配授权;桥的插件、窗口和工作区身份由原生调用来源确定,并限制可调用的宿主命令。
  2. 将 outcome["plan"] 传给解析器,补一项从真实 Core 响应贯穿宿主解析的回归,避免只测手造扁平对象。
  3. 原生命令按插件 ID 从已接受 catalog 获取完整 manifest;设置加载失败时显示错误,不能因空 values 隐藏错误。
  4. 初始化并更新运行目录,将 run/test 接入动作发现与启动入口,同时连接停用、卸载及工作区关闭时的进程清理。
  5. 在停用、卸载和替换包前销毁所属页面、清理状态,后续桥请求与资源请求统一拒绝失效页面。
  6. 将打开请求路由到所属工作区,校验路径位于其根目录内,保留 line/column,并接入现有文件打开服务。
  7. 宿主只负责有界版本探测,将输出交给 Core 的 checkRuntimeVersion,复用已有版本 fixture。
  8. 补充设置导航前后对比,以及安装审查、设置编辑和 Web 窗口截图;提供上下文中没有截图,视觉效果尚未验证。

Summary

两项 P1 分别涉及插件权限隔离和声明式 LSP 的必然启动失败,需在合并前修复。其余 P2 为非阻塞建议。当前测试主要证明契约校验、存储辅助函数和模拟映射,不能证明新增产品链路可用。

Verification

  • 实际检查:固定 cf0eae7...2d9860a 的清单与聚焦差异,重点追踪 plugin_host、前端管理/设置/运行/LSP、Tauri capability,以及直接相关的 Core 契约、fixtures、测试和 Agent Notes;跳过 Cargo.lock 正文。
  • 已提供结果:作者报告 Rust 323 项通过、Bun typecheck 和 Release 构建/启动扫描通过;这些是上下文提供的结果,非本次独立验证。所给 head checks 中 Windows 前端、Windows Rust 和插件测试为 skipped,另有排队或运行中的检查。
  • 本次未运行:任何仓库代码、测试、验证脚本或应用;未访问网络、修改文件或发布评论。上述复现路径均来自静态分析。
  • 验证缺口:未提供 UI 截图;真实 Intelephense、设置编辑、WebView2 页面与权限隔离、停用清理、发行资源不变性仍缺完整验收证据。

@xiaoyumuxi xiaoyumuxi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

跨 PR 审查:Windows 声明式插件宿主(head 2d9860a6)

结论:建议先处理安全隔离,再合并。 本轮是静态代码审查;尚未在 Windows WebView2 上执行恶意插件验证。

  1. [P1] 插件 WebView 可能继承过宽的 Tauri 权限。 windows/tauri/src-tauri/capabilities/main.json:5 仍是 "windows": ["*"],并允许文件读写、删除、通用 HTTP 等权限;plugin_host/web.rs:118-126 新建的 plugin-web-* 是 Tauri WebviewWindow,页面脚本又可使用内部 IPC。仅冻结 window.lithe、使用 CSP,并不足以证明不能直接调用其他已注册的 Tauri 命令。请将主窗口与插件窗口 capability 按 label/来源隔离,只赋予插件专用最小权限;补充从真实插件页调用未授权 fs/http/宿主命令应被拒绝的测试。
  2. [P1/P2] 桥接身份由请求方自行传入,缺少与调用窗口的绑定。 plugin_host/web.rs:253-283 的 declarative_plugins_web_bridge_request 接受客户端传来的 plugin_id / window_id,直接用作状态 key 或触发 workspace.openFile 事件,未核对 WebviewWindow.label、当前插件是否启用及该页面是否属于此插件。请从可信窗口上下文解析身份,做插件/窗口/工作区鉴权,添加跨插件伪造参数的拒绝回归。
  3. [P2] 停用/卸载的页面生命周期与窗口标识未完全收敛。 install.rs:152-194 只变更安装记录并 rescan,未同步关闭当前已打开的插件 WebView;web.rs:99-105,133-143 按裸 window_id 查找和构造 window label,不含 plugin_id,不同插件同名窗口可能互相干扰。建议 native 宿主统一跟踪并关闭所属窗口、进程和状态,标识使用组合 key。
  4. [P2] 目录导入缺少 ZIP 分支已有的总量和数量限制。 install.rs:349-404 的 copy_tree_bounded 只限制单文件大小与递归深度,没有限制累计字节数及条目数;导入大目录可耗尽应用数据盘。请与 ZIP 分支共享限额计数器,并在失败时清理 snapshot。
  5. [P2] 版本更新激活并非完全原子。 install.rs:298-309 删除现有 version_directory 后才 rename staging;rename 失败可能让原安装记录指向已被删除的版本。建议使用备份/交换和失败恢复,新增受文件占用影响的测试。

以上问题与本 PR 新增宿主的安全、生命周期和安装职责直接相关;正常 Release 启动验收不能替代恶意插件权限测试。

P1(两项):
- 插件 WebView 脱离主窗口通配 capability:main.json 收窄为 main/workspace-*,
  新增 plugin-web capability(core:event 最小集,无 fs/http/shell/dialog);
  桥身份改由调用 webview label 反解(bridge_identity),核对插件启用、
  窗口声明与归属,伪造参数/跨插件/非插件窗口一律 plugin.web.forbidden
- launch 解包 Core 响应信封后再解析(outcome["plan"]),新增走真实
  Core 命令的回归测试(此前单测绕过了信封导致该路径必然失败)

P2:
- 设置编辑器按 ID 从宿主 catalog 取完整 manifest(新增
  declarative_plugins_manifest 命令),resolve/workspaceConfiguration 不再
  传 ID 占位清单;加载与解析错误保持可见
- 运行入口接通:新增 declarative_plugins_language_owner 归属查询,run
  动作发现接入 useRunActionDiscovery(source: declarative),运行菜单
  直接分发;删除会漂移的前端目录缓存
- 停用/卸载/回滚前关闭插件全部 Web 窗口并清状态;scheme 文件服务只服务
  enabled 包
- workspace.openFile 接前端监听(plugin-web-open-file.ts):限工作区根内
  路径,走 handleFileSelect(带 line/column)
- 版本探测宿主只跑 versionArguments,判断委托 Core
  checkRuntimeVersion(共享 runtime-versions 语义),删除自建解析器
@Mucheen

Mucheen commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Review 意见已全部处理(commit e7130f9):

P1 两项

  1. 插件 WebView capability 隔离:capabilities/main.json 从 windows: ["*"] 收窄为 ["main", "workspace-*"];新增 capabilities/plugin-web.json,plugin-web-* 窗口只有 core:default + core:event:default,明确不授 fs/http/shell/dialog/store。页面即使直接使用 __TAURI_INTERNALS__.invoke 也无法触达任何插件 API——应用自有命令由 Rust 侧按窗口标签与插件身份门控。
    • 桥身份绑定:declarative_plugins_web_bridge_request 改为接收 webview: tauri::Webview(Tauri 从调用来源注入),从 label(plugin-web-<sanitized windowID>)反解身份,再对照当前 catalog 核对该插件启用、该窗口被声明、归属匹配——伪造参数、跨插件、非插件窗口一律 plugin.web.forbidden;页面传入的 plugin_id/window_id 参数已删除。
  2. launch 信封解包:resolve_launch 现在接收 outcome["plan"];新增回归测试直接跑真实 plugins.declarative.languageServerLaunchPlan 命令、把完整信封喂进生产解析路径(不再是手造扁平对象)。

P2 五项

  1. 设置解析用完整清单:新增 declarative_plugins_manifest 命令按 ID 返回原始 manifest(catalog 只含 enabled+accepted 包),编辑器的 resolve/workspaceConfiguration 都传完整清单;加载与解析失败时错误始终可见(不再因空 values 提前返回 null)。
  2. 运行入口接通:新增 declarative_plugins_language_owner 归属查询;run 动作发现接入 useRunActionDiscovery(新增 source: "declarative" 与运行菜单分发),归属来自宿主实时 catalog——删除了会漂移的独立前端目录缓存。
  3. 页面生命周期收敛:停用/卸载/回滚前关闭该插件全部 Web 窗口并清除页面状态;scheme 文件服务现在只服务 enabled 包(此前只查 accepted);桥身份同样要求 enabled(在 catalog 匹配中过滤)。
  4. workspace.openFile 接线:前端监听 plugin-web-open-file(bootstrap 注册),路径限定在工作区根内,走 handleFileSelect 打开并透传 line/column。
  5. 版本探测委托 Core:宿主只执行 versionArguments 拿输出,解析与比较走 plugins.declarative.checkRuntimeVersion(共享 runtime-versions.json 语义),删除自建解析器;新增测试覆盖 JAVA_TOOL_OPTIONS 干扰行与 openjdk 25 两段版本——正是审查指出的两个误判场景。

验证

  • cargo test(src-tauri 全量):325 passed, 0 failed(新增 4 项:真实信封回归、label 规范化、Core 版本判定委托、CSP/状态键保持)
  • bun run typecheck 干净
  • 组合套件 4 个失败为既有并发干扰(stash 对照确认,与本次无关)
  • 未在本轮补 UI 截图(Suggestion 8)——设置导航前后对比与 Web 窗口视觉验收留待实机走查,与本 PR 静态改动无阻塞关系。

@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

CI 失败诊断与处置:

上一轮 CI 的两个失败均与本 PR 代码无关:

  1. Test Windows Rust implementation(Test shared Rust Core 步骤):共享 Rust 套件在 CI runner 上总时长超出 1080 秒预算(343 秒时挂在 lithe-core 的 git_integration_preflight_scopes_cherry_pick 上,"The shared suite deadline expired")——全部失败测试是 lithe-core 的 git/git_push 测试,本 PR 未触碰;同日多个其他分支同一套件通过,属 runner 速度波动导致的预算边界问题。
  2. Test real JDT on Windows:real_jdtls_discovers_builds_and_launches_java_25_entrypoints 中 JDT LS 的 classpath 解析 60 秒超时——实机 JDT 集成测试对 runner 的 JVM/JDT 启动速度敏感,与本 PR(纯 plugin_host,不触 LSP 引擎)无关;该测试在更早一次本分支运行(15:10)也以同样的 git_workspace_commit_push_confirms_each_repository_separately 超预算失败过,进一步印证是共享套件的时间预算问题。

处置:分支此前落后 preview 3 个提交(#1331 push target、#1411 Agent parity、#1427 自然排序),已合并 origin/preview(df2b8b6c,无内容冲突)。合并后本地验证:src-tauri 全量 326 passed、bun run typecheck 干净、5 个 Windows 测试套件逐套件全过(与 CI 相同的独立进程方式)、矩阵与 Notes 校验通过。已推送触发新 CI。

另说明:工作树曾出现一批带冲突标记的残留文件,经核查是 2026-08-16 一份旧 stash(fix/windows-new-window-blank 分支的备份)撞上合并工作树的遗留,不属于本 PR 工作内容,已丢弃;合并 commit 本身内容干净(无冲突标记)。

@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

第二轮 CI 结果与处置:

上一轮 CI 的两个失败已消失(合并 preview 后 Test real JDT on Windows 与 shared Rust Core 均通过),出现的新失败已逐一排查:

  1. ci-plugins 的 macOS Swift 编译错误(WindowFullScreenCapabilityGuardTests.Sessions 不满足 ProjectWindowSessionHandling):CI merge ref 显示合并对象是当时的 preview tip(bad347e2)——#1410(原生全屏能力)与 #1416(TERM_PROGRAM_VERSION)已进 preview,RootView.swift 的 confirmRunServicesBeforeClose 协议要求与测试桩的兼容问题在 preview 新提交里引入,与本 PR 的 delta(纯 plugin_host 前后端文件,macOS 目录零改动)无关。本 PR 已把最新 preview(e697cb2f 合并)同步进来,以最新 tip 重跑验证。
  2. Windows Rust 的 run::tests::window_release_ends_its_process_trees / dropping_a_session_ends_its_tree:两次 attempt 分别失败在不同测试(断言 !process_is_alive(descendant)),是 fix: 关闭项目、退出和更新前确认并结束运行服务,Windows 用 Job Object 管理进程树 (#1137) #1431(Job Object 进程树收割)自带的进程收割竞态,在 CI runner 全量并发下偶发。本地高频压测(单测 8 次、run 模块全量 3 次)全部通过。这与本 PR delta 无关——delta 只含 plugin_host 文件。
  3. 本地全量套件另见 prelaunch_*、lightweight_tag_* 偶发失败(并行满载下 cmd.exe/git 超时),单测串行全部通过,CI 中这些测试均 PASS(lightweight_tag 1377ms)——同类机器负载噪声。

以上三个失败点没有一个是本 PR 代码路径:我们的 delta 在 merge ref 中只有 plugin_host/* 与前端 features/plugins/*、run-actions 接线文件。已推送同步 preview 的合并(e697cb2f)重跑全部 CI。

@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

@lithe review

@lithe-code-review

lithe-code-review Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⛔ 有阻塞
范围: preview ← feat/windows-declarative-plugin-host · head: e697cb2

Findings

  1. [P0] 阻止插件 WebView 调用未授权的应用命令
    windows/tauri/src-tauri/src/plugin_host/web.rs:217-220

    • 实际影响:插件页面可以绕过 lithe.request 的白名单,直接通过 Tauri IPC 调用全局注册的应用命令。比如 run_start_process 接收调用方提供的可执行文件和参数,且不核验调用 WebView 身份;插件页面因此可以在用户权限下启动任意进程,绕过插件声明与安装审查所表达的限制。
    • 触发条件:用户打开包含 ui.web 页面的插件后,该页脚本直接调用 window.__TAURI_INTERNALS__.invoke,请求 run_start_process。
    • 复现步骤:在插件页面通过 IPC 调用 run_start_process,传入非空 windowLabel、任意 executable 和 arguments;命令在 main.rs 的全局 handler 中注册,宿主处理函数只拒绝空窗口标签后继续启动进程。
    • 来源:本次引入。新增的 WebView 页面拿到了原生 IPC 入口,但只有 declarative_plugins_web_bridge_request 从调用 WebView 派生身份并校验;其他应用命令没有相同门控。
  2. [P2] 初始化 workspace.openFile 事件监听器
    windows/tauri/src/features/plugins/plugin-web-open-file.ts:19-21

    • 实际影响:Rust 桥发出的 plugin-web-open-file 事件没有前端消费者,插件页面请求打开文件时会得到成功响应,但编辑器不会打开文件。
    • 触发条件:插件页面调用 workspace.openFile,路径位于当前工作区内。
    • 复现步骤:沿 web.rs 的事件发送路径检查;监听函数虽已定义,但在 windows/tauri/src 中没有初始化调用。新增 bootstrap 步骤只执行插件 rescan。
    • 来源:本次引入。事件发送端和监听函数都新增了,但没有接入应用启动流程。
  3. [P2] 将 immediate 设置更新发送给运行中的语言服务器
    windows/tauri/src/features/plugins/declarative-plugin-settings-editor.tsx:79-80

    • 实际影响:设置值会持久化,但 declarativeWorkspaceConfiguration 的结果被丢弃;运行中的服务器不会收到新配置,所选的 immediate 效果不会生效。
    • 触发条件:用户修改声明为 effect: immediate 的插件设置,且对应语言服务器正在运行。
    • 复现步骤:修改设置后,编辑器调用 Core 计算 workspace configuration,却没有将返回值转发给 LSP 客户端或服务器;服务端继续使用旧配置。
    • 来源:本次引入。新设置编辑器增加了配置计算调用,但没有接到已有 LSP 更新链路。
  4. [P2] 停用或卸载插件时停止其运行进程
    windows/tauri/src-tauri/src/plugin_host/install.rs:205-208

    • 实际影响:停用或卸载只关闭插件 Web 窗口并重扫 catalog,不会停止由声明式 run plan 启动的进程;该插件启动的命令可在插件停用后继续运行。新增的 declarativePluginProcessOwner 也没有停用/卸载调用方。
    • 触发条件:通过运行菜单启动插件的运行计划,再从插件管理页停用或卸载该插件。
    • 复现步骤:运行 declarative_plugins_run_plan 生成的进程后调用 declarative_plugins_set_enabled 或卸载命令;宿主没有向前端进程 owner 发出停止请求,run-exit 监听也只清理 Worker 的 owner。
    • 来源:本次引入。声明式进程 owner 和插件管理操作分别新增,但没有连接生命周期。
  5. [P2] 为声明式 test plan 提供产品入口
    windows/tauri/src/features/plugins/declarative-plugin-launch.ts:227-237

    • 实际影响:插件声明的测试计划没有消费者,用户无法从运行/测试界面启动它;当前运行菜单只发现并派发普通 run action。
    • 触发条件:启用包含 test 模板的插件,并打开其测试文件或包含测试的工作区。
    • 复现步骤:新增的 resolveDeclarativeTestPlan 仅在此处定义;运行菜单链路只调用 declarativeRunActionForFile 和 resolveDeclarativeRunPlan,没有调用 test plan。
    • 来源:本次引入。Core 测试计划展开已接入 Rust 命令,但没有接入前端测试发现或执行流程。
  6. [P2] 在替换同版本包失败时保留原版本
    windows/tauri/src-tauri/src/plugin_host/install.rs:319-323

    • 实际影响:同一插件版本重新安装时,宿主先删除现有版本目录,再尝试将 staging 目录改名为正式目录。若改名失败,旧包已丢失,而 installation.json 仍指向旧版本,下一次扫描会隔离该安装。
    • 触发条件:重新导入相同插件 ID 和版本,并在激活改名阶段遇到文件占用或 I/O 错误。
    • 复现步骤:让 staging 校验成功,再使最终 rename 失败;staging_validate_and_activate 返回错误,但不会恢复已删除的版本目录。
    • 来源:本次引入。新安装链新增了先删除目标、后改名的激活顺序。
  7. [P3] 更新共享契约中的 Windows UI v2 状态
    windows/tauri/src-tauri/src/plugin_host/mod.rs:36-43

    • 实际影响:宿主现在声明支持 ui: [1, 2],但 shared/contracts/plugins/v1/README.md:1340-1342 仍写明 Windows 没有 WebView2 宿主、只支持 ui: [1],插件作者会据此误判支持范围。
    • 触发条件:插件作者按共享契约确认 Windows 是否接受 ui: 2 插件。
    • 复现步骤:对照宿主能力声明与共享契约的平台说明,两者相互矛盾。
    • 来源:本次引入。该 PR 改变了 Windows 的 UI 能力状态,但没有同步共享契约说明。

Scope Check

  • 关联 Issue 或需求是否完成:未找到可验证的关联 Issue;按给定 PR 目标和代码范围审查。
  • 已完成范围:新增 Windows 声明式插件存储、扫描、安装、设置、LSP 启动计划、run/test 计划与 Web 工具窗口,并接入部分前端管理和运行入口。
  • 未完成范围:插件 WebView 的应用命令隔离、workspace.openFile 监听接线、immediate 设置下推、插件运行进程清理及 test plan 产品入口仍有缺口;同版本安装失败恢复也未实现。新增 PHP 包 README 明确说明 LSP 入口仍为占位文件。
  • 是否复用了已有上游能力,或对自研边界给出充分证据:清单校验、计划展开和版本判断复用了 Core;LSP 与运行流程复用现有宿主接口。平台专属存储和 WebView 适配符合 Windows 宿主职责。
  • 每个 Finding 是否由本次变更引入或加剧:是,均由新增宿主、页面或对应接线造成。
  • 是否属于本次变更职责范围:是,涉及插件权限边界、生命周期、运行/测试计划、安装恢复及能力契约。

Suggestions

  1. 对插件 WebView 禁止访问通用应用 IPC,只提供经过 allowlist 和调用来源校验的桥;补充真实 WebView2 页面尝试启动任意命令、读取任意文件时均被拒绝的验证。
  2. 在主产品窗口 bootstrap 中注册并清理 initializePluginWebOpenFileListener,并增加从桥请求到编辑器打开文件的端到端覆盖。
  3. 将 Core 返回的配置发送到对应运行中 LSP 会话,并验证 immediate 设置修改后服务器收到更新。
  4. 在停用、卸载和工作区关闭路径停止声明式 run 会话,并让进程退出事件清理正确的 owner。
  5. 将 test plan 接入测试动作发现和共享运行链路,或将该能力明确标为未实现。
  6. 采用备份/交换或可恢复的目录替换流程;改名失败时恢复原包,并覆盖同版本重装失败路径。
  7. 更新共享契约对 Windows ui: 2 支持状态和现有限制的说明。
  8. 请在 PR 描述或评论中补充受影响设置页和 Web 工具窗口的截图;当前材料没有截图,视觉效果尚未验证。

Summary

Core 复用和 Windows 宿主分层整体符合仓库边界,但新增插件 WebView 仍可触达不受限的应用命令,构成合并阻塞的安全问题。其余问题会影响文件打开、运行时设置、进程清理和 test plan 的端到端可用性;安装激活和契约文档也需要补齐。

Verification

  • 实际检查:固定 bad347e...e697cb2 的指定差异;重点检查新增 plugin_host 模块、Tauri 命令注册与 capability 配置、插件管理/设置/运行/LSP 前端接线、Windows PHP manifest、Agent Note、相关插件契约说明及矩阵。对 main.rs 和主 capability 读取了 base 版本作对照。
  • 已提供的测试或 CI 结果:上下文中的作者报告包括 Windows Rust 与 frontend 检查通过、Windows CI gate 成功,以及本地 Rust/Bun 检查结果;这些结果未由本次独立验证。提供的 Check Run 中 plugin tests/package verification 为 skipped。
  • 本次未运行的测试:未运行仓库代码、测试、验证脚本或应用;未访问网络。Windows 实机 WebView2 安全隔离与完整插件功能验收未由本次验证。
  • 仍存在的非阻塞风险或平台验证缺口:没有 UI 截图;真实 Intelephense 仍未随样例包分发;平台矩阵保持 pending。按只读审查限制,本次也未运行 runtime bundle immutability 验证脚本。

@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

@lithe review

P0:
- invoke_handler 包装插件页门控:label 为 plugin-web-* 的调用只允许
  declarative_plugins_web_bridge_request(改为直调的普通函数,消费原始
  Invoke,绕过宏推断问题),其余应用命令(run_start_process 等进程/文件/
  HTTP 面)一律 resolver.reject——插件页面无法再通过原始 Tauri IPC 调用
  宿主命令
- generate_handler 宏在非直接实参位置无法推断 R:用
  Box<InvokeHandler<Wry>> 在绑定处钉定(本地最小复现 crate 验证)

P2:
- initializePluginWebOpenFileListener 真正接入 bootstrap(此前只有定义)
- immediate 设置:编辑器取 Core 展开的 configuration 后经
  lsp.updateWorkspaceConfiguration 下推到运行中的 LSP 会话
- 停用/卸载时 Rust 发 declarative-plugin-lifecycle 事件,前端进程 owner
  停止该插件全部声明式运行会话
- test plan 产品入口:declares_tests 标记进入 catalog,运行菜单的声明式
  动作按插件测试模板走 testPlan(v1 传当前文件供项目标记判定)
- 同版本重装激活失败时备份/恢复原版本目录,不再出现安装记录指向缺失
  字节的隔离状态(新增恢复路径测试)

P3:
- 共享契约 README 更新 Windows ui: [1,2] 状态与桥 v0 方法范围
@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

第三轮 review 意见已全部处理(commit a3c4dca):

P0:插件 WebView 调用未授权应用命令

  • invoke_handler 级门控:plugin-web-* 标签的 IPC 调用只允许 declarative_plugins_web_bridge_request,其余全部应用命令(含 run_start_process 的进程启动面)在任何 handler 运行前 resolver.reject。页面即使直接使用 __TAURI_INTERNALS__.invoke 也只能触达这一个经身份校验的入口。
  • 桥命令改为普通函数直调:不再走 #[tauri::command] 宏注册,而是由门控直接把原始 Invoke 交给 declarative_plugins_web_bridge_request(invoke)(自身解构 payload、经 label 反解身份),彻底消除宏注册的旁路。
  • 过程发现 generate_handler! 宏的闭包在非直接实参位置无法推断 Runtime 泛型(本地最小复现 crate 确认),用 Box<InvokeHandler<Wry>> 在绑定处钉定解决。

P2 五项

  1. initializePluginWebOpenFileListener 已接入 bootstrap 的 declarative plugins 步骤(此前确实只有定义)。
  2. immediate 设置:编辑器先取 Core 展开的 workspaceConfiguration,再经新增的 pushDeclarativeWorkspaceConfiguration(lsp-core-adapter)→ Core lsp.updateWorkspaceConfiguration 下推到运行中的会话,返回是否送达。
  3. 停用/卸载时 Rust 发 declarative-plugin-lifecycle 事件,前端 declarativePluginProcessOwner.stop(undefined, pluginId) 停止该插件全部运行会话。
  4. test plan 入口:catalog 新增 declaresTests(manifest 有 test 模板即置位),运行菜单的声明式动作按该标记走 testPlan(v1 传当前文件供项目标记判定,完整工作区扫描随测试面板集成跟进)。
  5. 同版本重装:激活改为备份/交换——当前版本先移到 .previous-tmp,staging rename 失败时恢复原目录,不再产生指向缺失字段的安装记录;新增恢复路径测试。

P3

  1. 共享契约 README 已更新:Windows 以 WebView2 + lithe-plugin:// 插件私有源实现 web content,声明 ui: [1, 2],桥 v0 方法范围(ui.getState/setState、workspace.openFile;build./functions. 返回 plugin.web.unknownMethod)。

验证

  • Rust 全量 327 passed(新增 R6 恢复路径测试);3 个失败(prelaunch/lightweight_tag)为本机全量并发的已知负载噪声,隔离重跑全过
  • bun run typecheck 干净;契约 README 变更过 JSON 契约校验(本地等价检查 0 错误,zsh 脚本为 macOS 专用)
  • 截图(Suggestion 8)仍留待实机走查

@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

@lithe review

@lithe-code-review

lithe-code-review Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⛔ 有阻塞
范围: preview ← feat/windows-declarative-plugin-host · head: 114bc8a

Findings

  1. [P1] 注册声明式插件的原生命令
    windows/tauri/src-tauri/src/main.rs:182

    • 实际影响:generate_handler! 中没有任何 declarative_plugins_* 命令。新增宿主无法从产品前端调用,启动扫描、插件管理、安装、设置和声明式 LSP 回落均失败。
    • 触发条件:启动应用或打开声明式插件设置页。
    • 复现步骤:bootstrap 调用 rescanDeclarativePlugins(),经 tauri-core.ts 直接请求 declarative_plugins_rescan;主窗口通过 plugin_gate 后,生成的 handler 找不到该命令。新增 Worker 安装占位命令也未注册。
    • 来源:本次引入。新增调用依赖的命令未接入本次重组后的注册表。
  2. [P2] 从桥请求正文读取 method
    windows/tauri/src-tauri/src/plugin_host/web.rs:262

    • 实际影响:所有桥方法都会返回 plugin.web.unknownMethod,包括状态读取、状态保存和打开文件。
    • 触发条件:插件页面调用任意 lithe.request 方法。
    • 复现步骤:页面发送命令 declarative_plugins_web_bridge_request,正文为 {method:"ui.getState",params:null};此处把命令名作为 method,而 BridgeArgs 没有 method 字段。bridge_dispatch 因此无法匹配 ui.getState 等分支。恢复 Finding 1 的注册后,该问题仍存在。
    • 来源:本次引入。新增原始 Invoke 适配没有解析桥方法字段。
  3. [P2] 为设置编辑器传入实际工作区
    windows/tauri/src/features/plugins/declarative-plugins-section.tsx:320

    • 实际影响:项目设置始终写入全局层;immediate 设置更新也永远不会下推到运行中的服务器。
    • 触发条件:用户在已打开项目中修改 project 或 immediate 设置。
    • 复现步骤:设置编辑器的唯一产品调用方固定传入 workspacePath={null}。编辑器因此将 scope 选为 global,并跳过要求 workspacePath 非空的配置下推分支。
    • 来源:本次引入。新增设置入口没有连接工作区上下文。
  4. [P2] 将初始 workspaceConfiguration 传递到 LSP 会话
    windows/tauri/src-tauri/src/plugin_host/launch.rs:234

    • 实际影响:声明式服务器启动时收不到清单展开后的配置,已保存设置及默认配置无法按契约生效。
    • 触发条件:启动声明了 workspaceConfiguration 的合法插件服务器。
    • 复现步骤:Core 的 languageServerLaunchPlan 返回 plan.workspaceConfiguration,但 ResolvedLaunch 仅保留初始化选项等字段。前端启动映射及 lsp.startServer 请求也没有传递该配置,Core 会话的插件配置保持为空。Finding 3 修复后,首次启动仍受影响。
    • 来源:本次引入。新增启动计划适配丢弃了 Core 已提供的配置字段。
  5. [P2] 按契约的 title 字段渲染枚举选项
    windows/tauri/src/features/plugins/declarative-plugin-settings-editor.tsx:198

    • 实际影响:枚举下拉列表的选项没有显示文本,用户无法按声明的名称选择设置值。
    • 触发条件:插件声明 enum 设置。
    • 复现步骤:使用 node-runtime.plugin.json 中的日志级别设置。共享契约与 Core SettingOption 输出 {value,title},扫描结果原样传给前端;此处读取不存在的 option.label,最终菜单渲染空标签。
    • 来源:本次引入。新增 TypeScript 类型和控件映射与实际契约不一致。
  6. [P2] 分别发现运行和测试动作,并提供真实项目标记
    windows/tauri/src/features/plugins/declarative-plugin-run.ts:72

    • 实际影响:常见测试项目无法通过新增入口执行测试;测试计划失败后会静默执行普通文件。声明测试模板还会将唯一的普通运行动作改成测试动作。
    • 触发条件:插件同时声明 run/test,例如本 PR 的 PHP 包,用户打开 tests/ItemTest.php。
    • 复现步骤:发现函数只生成一个动作并设置 declarativeTest=true。执行时仅将当前文件传作项目文件列表,遗漏 composer.json、phpunit.xml 等实际标记;Core 返回 plugin.test.unsupportedProject,随后 catch 回落到 runPlan,执行 PHP 文件而非 PHPUnit。
    • 来源:本次引入。新增测试入口没有提供项目检测所需输入,也没有独立的动作身份。
  7. [P2] 将声明式进程 owner 接入工作区关闭和退出清理
    windows/tauri/src/features/plugins/declarative-plugin-process-owner.ts:8

    • 实际影响:关闭项目标签后,声明式运行进程继续存活;正常退出的会话也一直保留在新增 owner 中。
    • 触发条件:启动长时间运行的声明式任务,然后关闭所属项目,保留原生窗口。
    • 复现步骤:新任务登记到独立的 declarativePluginProcessOwner,但 workspace-lifecycle.ts:488 仅停止 Worker 的 extensionProcessOwner;项目服务清理也没有停止新 owner。use-run-process-events.ts:46 同样只对 Worker owner 调用 finished。
    • 来源:本次引入。新增独立资源所有者没有接入已有工作区和进程退出生命周期。
  8. [P2] 对目录导入执行累计大小与条目数限制
    windows/tauri/src-tauri/src/plugin_host/install.rs:434

    • 实际影响:目录导入可绕过 ZIP 分支的 128 MiB、2000 条目限制,持续占用应用数据盘;中途失败的快照也不会在当前导入流程中清理。
    • 触发条件:通过目录导入命令提交包含大量合法小文件的包。
    • 复现步骤:有效清单旁放置 9 个各 16 MiB 文件,或超过 2000 个小文件,调用 prepare。递归复制只检查深度和单文件大小,没有跨递归累计预算;prepare 的复制、清单读取或审查失败路径直接返回,保留已复制内容。
    • 来源:本次引入。新增目录快照路径缺少 ZIP 路径已有的资源边界。

Scope Check

  • 关联需求:未找到可验证的关联 Issue;按固定差异及 PR 描述核对 M1–M4。
  • 已完成范围:新增存储、扫描、安装、设置、计划解析、管理界面和 Web 宿主代码;可见 IPC 门控、计划信封解包、Core 版本判断和事件监听初始化的修复。
  • 未完成范围:命令注册阻断整条产品链路;其余 Findings 是恢复注册后仍存在的功能与资源管理问题。
  • 上游复用:清单校验、版本判断及计划展开复用 Core,运行和 LSP 复用已有引擎;主要缺口在适配与生命周期接线。
  • 来源与职责:全部 Findings 均为本次引入,属于新增插件宿主及其直接调用链。明确延后的发布者签名、市场源和真实 Intelephense 不作为本轮遗漏。

Suggestions

  1. 将新增宿主及占位命令加入主窗口 handler,保留插件页门控;补充经过真实命令注册表的调用覆盖。
  2. 在 BridgeArgs 中解析并校验 method,将该字段传给分发器,覆盖实际 IPC 正文。
  3. 从所属工作区取得稳定上下文,传入设置页,分别验证项目覆盖和 immediate 更新。
  4. 将 workspaceConfiguration 贯穿 Rust 返回值、前端启动类型及 lsp.startServer 请求,验证服务器启动后的配置请求。
  5. 将选项类型与契约对齐,映射 title → label,使用共享枚举 fixture 验证可见文本。
  6. 分别生成 run/test 动作,传入真实项目标记和明确测试 scope;测试失败时展示错误,避免静默改为运行文件。
  7. 在工作区关闭时停止新增 owner,在进程退出时调用其 finished;覆盖关闭项目但保留窗口的场景。
  8. 为目录和 ZIP 导入共享累计预算,确保失败、取消和结束审批均释放快照。
  9. 补充设置导航前后对比,以及安装审查、设置编辑和 Web 工具窗口截图。提供的上下文没有截图,视觉效果尚未验证。

Summary

命令未注册是合并前必须修复的 P1。其余 P2 为非阻塞修改建议。分层与 Core 复用方向符合仓库边界,但现有辅助函数和契约测试不足以证明产品入口、设置传递及资源清理完整可用。

Verification

  • 实际检查:固定 1fba194...114bc8a 的文件清单和聚焦差异,重点追踪 plugin_host、命令注册/capability、前端管理/设置/运行/LSP、直接相关 Core 契约、fixtures、测试及 Agent Notes;跳过 Cargo.lock 正文。
  • 已提供结果:作者报告 Rust 327 项通过、3 项负载相关失败隔离重跑通过、Bun typecheck 通过,以及较早版本的 Release 构建和启动扫描成功。这些均为上下文报告,非本次独立验证;给定 head checks 尚有 queued/in_progress,不能视为完整 CI 通过。
  • 本次未运行:任何仓库代码、测试、应用或验证脚本,包括 runtime bundle immutability;未访问网络、修改文件或发布评论。上述复现路径均来自静态分析。
  • 验证缺口:尚缺当前 head 的产品调用链、真实 WebView2 权限隔离、完整插件功能及发行资源不变性验收;UI 截图需要补充。

@Mucheen

Mucheen commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

@lithe review

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: medium ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants