Repository navigation
feat(plugins): 插件注入的工具链框架与 macOS 只读环境设置页 - #1581
FxRayHughes wants to merge 2 commits into
Conversation
技术方案评审:
|
| 贡献 | 回答什么 | 形式 | 谁提供 |
|---|---|---|---|
| 工具定义 | 工具是什么:怎么识别、探测版本、导出环境、启动 | 清单里的静态声明 | 任何插件 |
| 提供方 | 装了什么、项目要什么、全局要什么 | 插件通过 API 返回事实 | 版本管理器插件 |
| 使用方 | 谁需要这个工具 | 已有(通过 tool ID 引用) |
语言服务器、构建系统、运行配置 |
1. 工具定义(示意)
"toolDefinitions": [
{ "id": "java", "displayName": "JDK",
"executables": ["bin/java"], "commands": ["java"],
"version": { "arguments": ["-version"], "pattern": "(?:1\\.)?(\\d+(?:\\.\\d+)*)" },
"exports": [{"name": "JAVA_HOME", "value": "${home}"},
{"name": "LITHE_JAVA_PATH", "value": "${executable}"}],
"pathPrepend": ["${home}/bin"],
"launch": ["${executable}", "${runtimeArguments}", "-jar", "${entry}", "${arguments}"] },
{ "id": "maven", "displayName": "Maven",
"executables": ["bin/mvn"], "commands": ["mvn"],
"projectExecutables": ["mvnw"],
"version": { "arguments": ["-v"] },
"pathPrepend": ["${home}/bin"] }
]这张“工具 ID 到宿主用途”的映射表就是适配层里本来要写死的东西,改成插件贡献。它取代的是:java 当 JDK home 的特判、maven → mvn 的文件名巧合、契约里写死的版本探测参数和 LITHE_<TOOL>_PATH 导出规则、launch.kind: runtime 里 tool 只能是 java 或 node 的限制,以及选择链里专门给 mvnw、gradlew 写的分支。
- ID 归属。 插件自己的工具用
插件id:工具;不带前缀的短名(java、node、go、maven)是保留名,只有宿主内置或官方签名插件能定义。同一 ID 被重复定义时拒绝后来者,不允许覆盖。 - 宿主内置的那几个用同一种格式的数据文件表达,放在
shared/下,各平台随应用打包,作为第一个贡献者注册。适配层里不出现任何工具名,以后各插件可以逐个接管自己的定义。 - 安全校验由 Core 统一做,与具体工具无关。 环境变量值只能是
${home}、${executable}模板;禁止PATH、DYLD_*、LD_*;pathPrepend只能在${home}之内;探测参数必须是-x/--xx形状。安装审查里列出每个定义会运行的程序和参数。
2. 提供方 API
清单只声明“我能回答哪些工具”,不描述目录结构和文件格式:
"toolchainProviders": [{ "id": "mise", "displayName": "mise",
"tools": ["java", "node", "maven", "pnpm", "go"] }],
"permissions": ["toolchains.provide"]宿主向插件发 toolchains/query({operationID, workspace, tools},workspace 为空表示只问全局),插件返回:
{ "installations": [
{"tool": "java", "version": "21.0.2", "home": "/…/installs/java/21.0.2"} ],
"statements": [
{"tool": "java", "scope": "project", "source": "/proj/mise.toml",
"request": "21", "installation": "/…/java/21.0.2"},
{"tool": "java", "scope": "global", "source": "~/.config/mise/config.toml",
"request": "17", "installation": "/…/java/17.0.9"},
{"tool": "node", "scope": "project", "source": "/proj/.tool-versions",
"request": "lts", "installation": null} ],
"watch": ["/proj/mise.toml", "/proj/.tool-versions", "…/installs/java"] }- 插件只能回答自己在
tools里声明过的、且有已启用插件定义的 ID,其余丢弃。这样“任意同名文件被注入”的口子就堵上了。 - 版本语义(哪个文件优先、
lts对应哪个目录)归插件,宿主不解释request。 installation: null表示项目要求了但没装。watch沿用 PR 里“缓存到输入变化”的思路:宿主只做stat指纹比较,没变就不再调用插件。- 沿用现有的
operationID、取消、超时和过期结果规则。 - 返回的路径由 Core 统一校验:绝对路径、存在、可执行、不落在工作区内(保留 PR 现有的这条安全规则)、数量有上限。
.tool-versions、mise.toml这类通用解析函数放进 lithe-plugin-docs 的 Node/Java SDK,不放进宿主。
3. 统一的选择规则
所有工具共用一条规则,按层从上到下取第一个有值的:
| 层 | 内容 | 范围 |
|---|---|---|
| 1 固定 | 用户在 Lithe 里为这个项目选的 | 项目 |
| 2 项目要求 | 提供方上报的 scope: project,以及 mvnw、gradlew 等项目自带的包装脚本 |
项目 |
| 3 全局默认 | Lithe 里的全局默认 → 提供方上报的 scope: global → JAVA_HOME/PATH |
全局 |
| 4 自动检测 | 满足最低版本的已安装最新版(现有的 Core 规则) | — |
- 没装任何插件时,第 2 层为空,行为与现在完全一致。
- Lithe 里固定的值失效:报错,不回落(与调试适配器那篇 Note 的做法一致)。
- 项目要求了但没装:回落到下一层继续工作,但状态标成“未满足”,界面明确提示。
- 界面上每个工具一行,显示“JDK 21 · 来自项目要求(mise.toml)”,展开后列出每层的值,被盖住的划掉。
对这个 PR 的建议
可以保留: 工作区内路径拒绝、按输入缓存、只读原则、i18n 词条、功能矩阵条目、RuntimeChoiceSource 的“来自 %@”展示。
需要重做: Core 里的 toolchain_sources.rs(管理器专属解析与选择语义)、清单 schema 里的目录与格式描述、ProjectRuntimeService 里三条各自的链。
建议拆分:
- 契约(工具定义、提供方、校验)加 Agent Note,先定契约再写代码;
- 适配层的查询客户端(缓存、取消、过期);
- 统一选择规则加运行时设置界面;
- mise 插件加 SDK 帮手,再写一个 SDKMAN 插件当验收,并把
MacRuntimeDiscovery里写死的 SDKMAN 位置迁出去。
契约在 shared/contracts/plugins/v1/,合并后要在 lithe-plugin-docs 运行 node scripts/contract.mjs sync 同步。
待讨论
- API 方向。 上面是“宿主请求、插件返回事实”,不是“插件主动推送”。推送需要插件进程为每个打开的项目常驻;请求式有明确的超时和取消。需要时可以在同一协议上加
toolchains/changed通知。 - 规则细节。 “固定 > 项目要求 > 全局默认”,以及“要求没装就回落并警告”,是建议值,欢迎提出不同意见。
- 保留名。 只允许官方签名的插件或宿主内置定义不带前缀的短名;若允许社区插件定义,冲突处理会复杂很多。
- 自举和免重启。
- 提供运行时的插件,它自己的后端不能依赖它要发现的运行时,否则循环依赖。后端必须自带运行时或是单文件可执行程序(契约已有
packagedRuntime)。 - PR 的
canActivateWithoutRestart只放行“无后端”的插件,这个条件需要放宽。 - 这两点我们还没有原型验证过。
- 提供运行时的插件,它自己的后端不能依赖它要发现的运行时,否则循环依赖。后端必须自带运行时或是单文件可执行程序(契约已有
- Core 的一次性改动。 加定义的校验,以及把
checkRuntimeVersion里 Java 8 的1.x特例改成定义里的正则。之后新增工具或管理器都不再改 Core。
|
这个具体的先后顺序啥的我记得构建工具里面我之前就提过一个方案,按照优先级去维护一个顺序,你可以去看看,具体细节我记不清楚了哎 |
|
感谢这么细的评审。结论先说:同意按「工具定义 / 提供方 / 使用方」重做,本 PR 不再往下推。 我对照代码核对了几处问题,都成立:
你说的「按优先级维护顺序」,我找到的是你在 #1420 的评审(#1420 (comment)):
如果是这条,我打算把新方案的选择规则直接建在它上面,不再写第二套分层逻辑。下面是调整方案,请看方向对不对。 1. 选择规则:写成每个工具定义的默认候选数组四层规则不再写成
示意(Maven;Java 结构相同,把 { "id": "maven", "displayName": "Maven", "commands": ["mvn"],
"version": { "arguments": ["-v"] },
"executables": [
{ "id": "pinned", "kind": "setting", "settingID": "executable", "layer": "project" },
{ "id": "wrapper", "kind": "projectFile", "path": "mvnw" },
{ "id": "projectRequest", "kind": "toolchain", "scope": "project" },
{ "id": "default", "kind": "setting", "settingID": "executable", "layer": "global" },
{ "id": "globalRequest", "kind": "toolchain", "scope": "global" },
{ "id": "home", "kind": "environment", "variable": "MAVEN_HOME", "path": "bin/mvn" },
{ "id": "path", "kind": "command", "command": "mvn" },
{ "id": "detected", "kind": "toolchain", "scope": "installed" }
] }各层在 resolver 里的语义:
这样三条链变成一条。Provider、模型加载和实际执行用同一个结果,#1420 已经保证了这一点。没装版本管理器插件时,所有 2. 提供方:mise 的语义交还给 mise同意「宿主请求,插件返回事实」。mise 插件改成带后端,后端直接调用 mise 自己的机器可读输出,任何一方都不再重写 mise 的规则:
本机验证(mise 2026.9.8):用
自举:后端做成单文件原生程序(Rust,按 darwin-arm64 / x64 分包,Store 已支持按架构下载),不依赖它要发现的 node 或 java。mise 本身的位置用 #1420 的候选声明:设置 → SDKMAN 适合做第二个验收插件: 3. 对待讨论各点的意见
4. 拆分按你的建议,先契约后代码,拆成 4 个 PR:
你列的可以保留的部分(工作区内路径拒绝、按输入缓存、只读原则、i18n 词条、功能矩阵、「来自 %@」展示)会分别带进第 2、3 个 PR。第 1 个提案 PR 提交后关闭本 PR,分支保留作参考;配套的 Store 收录 PR(Lithe-IDEA/Lithe-store#32)同时关闭,等新契约落地后重新提交。 如果方向没问题,我先提第 1 个提案 PR。 |
|
剩下的没啥问题了,你大胆去做吧 |
|
@FxRayHughes 这个可能需要催一下进度,因为有一个 PR 里面我要求使用你这个 PR 的版本管理方案来处理了,如果你没有空的话可以跟我说我来接手 |
改写后的计划:工具全部由插件注入上面几张截图换成了一个可交互的 HTML 原型(链接和用法在最后),以原型和这条评论为准。你 4 个 PR 的拆分和 #1420 候选数组的思路都保留;主要改动是下面这条原则。另外,#1558 的 Maven 迁移要用这里的工具定义来声明 Maven,所以第 1 个提案 PR 请优先提。 0. 原则:宿主不认识任何工具
1. 三类贡献:界面上每一块由谁注入
2. 选择规则:四层,建在 #1420 的候选数组上你在第 1 节给的
3. 按目录(模块)拆分和固定
4. 手动添加
5. 紧凑展示
6. 查询模块和文件监听
7. 拆分(改写后)
你第 3 节里的其余几点照原样进行:免重启第一阶段不放宽, 8. 现有代码里要清掉的写死
9. 原型怎么用
|
* feat(plugins): 声明式语法高亮与运行标记(API-25/27/29) - languages 2:插件语言可带 Monarch 语法和编辑器配置,Core 校验,纯语法插件免重启 - runMarkers 1:backend 2 插件函数回答可运行行,编辑器画运行按钮,点击经 commands/didRun 带 runMarker - 契约、fixture、Agent Note、计划文档与功能矩阵同步更新;Windows 暂不支持 Refs #570 * feat(plugins): 独立编辑器高亮与动作扩展 - languages 2 提供静态语法和独立编辑配置,插件正则在可终止 worker 中运行\n- editor/registerActions 运行期注册文本动作,点击复用普通函数调用,不扩张 Run/LSP/Build/Debug 领域契约\n- 补齐文档版本、动作租约、取消与清理、独立工具后端启用及安全模式边界\n- 更新 Skill、审查规则与决策笔记,总结 #1581/#1558 的架构边界\n- 同步契约、fixtures、中英词条、功能矩阵与资源复用说明\n\nRefs #570\nReworks #1617 * docs(plugins): 明确 API-25 JSON 贡献的支持范围与局限 区分包内静态 JSON 贡献与尚未开放的运行期 Provider 注册、替换和释放;说明独立语义 Provider、新引擎适配及平台、SDK 和验收限制。同步计划、架构笔记、正式契约与功能矩阵。 * fix(plugins): 修复编辑器扩展审查中的生命周期与高亮问题 动作刷新立即确认调度并隔离非致命失败,整批收集共享八秒期限并在撤销时清理计时任务。 Core 保留校验后的 grammar 原始文本,worker 按源顺序解析 cases 并显式默认 root;配置-only 别名借用既有 tokenizer,重映射 token 语言身份以保留独立注释配置。 模块状态观察弱引用 runtime,补齐有界、确定性的 Swift/Rust/WebKit 回归与测试文档模型清理,同步契约、Agent Note 和功能矩阵。 验证:57 项 Swift、1459 项 Rust、39 项编辑器和 119 项真实 WebKit 测试通过;静态边界、i18n、只读 bundle、资源复用与矩阵门禁通过。未执行 Windows 原生构建或打包安装验收。 Refs #1617, #570
按 1lck#1581 评审后的改写计划重做:宿主不认识任何工具,工具定义、版本管理器 (提供方)全部由插件声明;Core 只做校验和四层解析,不解析 mise 等管理器 的私有格式。 - 契约:新增 toolDefinitions 1、toolchainProviders 1,executableResolution 增加 v2(只用于工具定义候选);同步 schema、README、toolchains.md、 fixture 和 rust-core-api.md;proposals/toolchains/ 放完整方案草案 - Rust Core:新领域模块 rust/lithe-core/src/toolchain/(定义注册表、 提供方答复校验、目录固定、固定/项目文件/全局/自动四层解析、版本匹配、 扫描计划)和六个 toolchains.* 命令,全部只处理宿主快照,不启动进程、 不扫描、不写文件 - macOS:设置 › 构建新增「运行环境」「环境来源」两页,内容来自已启用 插件的声明和磁盘上的真实文件;未实现的添加、切换、保存、查询入口不显示 - 登录 Shell:新增只读 inspect,来源页显示读取状态、变量数、耗时和 PATH 各目录来源;重新读取不改变本进程 PATH - Debug 预览入口 --toolchain-settings-demo / --toolchain-sources-demo - Agent Note、设置层级 Note、功能矩阵和中英词条同步 Refs 1lck#1514
27852b0 to
71f9f65
Compare
Lithe Review结论: 未发现需要阻塞合并的问题。 Findings
Scope Check
Suggestions
Summary整体分层与延期边界清晰。建议在后续原生接入前修复上述两处解析不一致,避免设置展示与实际选择产生分歧。 Verification
|




关联
toolchainSources提交(27852b0)已经从分支上移除。那个提交在 Core 里解析 mise 的版本文件,以及lts、prefix:这类写法;评审时原作者已同意改成「工具定义 / 提供方 / 使用方」三类贡献重做(见 评审讨论)。现在分支基于最新的 preview,只有一个提交。原则
宿主不内置任何工具:Java、Maven、Node 等工具由插件定义,mise、nvm、SDKMAN 这类版本管理器由插件作为提供方接入。Core 只负责校验和按四层解析,不解释任何版本管理器的私有格式。
改动
插件契约(
shared/contracts/plugins/v1/)toolDefinitions: 1(工具定义)和toolchainProviders: 1(提供方声明,需要权限toolchains.provide)。executableResolution增加 v2,只用于工具定义的候选,不改变现有 Build 的候选集合。rust-core-api.md。新增toolchains.md,以及两个 fixture(tool-definition.plugin.json、toolchain-provider.plugin.json)。proposals/toolchains/存放完整方案草案,包括查询协议、local.json、清单贡献和解析结果的 schema 与用例。这些只用于评审,不是可以安装的清单。java、node等)只能由宿主验证过的官方插件定义;<pluginID>:<tool>;Rust Core(
rust/lithe-core/src/toolchain/)工具链是一个独立的领域。插件清单模块只在准入时调用它做校验。
definitions.rsquery.rspins.rs.lithe/toolchains/local.json的pins,找出最近祖先目录上的固定值resolution.rspaths.rstoolchains.registry、toolchains.decodeReply、toolchains.resolvePins、toolchains.resolve、toolchains.checkVersion、toolchains.discoveryPlan。它们只处理宿主传入的快照,不启动进程、不扫描目录,也不写文件。backend.rs:backend 2 的插件可以由已准入的工具链查询触发启动。declarative.rs:清单中的toolDefinitions(最多 32 个)和toolchainProviders(最多 8 个)必须非空,能力声明要精确对应。macOS
MacLoginShellEnvironment新增只读的inspect,供来源页使用。重新读取只更新缓存和页面显示,不会改变当前进程的PATH。--toolchain-settings-demo和--toolchain-sources-demo。预览里只有插件清单是样例,工作区文件、固定值和登录 Shell 都是真实读取的。可以用以下参数指定初始状态:--toolchain-demo-directory <目录>--toolchain-demo-expand <工具,…>--toolchain-demo-size <宽>x<高>--toolchain-demo-workspace <路径>文档与其他
2026-10-31-plugin-injected-toolchains.md;在设置层级 Note 里补充这两页属于哪个分组。plugin-injected-toolchains,macOS 和 Windows 都标为 partial / pending。尚未完成
以下内容这次都没做,页面上也不显示:
验证
verify-rust-core-comments.sh、verify-rust-core.sh、cargo fmt --check都通过。test-stability-macos共 71 个测试,0 个失败。ToolchainSettingsLoaderTests、ToolchainSettingsFeatureTests、ToolchainSettingsLayoutTestsMacLoginShellEnvironmentTests(13 个)SettingsSearchVocabulary、SettingsSidebarcoldStartReadsTheShellAndWarmStartUsesTheCachedPath用时约 1.7 秒,超过 1 秒的提醒线,但低于 15 秒上限。这个测试本 PR 没有改动。verify-runtime-bundle-immutability.sh、verify-service-boundaries.sh、verify-shared-contracts.shverify-platform-feature-matrix.sh、verify-agent-notes.sh、verify-test-stability.sh