跳转到内容

下载 Termii · v0.4.5

三分钟,装进你的 Dock

原生构建,包体仅数 MB。应用内自动更新,始终新鲜。

安装说明

macOS:若提示「无法打开」,在终端执行 xattr -dr com.apple.quarantine /Applications/Termii.app。Windows:首次运行可能触发 SmartScreen,选择「仍要运行」即可。

存储 / 事件 / 受限文件写

插件的数据与系统边界:持久化 KV、配置快照、全量凭据、Tauri 事件订阅、 原生对话框与受安全约束的文件写。

插件私有 KV,落在 config.json 的 settings.pluginSettings.<pluginId>, 随宿主配置同步落盘。同步读写:

storage: {
get<T>(key: string, fallback: T): T;
set(key: string, value: unknown): void;
}
const token = ctx.storage.get("token", "");
ctx.storage.set("token", "…");

要点:

  • 每个插件的存储互不可见(按 pluginId 分桶)。
  • 卸载插件时持久化状态保留在 pluginSettings 里,重装后可恢复 (目录与授权会被删除,见 信任模型与安全边界)。

配置同步类插件可以通过 ctx.config 导出/导入宿主完整配置:

config: {
export(): Promise<string>; // 当前完整配置快照(JSON 字符串)
import(json: string): Promise<void>; // 应用快照并立即落盘
}
// 导出
const snapshot = await ctx.config.export();
// 导入(例如从 WebDAV/S3 下载回来的配置)
await ctx.config.import(snapshot);

要点:

  • 需要 L1 process 能力(ctx.config 与全量 vault 共用同一把信任锁)。
  • 导出内容包含 hosts / settings / pluginSettings / layouts。
  • 导入会复用启动时的迁移/归一化逻辑,更新当前运行中的 store 并立即 store_write 一次,避免被后续防抖写覆盖。
  • 插件自身的数据也可以继续用 ctx.storage;ctx.config 面向“整机配置 备份/恢复”场景。

ctx.vault 除按 id 读写外,还提供全量备份/恢复:

vault: {
// 按 id(无能力门禁)
get(id: string): Promise<string | null>;
set(id: string, secret: string): Promise<void>;
delete(id: string): Promise<void>;
// 批量(需要 L1 `process`)
list(): Promise<string[]>;
export(): Promise<Record<string, string>>;
import(entries: Record<string, string>): Promise<void>; // 整体替换
}
const allSecrets = await ctx.vault.export();
// 上传到远端前建议先加密。
await ctx.vault.import(allSecrets);

import 是整体替换语义:传入 map 中不存在的旧条目会被删除,配合 export 可做完整恢复。

订阅 Tauri 事件。事件名必须在白名单前缀内,白名单外的订阅会被拒绝 并打印错误:

pty:// sshch:// serial:// ssh:// transfer://
fs-progress:// tray:// forward:// proc://(流式进程 chunk/exit 事件)
events: {
listen(event: string, cb: (payload: unknown) => void): Disposer;
}
// 订阅某主机隧道变更(forward://changed 由 ctx.tunnels.onChanged 封装,
// 一般直接用 tunnels.onChanged 即可)
const off = ctx.events.listen("forward://changed", (payload) => {
// payload: { sessionId?, hostId? }
});

事件名与负载没有统一的 schema(由各后端会话约定),使用前先确认对应 API 是否已有封装(如 tunnels.onChanged、terminal.onOutput、 ProcessHandle.onData 都已包装好,优先用它们)。

用于「导出结果到本地文件」一类的流程:

dialog: {
pickSavePath(opts?: { defaultName?: string; extensions?: string[] }): Promise<string | null>;
pickFile(opts?: { extensions?: string[] }): Promise<string | null>;
pickDirectory(): Promise<string | null>;
}
  • pickSavePath:弹「另存为」对话框;取消返回 null。选中的路径会在 本次会话内被 fs.writeText 授权可写。
  • pickFile:弹「打开文件」对话框;extensions 是可选扩展名白名单 (不含前导点、小写,如 ["tar"]);取消返回 null。官方 Docker 插件的 load image 对话框用。
  • pickDirectory:弹「选择目录」对话框;取消返回 null。适合让用户 指定本地同步/备份目录。
fs: {
writeText(path: string, content: string): Promise<void>; // UTF-8 文本
}

安全约束:fs.writeText 不做任意路径写——只允许写入本次会话内 经 ctx.dialog.pickSavePath 返回的路径(宿主在 context 内记录已授权 路径集合),防止插件乱写文件系统。取消对话框(返回 null)或未授权 路径都会抛错:

const path = await ctx.dialog.pickSavePath({ defaultName: "batch-result.txt" });
if (!path) return; // 用户取消
await ctx.fs.writeText(path, content); // 仅此路径可写

完整示例:导出结果到本地文件

Section titled “完整示例:导出结果到本地文件”
async function exportResults(rows) {
const content = rows.map((r) => r.join(",")).join("\n");
const path = await ctx.dialog.pickSavePath({ defaultName: "result.csv" });
if (!path) return;
try {
await ctx.fs.writeText(path, content);
ctx.ui.toast.success({ title: "已导出", description: path });
} catch (e) {
ctx.ui.toast.error({ title: "导出失败", description: String(e) });
}
}