feat(plugins): let a plugin keep a process running behind a handle (#2027)

Closes #1826

API 1.3 adds `run-command` (one-shot with stdin, cwd and environment) and a handle-based `start` for a long-running process with incremental stdin/stdout/stderr, both gated by the existing `process:<program>` grants. A plugin's environment entries are append-only: a key the child would already inherit (including the login-shell PATH) is refused with an error naming it, as are empty or malformed keys. Processes run in their own process group and are killed with their descendants when the handle is dropped, the plugin is disabled, unloaded or reclaimed, or the owning window closes; at most eight handles per plugin. Output is bounded and UTF-8 safe, stdin writes honour cancellation, and validation failures return typed errors instead of trapping the plugin. Verified on macOS; Windows/Linux not run.

Co-authored-by: chaoes <[email protected]>
Цей коміт міститься в:
WangTongchaoтаchaoes authored and GitHub committed 2026-11-08 02:53:04 +08:00
1 джерело e024c13db7
коміт 3efec1c848
63 files changed
+1549 -72

No files matched your search

+12
Переглянути файл
@@ -442,6 +442,8 @@ then run `openidea-plugin check`.
| `host::publish_diagnostics(path, items)` | `diagnostics.publish` | Replaces this plugin's problems for the file (editor squiggles and the Problems view); an empty list clears them. |
| `host::fetch(request)` → `{status, headers, body}` | `net.fetch` | See _Network_. |
| `Project::run_process(program, args, stdin)` → `{code, stdout, stderr}` | `process.run` | Only the pinned program of a `process:` grant; runs in the project root of a trusted project; 4 MiB of output; killed when the call is cancelled or times out. |
| `host::run_command(cmd, project, stdin)` → `{code, stdout, stderr}` | `process.run-command` | API 1.3: same limits as `run_process`, with append-only environment variables; needs the pinned `process:<program>` grant. |
| `host::start(cmd, project)` → `Child` | `process.start` | API 1.3: `poll(max_bytes)`, `write(text)`, `close_stdin()`, `wait(timeout_ms)`, `kill()`; at most 8 handles; dropping one kills its process. |
| `host::refresh_view(view)` | `views.refresh` | The view (or custom editors) render again; call it when the content changed. |
| `host::reveal_view(view)` | `views.reveal` | Brings one of your declared `[[contributes.views]]` to the front in the calling window. |
| `host::post_view_message(view, text)` | `views.post-message` | To the script of the view's `html` body. |
@@ -449,6 +451,16 @@ then run `openidea-plugin check`.
| `host::open_location(path, line, column)` | `views.open-location` | Opens a file inside the project in an editor of the invoking window, the caret at `line`/`column` (0-based, UTF-16): a view's links to code. Needs `project.read`. |
| `host::setting(key)`, `setting_bool`, `setting_string`, `setting_number` | `settings.get` | The user's value (project over global) or the schema's default. |
Both process APIs only add environment variables. A key already in the prepared child
environment (including the login-shell `PATH`) fails with an error naming the key;
variables are never replaced or removed. Names compare case-insensitively on Windows.
At most 64 entries are allowed: nonempty names up to 256 bytes without `=` or NUL,
and values up to 4 KiB without NUL. The approved file still decides what runs.
Unpolled child output keeps the last 1 MiB per stream; each read is capped at 1 MiB
and preserves complete characters. Disabling, unloading, reclaiming or stopping a
plugin ends its children, as does closing the window that started them.
`run-command` receives an optional string argument: `executeAction("plugin.<plugin id>.<command id>", "arg")`
from another feature, or nothing when run from Search Everywhere / a shortcut. `ctx.selected_paths()` lists the files and folders a Project view or editor tab menu ran the command on (the argument is the first of them); it is empty otherwise. No capability is needed.
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
5325c5c915e66d12cc47afc55b29e18e531a3f533e69c51933413f4f77a74438
7872480dee517db617120a9327d3bbbcc007d31bbf9e6c999ce202f38f0ed478
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
d23b9b067928d6e7648a4989b09e201cade971a67ab769b4d583a274f4e071ac
31f1d88c7e88553eed145fd170485c10e19967eccbf2021ec71fca862d05e952
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a49ab90f826a214d7de7058164f1fd886d9b762482c94953ee07495833742f92
6a2db880c49b15d7ce5ba7fda69ac8d1cac04f0412d42b6922550594ef94a7c5
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
29f7f3bd8b35edf079fdac3c4cc67c5b5ffd57c999064a9ee55793f4f22b6c6b
9299f10c517ee5582312f0f14871e21d36050ee8d621df31d7e6a4126c4ea37e
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a008cd053e851f86be07576b21c0a0a763cb8e239e7bba03127de0d7c5ab3a4b
1f86f15f5e864ec9a91cb31379047eafd4ae8c07ffacb5e1c85955ee07873342
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a598e79d3e15b7ce4c5fef6b0af56d7d17e572bc1736806bbfa93349126dfb33
540e60e34b752eb201ef9cacea44066b4f59a4e36c3d1a468bf208223569a628
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
481910e4fe978ad5060cc70dd9d66dda029f4f73a0e8cd172b78df85dfa6dcd1
106f4a0b300fdb98fd0bd8f8bc1b3ce2c692a8a7e0107353905d7edbb7c7ce4d
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a667fe84ceda6c32f4e74e86b39c39f9c0c7654e07ff911a294dae44a3e15c23
eba583c669da994dba24a8a16546c9d2cb50fb46db53207207809b20882680e8
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a59169bbd1e95017f28f92c9b8796059f0804c9361f1ec39d9ff2ec3fc7ca3ab
d1385eb9aa4d78bbcbdf98565fdf3b550050388af9248814f5c994b54d605708
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
fc5e1ed12c0238b7d9bc0621e41beb89bd62191fe0f30b8c66554207cde9cdf7
8f4c3af7f1f0f6e2001ce161d89e2b83ef700480d6fd5517f46e90437f74639e
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
343c6d46b9bf07f8631f16dbc737301a02a6b229b17eb0a455207509823680e1
2504128882832c282d627946668f10603fe4b165dca8f12a49131934d1a5c09d