feat(plugins): community plugins, version compatibility and categories in the marketplace (#1226)

* feat(plugins): list community plugins from a registry in the marketplace

The marketplace index can now carry plugins of other publishers, published
from their authors' repositories through the l0ng-ai/openidea-plugins
registry.

- `openidea-plugin index` with a key replaces only that publisher's plugins
  (it used to drop every other entry); without a key it indexes each package
  against the publishers the index already lists, checking the key and the
  id prefix instead of only the signature itself.
- `openidea-plugin merge` takes another index's publishers, plugins and
  revocations in place of every publisher but `--keep`; the other index can't
  list or revoke the kept publishers' plugins.
- `openidea-plugin check` accepts a `.oiplugin` file.
- Releases merge the registry's index into plugins-index.json and attach the
  Linux x64 tool; plugins-index.yml refreshes the community plugins in the
  latest release hourly or when the registry signals a change.
- plugins/README.md and CONTRIBUTING.md describe official and community
  plugins.

* feat(plugins): offer the newest plugin version this IDE can load

Index entries record the plugin API each version needs (`engine`: the
manifest's `engines.openidea` narrowed to the API its component was built
for), and an index may list several versions of a plugin. Settings › Plugins
offers the newest version this IDE can load; with none, the plugin is shown
as incompatible with the API it needs, and downloading it is refused.

`openidea-plugin index` keeps, of each plugin, the newest version per API
requirement, so users of an older IDE keep the last version built for them;
`--download-base` makes downloads absolute, and the release workflow points
them at the release that published them. Scratch directories are now unique
per call.

* feat(plugins): file marketplace plugins under categories

`plugin.toml` takes `categories` (up to 3 of languages, themes, icon-themes,
linters, vcs, ai, frameworks, tools, other). A plugin that declares none is
filed under what its contributions imply (themes, icon themes, languages),
else under other; categories an IDE doesn't know are ignored, and `check`
warns about them. Index entries carry the categories, and Settings › Plugins
› Available filters by them and shows them in a plugin's details. The
official plugins declare theirs.

* feat(plugins): let a marketplace index point to where it moved

An index with `moved` sends the IDE to the index at that URL (relative or
absolute, up to three hops; an https index can only move to https). Downloads
resolve against the index actually read, so a marketplace can change hosts
while released IDEs keep the address they were built with.

* fix(plugins): full-width category filter and aligned incompatibility note

* fix(plugins): translate the AI category in Chinese

* feat(plugins): keep a plugin's signer across updates and announce updates

- Index format: fields are only added; an IDE reading a newer `format`
  still applies its revocations and `moved`, lists no plugins and asks to
  be updated, instead of failing to read the index.
- Key continuity: an installed plugin updates silently only to a package
  signed with the same key, or a key it endorsed (`KeyEntry.endorsement`,
  `index --previous-key`, `openidea-plugin endorse`). Another signer, or an
  unsigned package replacing a signed one, is shown on the install page and
  needs the user's confirmation (`Consent.signerChange`).
- Official key: release builds carry the official publisher's public key
  (`OPENIDEA_OFFICIAL_PLUGIN_KEYS`, derived from PLUGIN_SIGNING_KEY);
  `openidea.*` plugins must be signed with it or a key it endorsed,
  whatever the index lists.
- Updates: the daily marketplace check announces new versions once, with
  Update All (also in the gear menu and as an action). An update that asks
  for nothing new installs with the grants the installed version has.
- Index entries need `engine`; `categories` beyond the first three are
  ignored instead of rejecting the manifest.
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-11-04 18:24:43 +08:00
1 джерело 48e23c012f
коміт 953e844541
61 files changed
+2030 -160

No files matched your search

+79 -2
Переглянути файл
@@ -79,6 +79,7 @@ authors = ["You <[email protected]>"]
homepage = "https://example.com" # optional
license = "MIT" # optional
change-notes = "1.2.0 — …" # shown under What's New
categories = ["tools"] # marketplace categories, see below
engines.openidea = "^1.0" # the plugin API this package needs
wasm = "dist/plugin.wasm" # omit for data-only plugins
activation = ["on-language:markdown"] # optional, see below
@@ -94,6 +95,13 @@ category = "Word Tools" # group in Search Everywhere / Keymap
when = "editorFocus" # optional: same expressions as built-in actions
```
`categories` (up to 3) file the plugin in the marketplace: `languages`,
`themes`, `icon-themes`, `linters` (linters and formatters), `vcs`, `ai`,
`frameworks`, `tools`, `other`. Without them, a plugin contributing themes,
icon themes or languages is filed under those, any other under `other`.
Categories an IDE doesn't know, and any after the first three it knows, are
ignored (`check` warns about both).
All paths are relative to the package directory and may not leave it. A
manifest is validated as a whole: if one contribution is invalid, nothing is
installed or loaded. Contribution types the IDE does not know (a newer API) are
@@ -739,6 +747,12 @@ granted, and starts the approved program.
the version it replaced automatically (you're told why); _Roll Back to …_
in the plugin's details does it by hand. Data (`storage`) is per plugin, not
per version.
- **Updates:** when the daily marketplace check finds newer versions of
installed plugins, the IDE says so once, with _Update All_ (also in the gear
menu and as the _Update All Plugins_ action). An update that asks for
nothing new installs with the grants the installed version has; the install
page is shown only for new capabilities, an unsigned package or another
signer (see _Distribution_).
- **Linked plugins:** `openidea-plugin link <dir>` (§1). Listed with a _linked_
tag; capabilities are granted per folder.
- **Project plugins:** put (or symlink) the package in
@@ -758,6 +772,9 @@ files).
openidea-plugin keygen acme.key # once; keep the file secret
openidea-plugin pack plugins/acme-tools --key acme.key --publisher acme
openidea-plugin index market/ --publisher acme --name "ACME" --key acme.key
openidea-plugin check acme.tools-1.0.0.oiplugin # a package as installing would check it
openidea-plugin merge market/index.json https://example.com/index.json --keep acme
openidea-plugin endorse <new public key> --key acme.key --publisher acme # when rotating keys
```
A **marketplace** is static: an `index.json` and the packages next to it, on
@@ -773,8 +790,68 @@ and the publisher's key, then shows the install page.
- **Revocation:** the index can revoke plugin versions (or all versions) and
publisher keys. The IDE reads the index daily; revoked plugins are disabled
with the reason and the user is notified — also after a restart, before
their code could run. A publisher rotates keys by adding the new one; older
keys stay valid until revoked.
their code could run.
- **Key rotation:** a publisher rotates keys by registering the new one with
`index --key new.key --previous-key old.key`: the old key endorses the new
one, and older keys stay valid until revoked (`endorse` prints the
endorsement for an index someone else writes, such as the registry's). An installed plugin updates
silently only to a package signed with the key it was installed with, or
one that key endorsed (directly or through endorsed keys; endorsements by
revoked keys don't count). Another signer — an unendorsed key, or an
unsigned package replacing a signed one — is shown on the install page and
needs the user's confirmation.
- **Official key:** release builds carry the official publisher's public key
(`OPENIDEA_OFFICIAL_PLUGIN_KEYS`, derived from the release's signing key).
`openidea.*` plugins must be signed with it, or with a key it endorsed,
whatever keys an index lists for `openidea`.
- **Format:** fields are only ever added to the index (an IDE ignores those it
doesn't know), and `format` stays `1` unless older IDEs can't read the
change. An IDE reading a newer format still applies its revocations and
`moved`, lists no plugins, and asks the user to update.
- **Moving:** an index with `"moved": "<url>"` sends the IDE to the index at
that URL (relative or absolute; https stays https), so a marketplace can
change hosts while released IDEs keep the address they were built with.
**Versions.** Each index entry records the plugin API it needs (`engine`: the
manifest's `engines.openidea`, narrowed to the API its component was built
for; an entry without one is offered to no IDE), and a plugin may be listed in
several versions. Settings › Plugins
offers the newest version this IDE can load; when there is none, it shows the
plugin as incompatible and says which API it needs. Entries also carry the
plugin's categories, which Settings › Plugins › Available filters by. Indexing keeps, of each
plugin, the newest version per API requirement, so users of an older IDE keep
the last version built for them.
`index` with a key registers the publisher and lists the folder's packages
as its plugins (their earlier versions stay as above; plugins no longer in the
folder go), keeping the other publishers'; `--download-base` makes downloads
absolute, so a version stays where it was first published. Without a key, it
lists every package in the folder, each checked against the publishers the
index already has. `merge` takes the
publishers, plugins and revocations of another index (downloads made absolute)
in place of every publisher but `--keep`; `check` also takes a `.oiplugin`.
### 8.1 Official and community plugins
The IDE's default marketplace is `plugins-index.json` of the latest release.
It lists two kinds of plugins:
- **Official** (`plugins/official/`, ids `openidea.*`): maintained in this
repository, built and signed by its release workflow, and kept working as
the API changes. New official plugins are proposed in an issue first; they
cover general needs or show how to use part of the API.
- **Community** (their authors' ids and keys): developed in their authors'
repositories and listed through the registry,
[l0ng-ai/openidea-plugins](https://github.com/l0ng-ai/openidea-plugins).
An author registers a publisher (an id prefix, its public key and the GitHub
accounts that may change it) and adds each plugin version by pull request;
the registry checks the package (digest, signature, `openidea-plugin check`)
and publishes its index, which `plugins-index.yml` merges into the release's
within the hour. Its README has the rules.
Pull requests that add a plugin to this repository are for official plugins.
A plugin that needs a new extension point brings the extension point here, as
its own pull request, and the plugin to the registry.
## 9. Testing the host
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
bf264a97dc918f24ac00f0e9e0d0373b68391a96921b44345b466c2a087c5283
aa09a92c0ba9d85a01c56bbf4455e40ae44f005b443ce1002bbe8011408ff975
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Inline code suggestions (ghost text) from a local model (Ollama)
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: local (Ollama) and OpenAI backends; the key stays in the system keychain."
categories = ["ai"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-command:*"]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
6095c7e30658b93d79270e6355d42941a4cfe13eeb8bada07acc7ae462194853
908c2fd177ad2bb9a8a8492505151c3b81579191156c5ec53f705127f50a8ee9
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Checks Java files with Checkstyle when you save them: problems in
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: check on save (a newer save cancels the running check), Google style by default, quick fixes."
categories = ["linters"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-event:file-saved", "on-command:*"]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
b96941af697ce7a6c1c8ef1539d34aa7c23d2d41dd1b2ccffdfda5648f90cf56
64270fa0f5b3084c78764f09b8ce161742fdbff9c28fb90590da39594e558db1
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Edit CSV and TSV files as a table. Edits go to the file's text, s
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: table editor for .csv and .tsv with cell editing, inserting and deleting rows; large files load the visible rows only."
categories = ["tools"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-view:table"]
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Draw and edit Excalidraw diagrams (.excalidraw). The drawing is t
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: the Excalidraw canvas next to (or instead of) the file's text, kept in step both ways; export to PNG and SVG through the save dialog; a file icon and a New › Excalidraw Drawing template. Based on the Excalidraw editor proposed by Deng Liu (#738)."
categories = ["tools"]
engines.openidea = "^1.0"
[capabilities]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
ad545605b33c6f4e55a06a376333de3162739ed920b5d7d277942e50a4420083
22876f842f727a2b9aa292986df3393f5b5f30c5c34319f5665f7d2701c8accf
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Fetches your repositories in the background and shows the branch
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: auto-fetch every 5 minutes, ahead/behind in the status bar, Fetch Now."
categories = ["vcs"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
# Fetches on schedule (also after being reclaimed while idle) and refreshes
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
de353be96a6b66629901f55a8027a0f27107a7ac2fb9d891089db1b7fd716299
14c60782eca79a25cf2c38d58c05be0eb97c4afc607d4cc83302d1445d094aab
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Graphviz DOT files (.dot, .gv) with a live preview beside the tex
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: live preview (Graphviz compiled to WebAssembly), layout engines, zoom, errors as problems, SVG and PNG export."
categories = ["languages", "tools"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-view:preview"]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
345c39f818e4950914bf3fcc795fac9e30ff2d87ad3e66f24c7bc00800f92980
7f5aacde2b8a8bcee5365c8cbda91045195688899c6537dc2159037cca74a672
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Shows the keyboard shortcut of an action you ran with the mouse,
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: shortcut tips, statistics, per-action \"don't show again\"."
categories = ["tools"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
# Actions arrive in batches (a few seconds apart while the plugin is idle),
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
6a4210a3978f058b3526427eb41ef398924e8c011cc4dc53c19f5419d31501f3
45a7ee993b5c234b0d25e5b05ef60e714fa3edf63d6bd0d6a29b13587c8d09ce
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Keeps a copyright and SPDX license header at the top of source fi
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: missing headers reported on open and save, Add License Header quick fix and command, project scan in the background."
categories = ["tools"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-event:file-opened", "on-event:file-saved", "on-command:*"]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
f7df58bb0ab2b92370b148de2614174ec1ff9b01c0078b5c20bd331f82298616
d5cf81455ae8b2b0fd6d1402d8775913bd7999db62b9bdf87675890ce52b1e80
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Jumps between MyBatis mapper interfaces and their XML statements
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: navigation both ways, line markers, id and parameter completion."
categories = ["frameworks"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-command:*"]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
d2e35f3e4ee6b699899ea43fdf96d5ec99f0730c502f94fd6f3246e605be99a7
227c8150c6ad7a5a20236764f149c876cbbc3b082763bf2e7fc05de99726b740
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Try a regular expression against sample text: matches highlighted
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: live matching with flags, capture groups, replacement preview, the editor's selection as test text."
categories = ["tools"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
activation = ["on-view:regex", "on-command:*"]
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
3893bd8ad06d3e19c0112081c6643b3bf3c67f5a184098432a6fd338426cd682
bbadcaa7a86a3e0a3f8be6d86aed2229d5cdfd128fda3435b4d64c7e19ad1df2
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Solidity support: file detection, highlighting, structure view an
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: .sol detection, highlighting, structure view, folding; language server via @nomicfoundation/solidity-language-server or solc."
categories = ["languages"]
engines.openidea = "^1.0"
# Highlighting, structure and folding come from this component; it is loaded
# when the first Solidity file is opened.
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a88c39d5782369329efa55b12330daa906e5c8d91252bd57c7343648840bac79
09cc92e8e731f8f59f3857a71f9a30cc3d9e04a1c32092920f573b0f26592855
+1
Переглянути файл
@@ -5,6 +5,7 @@ description = "Case conversion (camelCase, snake_case, kebab-case…), encoding
authors = ["OpenIDEA contributors"]
license = "Apache-2.0"
change-notes = "0.1.0 — first release: 26 commands in four groups."
categories = ["tools"]
engines.openidea = "^1.0"
wasm = "dist/plugin.wasm"
# The commands are declared below, so Find Action and the keymap list them