feat(plugin): add new plugin creation, packaging, and example commands; enhance Vite config and documentation for plugin development
This commit is contained in:
1 parent
7201039472
commit
bb0c24d5e5
84 files changed
+9683
-19
No files matched your search
@@ -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.
|
||||
@@ -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。
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
|
||||
```
|
||||
Reference in new issue
Block a user