From 09e604f13f93ff3489f834d221154988adacc4a6 Mon Sep 17 00:00:00 2001 From: Yao Jingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Nov 2026 16:27:58 +0800 Subject: [PATCH] =?UTF-8?q?feat!:=20=E7=B4=A2=E5=BC=95=E5=92=8C=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=88=87=E5=88=B0=E6=8F=92=E4=BB=B6=E5=A5=91=E7=BA=A6?= =?UTF-8?q?=20v0=EF=BC=8C=E6=92=A4=E4=B8=8B=E6=97=A7=E5=A5=91=E7=BA=A6?= =?UTF-8?q?=E6=9D=A1=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 插件契约在 Core PR #17 把所有格式版本归零。Store 整份切到 v0: - validate-index.mjs:索引 schemaVersion 必须是 0;版本项 pluginAPIVersion 为非负整数, requires 必填、非空、能力版本为非负整数;归档校验拒绝 schemaVersion 或 pluginAPIVersion 不是 0 的包(plugin.schema.unsupported / plugin.api.unsupported)。 - track-releases.mjs:版本项始终写入 requires,按 v0 索引校验。 - fixtures 换成 Core shared/plugins/contract/v1/fixtures/marketplace 的 v0 副本,测试按 v0 重写。 - validate.yml 对照 Core 仓库的 plugin-index.schema.json,不再指向已退役的 lithe-plugin-docs。 - lithe-plugin-index.json 改为 schemaVersion 0,撤下全部旧契约(插件 API 1–4)条目; README 总览随之重新生成。 - 文档改指向 Core shared/plugins,并在 maintainers.md 记录这次切换与旧版 Lithe 的影响。 旧版 macOS Lithe 会拒绝整份索引,插件市场不再能读取 Lithe Store。 --- .github/workflows/validate.yml | 12 +- CONTRIBUTING.md | 10 +- README.md | 110 +--- docs/maintainers.md | 34 +- docs/publishing.md | 71 ++- docs/versioning-and-updates.md | 10 +- lithe-plugin-index.json | 638 +------------------- scripts/fixtures/index.json | 73 ++- scripts/fixtures/invalid-index-entries.json | 8 +- scripts/store.test.mjs | 155 +++-- scripts/track-releases.mjs | 4 +- scripts/validate-index.mjs | 43 +- 12 files changed, 250 insertions(+), 918 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index fa5be7a..a85f751 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -56,15 +56,17 @@ jobs: - name: Check the index contract has not changed # validate-index.mjs follows the index rules of the Lithe plugin - # contract, published in lithe-plugin-docs. A different schema there - # means the rules changed; see docs/maintainers.md. + # contract (v0), published in the Rust Core repository under + # shared/plugins/contract/v1. A different schema there means the rules + # changed; see docs/maintainers.md. env: - SCHEMA_URL: https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/plugin-index.schema.json - SCHEMA_SHA256: 6771cb0e8c1ba101e6fc90b436de1b74f057ff0d58cc90f92e669edc9a102fc6 + SCHEMA_URL: https://raw.githubusercontent.com/Lithe-IDEA/Lithe-core-rust/main/shared/plugins/contract/v1/plugin-index.schema.json + SCHEMA_SHA256: 18451ea7969bbf1c28a7df0554718c62599156e1155519059a6a9d16b9f89bea run: | status=$(curl -sS -o "$RUNNER_TEMP/plugin-index.schema.json" -w '%{http_code}' "$SCHEMA_URL") if [ "$status" = "404" ]; then - echo "::notice::lithe-plugin-docs does not publish plugin-index.schema.json yet; skipping contract check." + echo "::error::Lithe-core-rust no longer publishes shared/plugins/contract/v1/plugin-index.schema.json; find where the contract moved and update SCHEMA_URL; see docs/maintainers.md." + exit 1 elif [ "$status" != "200" ]; then echo "::warning::Could not fetch the published schema (HTTP $status)." else diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e76278..842ca0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,8 @@ - 插件图标填在条目的 `icon` 字段,指向插件仓库内固定到提交的 SVG/PNG(`raw.githubusercontent.com////…`);没有填写时 README 使用默认图标。 - 插件的 bug 与功能请求请提到插件自己的仓库,本仓库只处理收录相关事务。 -怎么写插件(开发指南、LSP 与构建工具教程、插件契约和 SDK)见 -[lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs);写好以后按[发布文档](docs/publishing.md)提交收录。 +怎么写插件(开发指南、LSP 与构建工具教程、插件契约和 SDK)见 Rust Core 仓库的 +[`shared/plugins/`](https://github.com/Lithe-IDEA/Lithe-core-rust/tree/main/shared/plugins);写好以后按[发布文档](docs/publishing.md)提交收录。 审查标准见 [docs/maintainers.md](docs/maintainers.md#审查清单)。 @@ -45,7 +45,7 @@ Lithe 不再区分官方插件和普通插件:本仓库收录的每个插件 | | Lithe-IDEA 组织插件 | 组织外插件 | | --- | --- | --- | -| 范围 | Lithe-IDEA 组织下的公开 `lithe-plugin-*` 仓库(`lithe-plugin-docs` 除外) | 其他公开 GitHub 仓库 | +| 范围 | Lithe-IDEA 组织下的公开 `lithe-plugin-*` 仓库(已退役的文档仓库 `lithe-plugin-docs` 除外) | 其他公开 GitHub 仓库 | | 首次收录 | bot 发现带插件包的正式 Release 后自动收录,不需要提交 PR | 提 PR 或开“插件收录申请” issue,维护者按[审查清单](docs/maintainers.md#审查清单)审核后合并 | | 新版本 | bot 开 PR,校验通过后自动合入 | bot 开 PR,维护者审核后合并 | @@ -58,7 +58,7 @@ Lithe 主仓库仍内置少量迁移前的 `dev.lithe.*` 插件,它们正在 | 文档 | 内容 | | --- | --- | -| [lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs) | 插件开发:开发指南、LSP 与构建工具教程、插件契约与 JSON Schema、SDK 下载 | +| [Lithe-core-rust `shared/plugins/`](https://github.com/Lithe-IDEA/Lithe-core-rust/tree/main/shared/plugins) | 插件开发:开发指南、LSP 与构建工具教程、插件契约与 JSON Schema、SDK | | [docs/publishing.md](docs/publishing.md) | 插件作者:如何打包、发布 Release、提交收录和发布新版本 | | [docs/versioning-and-updates.md](docs/versioning-and-updates.md) | 版本号如何追踪、Lithe 如何选择可安装版本、用户如何更新/回退 | | [docs/maintainers.md](docs/maintainers.md) | 维护者:审查清单、下架与撤回、跟进插件契约变化 | @@ -70,7 +70,7 @@ lithe-plugin-index.json # 唯一的数据文件,Lithe 客户端读取 scripts/validate-index.mjs # 与 Lithe Rust Core 同规则的校验 + 下载包校验 scripts/track-releases.mjs # 追踪已收录插件和 Lithe-IDEA 组织 lithe-plugin-* 仓库的新 Release,生成索引条目;--scope 区分组织插件与组织外插件 scripts/render-readme.mjs # 根据索引生成 README 的插件总览 -scripts/store.test.mjs # 校验脚本的测试(fixtures 复制自插件契约) +scripts/store.test.mjs # 校验脚本的测试(fixtures 复制自 Core 的插件契约) assets/ # README 用到的图片(无图标插件的默认图标) .github/workflows/ # validate(PR/推送/每周)、track-releases(每 6 小时,组织插件自动合入、组织外插件开 PR 审查)、sync-readme(main 更新后开 PR 同步 README) ``` diff --git a/README.md b/README.md index 1933fbf..bc7f671 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

🧩 浏览插件 · ⬇️ 安装方式 · - 📚 开发插件 · + 📚 开发插件 · 📤 发布插件 · 🤝 参与贡献

@@ -27,120 +27,24 @@ -**全部 · 12** | 构建工具 · 3 | 工具 · 5 | 语言支持 · 3 | 版本控制 · 1 +**全部 · 0** - - - - - - - - - - - - - - - - - - - - - - - -
-

Cargo

-

Lithe Community · v0.1.1 · API 3(需 buildProviders、buildSystems、serverProcess、settings)

-

Cargo 构建预发布插件:原生 workspace/包编译、检查、测试、清理及 binary/example 运行,支持 Release、Features 和 Offline。0.1.1 新增 Cargo 可执行文件路…

-

🏷️ 构建工具 · 💻 macOS · GitHub stars

-

build cargo rust testing

-
-

Database

-

Lithe Community · v0.1.1 · API 3(需 backend、serverProcess、ui)

-

数据库工具窗口:连接 MySQL、MariaDB、PostgreSQL、SQLite、SQL Server、MongoDB、Redis 和 Nacos,提供 SQL 控制台、表数据与结构编辑、CSV/JSON/SQL 导…

-

🏷️ 工具 · 💻 macOS · GitHub stars

-

tool database sql mysql postgresql redis

-
-

Docker

-

Lithe Community · v0.1.1 · API 3(需 backend、serverProcess、ui)

-

在工具窗口中管理本机 Docker 容器、镜像、卷和网络,支持容器原生终端、Compose 分组及配置路径、日志搜索与 JSON 高亮、可调采样间隔的 CPU/内存/网络/磁盘实时图,以及普通文件上传下载。需要支持 ba…

-

🏷️ 工具 · 💻 macOS · GitHub stars

-

tool docker containers compose logs monitoring

-
-

Go Support

-

Lithe Community · v0.1.0 · API 1

-

