feat(plugin): add new plugin creation, packaging, and example commands; enhance Vite config and documentation for plugin development

This commit is contained in:
Kang committed 2026-10-03 16:09:58 +08:00
1 parent 7201039472
commit bb0c24d5e5
84 files changed
+9683 -19

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
# Plugin development and diagnostics
This stage completes four deliverables on top of the local plugin host:
Implemented and verified. See [the development guide](plugin-development.md),
[native SDK reference](../plugins/sdk/rust/nyaterm-plugin-sdk/README.md) and
[validation report](plugin-system-validation.md#developer-tooling-and-diagnostics-validation).
- `pnpm plugin:create <id> <directory> --template ui|rust` creates a new, buildable
project without overwriting existing content. Each template includes a manifest,
working entrypoint, SDK integration, build/package instructions and README.
- A standalone Rust native SDK handles identity/API handshake, JSONL and framed
transport, concurrent requests, scoped reverse host calls, structured errors,
timeouts, cooperative cancellation and logging. The existing native counter
and generated Rust projects use the SDK.
- The Plugins panel displays idle/starting/running/stopped/error states for native
backends and distinguishes UI-only and disabled plugins. Users can stop a backend
and see its next invocation activate a fresh process.
- Users can inspect, refresh, copy and clear bounded in-memory logs. Startup,
protocol, crash and invocation errors are retained even during the handshake.
Old processes cannot overwrite the state of a newer activation. Logs are
treated as untrusted text, redact host-known secrets/scope tokens, and are not
persisted or added to backup/cloud sync.
Verification must exercise generated projects through build/package validation,
the SDK through the real host runtime in both transports (including reverse RPC,
errors, cancellation and crash handling), status/log transitions and retention,
and the frontend diagnostic controls. Type checking, relevant lint and frontend
build remain final gates. Marketplace, connection provider APIs and browser data
stream subscriptions remain separate stages.
+87
View File
@@ -0,0 +1,87 @@
# 插件开发与排错
插件安装、授权、运行状态和日志都在 **插件面板** 中操作。
## 创建项目
在 NyaTerm 仓库根目录执行:
```sh
# 纯界面插件:查看选中终端的会话信息
pnpm plugin:create example.session temp/plugins/my-session --template ui
pnpm plugin:pack temp/plugins/my-session temp/plugins/my-session.nyap
# Rust 插件:界面调用后台,也提供原生命令
pnpm plugin:create example.hello temp/plugins/my-hello --template rust
node temp/plugins/my-hello/build.mjs
pnpm plugin:pack temp/plugins/my-hello temp/plugins/my-hello.nyap
```
创建器拒绝覆盖已有目录。项目包含 manifest、可用入口、SDK 和 README;
Rust 模板还包含编译脚本,编译后自动填入当前平台的可执行文件路径。
需要 Node 和 Rust 1.94 或更新版本。Rust SDK 使用本地路径依赖,移动项目时
应一起提供 SDK 或调整 Cargo.toml 中的路径。
将生成的 `.nyap` 安装到插件面板,检查权限并启用。UI 模板需先选中已连接终端;
Rust 模板可以打开界面点击运行,也可以直接执行插件卡片上的原生命令。
## 编写 Rust 后台
入口实现 `Plugin`,由 `Server::from_env()` 启动服务。SDK 负责握手、传输、
并发、作用域、超时和取消,无需手写 JSON-RPC:
```rust
#[async_trait]
impl Plugin for MyPlugin {
async fn call(&self, context: Context, method: &str, input: Value) -> Result<Value> {
match method {
"ui/session" => {
let session = context.host().session().await?;
context.log("info", "Session inspected").await?;
Ok(session)
}
_ => Err(RpcError::method_not_found(method)),
}
}
}
```
以上会话读取需在 manifest 中声明并获授 `session.read`;原生执行需 `native`。
UI 调用 `NyaTerm.backend("ui/session")`,contributions.commands 中的原生命令
则使用 manifest 声明的方法。不要向 stdout 打印日志,它专用于协议;使用
`Context::log` 或 stderr。不要记录口令、令牌、终端输入输出等敏感内容。
默认采用 JSONL。将 `backend.transport` 改为 `stdio-framed` 后,SDK 根据宿主
环境自动切换协议,支持二进制帧;界面流式订阅尚未提供。完整 API 和取消约定
见 [Rust SDK](../plugins/sdk/rust/nyaterm-plugin-sdk/README.md)。
## 查看状态和日志
原生后台按需启动并复用进程。卡片显示未启动、启动中、运行中、已停止或运行异常;
禁用插件和仅界面插件有独立标识。RPC 方法返回错误时,只记录调用失败,后台仍可
继续运行;进程退出、握手或协议失败则显示运行异常。
点击 **查看日志** 可刷新、复制和清空。日志包括时间、版本、级别、来源和消息。
日志弹窗每秒刷新,卡片状态每 2.5 秒刷新;关闭插件管理面板后停止轮询。
每个插件最多保留 100 条、64 KiB 消息与来源/版本文本,单条消息最多 2 KiB。
stderr 每次启动最多采集约 64 KiB,超过后继续排空而不保留。
日志只在内存中保留,退出应用即清除,不进入备份或云同步。
宿主会遮蔽常见凭据和作用域令牌,但插件作者仍需自行避免写入敏感内容。
**停止后台** 保留启用状态和权限,下次调用会启动新进程,进程内计数等状态会重置。
插件有进行中的请求时会提示忙碌;先结束或关闭该操作,再停止后台。
禁用、更新、切换版本、锁屏和退出应用也会停止后台。旧进程的延迟消息不会覆盖
新启动进程的状态。更新插件必须提升版本号,同版本不同内容不能重新安装。
## 本地验证
```sh
node --test scripts/create-plugin.node-test.mjs
cargo test --manifest-path plugins/sdk/rust/nyaterm-plugin-sdk/Cargo.toml
cargo test --manifest-path src-tauri/crates/nyaterm-plugin-runtime/Cargo.toml --features sdk-fixture
pnpm exec vitest run src/components/plugins/PluginDiagnostics.test.tsx --maxWorkers=4
```
`native-counter` 示例已使用 SDK,并升到 1.0.1:执行两次可看到计数递增和日志;
停止后台后再执行,计数从 1 开始。新版安装包为 `temp/plugins/native-counter.nyap`。
模板编译和打包已在 Windows 验证;其他平台需构建对应二进制并填写 executables。
+22
View File
@@ -0,0 +1,22 @@
# NyaTerm plugin system implementation
The requested plugin system builds on the preceding architecture review. The implementation must provide a usable extension host, local package installation, lifecycle management, permissions, isolated UI, native runtime, documentation and an end-to-end example.
Implemented as the local tool/panel extension host v1. See the [plugin guide](plugins.md)
and [validation report](plugin-system-validation.md) for the supported APIs, evidence
and remaining desktop release QA limits.
## Completion requirements
- A versioned, validated manifest declares panels, commands, menu locations, permissions and optional platform-specific native backends.
- `.nyap` packages can be inspected before installation, safely extracted with strict size/path/checksum validation, installed as immutable versions, enabled/disabled, updated, rolled back and uninstalled.
- Permissions are granted explicitly, enforced in Rust, and checked together with live session scope and application lock state. Native execution is presented as trusted code with OS user permissions.
- Plugin panels integrate with the existing activity bar and panel layouts; plugin commands are discoverable and available in declared context menus.
- Third-party HTML/JS runs in a sandboxed iframe with a restricted CSP and a bridge bound to its own iframe, plugin identity and backend-issued scope token. It cannot invoke arbitrary Tauri commands.
- The host API supports scoped session metadata, recent terminal output, approved command execution, scoped file reads, namespaced settings and declared network origins. Revocation, session closure and window teardown invalidate access.
- Optional sidecars use lazy persistent processes, a versioned initialization handshake, bounded JSON-RPC/binary frames, timeouts, crash cleanup and stop-on-disable/update behavior.
- Updates and removal do not race active requests; plugin state and permissions survive restarts. Plugin layouts survive temporary unavailability.
- A JavaScript SDK, packager, working example plugin and developer documentation cover the installation and authoring workflow.
- Meaningful backend and frontend tests, type checking, relevant linting and a build verify the implementation. A runtime smoke check verifies installation and UI/bridge integration where the environment permits.
New connection protocols and replacing built-in SSH/SFTP/RDP/VNC are subsequent provider API work described by the review, rather than requirements of this tool/panel extension host. A remote marketplace is also a separate service. Local packages and the extension runtime must be complete independently.
+85
View File
@@ -0,0 +1,85 @@
# Plugin v1 validation
Developer tooling and diagnostics were subsequently completed; see
[the development guide](plugin-development.md) and the updated verification below.
Validated on Windows on 2026-10-03. Scope follows
[the implementation plan](plugin-system-plan.md).
| Requirement | Implementation and evidence |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Versioned manifest and contributions | Independent runtime validates API/engine versions, paths, permissions, panels and command/menu declarations. Frontend contribution tests verify enabled-version discovery and namespaced IDs. |
| Local package lifecycle | Package/registry tests cover traversal, case collisions, complete checksums, modified content, inspection digest, immutable versions, persisted grants, permission revocation, update, rollback and publisher continuity. |
| Permissions and scope | Rust gateway allowlists capabilities and combines grants with plugin version, window ownership, connected session and lock checks. Approved command/file operations revalidate scope before execution. Permission review is bound to the active version to reject stale dialogs after an update. |
| Isolated UI | Bridge tests cover unrelated/spoofed frames, opaque origin, fixed scope token, arbitrary native command attempts, duplicate IDs, oversized messages and disposal. IPC tests cover top-frame initialization and Tauri binary/channel/fallback serialization. |
| Workspace integration | Unified Plugins management panel, activity panels, floating/multiple panel layouts and terminal/connection menu contributions integrate with existing workspace components. Existing workspace and context-menu tests pass. |
| Native runtime | Real child-process tests exercise handshake identity, persistent reuse, reverse RPC, timeout recovery and crash cleanup. Framed transport tests verify binary round trips and size limits. Activation cancellation and application exit/lock cleanup are implemented. |
| Developer workflow | UI and native examples both build/package. The extracted native package initializes and returns counts 1 then 2 from the same executable process. UI SDK round-trip tests and browser smoke verify the authoring path. |
| Documentation | [Plugin guide](plugins.md) specifies installation, trust, package lifecycle, storage, manifest, UI API, native protocol, limitations and maintenance. |
Checks completed:
- `pnpm exec vitest run --maxWorkers=4`: **152 files / 964 tests passed**.
Default high parallelism exceeded the existing AI settings test's five-second
timeout; limiting workers passed the entire suite. Temporary reference projects
under `temp/` are now excluded from test discovery.
- `pnpm build`: frontend type checking, MCP sidecar build and production Vite build
passed. Existing Browserslist age and large-bundle notices remain.
- `pnpm lint`: passed; one existing dependency warning in `CommandSuggestions.tsx`
remains. Plugin frontend files have no lint diagnostics.
- Runtime Cargo tests: **9 unit tests and 2 real-process tests passed**.
- Runtime `cargo clippy --all-targets -- -D warnings`: passed.
- Tauri `cargo check --lib` and `cargo clippy --lib`: passed. Existing project-wide
Clippy warnings remain; plugin-module diagnostics were checked separately.
- Tauri plugin tests: **2 passed**, for atomic lifecycle admission and rendered IPC
script templates. The Windows library-test binary failed in the loader before
running any tests; a temporary copy with a Common Controls v6 activation manifest
ran both successfully. Production application files were not altered for this
workaround.
- Real Chrome smoke using the actual bridge, SDK and Session Toolbox files:
panel loading, selected-session request, theme propagation, opaque message origin,
fixed-scope routing, parent DOM isolation, blocked direct network and absent
child-frame IPC transport all passed. Native host calls were stubbed in this
browser check.
This validation does not include a manually operated packaged Tauri desktop build
or macOS/Linux WebView testing. The browser smoke uses HTTP asset serving with the
equivalent sandbox/CSP and mocked native commands; it does not exercise Tauri's
custom resource protocol or real terminal execution dialogs. Verify those in
desktop release QA. Native plugins remain trusted OS processes; the current
format has no cryptographic publisher signatures or marketplace.
## Developer tooling and diagnostics validation
Completed on Windows, 2026-10-03:
- Project creation: UI and Rust starters generated successfully. Three Node tests
cover external paths (including spaces), valid SDK references, invalid arguments
and refusal to overwrite existing source.
- Generated UI/Rust packages pass the real package validator. The Rust starter
builds in release mode. Extracted native-counter **1.0.1** and the generated
Rust package both complete their SDK handshake, return counts **1, 2** from a
persistent process, and exit cleanly on stdin EOF.
- Rust SDK: two wire tests cover bounded frames, binary round trips, clean EOF and
truncated input. Runtime: **10 unit tests + 5 process integration tests** pass
with `--features sdk-fixture`. SDK process tests cover JSONL/framed transport,
concurrent and reverse RPC, binary data, structured logs, request cancellation,
cancellation of timed-out reverse host calls, method errors, panic recovery,
crashes, and stderr collected during a failing initialization handshake.
- Diagnostic storage tests cover retention limits, UTF-8 truncation, credential
redaction, stop/restart generations, clearing and removal. The panel distinguishes
UI-only/disabled plugins and backend states; three frontend tests cover stopping,
refresh/copy/clear, untrusted log text and stale responses after locking.
- Frontend full suite: **153 files / 967 tests pass**. `pnpm build` and `pnpm lint`
pass; the existing CommandSuggestions hook warning remains.
- SDK/runtime Clippy with `--all-targets -- -D warnings` passes. Tauri library
Clippy/check passes; there are no plugin-module diagnostics, while existing
project-wide warnings remain.
- `pnpm i18n:check` still reports existing ordering issues in the AI text sections
of all four locales. Plugin sections were formatted independently to preserve
unrelated content. All four locales contain the same diagnostic keys.
The user reported successful desktop testing of the earlier native-counter
package. The new diagnostics controls have automated frontend coverage; the
updated 1.0.1 package is ready for desktop smoke testing. This does not claim
macOS/Linux validation or a full manual desktop lifecycle test.
+309
View File
@@ -0,0 +1,309 @@
# NyaTerm plugins v1
NyaTerm supports locally installed tool and panel extensions. A plugin can add
sandboxed HTML panels, terminal/connection context-menu commands, and an optional
persistent native backend. Built-in connection protocols remain managed by
NyaTerm. Connection/filesystem providers and an online marketplace are future APIs.
## Install and use
1. Open the **Plugins** activity panel, choose **Install local package**, and select a `.nyap` file.
2. Review the plugin identity, publisher label and declared permissions. Installation
validates the package and leaves it disabled.
3. Choose **Enable**, select the permissions to grant, and save. A native backend
requires explicit `native` approval.
4. Open its panel or command from the Plugins activity item. Panels also appear in
the activity bar. Declared menu commands appear in terminal/connection menus.
The terminal selected when a panel opens defines its session scope. Selecting
another terminal recreates that scope. A terminal-menu command is scoped to that
terminal; a saved-connection-menu command has no live terminal scope. It cannot
silently access an unrelated active terminal. Opening a panel from a context menu
keeps that explicit session selection until another open intent replaces it.
Command execution and file reading each require a separate approval dialog, even
after granting their manifest permissions. Closing the panel or command dialog,
closing its session/window, locking NyaTerm, or changing plugin activation revokes
the associated scope and cancels host requests. Cancellation cannot undo a command
that has already run on the terminal.
Install a newer package with the same ID to update. Versions are immutable: the
same version cannot be replaced with different contents. Updates and switching
the version selector leave the selected version disabled for review. The old
versions remain available for rollback. Updates, grant changes and uninstall fail
while a plugin request is active; finish or close it, then retry. Disable stops the
native backend. Native processes are also stopped on lock and application exit.
## Trust and storage
UI panels run in an iframe with `sandbox="allow-scripts"`, an opaque origin and
a restrictive response CSP. Scripts, styles, images and fonts are local package
assets; direct network access, child frames, form submissions and popups are
blocked. The bridge accepts messages only from its own frame and binds requests
to a Rust-issued plugin/version/window/session scope. Plugins do not receive an
arbitrary Tauri command gateway. On Windows, where Wry injects initialization
scripts into subframes, the native IPC transport is explicitly initialized only
in the top-level document.
**Native plugins are trusted executables running with your OS user permissions.**
Host permission checks do not sandbox their filesystem, network or subprocess
access. Only enable native packages from a source you trust. SHA-256 checksums
verify consistency and the reviewed package digest prevents a file swap between
inspection and installation; these are not publisher authentication. Publisher
names are self-declared. A changed publisher label cannot overwrite an existing
ID without uninstalling it first, but that continuity check is not a signature.
The local v1 format does not include cryptographic publisher signing.
Plugin files live in the active runtime's `plugins/` directory (normally
`~/.nyaterm/plugins/`):
```text
plugins/
registry.json
vendor.plugin/versions/1.0.0/...
data/vendor.plugin/settings.json
```
`storage` holds plaintext, nonsecret JSON preferences, separately for each ID.
Do not store passwords, tokens, terminal commands or terminal output there.
Uninstall removes plugin code and registration; preferences are retained for a
later reinstall. Plugin packages, grants and preferences are excluded from the
existing portable snapshot/backup/cloud-sync payloads.
## Build the UI example
For project generation, Rust SDK APIs, status and logs, see the
[development guide](plugin-development.md) and
[native SDK reference](../plugins/sdk/rust/nyaterm-plugin-sdk/README.md).
From the NyaTerm repository (Node/pnpm and Rust required):
```sh
pnpm plugin:pack plugins/examples/session-toolbox temp/plugins/session-toolbox.nyap
```
Install the generated file, enable it with the permissions you want to try, select
a live terminal and open **Session Toolbox**. Read output, request a command or
request a UTF-8 file read. The latter two actions show the native host approval UI.
Terminal execution uses the existing execution profile and shell capture engine;
sessions that disable execution retain that restriction.
The packager includes `ui/nyaterm-sdk.js` automatically. It is reserved and cannot
be overwritten with different bytes. Only `manifest.json`, `ui/`, `assets/` and
`bin/` are collected, so source code/build artifacts outside these directories do
not enter the archive. UI entry assets must be at most 4 MiB each.
## Manifest
```json
{
"contributions": {
"panels": [
{ "id": "overview", "title": "Overview", "entry": "ui/index.html" }
],
"commands": [
{
"id": "open-overview",
"title": "Open Overview",
"panel": "overview",
"menus": ["terminal", "connection"]
}
]
},
"description": "Tools for a selected terminal",
"engine": ">=1.2.12, <2.0.0",
"id": "example.tools",
"manifestVersion": 1,
"name": "Example Tools",
"permissions": ["session.read", "terminal.read", "storage"],
"publisher": "Example",
"version": "1.0.0"
}
```
Unknown fields are rejected. IDs use lowercase ASCII letters, digits, `.`, `_`
and `-`, start with a letter/digit and are at most 128 bytes; `..` is prohibited.
Plugin IDs also contain a dot to separate their namespace.
Versions use canonical SemVer; `engine` uses Rust semver requirements, including
comma-separated comparators. Panel entries are `ui/*.html`. A command references
exactly one panel or a declared `command/*` native method. Panel and command IDs
are unique across the contribution set. Optional menus are `terminal` and
`connection`. Panels use persisted `plugin:<pluginId>:<panelId>` activity IDs and
retain their layout when temporarily disabled or unavailable.
Limits: 16 panels, 64 commands, 24 permissions including at most 8 exact HTTPS
origins. Package limits: 128 MiB compressed, 256 MiB expanded, 64 MiB per file and
1024 archive entries. Paths must be portable relative paths; traversal, symlinks,
special files, Windows aliases and case collisions are rejected. `checksums.json`
maps every other archive filename, including the manifest, to its SHA-256 hash.
Use the packager to produce this file rather than maintaining hashes by hand.
## UI SDK and host API
Load classic scripts from local files. Inline styles are allowed; use a local
bundled JS file rather than a CDN or dynamic imports requiring cross-origin access.
```html
<script src="nyaterm-sdk.js"></script>
<script src="app.js"></script>
```
```js
await NyaTerm.ready;
const session = await NyaTerm.session();
const { output } = await NyaTerm.terminal.read(100);
document.getElementById("output").textContent = output;
```
Use `textContent` when rendering terminal/file/network data. Typings are in
`plugins/sdk/nyaterm.d.ts`. SDK operations return promises and reject on denied
permission, invalid scope, cancellation or runtime errors.
| SDK method | Host method / permission | Behavior |
| ---------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `session()` | `host/session` / `session.read` | Selected `{id,name,type,connected}` or `null`; no credentials |
| `terminal.read(lines = 100)` | `host/terminal/read` / `terminal.read` | `{output}`; 1–500 recent lines |
| `terminal.execute(command, timeoutMs = 30000)` | `host/terminal/execute` / `terminal.execute` | Individual approval, audit and existing execution engine; 1–120 s execution timeout |
| `filesystem.read(path)` | `host/filesystem/read` / `filesystem.read` | Individual approval; at most 256 KiB UTF-8 text |
| `storage.get(key)` | `host/storage/get` / `storage` | JSON value or `null` |
| `storage.set(key, value)` | `host/storage/set` / `storage` | `null` deletes; otherwise saves nonsecret JSON |
| `network.request(url, options)` | `host/network/request` / `network:https://origin` | GET/POST, declared exact origin, no redirects, 30 s timeout |
| `backend(method, input)` | `ui/*` or declared `command/*` / `native` | Request to persistent backend |
Execute results contain `output`, `exitCode`, `durationMs`, `timedOut` and
`sourceTruncated`. File reads expose `content` (SSH may return extra file metadata).
Local file paths are confined to the selected session's canonical working
directory. Remote reads require SSH/SFTP; serial and Telnet do not offer file reads.
Storage allows 128 portable-ID keys and 1 MiB serialized JSON per plugin. Network
options are `{method, body, contentType}`; request bodies are capped at 256 KiB and
responses at 1 MiB. Results are `{status, contentType, body}` with text bodies.
URLs cannot contain credentials. HTTPS origins include their nondefault port and
must match exactly; no wildcards, paths or implicit subdomain grants.
`NyaTerm.context` contains plugin identity/version and CSS theme variables;
`NyaTerm.onContextChange(listener)` returns an unsubscribe function. Theme variables
are applied to the iframe document automatically. This context contains no scope
token. Tokens are managed by the parent bridge. Scopes expire after 30 minutes;
open panels renew at 25 minutes by recreating their document. Preserve needed
nonsecret preferences in plugin storage.
UI requests are limited to 32 outstanding calls and 1 MiB request parameters.
Host calls have a 180 s envelope including approval; SDK calls time out at 185 s.
An approval dialog times out at 120 s. A native backend request times out at 170 s,
including any reverse host calls, so long commands and approvals share that budget.
## Native backend protocol
Build and package a working persistent-process example:
```sh
pnpm plugin:example:native
pnpm plugin:pack temp/plugins/native-counter temp/plugins/native-counter.nyap
```
Install and explicitly grant `native`. Invoke **Increment native counter** from
the Plugins panel twice: its count increases in the same process. Disable/re-enable
and invoke again: activation starts a fresh process. The build script generates
a manifest for the current OS/architecture. Production plugins should bundle the
executables needed for each supported platform:
```json
{
"executables": {
"windows-x86_64": "bin/backend.exe",
"linux-x86_64": "bin/backend"
},
"transport": "stdio-jsonl"
}
```
Put that object under `backend`, declare the `native` permission, and declare a
command with `"method": "command/count"`. Accepted platforms are Windows, Linux
and macOS on `x86_64` or `aarch64`. Activation is lazy, per plugin; calls reuse the
process and can run concurrently. stdout is exclusively protocol traffic; write
diagnostics to stderr. Environment inheritance is restricted to PATH, OS/temp and
locale values, plus `NYATERM_PLUGIN_ID`, `NYATERM_PLUGIN_VERSION`, `NYATERM_APP_VERSION`.
The working directory is the immutable installed version directory.
The host first sends a JSON-RPC 2.0 request:
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "plugin/initialize",
"params": {
"pluginId": "example.tools",
"pluginVersion": "1.0.0",
"appVersion": "1.2.12",
"apiVersion": 1,
"protocolVersion": 1,
"permissions": ["native"]
}
}
```
Reply within 10 seconds with matching identity/version/protocol:
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"pluginId": "example.tools",
"pluginVersion": "1.0.0",
"protocolVersion": 1
}
}
```
Normal host requests have numeric IDs and params `{scopeToken, input}`. Respond
with the same numeric ID and `result` or `error:{code,message}`. Backends may make
reverse host requests using a **string ID**, one of the host methods in the table,
and params `{scopeToken, input}`. They must propagate the token associated with the
specific call, not keep a global "current session". Host enforcement checks the
token's plugin, live session/window, version, grants and lock state. Other plugins'
tokens are rejected. Backends should handle `$/cancelRequest` notifications for
timed-out numeric host requests. Closing a scope stops waiting for its response;
native code must arrange its own cooperative work cancellation.
`stdio-jsonl` is one UTF-8 JSON object per newline. `stdio-framed` uses:
```text
kind: u8 | payload length: u32 little-endian | payload
kind 1: UTF-8 JSON-RPC
kind 2: channel length: u16 little-endian | UTF-8 channel | raw bytes
```
Frames are capped at 8 MiB; JSON at 2 MiB; channel names at 128 bytes. The runtime
supports notification/binary subscriptions for future provider integration, but
v1 browser APIs expose request/response calls only. There is no public UI binary
stream or backend event subscription. Do not use those frames to implement a UI
API yet. The runtime bounds queues and pending requests and terminates the child
on protocol failures, failed handshake, disable/update/uninstall, lock or exit.
A crashed backend is activated again on the next call.
## Maintaining the host
The independent runtime lives in `src-tauri/crates/nyaterm-plugin-runtime`; Tauri
adapters and permission checks live in `src-tauri/src/core/plugins`. Plugin UI
bridge code and the SDK are separately tested.
`ipc_transport.js` and `process_ipc_message.js` preserve Tauri 2.11.5 transport
semantics, with a top-frame guard before initializing the invoke key. When
upgrading Tauri, compare these files with its upstream scripts and rerun the IPC
isolation/serialization tests. Do not remove the guard or add `allow-same-origin`
to plugin iframes to work around compatibility issues.
On Windows, the full Tauri library test executable may need a Common Controls v6
activation manifest to load its native UI dependencies. An entrypoint-not-found
error before any tests run is a loader failure; check the executable manifest
rather than interpreting it as a failed plugin assertion.
```sh
pnpm exec vitest run
pnpm build
pnpm lint
cargo test --manifest-path src-tauri/crates/nyaterm-plugin-runtime/Cargo.toml
cargo test --manifest-path src-tauri/Cargo.toml --lib core::plugins
```