【免费下载链接】Netcatty
SSH workspace, SFTP, and terminals in one
导读
Netcatty 是集 SSH 工作区、SFTP 与终端于一体的桌面应用,其插件平台为开发者提供了在沙箱化运行时中扩展原生设置、命令面板、终端补全与高亮等能力的安全通道。本文以仓库内可直接运行的官方示例 examples/plugins/hello-netcatty/README.md 为核心,完整讲解插件的构建校验打包流程、netcatty.plugin.json清单的每个字段、definePlugin入口的注册方式,以及终端 completion / decoration / theme 三类 Provider 的底层契约。读完本文,你将能独立完成一个 Netcatty 插件从零到安装验证的全过程,并理解其安全边界与权限模型的设计意图。
一、Hello Netcatty:一个可运行的插件 API 全景示例
hello-netcatty是 Netcatty 仓库为插件 API 提供的内置可运行示例。它体积虽小,却覆盖了插件平台的核心能力面:
- 原生设置(native settings):通过清单
contributes.settings声明一个Greeting设置项,插件运行时经宿主 settings broker 读写; - 命令面板贡献(command-palette contributions):注册
Examples: Say Hello命令并在命令面板中放置入口; - 惰性激活(lazy activation):命令与 Provider 均通过
activationEvents声明式激活,插件在真正被调用前不会启动; - 本地化(localization):清单中的
displayName同时提供en与zh-CN两个语言版本; - 终端 Provider(terminal Providers):注册补全、装饰与主题三类 Provider;
- 运行时 SDK:使用 @netcatty/plugin-sdk 导出的
definePlugin、PluginContext等类型化 API。
从根目录执行的构建命令可见,示例插件被纳入仓库统一构建管线:根 package.json 中build:plugin-packages依次执行合约生成、运行时依赖构建,然后通过npm run build --workspace @netcatty/example-hello-plugin调用示例自身的tsc编译。
二、构建、校验、兼容性检查与打包
2.1 构建插件包
仓库根 package.json 提供了聚合构建脚本:
npm run build:plugin-packages该命令会先生成插件合约与运行时依赖,再构建@netcatty/example-hello-plugin工作区。示例自身的 package.json 中build脚本为tsc -p tsconfig.json,其 tsconfig.json 采用ES2022、NodeNext模块解析与strict模式,输出到dist/目录。
2.2 使用 netcatty-plugin CLI 验证与打包
仓库内置于 packages/plugin-cli/src/cli.ts 的netcatty-plugin命令行工具支持五个子命令:
| 命令 | 说明 |
|---|---|
init | 在指定目录初始化一个新插件项目,需--id <reverse.dns.id>,可选--name |
validate | 校验插件目录或.ncpkg包的清单与结构 |
compatibility | 检查插件与指定 Netcatty 版本的兼容性,需--netcatty <version> |
build | 编译插件源码 |
pack | 打包为.ncpkg安装包 |
README 中给出的完整操作序列为:
npm run build:plugin-packages npm exec -- netcatty-plugin validate examples/plugins/hello-netcatty npm exec -- netcatty-plugin compatibility examples/plugins/hello-netcatty --netcatty 0.0.0 npm exec -- netcatty-plugin pack examples/plugins/hello-netcatty --out /tmp/hello-netcatty.ncpkg各步骤含义如下:
- validate:对插件目录执行清单语义、路径安全、资源引用等校验(从 cli.ts 的
validateTarget调用可知,通过后会输出Valid <kind>: <id>@<version>); - compatibility:将插件清单与目标 Netcatty 版本及 API 版本做引擎匹配(
checkPluginCompatibility),兼容时输出Compatible: <id>@<version>与启用的特性列表;不兼容则以错误形式列出全部原因并退出码 1; - pack:将插件归档为
.ncpkg包,输出文件数与 SHA-256 摘要。示例中输出到/tmp/hello-netcatty.ncpkg。
2.3 安装与激活的前置条件
hello-netcatty的 README 明确说明:需要以NETCATTY_PLUGIN_DEV=1环境变量运行 Netcatty,才能打包并安装插件。这与插件平台的开发门禁一致——docs/plugin-platform/isolated-runtime.md 指出,整个插件宿主当前处于0.1.0-internal内部预览阶段,运行时完全隐藏在NETCATTY_PLUGIN_DEV=1之后,目前尚不存在生产环境的插件入口。
三、插件清单逐字段剖析
插件的声明式契约全部位于 netcatty.plugin.json,它通过$schema引用 packages/plugin-contract/schema/plugin-contract.schema.json 定义的规范。
3.1 身份与元信息
{ "$schema": "../../../packages/plugin-contract/schema/plugin-contract.schema.json", "manifestVersion": 1, "id": "com.netcatty.hello", "name": "hello-netcatty", "displayName": { "en": "Hello Netcatty", "zh-CN": "你好,Netcatty" }, "description": "Minimal plugin contract and SDK example.", "version": "0.1.0", "publisher": "netcatty", "license": "GPL-3.0-or-later" }id采用反向 DNS 命名空间,是插件在安装数据库与运行时中的唯一标识;displayName按语言键提供本地化文案,这正是 README 提到的 localization 能力;license声明插件的开源许可。
3.2 引擎约束
"engines": { "netcatty": ">=0.0.0", "api": ">=0.1.0-internal <0.2.0" }engines.netcatty约束宿主版本,engines.api约束插件 API 版本区间,是compatibility子命令的判定依据。
3.3 入口与激活事件
"main": { "browser": "dist/index.js" }, "activationEvents": [ "onCommand:com.netcatty.hello.sayHello", "onProvider:com.netcatty.hello.completion", "onProvider:com.netcatty.hello.decoration", "onProvider:com.netcatty.hello.theme" ]main.browser指向编译产物dist/index.js,说明本插件采用浏览器运行时(Node 入口可通过main.node声明);activationEvents声明式地列出激活条件:命令被调用时(onCommand:)或 Provider 首次被请求时(onProvider:)才激活插件,实现 README 所说的惰性激活。
3.4 权限声明
"permissions": { "required": [ "commands", "settings.read", "menus", "provider.terminal", "terminal.complete", "terminal.output", "terminal.decorate" ], "optional": [] }required中的权限在安装即授予,optional则会在首次使用时弹出提示(docs/plugin-platform/terminal-providers.md 中说明必需授权会被复用、可选声明在首次使用时提示,拒绝/取消则不会把终端数据交给运行时)。这里provider.terminal与terminal.*系列权限共同构成终端 Provider 的最小权限集合。
3.5 contributes:设置、命令、菜单与 Provider
"contributes": { "settings": [ { "id": "com.netcatty.hello.greeting", "label": "Greeting", "description": "Text used by the example command.", "control": "text", "scope": "application", "default": "Hello from Netcatty" } ], "commands": [ { "id": "com.netcatty.hello.sayHello", "title": "Say Hello", "category": "Examples" } ], "menus": [ { "command": "com.netcatty.hello.sayHello", "location": "commandPalette", "group": "examples", "order": 10 } ], "providers": [ { "id": "com.netcatty.hello.completion", "label": "Hello completion", "description": "Suggests the example hello command.", "kind": "terminal.completion" }, { "id": "com.netcatty.hello.decoration", "label": "Hello decoration", "description": "Highlights the example greeting in terminal output.", "kind": "terminal.decoration" }, { "id": "com.netcatty.hello.theme", "label": "Hello theme", "description": "Adds a subtle cursor color to the example terminal theme.", "kind": "terminal.theme" } ] }要点:
- settings:
com.netcatty.hello.greeting为text控件、application作用域,默认值Hello from Netcatty,对应 README 中"在 Settings → Plugins 下修改 Greeting"的入口; - commands + menus:
Say Hello命令被放置到commandPalette位置,分组examples、排序 10,即 README 中从命令面板执行Examples: Say Hello的来源; - providers:三个 Provider 的
kind分别对应terminal.completion、terminal.decoration、terminal.theme,id与激活事件中的onProvider:一一对应。
四、插件入口与运行时 SDK 解析
插件入口 src/index.ts 是理解 SDK 用法的核心样本。
4.1 definePlugin 与 activate
import { definePlugin } from "@netcatty/plugin-sdk"; export default definePlugin({ activate(context) { context.logger.info("Hello Netcatty example activated", { pluginId: context.pluginId, }); // ... }, });definePlugin来自 packages/plugin-sdk/src/index.ts,只是一个类型安全的恒等包装,让插件对象获得完整的类型推导。activate(context)是运行时生命周期入口,context为PluginContext,其中包含:
pluginId、netcattyVersion、apiVersion:插件与宿主版本标识;subscriptions:DisposableStore,统一管理注册资源的释放(SDK 中该实现会在插件停用时批量dispose);settings:PluginSettings,提供get/update/onDidChange,即 README 所说的 host settings broker;commands:PluginCommands,registerCommand/executeCommand;providers:PluginProviders,register(providerId, kind, handler);logger:PluginLogger,带结构化字段的debug/info/warn/error。
4.2 注册命令并读取设置
context.subscriptions.add(context.commands.registerCommand( "com.netcatty.hello.sayHello", async () => { const greeting = await context.settings.get<string>("com.netcatty.hello.greeting"); context.logger.info(greeting ?? "Hello from Netcatty"); return { greeting: greeting ?? "Hello from Netcatty" }; }, ));命令 ID 与清单contributes.commands一致。处理器通过settings.get<string>(...)从宿主设置代理读取Greeting设置——这正是 README 所述"设置经由宿主 settings broker 读取、命令处理器注册在沙箱化插件运行时内部"的机制。返回值为 JSON 值,可被宿主用于 UI 反馈。
4.3 注册终端补全 Provider
context.subscriptions.add(context.providers.register( "com.netcatty.hello.completion", "terminal.completion", ({ payload }) => { const { input } = payload; return input && "netcatty-hello".startsWith(input) ? { items: [{ text: "netcatty-hello", score: 5_000 }] } : { items: [] }; }, ));补全 Provider 接收TerminalCompletionPayload(packages/plugin-sdk/src/index.ts),其中input是当前提示符输入片段、cursor为光标位置、hostOs标识宿主操作系统、maximum为宿主给出的结果数量上限。返回TerminalCompletionResult,其items为TerminalCompletionItem[],每项含text、可选displayText/description与score排序分。README 中"在 shell 提示符输入netc即可触发该补全 Provider"正是此逻辑:输入前缀与netcatty-hello匹配时返回高优先级建议。
4.4 注册装饰 Provider
context.subscriptions.add(context.providers.register( "com.netcatty.hello.decoration", "terminal.decoration", () => ({ rules: [{ id: "greeting", label: "Netcatty greeting", patterns: ["\\bHello from Netcatty\\b"], color: "#34D399", }], }), ));装饰 Provider 只返回声明式规则(TerminalDecorationRule:id、label、patterns正则数组与color十六进制色值),绝不涉及 xterm 对象或原始输出流。宿主侧会限制规则数量与字符串长度、校验颜色必须为显式 hex 值、拒绝不支持的表达式,并用线性时间 RE2JS 引擎编译执行(详见 docs/plugin-platform/terminal-providers.md)。装饰结果在 Provider 扇出后还会被再次限流:最多 16 条活动规则、32 个总模式,每段文本最多匹配前 4096 个字符、每次终端写入最多保留 256 个插件匹配。README 中"在终端打印Hello from Netcatty以触发装饰高亮"即为验证该规则的实操步骤。
4.5 注册主题 Provider
context.subscriptions.add(context.providers.register( "com.netcatty.hello.theme", "terminal.theme", () => ({ colors: { cursor: "#34D399" } }), ));主题 Provider 接收当前宿主完整调色板(TerminalThemePayload.currentTheme),返回有界的部分调色板(TerminalThemeResult.colors,键为TerminalThemeColorName联合类型,如background/foreground/cursor/selection与 16 色)。多个主题 Provider 按确定性优先级顺序合并,每色取第一个值,宿主颜色对缺省值保持权威。
五、终端 Provider 的底层契约与安全边界
5.1 一次调用会得到什么
从 docs/plugin-platform/terminal-providers.md 可以确认,普通终端 Provider 每次调用收到的都是不可变的会话快照(session:会话/主机/工作区 ID、协议、连接状态、cwd、标题、shell 类型、尺寸与备用屏幕状态),加上该 Provider 类型的专用负载。SDK 中所有 Provider 类型均有精确的负载/结果映射(OrdinaryTerminalProviderPayloadByKind/ResultByKind/OperationByKind,见 packages/plugin-sdk/src/index.ts),涵盖 completion、decoration、link、hover、matcher、semantic、prompt、background、theme 共九种普通 Provider 类型。
5.2 不会得到什么
README 特别强调:两类 Provider 都不会收到原始 xterm 对象,也不会收到敏感的终端输入/输出流。这与平台设计一致——普通 Provider 路径刻意省略命令文本、密码/提示内容、原始终端输出、xterm 对象、后端句柄与 terminal-worker 端口。只有在provider.terminal与terminal.intercept.input/terminal.intercept.output双权限下,高级工具运行时才能走独立的 MessagePort 快速通道处理原始字节拦截,且该通道还有 4ms 输入 / 50ms 输出截止、256 KiB 排队窗口与熔断机制等约束。此外,宿主标记为敏感/不回显的输入(如密码提示状态期间的每个字符)会在缓冲区创建前绕过该通道,插件无法接触凭据。
5.3 截止时间与并发控制
- 普通终端 Provider 请求默认 1.5 秒截止;自动补全使用更短的 750ms 运行时截止加上 800ms 渲染器端到端等待上限(覆盖惰性激活与首次授权);
- 同一会话、同一 Provider 类型同时只允许一个活动请求,新请求会取消并抑制旧结果;
- 一次扇出最多调用前 32 个按确定性顺序排列的 Provider;单个渲染器最多保留 64 个活动终端请求;
- 控制面 JSON 预算为 1 MiB,每个终端 Provider 的负载与结果额外限制为 128 KiB。
六、安装后的端到端验证
README 给出的验证路径非常具体,安装完成后按序执行即可:
- 修改 Greeting 设置:进入Settings → Plugins,将Greeting改为自定义文案。该设置经宿主 settings broker 持久化,插件通过
context.settings.get("com.netcatty.hello.greeting")读取; - 执行命令:打开命令面板运行Examples: Say Hello。命令处理器注册在沙箱化插件运行时内,日志中会输出当前 Greeting 值(缺省回退为
Hello from Netcatty); - 验证补全:在任意 shell 提示符输入
netc,应看到补全建议netcatty-hello(对应"netcatty-hello".startsWith(input)的匹配逻辑); - 验证装饰:在终端打印
Hello from Netcatty,该文本会被声明式规则\bHello from Netcatty\b以#34D399绿色高亮; - 验证主题:可观察到终端光标颜色变为
#34D399。
以上每一步都对应 README 描述的功能面,且与源码一一印证。
七、隔离运行时:插件为什么安全
README 将命令处理器所在的运行时称为"沙箱化插件运行时",其具体保障在 docs/plugin-platform/isolated-runtime.md 中有完整描述:
- 普通浏览器运行时:每个插件获得一个隐藏
BrowserWindow、独立内存会话与不可猜测的协议权限,启用 Chromium OS 沙箱、nodeIntegration=false、contextIsolation=true,无 DevTools/弹窗/导航/下载/网络权限,会话强制离线;preload 只负责传递一个宿主创建的 MessagePort,不暴露 Electron、Node 或应用内部 IPC; - RPC 与流:插件与宿主通过 MessagePort 上的 JSON-RPC 通信,所有入站包先通过深度/节点预算、字节预算与 JSON Schema 校验;控制消息限制 1 MiB,更大负载走带字节信用机制的流;
- 生命周期:激活有 5 秒截止;正常停止先请求
plugin.deactivate(2 秒截止)再关闭端口;5 分钟内 3 次失败会隔离插件,隔离状态跨重启保留; - 数据库:插件安装目录
userData/plugins/由 SQLite 管理,包从不直接解压进活动目录,而是经暂存目录、校验快照后以事务方式发布。
正因如此,"Providers 不会收到原始 xterm 对象或敏感终端输入输出流"不仅是一条 API 约定,更是由运行时隔离与权限边界共同保证的安全属性。
八、从示例出发的进阶路径
若想深入,仓库提供了成套的插件平台文档,可直接与示例对照阅读:
- docs/plugin-platform/contract-and-sdk.md:清单、合约与 SDK 的整体关系;
- docs/plugin-platform/isolated-runtime.md:隔离运行时、安装事务、数据库所有权与生命周期;
- docs/plugin-platform/terminal-providers.md:九种普通终端 Provider 的负载/结果形状与宿主适配器细节;
- docs/plugin-platform/security-and-permissions.md:权限模型与凭据保护;
- docs/plugin-platform/runtime-extension-boundaries.md:运行时扩展边界。
此外,netcatty-plugin init <directory> --id <reverse.dns.id> [--name <display name>](见 packages/plugin-cli/src/cli.ts)可用于生成自己的插件脚手架,再以hello-netcatty为模板逐步替换:先增加更多activationEvents与contributes条目,再尝试 link / hover / matcher / semantic 等其余 Provider 类型,最后按validate → compatibility → pack流程交付.ncpkg安装包。
结语
hello-netcatty虽名为 Hello World,却完整覆盖了 Netcatty 插件平台从清单声明、惰性激活、权限授予、SDK 注册到隔离运行时的全部关键环节。以它为起点,配合netcatty-pluginCLI 与插件平台文档,即可在 Netcatty 中安全地构建属于自己的终端能力扩展——补全、高亮、主题乃至未来的连接与同步 Provider,都建立在这套声明式清单与受限运行时的基础之上。
【免费下载链接】Netcatty
SSH workspace, SFTP, and terminals in one
相关推荐
Netcatty 插件契约与 SDK 开发指南:从 Manifest 到 RPC、流与 TypeScript SDK
Netcatty 插件契约与 SDK 开发指南:从 Manifest 到 RPC、流与 TypeScript SDK 本文以 Netcatty 官方插件平台文档
Netcatty 插件平台隔离运行时:安装事务、双运行时架构与故障隔离原理
Netcatty 插件平台隔离运行时:安装事务、双运行时架构与故障隔离原理 导读:本文以 docs/plugin platform/isolated runti
Netcatty 插件运行时扩展边界:宿主权威、RPC 注册表与终端快速路径架构解析
Netcatty 插件运行时扩展边界:宿主权威、RPC 注册表与终端快速路径架构解析 导读 本文基于 Netcatty 插件平台内部架构评审文档 runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考