使用你自己安装的 gopls 提供 Go 补全、跳转定义和错误提示,并支持运行 main 包和 go test(整个模块、单个文件所在的包或单个测试函数)。需要先安装 Go 和 gopls(go install gola…

-

🏷️ 语言支持 · 💻 macOS · GitHub stars

-

language go

-
-

Gradle

-

Lithe Community · v0.1.1 · API 3(需 buildSystems)

-

预发布 Gradle 集成:项目与任务树、JavaExec 运行入口、筛选测试、编译诊断及依赖树。需要支持 API 3 buildSystems 和原生构建窗口的 Lithe preview 开发版、Gradle 7.6…

-

🏷️ 构建工具 · 💻 macOS · GitHub stars

-

build gradle java testing

-
-

HTTP Client

-

Lithe Community · v0.1.2 · API 3(需 backend、serverProcess、ui)

-

HTTP 请求工具预览版:支持打开请求文件、语法高亮、会话历史、可调整分栏、响应头阅读、原始响应保存和编辑区 HTML 预览。正文预览默认 128 KiB,可按 1 KiB 调整。仅支持 macOS;原生文件对话框和编辑…

-

🏷️ 工具 · 💻 macOS · GitHub stars

-

http rest tool

-
-

LLDB Debugger

-

Lithe Community · v0.1.0 · API 4(需 debugAdapters、languages、serverProcess、settings)

-

预发布 LLDB 调试器:通过你电脑上的 lldb-dap 调试 C、C++、Objective-C、Swift 和 Rust,支持断点、单步、变量,以及带补全的 lldb 控制台。需要支持插件 API 4 的 Lith…

-

🏷️ 工具 · 💻 macOS · GitHub stars

-

tool debug lldb cpp rust swift

-
-

Lua Support

-

Lithe Community · v0.3.1 · API 2

-

内置 lua-language-server(LuaLS)提供 Lua 补全、跳转定义和错误提示,无需另外安装;“运行当前文件”使用你自己的 lua(brew install lua)。

-

🏷️ 语言支持 · 💻 macOS · GitHub stars

-

language lua

-
-

Maven

-

Lithe Community · v0.1.1 · API 3(需 buildProviders、buildSystems、serverProcess)

-

预发布 Maven 集成:反应堆与目标树、Profiles、带 JUnit 报告的筛选测试、编译诊断及依赖树。附带只读 Maven 核心扩展;需要支持 API 3 buildSystems 和原生构建窗口的 Lithe…

-

🏷️ 构建工具 · 💻 macOS · GitHub stars

-

build maven java testing

-
-

mise

-

FxRayHughes · v0.2.1 · API 3(需 backend、serverProcess、settings、toolchainProviders)

-

预发布工具链提供方:告诉 Lithe mise 装了哪些 JDK、Node.js、Maven、Gradle、Go、Python 和 pnpm,以及项目的 mise.toml / .tool-versions 和全局 mi…

-

🏷️ 工具 · 💻 macOS · GitHub stars

-

tool toolchain mise java node

-
-

PHP Support

-

Lithe Community · v0.1.0 · API 1

-

使用你自己安装的 Intelephense 提供 PHP 补全、跳转定义和错误提示,并支持运行 PHP 文件和 PHPUnit 测试。插件不内置 Intelephense(其许可证不允许再分发),请用 npm insta…

-

🏷️ 语言支持 · 💻 macOS · GitHub stars

-

language php

-
-

Subversion

-

Lithe Community · v0.1.0 · API 3(需 serverProcess、vcsProviders)

-

Subversion 预发布插件:在版本控制工具窗口查看 SVN 工作副本的变更,支持提交、更新、回滚、解决冲突、清理、历史、Annotate 和与基线比较;项目树移动通过 svn move 记录。需要支持 API 3…

-

🏷️ 版本控制 · 💻 macOS · GitHub stars

-

vcs svn subversion

-
## ⬇️ 在 Lithe 中安装 -需要 Lithe 0.6.1 或更新版本(目前插件市场只在 macOS 提供)。Lithe 已预置本仓库作为插件源, -打开 **设置 → 插件 → 插件市场** 即可浏览和安装;安装前会弹窗列出插件权限、要运行的程序和来源仓库。 +本仓库收录的插件按插件契约 v0 编写,需要新版 Lithe 桌面端(`desktop-v*` 预发布版本)。 +旧版 macOS Lithe(0.x)读不了这份索引,插件市场会显示无法读取 Lithe Store。 -如果之前移除了 Lithe Store,在 **设置 → 插件 → ⚙ → 管理插件仓库…** 中重新添加 -`https://github.com/Lithe-IDEA/Lithe-store` 即可。 +桌面端已预置本仓库作为插件源:打开 **设置 → 插件 → 插件市场**,在“来源”中选择 Lithe Store +即可浏览和安装;安装前会弹窗列出插件权限、要运行的程序和来源仓库。 ## 🤝 参与贡献 -想写插件,先看 [lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs)。想收录自己的插件、发布新版本或报告有问题的插件,请看 [CONTRIBUTING.md](CONTRIBUTING.md)。 +想写插件,先看 Rust Core 仓库的 [`shared/plugins/`](https://github.com/Lithe-IDEA/Lithe-core-rust/tree/main/shared/plugins)。想收录自己的插件、发布新版本或报告有问题的插件,请看 [CONTRIBUTING.md](CONTRIBUTING.md)。 上方的插件总览由 `scripts/render-readme.mjs` 根据 [`lithe-plugin-index.json`](lithe-plugin-index.json) 生成,索引合并到 main 后由 CI 自动同步。 ## 📄 许可 diff --git a/docs/maintainers.md b/docs/maintainers.md index 4174c74..b6037a3 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -4,9 +4,10 @@ CI(`Validate index`)已经自动保证: -- 索引符合 Lithe 契约 v1 与本仓库策略(按 `id` 排序、无重复 ID、无 `dev.lithe.*`); +- 索引符合 Lithe 插件契约 v0 与本仓库策略(按 `id` 排序、无重复 ID、无 `dev.lithe.*`); - 每个新增或变更的下载包可下载、SHA-256 一致; -- 包内恰好一个匹配的 `plugin.json`,`version`、`pluginAPIVersion` 与索引一致, +- 包内恰好一个匹配的 `plugin.json`,`schemaVersion` 与 `pluginAPIVersion` 为 `0`, + `version`、`pluginAPIVersion`、`requires` 与索引一致, `vendor.signatureRequirement` 为 `"none"`,没有不安全的路径。 维护者人工确认(Lithe-IDEA 组织插件的 bot PR 由工作流自动合入,不走这份清单,见[自动追踪新版本](#自动追踪新版本)): @@ -26,7 +27,7 @@ bot 首次收录的组织插件(组织 PR 描述里的“首次收录”)合 `Track plugin releases` 每 6 小时运行一次,也可在 Actions 中对 `main` 手动运行(可只指定一个插件 ID;从其他分支启动不会执行)。 它扫描已收录插件的 Release,以及 Lithe-IDEA 组织下尚未收录的公开 `lithe-plugin-*` 仓库 -(`lithe-plugin-docs` 不发布插件,固定排除),只收录正式 Release;新插件的条目生成规则见 +(已退役的文档仓库 `lithe-plugin-docs` 不发布插件,固定排除),只收录正式 Release;新插件的条目生成规则见 [发布插件的首次收录](publishing.md#3-首次收录)。手动运行时指定的插件 ID 可以是已收录插件, 也可以是组织中尚未收录的插件。 @@ -104,18 +105,31 @@ README 的插件总览由 `scripts/render-readme.mjs` 根据索引生成。`main ## 跟进插件契约变化 +插件契约只在 Rust Core 仓库 [Lithe-IDEA/Lithe-core-rust](https://github.com/Lithe-IDEA/Lithe-core-rust/tree/main/shared/plugins) 的 +`shared/plugins/contract/v1/`(目录名是 `v1`,其中的格式版本都是 `0`),本仓库不保存 Schema 副本。 `scripts/validate-index.mjs` 实现与 Lithe Core(`rust/lithe-core/src/plugins/marketplace.rs`) -相同的索引条目规则(本仓库更严格:Lithe 会忽略的无效条目在这里直接让 CI 失败); -`scripts/fixtures/` 复制自插件契约的 `fixtures/marketplace/`。插件契约的公开副本在 -[lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs) 的 `contract/v1/`,本仓库不再保存 Schema 副本。 +相同的索引条目规则(本仓库更严格:Lithe 会忽略的无效条目在这里直接让 CI 失败),归档校验另外按 +契约拒绝 `schemaVersion` 或 `pluginAPIVersion` 不是 `0` 的包;`scripts/fixtures/` 复制自 +契约的 `fixtures/marketplace/`。 -`Validate index` 会检查 lithe-plugin-docs 上 `plugin-index.schema.json` 的 SHA-256 是否等于 +`Validate index` 会检查 Core 仓库 `main` 上 `plugin-index.schema.json` 的 SHA-256 是否等于 工作流里记录的值;不一致说明索引契约变了,CI 失败。此时: -1. 对照 lithe-plugin-docs 的提交记录查看 Schema 和契约 README 改了什么; +1. 对照 Core 仓库 `shared/plugins/` 的提交记录和 + [插件协议更新记录](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/reference/protocol-changelog.md) 查看 Schema 和契约 README 改了什么; 2. 复制新的 `fixtures/marketplace/{index,invalid-index-entries}.json`,按契约 README 更新 `validate-index.mjs`; 3. 运行 `node --test scripts/store.test.mjs`,所有 fixture 用例必须通过; 4. 把 `.github/workflows/validate.yml` 里的 `SCHEMA_SHA256` 换成新 Schema 的 SHA-256。 -如果契约升级到新的 `schemaVersion`,旧版 Lithe 会拒绝整个索引,需要先与主仓库商定迁移方案 -(例如并行发布新文件名),不要直接改 `lithe-plugin-index.json` 的 `schemaVersion`。 +### 2026-11-12:切换到契约 v0 + +插件契约在 2026-11-11 把所有格式版本归零(Core PR #17):索引 `schemaVersion` 为 `0`, +包清单 `schemaVersion`、`pluginAPIVersion` 都是 `0`,`requires` 必填,每项能力版本是 `0`。 +本仓库在 2026-11-12 整份切到 v0,并撤下了全部按旧契约(索引 `schemaVersion` 1、插件 API 1–4) +发布的条目: + +- 旧版 macOS Lithe 会拒绝整份索引,它的插件市场不再能读取 Lithe Store;这是有意的取舍, + 旧版 Lithe 已停止更新,新版桌面端只认 v0。 +- 被撤下的组织插件要按 v0 重写清单并发布新版本,才能重新收录;组织外插件请作者同样按 v0 发布后再提 PR。 +- 以后契约再换 `schemaVersion` 时,先与主仓库商定迁移方案(例如并行发布新文件名), + 不要直接改 `lithe-plugin-index.json` 的 `schemaVersion`。 diff --git a/docs/publishing.md b/docs/publishing.md index 8bff64e..64da083 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -2,10 +2,11 @@ 这份文档面向插件作者:从打包到被 Lithe 用户看到的完整流程。 -还没有可安装的插件包时,先跟着 lithe-plugin-docs 的 -[LSP 插件完整生命周期教程](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/guides/lsp-plugin-lifecycle.md) +插件契约、开发指南和 SDK 都在 Rust Core 仓库的 [`shared/plugins/`](https://github.com/Lithe-IDEA/Lithe-core-rust/tree/main/shared/plugins)。 +还没有可安装的插件包时,先跟着 +[LSP 插件完整生命周期教程](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/guides/lsp-plugin-lifecycle.md) 完成清单、语言服务器构建和运行验收;需要接入命令、测试或构建后端时,阅读 -[构建、运行与测试接入指南](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/guides/build-tool-integration.md)。 +[构建、运行与测试接入指南](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/guides/build-tool-integration.md)。 ```text 你的插件仓库 Lithe-store Lithe 用户 @@ -19,20 +20,22 @@ ## 1. 插件包要求 -插件包遵守 Lithe 插件契约 v1 -([`shared/contracts/plugins/v1/README.md`](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/plugins/v1/README.md))。 -收录到本仓库的插件额外要求: +插件包遵守 Lithe 插件契约 v0 +([`shared/plugins/contract/v1/README.md`](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/contract/v1/README.md);目录名仍是 `v1`, +其中的格式版本都是 `0`)。只有新版 Lithe 桌面端(`desktop-v*`)能安装 v0 插件,旧版 macOS Lithe +读不了本仓库的索引。收录到本仓库的插件额外要求: - `plugin.json` 的 `id` 使用你自己的反向域名,例如 `io.github.<你的用户名>.zig-support`; **不能**使用 `dev.lithe` 或 `dev.lithe.*`。 - `vendor.signatureRequirement` 必须是 `"none"`(插件包不带发布者签名)。 - `version` 是 `MAJOR.MINOR.PATCH`,每次发布都必须递增,已发布的版本号不能复用。 -- `pluginAPIVersion: 3` 必须有非空 `requires`,逐项声明包实际使用的能力及其契约版本。 - 例如只使用声明式构建系统的包为 `"requires": { "buildSystems": 1 }`;不要无故增加 - `buildProviders` 等能力。API 1/2 继续使用原有格式,不能带 `requires`。 - API 4(调试适配器)同样在 `requires` 里逐项声明,例如 `"debugAdapters": 1`。 +- `plugin.json` 的 `schemaVersion` 和 `pluginAPIVersion` 都必须是 `0`,其他值的包不收录。 +- `requires` 必填且不能为空,逐项列出包实际使用的能力,目前每项的版本都是 `0`。 + 例如只使用声明式构建系统和设置的包为 `"requires": { "buildSystems": 0, "settings": 0 }`; + 不要列出没用到的能力,也不要漏掉用到的能力,Core 会拒绝两者不一致的清单。 - `requires` 的键是小写字母开头、后接英文字母或数字、最长 64 个字符的能力 ID, - 值是正整数。索引允许未知能力 ID,由宿主能力表决定可安装性;这不表示当前宿主支持它。 + 值是非负整数。索引允许未知能力 ID 和更高的能力版本,由宿主能力表决定能不能安装; + 这不表示当前宿主支持它。 - 包内路径只用 `/`,不能有 `..`、绝对路径或符号链接。 - 可以附带可执行文件(例如打包好的语言服务器),但它们必须在 `plugin.json` 中声明; 用户安装前会看到完整的程序列表并确认。版本更新新增了程序时,用户需要重新确认。 @@ -50,7 +53,7 @@ zig-support-2.0.0-darwin-arm64.zip ``` 同一插件版本的所有平台包必须使用相同的 `version`、`pluginAPIVersion` 和 `requires`。 -能力表按键和值比较,键的书写顺序可以不同。API 3 索引版本项必须逐项复制包清单的 +能力表按键和值比较,键的书写顺序可以不同。索引版本项必须逐项复制包清单的 `requires`;不同平台需要不同能力时,应使用不同的插件版本,不能共用一个版本项。 Release 资产名必须以平台结尾,自动追踪才能识别: @@ -77,7 +80,7 @@ shasum -a 256 zig-support-2.0.0-darwin-arm64.zip ## 3. 首次收录 -Lithe-IDEA 组织下名为 `lithe-plugin-*` 的公开仓库(`lithe-plugin-docs` 除外,它不发布插件) +Lithe-IDEA 组织下名为 `lithe-plugin-*` 的公开仓库(已退役的文档仓库 `lithe-plugin-docs` 除外,它不发布插件) 不需要手工提交首次收录 PR。`Track plugin releases` 发现这类仓库还没收录、并且已经有带插件包的 正式 Release 时,会把它连同全部正式版本一起放进组织插件的 bot PR,校验通过后自动合入 main, 条目按以下来源生成: @@ -114,7 +117,8 @@ pre-release 的仓库不会被收录。 "versions": [ { "version": "2.0.0", - "pluginAPIVersion": 1, + "pluginAPIVersion": 0, + "requires": { "languageServers": 0, "languages": 0 }, "releaseDate": "2026-10-12", "downloads": [ { @@ -128,19 +132,14 @@ pre-release 的仓库不会被收录。 } ``` - 字段说明见 [plugin-index.schema.json](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/contract/v1/plugin-index.schema.json)。`homepage`、 + 字段说明见 [plugin-index.schema.json](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/contract/v1/plugin-index.schema.json) + 和[插件市场参考](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/reference/domains/marketplace.md)。`homepage`、 `localizedDescriptions`、`tags`、`icon` 可选;`icon` 只能是同一仓库的 raw 文件。 - API 3 的版本项还必须在 `pluginAPIVersion` 旁加入与包内相同的能力表,例如: + 每个版本项的 `pluginAPIVersion` 和 `requires` 都必须与包内 `plugin.json` 相同 + (自动追踪就是这样写的),宿主不用下载就能据此判断自己是否具备这些能力。 - ```json - "pluginAPIVersion": 3, - "requires": { "buildSystems": 1 } - ``` - - 提高 API 等级或能力版本时发布新的插件版本,并保留最后一个旧要求的版本项, - 让旧宿主仍能选择可安装版本。Core 对 API 3 以上的索引项允许省略 `requires`, - 提供时仍校验能力表;API 4 的版本项应像 API 3 一样逐项复制包内的 `requires` - (自动追踪就是这样写的),宿主才能据此判断自己是否具备这些能力。 + 以后改用新的能力版本时发布新的插件版本,并保留最后一个旧要求的版本项, + 让还没支持新版本的宿主仍能选择可安装版本。 2. 统一格式并本地校验(CI 要求索引使用统一格式:两格缩进,纯值列表写在一行): @@ -150,8 +149,8 @@ pre-release 的仓库不会被收录。 ``` 3. 提交 PR,按模板填写。CI 会下载每个新增或校验预期发生变化的包,核对 SHA-256、 - 包内 `plugin.json` 的 `id`、`version`、`pluginAPIVersion`、`requires` 和 - `signatureRequirement`。即使 URL 和 SHA-256 不变,修改 ID、版本、API 等级或能力表 + 包内 `plugin.json` 的 `id`、`version`、`schemaVersion`、`pluginAPIVersion`、`requires` 和 + `signatureRequirement`。即使 URL 和 SHA-256 不变,修改 ID、版本、插件 API 版本或能力表 也会重新校验;完整校验不使用基线跳过。维护者审查后合并。 索引与归档检查只覆盖上述发布字段、社区信任声明和现有路径检查,不替代完整的 Core @@ -177,19 +176,17 @@ pre-release 的仓库不会被收录。 (保持新版本在前)。 自动追踪只收录正式 Release,继续跳过 draft 和 pre-release;预发布版本需要手工提交 -索引条目并接受相同的校验,不能靠调整追踪策略自动收录。Maven、Gradle 的 `0.1.0` -预发布收录属于这种手工流程,条目是否存在不代表发布或运行验证已经完成。 +索引条目并接受相同的校验,不能靠调整追踪策略自动收录。条目是否存在不代表发布或运行验证已经完成。 -需要新插件 API 的版本(例如调试适配器插件需要 API 4)要等到稳定版 Lithe 支持该 API 才能让 -稳定版用户安装;稳定版是否支持,以 Lithe 的发行说明为准。支持之前,把 GitHub Release 标为 -预发布,并手工提交索引条目(LLDB Debugger `0.1.0` 就是这样收录的)。稳定版 Lithe 遇到自己 -不支持的 API 等级只会显示“需要更新 Lithe”,不会安装,所以不会装出坏插件。稳定版支持之后, -把 Release 转为正式发布(或发布新版本),自动追踪就会接手后续版本;还没收录的 Lithe-IDEA -组织插件会在这时被自动首次收录。调试适配器插件的写法见 -[编写调试适配器插件](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/guides/debug-adapter-plugin.md)。 +新版 Lithe 桌面端以 `desktop-v*` 预发布版本发布期间,v0 插件也以 GitHub 预发布版本发布, +并手工提交索引条目(Go Support、Maven、LLDB Debugger 的 `0.0.1` 就是这样收录的)。 +宿主遇到自己不具备的能力或能力版本只会显示“需要更新 Lithe”,不会安装,所以不会装出坏插件。 +桌面端发布正式版之后,把 Release 转为正式发布(或发布新版本),自动追踪就会接手后续版本; +还没收录的 Lithe-IDEA 组织插件会在这时被自动首次收录。调试适配器插件的写法见 +[编写调试适配器插件](https://github.com/Lithe-IDEA/Lithe-core-rust/blob/main/shared/plugins/guides/debug-adapter-plugin.md)。 自动追踪会在日志中说明归档无法收录的原因:没有符合命名的 zip、包内 `plugin.json` -不匹配或缺失/畸形的能力表、多个资产声明了不同的版本号、API 等级或能力表、 +不匹配或缺失/畸形的能力表、多个资产声明了不同的版本号、插件 API 版本或能力表、 版本号已被别的资产占用。单个发布存在分歧时拒绝整个版本,不只保留其中一个平台包。 ## 5. 修改信息、转移或下架 diff --git a/docs/versioning-and-updates.md b/docs/versioning-and-updates.md index 1a6233e..48364a9 100644 --- a/docs/versioning-and-updates.md +++ b/docs/versioning-and-updates.md @@ -14,7 +14,8 @@ | 字段 | 作用 | | --- | --- | -| `pluginAPIVersion` | 该版本需要的 Lithe 插件 API 版本 | +| `pluginAPIVersion` | 该版本的插件 API 版本,契约 v0 中固定为 `0` | +| `requires` | 该版本使用的能力及每项能力的版本,与包内 `plugin.json` 相同 | | `releaseDate` | 发布日期,显示在插件详情中 | | `downloads[].platforms` | 该版本提供哪些平台的包;每个平台在一个版本中只能出现一次 | @@ -26,10 +27,11 @@ Windows 用户会被安装 `1.9.0`。 Lithe 的 Rust Core(`plugins.marketplace.validateIndex`)对每个插件计算 `availability`: 1. 从新到旧遍历 `versions`; -2. 选第一个**有当前平台的包**且 **`pluginAPIVersion` 不高于当前 Lithe 支持版本**的版本; +2. 选第一个**有当前平台的包**、`pluginAPIVersion` 为 `0`,且 **`requires` 里每项能力都被当前 Lithe + 以所写版本支持**的版本; 3. 找不到时显示原因: - 当前平台在任何版本都没有包 → “暂不支持 <平台> 平台”(`plugin.index.platformUnsupported`); - - 有包但都需要更新的插件 API → 提示升级 Lithe(`plugin.index.apiUnsupported`)。 + - 有包但都需要当前 Lithe 没有的能力或能力版本 → 提示升级 Lithe(`plugin.index.apiUnsupported`)。 所以旧版 Lithe 用户会自动停在最后一个兼容版本,不会被推送无法运行的新版本。 **这也是不能删除历史版本的原因**:删掉后,旧 Lithe 用户就没有可装的版本了。 @@ -50,6 +52,6 @@ Lithe 的 Rust Core(`plugins.marketplace.validateIndex`)对每个插件计 | 想做的事 | 正确做法 | | --- | --- | | 修复已发布版本的 bug | 发新的 PATCH 版本,不要替换旧 Release 的资产 | -| 新版本需要更新的 Lithe 插件 API | 在新版本 `plugin.json` 中提高 `pluginAPIVersion`;旧版本保留给旧 Lithe | +| 新版本要用新的能力或能力版本 | 在新版本 `plugin.json` 的 `requires` 中写上它,索引版本项照抄;旧版本保留给旧 Lithe | | 暂时只支持部分平台 | 只上传对应平台的 zip;其他平台用户会继续使用旧版本或看到“不支持” | | 某个版本有严重问题 | 发修复版本,并按[维护者文档](maintainers.md#下架与撤回)申请撤回问题版本 | diff --git a/lithe-plugin-index.json b/lithe-plugin-index.json index 298804c..a9c68b5 100644 --- a/lithe-plugin-index.json +++ b/lithe-plugin-index.json @@ -1,638 +1,4 @@ { - "schemaVersion": 1, - "plugins": [ - { - "id": "io.github.fxrayhughes.mise", - "name": "mise", - "author": "FxRayHughes", - "repository": "https://github.com/FxRayHughes/lithe-plugin-mise", - "homepage": "https://github.com/FxRayHughes/lithe-plugin-mise#readme", - "description": "Preview toolchain provider: tells Lithe which JDK, Node.js, Maven, Gradle, Go, Python and pnpm mise installed, and which versions a project's mise.toml or .tool-versions and your global mise config request, so no paths are added by hand. Its backend only runs read-only mise commands (mise ls, mise bin-paths) offline; it never installs or trusts anything. Requires a Lithe preview build with the toolchainProviders capability and mise on your PATH; macOS only.", - "localizedDescriptions": { - "zh-Hans": "预发布工具链提供方:告诉 Lithe mise 装了哪些 JDK、Node.js、Maven、Gradle、Go、Python 和 pnpm,以及项目的 mise.toml / .tool-versions 和全局 mise 配置要求哪个版本,无需手动添加路径。后端只以离线方式运行只读的 mise 命令(mise ls、mise bin-paths),不会安装或信任任何东西。需要支持 toolchainProviders 能力的 Lithe preview 开发版,以及 PATH 中可用的 mise,仅支持 macOS。" - }, - "tags": ["tool", "toolchain", "mise", "java", "node"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/FxRayHughes/lithe-plugin-mise/ced89b3dd0a5fe11f9d5e6da73dfaa3e65a350e9/io.github.fxrayhughes.mise/icons/mise.svg", - "versions": [ - { - "version": "0.2.1", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "serverProcess": 1, - "settings": 2, - "toolchainProviders": 1 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/FxRayHughes/lithe-plugin-mise/releases/download/v0.2.1/mise-0.2.1-darwin.zip", - "sha256": "635366955e73f64ef6a821e3af9bee043c8c1ac871e6a44ddc3d43aae4823755" - } - ] - }, - { - "version": "0.2.0", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "serverProcess": 1, - "settings": 2, - "toolchainProviders": 1 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/FxRayHughes/lithe-plugin-mise/releases/download/v0.2.0/mise-0.2.0-darwin.zip", - "sha256": "2b088c02aec5707b78e3da4de82ad45a8d22e8c8010a8a01afa26947b8561f82" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "toolchainSources": 1 - }, - "releaseDate": "2026-10-29", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/FxRayHughes/lithe-plugin-mise/releases/download/v0.1.0/mise-0.1.0-darwin.zip", - "sha256": "e8020787ee45264159c0026717b694d6b64976a5afc951eba20e153128527a1e" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.cargo-build", - "name": "Cargo", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-cargo", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-cargo#readme", - "description": "Preview Cargo build integration: native workspace/package build, check, test, clean and binary/example runs, with Release, Features and Offline. Version 0.1.1 adds a Cargo executable path setting and requires buildSystems 2/settings 2, Cargo 1.70+ and Node.js 20.3+. Older hosts retain 0.1.0. Full native UI acceptance is pending; macOS only.", - "localizedDescriptions": { - "zh-Hans": "Cargo 构建预发布插件:原生 workspace/包编译、检查、测试、清理及 binary/example 运行,支持 Release、Features 和 Offline。0.1.1 新增 Cargo 可执行文件路径设置,需要 buildSystems 2/settings 2、Cargo 1.70+ 和 Node.js 20.3+;旧宿主保留兼容的 0.1.0。完整原生界面验收待完成,仅支持 macOS。" - }, - "tags": ["build", "cargo", "rust", "testing"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-cargo/8fea6ad38995ce31d657da371420df6b8e8c0e7d/io.github.lithe-idea.cargo-build/icons/cargo.svg", - "versions": [ - { - "version": "0.1.3", - "pluginAPIVersion": 3, - "requires": { - "buildProviders": 1, - "buildSystems": 1, - "executableResolution": 1, - "serverProcess": 1, - "settings": 2 - }, - "releaseDate": "2026-11-10", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-cargo/releases/download/v0.1.3/cargo-build-0.1.3-darwin.zip", - "sha256": "54a9df8a3dab75086cbce985d98c418896a5b59a765cb9a914bc8fdfb9539497" - } - ] - }, - { - "version": "0.1.1", - "pluginAPIVersion": 3, - "requires": { - "buildProviders": 1, - "buildSystems": 2, - "serverProcess": 1, - "settings": 2 - }, - "releaseDate": "2026-10-22", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-cargo/releases/download/v0.1.1/cargo-build-0.1.1-darwin.zip", - "sha256": "cfcb293bc25aede904ca3d40d699ba22d84a0bcbac446f818aa0eec8549f2a4f" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "buildProviders": 1, - "buildSystems": 1, - "serverProcess": 1 - }, - "releaseDate": "2026-10-22", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-cargo/releases/download/v0.1.0/cargo-build-0.1.0-darwin.zip", - "sha256": "a6148b3fe931e0b8d51f3a305592d4827ba5b452ec905f921560bb84a3f949a5" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.database", - "name": "Database", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-database", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-database#readme", - "description": "Database tool window: connections to MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, MongoDB, Redis and Nacos, with a SQL console, table data and structure editing, CSV/JSON/SQL import and export, SQL history, read-only and production protection, and DBX connection import. Replaces Lithe's former built-in database module, whose saved connections and passwords move in on first start. Requires a Lithe preview with backend 1 and web bridge 2; macOS only.", - "localizedDescriptions": { - "zh-Hans": "数据库工具窗口:连接 MySQL、MariaDB、PostgreSQL、SQLite、SQL Server、MongoDB、Redis 和 Nacos,提供 SQL 控制台、表数据与结构编辑、CSV/JSON/SQL 导入导出、SQL 历史、只读与生产保护,以及 DBX 连接导入。取代 Lithe 以前内置的数据库模块,首次启动时自动迁入已保存的连接和密码。需要支持 backend 1 和网页桥接第 2 版的 Lithe preview;仅支持 macOS。" - }, - "tags": ["tool", "database", "sql", "mysql", "postgresql", "redis"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-database/bf531b5a40495c404d857aab5fd6a55d3a116921/assets/database.svg", - "versions": [ - { - "version": "0.1.1", - "pluginAPIVersion": 3, - "requires": { - "backend": 1, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-10-24", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-database/releases/download/v0.1.1/database-0.1.1-darwin.zip", - "sha256": "8882e0e2f5106286ea16e89f0c21e7d73de9765306966d63b4580e5286d33005" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "backend": 1, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-10-24", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-database/releases/download/v0.1.0/database-0.1.0-darwin.zip", - "sha256": "295fd702dbf6839ca3745e74e17d43ae88ed2f8ecc7981817e658534e4fc03c6" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.docker", - "name": "Docker", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-docker", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-docker#readme", - "description": "Manage local Docker containers, images, volumes and networks in a tool window, with native container terminals, Compose groups and configuration paths, searchable logs and JSON highlighting, live CPU/memory/network/disk charts with adjustable sampling, and regular-file upload/download. Requires a macOS Lithe host with backend 2, Web UI 2, file dialogs and browser.open. Terminals require API-24 support in a local workspace, Docker CLI on the terminal PATH, and the selected /bin/sh, /bin/bash or /bin/ash shell inside the container. Container file browsing needs /bin/sh and stat -c; transfers are limited to regular files up to 256 MiB. Native terminal, file-dialog and Finder drag-in acceptance is pending; macOS only.", - "localizedDescriptions": { - "zh-Hans": "在工具窗口中管理本机 Docker 容器、镜像、卷和网络,支持容器原生终端、Compose 分组及配置路径、日志搜索与 JSON 高亮、可调采样间隔的 CPU/内存/网络/磁盘实时图,以及普通文件上传下载。需要支持 backend 2、Web UI 2、文件对话框和 browser.open 的 macOS Lithe 宿主。终端需要支持 API-24 的本机工作区、终端 PATH 中可用的 Docker CLI,以及容器内所选的 /bin/sh、/bin/bash 或 /bin/ash。容器文件浏览需要 /bin/sh 和 stat -c;单文件传输上限 256 MiB。原生终端、文件对话框与 Finder 拖入验收待完成,仅支持 macOS。" - }, - "tags": ["tool", "docker", "containers", "compose", "logs", "monitoring"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docker/fb0e6eed5be851e282eb4d38236554f615deab92/icons/docker.svg", - "versions": [ - { - "version": "0.1.1", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-docker/releases/download/v0.1.1/docker-0.1.1-darwin.zip", - "sha256": "736b9d49f5218caf3e8fd119fc46159b61659842dd2da48d27c73641a2b98c40" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-docker/releases/download/v0.1.0/docker-0.1.0-darwin.zip", - "sha256": "ba56adf2caf4899b2c368a683da682a638e8539cfb574facd4b85cc54068d6c9" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.go-support", - "name": "Go Support", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-go", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-go#readme", - "description": "Go code intelligence through your own gopls, plus running main packages and go test (whole module, one file's package, or a single test). Needs Go and gopls (go install golang.org/x/tools/gopls@latest).", - "localizedDescriptions": { - "zh-Hans": "使用你自己安装的 gopls 提供 Go 补全、跳转定义和错误提示,并支持运行 main 包和 go test(整个模块、单个文件所在的包或单个测试函数)。需要先安装 Go 和 gopls(go install golang.org/x/tools/gopls@latest)。" - }, - "tags": ["language", "go"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-go/14321930779d1b441e1b7d13d2a4a741ef8f305c/io.github.lithe-idea.go-support/icons/go.svg", - "versions": [ - { - "version": "0.1.0", - "pluginAPIVersion": 1, - "releaseDate": "2026-10-20", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-go/releases/download/v0.1.0/go-support-0.1.0-darwin.zip", - "sha256": "032ec66b0748fb8d68fa54063a6a735f7b4273e588eb8ecca9db42c1ed06e1df" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.gradle-build", - "name": "Gradle", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-gradle", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-gradle#readme", - "description": "Preview Gradle integration: project and task trees, JavaExec run entries, filtered tests, compiler diagnostics and dependency trees. Requires a Lithe preview build with API 3 buildSystems and native build windows, Gradle 7.6+ and a compatible Java runtime; Gradle is not bundled.", - "localizedDescriptions": { - "zh-Hans": "预发布 Gradle 集成:项目与任务树、JavaExec 运行入口、筛选测试、编译诊断及依赖树。需要支持 API 3 buildSystems 和原生构建窗口的 Lithe preview 开发版、Gradle 7.6+ 及兼容的 Java;插件不附带 Gradle。" - }, - "tags": ["build", "gradle", "java", "testing"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-gradle/88a86cae73403d715d1385e447d8e4a9273e8f2f/io.github.lithe-idea.gradle-build/icons/gradle.svg", - "versions": [ - { - "version": "0.1.2", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "buildSystems": 1, - "pluginFunctions": 1, - "projectGenerators": 1, - "serverProcess": 1 - }, - "releaseDate": "2026-11-10", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-gradle/releases/download/v0.1.2/gradle-build-0.1.2-darwin.zip", - "sha256": "c52e4aed2e41b381813373c97a56af1674e71e51bb164b4766c6209d490c0a1b" - } - ] - }, - { - "version": "0.1.1", - "pluginAPIVersion": 3, - "requires": { - "buildSystems": 1 - }, - "releaseDate": "2026-10-18", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-gradle/releases/download/v0.1.1/gradle-build-0.1.1-darwin.zip", - "sha256": "6d0c49d4c95d9a03afff372d4cad40017a97120bd7b43d903b7dc2d8e82a841f" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "buildSystems": 1 - }, - "releaseDate": "2026-10-18", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-gradle/releases/download/v0.1.0/gradle-build-0.1.0-darwin.zip", - "sha256": "3c8ac1398b53df1dcb7ffd4a3e93308f39d6796f37f65b9692f4b9327c180215" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.http-client", - "name": "HTTP Client", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-http-client", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-http-client#readme", - "description": "Preview HTTP workbench with request-file loading, syntax highlighting, session history, resizable panes, readable headers, raw response exports and HTML editor previews. Body previews default to 128 KiB, adjustable in 1 KiB steps. macOS only; full native file-dialog and editor-preview acceptance remains pending.", - "localizedDescriptions": { - "zh-Hans": "HTTP 请求工具预览版:支持打开请求文件、语法高亮、会话历史、可调整分栏、响应头阅读、原始响应保存和编辑区 HTML 预览。正文预览默认 128 KiB,可按 1 KiB 调整。仅支持 macOS;原生文件对话框和编辑区预览的完整验收仍待完成。" - }, - "tags": ["http", "rest", "tool"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-http-client/e3cead6da09ecd07fa6964e66ca903219b374634/assets/http.svg", - "versions": [ - { - "version": "0.1.2", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-http-client/releases/download/v0.1.2/http-client-0.1.2-darwin.zip", - "sha256": "1a4aecbb8901c3243c213643fd2fc6e4e65655c170173eda005c8f0794e9ad9f" - } - ] - }, - { - "version": "0.1.1", - "pluginAPIVersion": 3, - "requires": { - "backend": 1, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-http-client/releases/download/v0.1.1/http-client-0.1.1-darwin.zip", - "sha256": "7eab10168660a9dd49423916ed3303559fae53e1668f1f32fa61e6ad992cc24c" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "backend": 1, - "serverProcess": 1, - "ui": 2 - }, - "releaseDate": "2026-11-02", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-http-client/releases/download/v0.1.0/http-client-0.1.0-darwin.zip", - "sha256": "699d04a4ddc69e46c27d79ac6063f214eef6af2ee81edf0b30db7595e9657b74" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.lldb-debug", - "name": "LLDB Debugger", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-lldb", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-lldb#readme", - "description": "Preview LLDB debugger: debug C, C++, Objective-C, Swift and Rust through your own lldb-dap, with breakpoints, stepping, variables and an lldb console with completion. Requires a Lithe preview build with plugin API 4, and lldb-dap from the Xcode Command Line Tools or LLVM.", - "localizedDescriptions": { - "zh-Hans": "预发布 LLDB 调试器:通过你电脑上的 lldb-dap 调试 C、C++、Objective-C、Swift 和 Rust,支持断点、单步、变量,以及带补全的 lldb 控制台。需要支持插件 API 4 的 Lithe preview 开发版,以及来自 Xcode 命令行工具或 LLVM 的 lldb-dap。" - }, - "tags": ["tool", "debug", "lldb", "cpp", "rust", "swift"], - "license": "Apache-2.0", - "versions": [ - { - "version": "0.1.0", - "pluginAPIVersion": 4, - "requires": { - "debugAdapters": 1, - "languages": 1, - "serverProcess": 1, - "settings": 2 - }, - "releaseDate": "2026-10-26", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lldb/releases/download/v0.1.0/lldb-debug-0.1.0-darwin.zip", - "sha256": "b550668dd9ea3e25098cf3fd32850a51f144865cc109c7164ee1b877e02fe69f" - } - ] - } - ], - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-lldb/24ac947b5db12dc49414c1933a7af6057d4b1051/assets/lldb.svg" - }, - { - "id": "io.github.lithe-idea.lua-support", - "name": "Lua Support", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-lua", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-lua#readme", - "description": "Lua code intelligence through the bundled lua-language-server (LuaLS), plus running the current file with your own lua. Nothing else to install for completion, navigation, and diagnostics; running files needs lua (brew install lua).", - "localizedDescriptions": { - "zh-Hans": "内置 lua-language-server(LuaLS)提供 Lua 补全、跳转定义和错误提示,无需另外安装;“运行当前文件”使用你自己的 lua(brew install lua)。" - }, - "tags": ["language", "lua"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-lua/c5f88bf94e9d5616c7770e58febc53a0d520dd50/io.github.lithe-idea.lua-support/icons/lua.svg", - "versions": [ - { - "version": "0.3.1", - "pluginAPIVersion": 2, - "releaseDate": "2026-11-04", - "downloads": [ - { - "platforms": ["darwin-arm64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lua/releases/download/v0.3.1/lua-support-0.3.1-darwin-arm64.zip", - "sha256": "0b9b88f7e02f4190d8ee4d3d982003362f078c94cd4e6690d711b9fe77b12efa" - }, - { - "platforms": ["darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lua/releases/download/v0.3.1/lua-support-0.3.1-darwin-x64.zip", - "sha256": "a44059ddfa414960c8f05cea9ad25082fafef3298ea0626160b2c25d92a8791b" - } - ] - }, - { - "version": "0.3.0", - "pluginAPIVersion": 1, - "releaseDate": "2026-10-12", - "downloads": [ - { - "platforms": ["darwin-arm64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lua/releases/download/v0.3.0/lua-support-0.3.0-darwin-arm64.zip", - "sha256": "8a01d3ca34bd43217525f0c0304501187ecebb5360a278df1189af7698099924" - }, - { - "platforms": ["darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lua/releases/download/v0.3.0/lua-support-0.3.0-darwin-x64.zip", - "sha256": "35e6c1c9b80b73770cafd04660070d3b856e690304b5de817fbe59c4d6507ccf" - } - ] - }, - { - "version": "0.2.0", - "pluginAPIVersion": 1, - "releaseDate": "2026-10-12", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lua/releases/download/v0.2.0/lua-support-0.2.0-darwin.zip", - "sha256": "9d9def8e72e225d9c76121d16641ad87f5b02a220178a06f8cf06dd895d15a14" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 1, - "releaseDate": "2026-10-12", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-lua/releases/download/v0.1.0/lua-support-0.1.0-darwin.zip", - "sha256": "7e496eca6f265f2bca13b1c445d30d801fb1aec1d443cc78894b7760ada18dc0" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.maven-build", - "name": "Maven", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-maven", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-maven#readme", - "description": "Preview Maven integration: reactor and goal trees, profiles, filtered tests with JUnit reports, compiler diagnostics and dependency trees. Includes a read-only Maven core extension; requires a Lithe preview build with API 3 buildSystems and native build windows, Maven 3.6.3+ and Java 8+.", - "localizedDescriptions": { - "zh-Hans": "预发布 Maven 集成:反应堆与目标树、Profiles、带 JUnit 报告的筛选测试、编译诊断及依赖树。附带只读 Maven 核心扩展;需要支持 API 3 buildSystems 和原生构建窗口的 Lithe preview 开发版、Maven 3.6.3+ 与 Java 8+。" - }, - "tags": ["build", "maven", "java", "testing"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-maven/177ee4e9b12b9fe06a07498eb316539f7472d5d8/io.github.lithe-idea.maven-build/icons/maven.svg", - "versions": [ - { - "version": "0.1.3", - "pluginAPIVersion": 3, - "requires": { - "backend": 2, - "buildProviders": 1, - "buildSystems": 1, - "pluginFunctions": 1, - "projectGenerators": 1, - "serverProcess": 1 - }, - "releaseDate": "2026-11-10", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-maven/releases/download/v0.1.3/maven-build-0.1.3-darwin.zip", - "sha256": "a153ae63f590511be3c6793e8f5ab1f1ff644a4a010d060024c98e6de769f6e6" - } - ] - }, - { - "version": "0.1.1", - "pluginAPIVersion": 3, - "requires": { - "buildProviders": 1, - "buildSystems": 1, - "serverProcess": 1 - }, - "releaseDate": "2026-10-20", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-maven/releases/download/v0.1.1/maven-build-0.1.1-darwin.zip", - "sha256": "5887e3fc661e5c493170510a23f0e349e3d1d3cfb6f0d60960e29eea05cb41fd" - } - ] - }, - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "buildSystems": 1 - }, - "releaseDate": "2026-10-18", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-maven/releases/download/v0.1.0/maven-build-0.1.0-darwin.zip", - "sha256": "4543ec130cc8c0ffc9a1c6c01b0436547b88a5273987c2c96d6ca110c1e6a655" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.php-support", - "name": "PHP Support", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-php", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-php#readme", - "description": "PHP code intelligence through your own Intelephense, plus running PHP files and PHPUnit tests. Intelephense is not bundled (its licence does not allow redistribution): install it with npm install -g intelephense. Needs Node.js; running needs php and project PHPUnit.", - "localizedDescriptions": { - "zh-Hans": "使用你自己安装的 Intelephense 提供 PHP 补全、跳转定义和错误提示,并支持运行 PHP 文件和 PHPUnit 测试。插件不内置 Intelephense(其许可证不允许再分发),请用 npm install -g intelephense 安装;需要 Node.js,运行和测试需要 php 与项目内的 PHPUnit。" - }, - "tags": ["language", "php"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-php/a0b5600d2629e7acc72dda5871dbe403ce1922a9/io.github.lithe-idea.php-support/icons/php.svg", - "versions": [ - { - "version": "0.1.0", - "pluginAPIVersion": 1, - "releaseDate": "2026-10-20", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-php/releases/download/v0.1.0/php-support-0.1.0-darwin.zip", - "sha256": "a9ae2654a7a24fb897ce73f3fbe6c7269a4f23686e4a7fecae3e7997eb6e2b2d" - } - ] - } - ] - }, - { - "id": "io.github.lithe-idea.svn-support", - "name": "Subversion", - "author": "Lithe Community", - "repository": "https://github.com/Lithe-IDEA/lithe-plugin-svn", - "homepage": "https://github.com/Lithe-IDEA/lithe-plugin-svn#readme", - "description": "Preview Subversion integration: shows SVN working copy changes in a version control tool window, with check-in, update, rollback, conflict resolution, cleanup, history, annotate and base diff; project tree moves go through svn move. Requires a Lithe preview build with API 3 vcsProviders 1 and serverProcess 1, Subversion 1.8+ and Node.js 20.3+. Full native UI acceptance is pending; macOS only.", - "localizedDescriptions": { - "zh-Hans": "Subversion 预发布插件:在版本控制工具窗口查看 SVN 工作副本的变更,支持提交、更新、回滚、解决冲突、清理、历史、Annotate 和与基线比较;项目树移动通过 svn move 记录。需要支持 API 3 vcsProviders 1 与 serverProcess 1 的 Lithe preview 开发版、Subversion 1.8+ 和 Node.js 20.3+。完整原生界面验收待完成,仅支持 macOS。" - }, - "tags": ["vcs", "svn", "subversion"], - "license": "Apache-2.0", - "icon": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-svn/717d23cd417f796d8487b3b7cbc8150a8356b512/assets/subversion.svg", - "versions": [ - { - "version": "0.1.0", - "pluginAPIVersion": 3, - "requires": { - "serverProcess": 1, - "vcsProviders": 1 - }, - "releaseDate": "2026-10-24", - "downloads": [ - { - "platforms": ["darwin-arm64", "darwin-x64"], - "downloadURL": "https://github.com/Lithe-IDEA/lithe-plugin-svn/releases/download/v0.1.0/svn-support-0.1.0-darwin.zip", - "sha256": "23fb009cf9fc51f1093a77809f1865b52a781565bbde0c49a1ed890a3b0c46fc" - } - ] - } - ] - } - ] + "schemaVersion": 0, + "plugins": [] } diff --git a/scripts/fixtures/index.json b/scripts/fixtures/index.json index 3d84610..36cdb42 100644 --- a/scripts/fixtures/index.json +++ b/scripts/fixtures/index.json @@ -1,5 +1,5 @@ { - "schemaVersion": 1, + "schemaVersion": 0, "plugins": [ { "id": "io.example.zig-support", @@ -15,7 +15,8 @@ "versions": [ { "version": "1.9.0", - "pluginAPIVersion": 1, + "pluginAPIVersion": 0, + "requires": { "languageServers": 0, "languages": 0 }, "releaseDate": "2026-08-01", "downloads": [ { @@ -27,7 +28,8 @@ }, { "version": "2.0.0", - "pluginAPIVersion": 1, + "pluginAPIVersion": 0, + "requires": { "languageServers": 0, "languages": 0 }, "releaseDate": "2026-09-20", "downloads": [ { @@ -45,8 +47,8 @@ ] }, { - "id": "dev.lithe.fixture.php-support", - "name": "PHP Support Fixture", + "id": "dev.lithe.fixture.language-packaged-server", + "name": "Packaged Language Server Fixture", "author": "Lithe", "repository": "https://github.com/Lithe-IDEA/Lithe-IDEA", "description": "PHP language server, run and test support.", @@ -55,7 +57,8 @@ "versions": [ { "version": "1.1.0", - "pluginAPIVersion": 2, + "pluginAPIVersion": 99, + "requires": { "languageServers": 0, "languages": 0 }, "releaseDate": "2026-10-10", "downloads": [ { @@ -67,7 +70,8 @@ }, { "version": "1.0.0", - "pluginAPIVersion": 1, + "pluginAPIVersion": 0, + "requires": { "languageServers": 0, "languages": 0 }, "releaseDate": "2026-10-01", "downloads": [ { @@ -89,7 +93,8 @@ "versions": [ { "version": "3.0.0", - "pluginAPIVersion": 2, + "pluginAPIVersion": 99, + "requires": { "languages": 0 }, "releaseDate": "2026-10-05", "downloads": [ { @@ -101,6 +106,55 @@ } ] }, + { + "id": "io.example.elixir-tools", + "name": "Elixir Tools", + "author": "Example Developer", + "repository": "https://github.com/example/lithe-elixir", + "description": "Each version needs more capabilities: 1.0.0 a language server, 2.0.0 also the Mix build system, 3.0.0 a capability no host knows yet.", + "license": "MIT", + "versions": [ + { + "version": "3.0.0", + "pluginAPIVersion": 0, + "requires": { "debuggers": 0, "languages": 0 }, + "releaseDate": "2026-10-15", + "downloads": [ + { + "platforms": ["darwin-arm64", "windows-x64"], + "downloadURL": "https://github.com/example/lithe-elixir/releases/download/v3.0.0/elixir-tools-3.0.0.zip", + "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + { + "version": "2.0.0", + "pluginAPIVersion": 0, + "requires": { "buildProviders": 0, "buildSystems": 0, "languageServers": 0, "languages": 0 }, + "releaseDate": "2026-10-12", + "downloads": [ + { + "platforms": ["darwin-arm64", "windows-x64"], + "downloadURL": "https://github.com/example/lithe-elixir/releases/download/v2.0.0/elixir-tools-2.0.0.zip", + "sha256": "9999999999999999999999999999999999999999999999999999999999999999" + } + ] + }, + { + "version": "1.0.0", + "pluginAPIVersion": 0, + "requires": { "languageServers": 0, "languages": 0 }, + "releaseDate": "2026-10-02", + "downloads": [ + { + "platforms": ["darwin-arm64", "windows-x64"], + "downloadURL": "https://github.com/example/lithe-elixir/releases/download/v1.0.0/elixir-tools-1.0.0.zip", + "sha256": "8888888888888888888888888888888888888888888888888888888888888888" + } + ] + } + ] + }, { "id": "io.example.zig-support", "name": "Zig Support (copy)", @@ -111,7 +165,8 @@ "versions": [ { "version": "9.0.0", - "pluginAPIVersion": 1, + "pluginAPIVersion": 0, + "requires": { "languageServers": 0, "languages": 0 }, "releaseDate": "2026-10-11", "downloads": [ { diff --git a/scripts/fixtures/invalid-index-entries.json b/scripts/fixtures/invalid-index-entries.json index 8d183cf..07dd0f8 100644 --- a/scripts/fixtures/invalid-index-entries.json +++ b/scripts/fixtures/invalid-index-entries.json @@ -23,8 +23,10 @@ { "name": "tag with whitespace", "edits": [{ "op": "set", "path": "/tags", "value": ["two words"] }], "expectedCode": "plugin.index.invalid" }, { "name": "localized description with a malformed language tag", "edits": [{ "op": "set", "path": "/localizedDescriptions", "value": { "Chinese": "Zig 语言服务器集成。" } }], "expectedCode": "plugin.index.invalid" }, { "name": "empty localized description", "edits": [{ "op": "set", "path": "/localizedDescriptions", "value": { "zh-Hans": " " } }], "expectedCode": "plugin.index.invalid" }, - { "name": "requires below plugin API 3", "edits": [{ "op": "set", "path": "/versions/0/requires", "value": { "languages": 1 } }], "expectedCode": "plugin.index.invalid" }, - { "name": "plugin API 3 version without requires", "edits": [{ "op": "set", "path": "/versions/0/pluginAPIVersion", "value": 3 }], "expectedCode": "plugin.index.invalid" }, - { "name": "capability version is not a positive integer", "edits": [{ "op": "set", "path": "/versions/0/pluginAPIVersion", "value": 3 }, { "op": "set", "path": "/versions/0/requires", "value": { "languages": 0 } }], "expectedCode": "plugin.index.invalid" } + {"name": "version without requires", "edits": [{"op": "remove", "path": "/versions/0/requires"}], "expectedCode": "plugin.index.invalid"}, + {"name": "empty requires", "edits": [{"op": "set", "path": "/versions/0/requires", "value": {}}], "expectedCode": "plugin.index.invalid"}, + {"name": "capability version is not a non-negative integer", "edits": [{"op": "set", "path": "/versions/0/requires", "value": {"languages": -1}}], "expectedCode": "plugin.index.invalid"}, + {"name": "malformed capability ID in requires", "edits": [{"op": "set", "path": "/versions/0/requires", "value": {"Language Servers": 0}}], "expectedCode": "plugin.index.invalid"}, + {"name": "plugin API version is not a non-negative integer", "edits": [{"op": "set", "path": "/versions/0/pluginAPIVersion", "value": -1}], "expectedCode": "plugin.index.invalid"} ] } diff --git a/scripts/store.test.mjs b/scripts/store.test.mjs index e5ea983..c50c8ce 100644 --- a/scripts/store.test.mjs +++ b/scripts/store.test.mjs @@ -14,7 +14,7 @@ import { checkManifest, formatIndex, inspectArchive, readPackageManifests, validateEntry, validateIndex, verifyArchives, } from "./validate-index.mjs"; -// Fixtures are copied from the plugin contract (lithe-plugin-docs contract/v1/fixtures/marketplace) +// Fixtures are copied from the plugin contract (Lithe-core-rust shared/plugins/contract/v1/fixtures/marketplace) // so this validator reports the same code as Lithe's Rust Core for each case. const fixtureIndex = JSON.parse(readFileSync(new URL("./fixtures/index.json", import.meta.url))); const invalidCases = JSON.parse(readFileSync(new URL("./fixtures/invalid-index-entries.json", import.meta.url))).cases; @@ -53,8 +53,7 @@ for (const { name, edits, expectedCode } of invalidCases) { function capabilityEntry() { const entry = validEntry(); entry.versions = [entry.versions[1]]; - entry.versions[0].pluginAPIVersion = 3; - entry.versions[0].requires = { buildSystems: 1 }; + entry.versions[0].requires = { buildSystems: 0 }; return entry; } @@ -62,11 +61,11 @@ function capabilityEntry() { function manifestFor(entry) { const version = entry.versions[0]; return { - schemaVersion: 2, + schemaVersion: 0, id: entry.id, version: version.version, pluginAPIVersion: version.pluginAPIVersion, - ...(Object.hasOwn(version, "requires") ? { requires: structuredClone(version.requires) } : {}), + requires: structuredClone(version.requires), vendor: { signatureRequirement: "none" }, }; } @@ -92,90 +91,79 @@ function releaseFixture(entry, manifests) { }; } -test("API 3 requires a non-empty capability map and accepts future capability IDs", () => { +test("requires is a non-empty capability map that accepts future capability IDs and versions", () => { const entry = capabilityEntry(); assert.equal(codeOf(entry), "ok"); entry.versions[0].requires = { futureFeature: 99 }; assert.equal(codeOf(entry), "ok"); delete entry.versions[0].requires; assert.equal(codeOf(entry), "plugin.index.invalid"); - for (const requires of [null, {}, [], "buildSystems", { buildSystems: 0 }, { buildSystems: -1 }, - { buildSystems: 1.5 }, { buildSystems: "1" }, { buildSystems: true }, { "build-systems": 1 }, - { BuildSystems: 1 }, { "1buildSystems": 1 }, { ["a".repeat(65)]: 1 }]) { + for (const requires of [null, {}, [], "buildSystems", { buildSystems: -1 }, + { buildSystems: 1.5 }, { buildSystems: "1" }, { buildSystems: true }, { "build-systems": 0 }, + { BuildSystems: 0 }, { "1buildSystems": 0 }, { ["a".repeat(65)]: 0 }]) { entry.versions[0].requires = requires; assert.equal(codeOf(entry), "plugin.index.invalid", JSON.stringify(requires)); } - entry.versions[0].requires = { ["a".repeat(64)]: 1 }; + entry.versions[0].requires = { ["a".repeat(64)]: 0 }; assert.equal(codeOf(entry), "ok"); }); -test("API 1/2 remain valid without requires and reject it when supplied", () => { - for (const api of [1, 2]) { - const entry = validEntry(); - entry.versions[0].pluginAPIVersion = api; - assert.equal(codeOf(entry), "ok"); - for (const requires of [{ buildSystems: 1 }, null]) { - entry.versions[0].requires = requires; - assert.equal(codeOf(entry), "plugin.index.invalid"); - } - } -}); - -test("future API levels may omit requires but validate a supplied map", () => { +test("an index version may name a plugin API no host runs yet, but not a malformed one", () => { const entry = capabilityEntry(); - entry.versions[0].pluginAPIVersion = 4; - delete entry.versions[0].requires; - assert.equal(codeOf(entry), "ok"); - entry.versions[0].requires = { futureFeature: 1 }; + // Core reports such a version as unavailable, not as an invalid entry. + entry.versions[0].pluginAPIVersion = 99; assert.equal(codeOf(entry), "ok"); - entry.versions[0].requires = {}; + for (const api of [-1, 1.5, "0", null]) { + entry.versions[0].pluginAPIVersion = api; + assert.equal(codeOf(entry), "plugin.index.invalid", JSON.stringify(api)); + } + delete entry.versions[0].pluginAPIVersion; assert.equal(codeOf(entry), "plugin.index.invalid"); }); test("package and index requirements agree regardless of key order", () => { const entry = capabilityEntry(); const version = entry.versions[0]; - version.requires = { settings: 1, buildSystems: 1 }; - const manifest = { ...manifestFor(entry), requires: { buildSystems: 1, settings: 1 } }; + version.requires = { settings: 0, buildSystems: 0 }; + const manifest = { ...manifestFor(entry), requires: { buildSystems: 0, settings: 0 } }; assert.deepEqual(checkManifest([manifest], entry, version), []); - for (const requires of [{ buildSystems: 2, settings: 1 }, { buildSystems: 1 }, - { buildSystems: 1, settings: 1, futureFeature: 1 }]) { + for (const requires of [{ buildSystems: 1, settings: 0 }, { buildSystems: 0 }, + { buildSystems: 0, settings: 0, futureFeature: 0 }]) { assert.match(checkManifest([{ ...manifest, requires }], entry, version).join(), /requires != index requires/); } delete manifest.requires; - assert.match(checkManifest([manifest], entry, version).join(), /must list its capabilities/); + assert.match(checkManifest([manifest], entry, version).join(), /must list the capabilities/); }); -test("package checks retain identity, API and community trust checks", () => { +test("package checks retain identity, format, API and community trust checks", () => { const entry = capabilityEntry(); const version = entry.versions[0]; const manifest = manifestFor(entry); + assert.deepEqual(checkManifest([manifest], entry, version), []); for (const [edit, expected] of [ [{ id: "io.example.other" }, /plugin\.package\.mismatch/], [{ version: "9.0.0" }, /plugin\.package\.mismatch/], - [{ pluginAPIVersion: 4 }, /pluginAPIVersion/], + [{ schemaVersion: 2 }, /plugin\.schema\.unsupported/], + [{ pluginAPIVersion: 3 }, /plugin\.api\.unsupported/], [{ vendor: { signatureRequirement: "publisherPackage" } }, /signatureRequirement/], ]) { assert.match(checkManifest([{ ...manifest, ...edit }], entry, version).join(), expected); } - for (const api of [1, 2]) { - const legacyEntry = validEntry(); - legacyEntry.versions[0].pluginAPIVersion = api; - const legacyManifest = manifestFor(legacyEntry); - assert.deepEqual(checkManifest([legacyManifest], legacyEntry, legacyEntry.versions[0]), []); - assert.match(checkManifest([{ ...legacyManifest, requires: { languages: 1 } }], legacyEntry).join(), /forbidden/); - } + // A package Lithe refuses is never listed, even when the index entry agrees with it. + const future = capabilityEntry(); + future.versions[0].pluginAPIVersion = 1; + assert.match(checkManifest([manifestFor(future)], future, future.versions[0]).join(), /plugin\.api\.unsupported/); }); -test("release tracking copies API 3 requires and normalizes its key order", { timeout: 5_000 }, async () => { +test("release tracking copies requires and normalizes its key order", { timeout: 5_000 }, async () => { const entry = capabilityEntry(); - const manifest = { ...manifestFor(entry), requires: { settings: 1, buildSystems: 1 } }; - const fixture = releaseFixture(entry, [manifest, { ...manifest, requires: { buildSystems: 1, settings: 1 } }]); + const manifest = { ...manifestFor(entry), requires: { settings: 0, buildSystems: 0 } }; + const fixture = releaseFixture(entry, [manifest, { ...manifest, requires: { buildSystems: 0, settings: 0 } }]); const result = await versionFromRelease(entry, fixture.release, fixture); assert.equal(result.problems, undefined); - assert.deepEqual(result.version.requires, { buildSystems: 1, settings: 1 }); + assert.deepEqual(result.version.requires, { buildSystems: 0, settings: 0 }); assert.deepEqual(Object.keys(result.version.requires), ["buildSystems", "settings"]); - assert.equal(result.version.pluginAPIVersion, 3); + assert.equal(result.version.pluginAPIVersion, 0); assert.equal(result.version.downloads.length, 2); assert.equal(result.version.downloads[0].sha256, createHash("sha256").update(fixture.release.assets[0].bytes).digest("hex")); }); @@ -186,12 +174,12 @@ test("release tracking rejects malformed or conflicting requirements across plat const missing = { ...manifest }; delete missing.requires; for (const [other, expected] of [ - [{ ...manifest, requires: { buildSystems: 2 } }, /disagree.*requires/], - [{ ...manifest, requires: { buildSystems: 1, settings: 1 } }, /disagree.*requires/], + [{ ...manifest, requires: { buildSystems: 1 } }, /disagree.*requires/], + [{ ...manifest, requires: { buildSystems: 0, settings: 0 } }, /disagree.*requires/], [{ ...manifest, requires: {} }, /non-empty/], - [missing, /must list its capabilities/], + [missing, /must list the capabilities/], [{ ...manifest, version: "3.0.0" }, /disagree.*version/], - [{ ...manifest, pluginAPIVersion: 4 }, /disagree.*pluginAPIVersion/], + [{ ...manifest, pluginAPIVersion: 1 }, /plugin\.api\.unsupported/], ]) { const fixture = releaseFixture(entry, [manifest, other]); const result = await versionFromRelease(entry, fixture.release, fixture); @@ -200,18 +188,6 @@ test("release tracking rejects malformed or conflicting requirements across plat } }); -test("release tracking preserves API 1/2 releases without adding requires", { timeout: 5_000 }, async () => { - for (const api of [1, 2]) { - const entry = validEntry(); - entry.versions[0].pluginAPIVersion = api; - const fixture = releaseFixture(entry, [manifestFor(entry)]); - const result = await versionFromRelease(entry, fixture.release, fixture); - assert.equal(result.problems, undefined); - assert.equal(result.version.pluginAPIVersion, api); - assert.equal(Object.hasOwn(result.version, "requires"), false); - } -}); - // Organization discovery fixtures: GitHub responses are plain objects, and each // archive's bytes are its plugin.json text, so no test touches the network or unzip. const REPOSITORIES_PATH = "orgs/Lithe-IDEA/repos?type=public&per_page=100&page=1"; @@ -246,11 +222,12 @@ function organizationRelease(repository, version, overrides = {}) { function zigManifest(version) { return { - schemaVersion: 2, + schemaVersion: 0, id: ZIG_ID, displayName: "Zig Support", version, - pluginAPIVersion: 2, + pluginAPIVersion: 0, + requires: { languageServers: 0, languages: 0 }, vendor: { displayName: "Lithe Community", signatureRequirement: "none" }, }; } @@ -292,7 +269,7 @@ function zigOrganization() { } test("organization discovery considers only active, unlisted lithe-plugin- repositories", () => { - const index = { schemaVersion: 1, plugins: [{ ...validEntry(), repository: "https://github.com/Lithe-IDEA/Lithe-Plugin-Listed" }] }; + const index = { schemaVersion: 0, plugins: [{ ...validEntry(), repository: "https://github.com/Lithe-IDEA/Lithe-Plugin-Listed" }] }; const names = unlistedPluginRepositories([ organizationRepository("lithe-plugin-zig"), organizationRepository("lithe-plugin-docs"), @@ -307,7 +284,7 @@ test("organization discovery considers only active, unlisted lithe-plugin- repos test("organization discovery lists a new plugin with its stable releases", { timeout: 5_000 }, async () => { const { releases, fixture } = zigOrganization(); - const index = { schemaVersion: 1, plugins: [validEntry(), { ...validEntry(), id: "org.example.zig-tools" }] }; + const index = { schemaVersion: 0, plugins: [validEntry(), { ...validEntry(), id: "org.example.zig-tools" }] }; const result = await discoverPlugins(index, fixture); assert.deepEqual(result.skipped, []); @@ -333,13 +310,13 @@ test("organization discovery lists a new plugin with its stable releases", { tim test("organization discovery keeps only the plugin named by --plugin", { timeout: 5_000 }, async () => { const other = zigOrganization(); - const otherIndex = { schemaVersion: 1, plugins: [validEntry()] }; + const otherIndex = { schemaVersion: 0, plugins: [validEntry()] }; const skippedRun = await discoverPlugins(otherIndex, { ...other.fixture, onlyPlugin: "io.example.other" }); assert.deepEqual(skippedRun.added, []); assert.equal(otherIndex.plugins.length, 1); const named = zigOrganization(); - const namedIndex = { schemaVersion: 1, plugins: [validEntry()] }; + const namedIndex = { schemaVersion: 0, plugins: [validEntry()] }; const namedRun = await discoverPlugins(namedIndex, { ...named.fixture, onlyPlugin: ZIG_ID }); assert.deepEqual(namedRun.newPlugins, [ZIG_ID]); }); @@ -367,7 +344,7 @@ test("organization discovery skips repositories that cannot be listed yet", { ti [anonymousRelease.assets[0].browser_download_url]: { ...zigManifest("1.0.0"), id: "io.github.lithe-idea.anonymous", vendor: { signatureRequirement: "none" } }, [officialRelease.assets[0].browser_download_url]: { ...zigManifest("1.0.0"), id: "dev.lithe.official" }, }); - const index = { schemaVersion: 1, plugins: [validEntry()] }; + const index = { schemaVersion: 0, plugins: [validEntry()] }; const result = await discoverPlugins(index, fixture); assert.deepEqual(result.added, []); @@ -405,7 +382,7 @@ function scopedTracking() { license: "Apache-2.0", versions: [], }; - const index = { schemaVersion: 1, plugins: [validEntry(), organizationEntry] }; + const index = { schemaVersion: 0, plugins: [validEntry(), organizationEntry] }; const fixture = organizationFixture({ [REPOSITORIES_PATH]: [zigRepository], "repos/Lithe-IDEA/lithe-plugin-zig/releases?per_page=20": [organizationPluginRelease], @@ -447,7 +424,7 @@ test("archive verification rechecks changed manifest expectations for the same U const manifest = manifestFor(entry); const bytes = Buffer.from(JSON.stringify(manifest)); for (const download of entry.versions[0].downloads) download.sha256 = createHash("sha256").update(bytes).digest("hex"); - const index = { schemaVersion: 1, plugins: [entry] }; + const index = { schemaVersion: 0, plugins: [entry] }; let downloadCount = 0; const dependencies = { download: async () => { downloadCount += 1; return bytes; }, @@ -457,8 +434,8 @@ test("archive verification rechecks changed manifest expectations for the same U assert.deepEqual(await verifyArchives(index, structuredClone(index), dependencies), []); assert.equal(downloadCount, 0); for (const editBase of [ - (oldEntry) => { oldEntry.versions[0].requires = { buildSystems: 2 }; }, - (oldEntry) => { oldEntry.versions[0].pluginAPIVersion = 4; }, + (oldEntry) => { oldEntry.versions[0].requires = { buildSystems: 1 }; }, + (oldEntry) => { oldEntry.versions[0].pluginAPIVersion = 1; }, (oldEntry) => { oldEntry.versions[0].version = "1.0.0"; }, (oldEntry) => { oldEntry.id = "io.example.other"; }, ]) { @@ -469,16 +446,16 @@ test("archive verification rechecks changed manifest expectations for the same U assert.equal(downloadCount, entry.versions[0].downloads.length); } const changed = structuredClone(index); - changed.plugins[0].versions[0].requires = { buildSystems: 2 }; + changed.plugins[0].versions[0].requires = { buildSystems: 1 }; assert.match((await verifyArchives(changed, index, dependencies)).join(), /requires != index requires/); }); test("archive verification compares requires by value and checks hashes before manifests", { timeout: 5_000 }, async () => { const entry = capabilityEntry(); - entry.versions[0].requires = { settings: 1, buildSystems: 1 }; - const index = { schemaVersion: 1, plugins: [entry] }; + entry.versions[0].requires = { settings: 0, buildSystems: 0 }; + const index = { schemaVersion: 0, plugins: [entry] }; const base = structuredClone(index); - base.plugins[0].versions[0].requires = { buildSystems: 1, settings: 1 }; + base.plugins[0].versions[0].requires = { buildSystems: 0, settings: 0 }; const forbidden = () => { assert.fail("unchanged archives must not be downloaded or inspected"); }; assert.deepEqual(await verifyArchives(index, base, { download: forbidden, readManifests: forbidden }), []); const problems = await verifyArchives(index, undefined, { @@ -504,19 +481,22 @@ test("formatting keeps plain arrays inline and round-trips", () => { assert.deepEqual(JSON.parse(text), fixtureIndex); assert.match(text, /"platforms": \["darwin-arm64"\]/); assert.match(text, /"tags": \["Language", "zig"\]/); - assert.equal(formatIndex({ schemaVersion: 1, plugins: [] }), '{\n "schemaVersion": 1,\n "plugins": []\n}\n'); + assert.equal(formatIndex({ schemaVersion: 0, plugins: [] }), '{\n "schemaVersion": 0,\n "plugins": []\n}\n'); }); test("duplicate ids and unsorted plugins fail the store policy", () => { const second = { ...validEntry(), id: "io.example.another" }; - assert.match(validateIndex({ schemaVersion: 1, plugins: [validEntry(), validEntry()] }).join("\n"), /plugin\.index\.duplicate/); - assert.match(validateIndex({ schemaVersion: 1, plugins: [validEntry(), second] }).join("\n"), /sorted by id/); - assert.deepEqual(validateIndex({ schemaVersion: 1, plugins: [second, validEntry()] }), []); + assert.match(validateIndex({ schemaVersion: 0, plugins: [validEntry(), validEntry()] }).join("\n"), /plugin\.index\.duplicate/); + assert.match(validateIndex({ schemaVersion: 0, plugins: [validEntry(), second] }).join("\n"), /sorted by id/); + assert.deepEqual(validateIndex({ schemaVersion: 0, plugins: [second, validEntry()] }), []); }); test("a wrong schema version or extra root field is refused", () => { - assert.notDeepEqual(validateIndex({ schemaVersion: 2, plugins: [] }), []); - assert.notDeepEqual(validateIndex({ schemaVersion: 1, plugins: [], extra: true }), []); + for (const schemaVersion of [1, 2, "0", undefined]) { + assert.match(validateIndex({ schemaVersion, plugins: [] }).join(), /plugin\.index\.schemaUnsupported/); + } + assert.deepEqual(validateIndex({ schemaVersion: 0, plugins: [] }), []); + assert.notDeepEqual(validateIndex({ schemaVersion: 0, plugins: [], extra: true }), []); }); test("asset names map to platforms", () => { @@ -549,7 +529,10 @@ test("archives must hold one matching community plugin.json", () => { execFileSync("zip", ["-qr", archive, entry.id], { cwd: join(workDir, "pkg") }); return archive; }; - const manifest = { id: entry.id, version: version.version, pluginAPIVersion: 1, vendor: { signatureRequirement: "none" } }; + const manifest = { + schemaVersion: 0, id: entry.id, version: version.version, pluginAPIVersion: 0, + requires: structuredClone(version.requires), vendor: { signatureRequirement: "none" }, + }; assert.deepEqual(inspectArchive(build(manifest), entry, version), []); assert.match(inspectArchive(build({ ...manifest, version: "9.9.9" }), entry, version).join(), /plugin\.package\.mismatch/); assert.match( @@ -570,7 +553,7 @@ test("the overview renders one escaped card per plugin with a default icon", () const entry = validEntry(); delete entry.icon; entry.name = ""; - const html = renderOverview({ schemaVersion: 1, plugins: [entry] }); + const html = renderOverview({ schemaVersion: 0, plugins: [entry] }); assert.match(html, /<Zig>/); assert.match(html, /assets\/plugin-default\.svg/); assert.equal(html.match(/ [id, requires[id]])) }), + requires: Object.fromEntries(Object.keys(requires).sort().map((id) => [id, requires[id]])), releaseDate: (release.published_at ?? release.created_at).slice(0, 10), downloads: downloads.sort((left, right) => left.platforms[0].localeCompare(right.platforms[0])), }; // Refuse an invalid release before it can affect other plugins' tracked updates. - const versionProblems = validateIndex({ schemaVersion: 1, plugins: [{ ...entry, versions: [version] }] }); + const versionProblems = validateIndex({ schemaVersion: 0, plugins: [{ ...entry, versions: [version] }] }); return versionProblems.length > 0 ? { problems: versionProblems } : { version }; } diff --git a/scripts/validate-index.mjs b/scripts/validate-index.mjs index 3512d53..59513a7 100644 --- a/scripts/validate-index.mjs +++ b/scripts/validate-index.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node -// Validates lithe-plugin-index.json against the Lithe plugin contract v1 -// (https://github.com/1lck/Lithe-IDEA/tree/preview/shared/contracts/plugins/v1). +// Validates lithe-plugin-index.json against the Lithe plugin contract v0, published in +// the Rust Core repository +// (https://github.com/Lithe-IDEA/Lithe-core-rust/tree/main/shared/plugins/contract/v1). // // Lithe's Rust Core (`plugins.marketplace.validateIndex`) is the source of truth // and silently drops invalid entries. This store is stricter: any entry that @@ -68,36 +69,34 @@ function isCalendarDate(text) { return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === text; } -/** Validates the index-level API and capability shape, without deciding host support. */ +/** + * Validates the API and capability shape that an index version and a package + * manifest share, without deciding host support: `pluginAPIVersion` and every + * capability version are non-negative integers, and `requires` is required. + */ function validateVersionRequirements(version) { - if (!Number.isInteger(version.pluginAPIVersion) || version.pluginAPIVersion < 1) { - throw invalid("`pluginAPIVersion` must be a positive integer"); - } - const present = Object.hasOwn(version, "requires"); - if (version.pluginAPIVersion < 3 && present) { - throw invalid("`requires` is forbidden below pluginAPIVersion 3"); + if (!Number.isInteger(version.pluginAPIVersion) || version.pluginAPIVersion < 0) { + throw invalid("`pluginAPIVersion` must be a non-negative integer"); } - if (version.pluginAPIVersion === 3 && !present) { - throw invalid("pluginAPIVersion 3 must list its capabilities in `requires`"); + if (!Object.hasOwn(version, "requires")) { + throw invalid("`requires` must list the capabilities the package uses"); } - // Core permits API levels above 3 without requires, but validates it when supplied. - if (!present) return; if (!isObject(version.requires) || Object.keys(version.requires).length === 0) { throw invalid("`requires` must be a non-empty capability object"); } for (const [id, capabilityVersion] of Object.entries(version.requires)) { - if (id.length > 64 || !/^[a-z][A-Za-z0-9]*$/.test(id)) { + if (!/^[a-z][A-Za-z0-9]{0,63}$/.test(id)) { throw invalid(`malformed capability ID \`${id}\` in requires`); } - if (!Number.isInteger(capabilityVersion) || capabilityVersion < 1) { - throw invalid(`\`requires.${id}\` must be a positive integer`); + if (!Number.isInteger(capabilityVersion) || capabilityVersion < 0) { + throw invalid(`\`requires.${id}\` must be a non-negative integer`); } } } /** Stable equality key: capability-map property order has no contract meaning. */ export function requirementsKey(requires) { - if (requires === undefined) return null; + if (!isObject(requires)) return null; return JSON.stringify(Object.keys(requires).sort().map((id) => [id, requires[id]])); } @@ -228,7 +227,7 @@ export function validateIndex(index) { for (const key of Object.keys(index)) { if (key !== "schemaVersion" && key !== "plugins") problems.push(`unknown root field \`${key}\``); } - if (index.schemaVersion !== 1) problems.push("`schemaVersion` must be 1"); + if (index.schemaVersion !== 0) problems.push("plugin.index.schemaUnsupported: `schemaVersion` must be 0"); if (!Array.isArray(index.plugins)) return [...problems, "`plugins` must be an array"]; const seen = new Set(); @@ -289,6 +288,14 @@ export function checkManifest(manifests, entry, expected) { if (manifest.id !== entry.id) { problems.push(`plugin.package.mismatch: plugin.json id ${manifest.id} != index ${entry.id}`); } + // Lithe refuses any other manifest format or plugin API version, so such a + // package installs nowhere even though the index may list it as unavailable. + if (manifest.schemaVersion !== 0) { + problems.push(`plugin.schema.unsupported: plugin.json schemaVersion ${manifest.schemaVersion} != 0`); + } + if (manifest.pluginAPIVersion !== 0) { + problems.push(`plugin.api.unsupported: plugin.json pluginAPIVersion ${manifest.pluginAPIVersion} != 0`); + } try { validateVersionRequirements(manifest); } catch (error) {