口哨声( W)@fungi.computer/whistle(中文(简体) )
Whistle是应用软件的输入命令图,它在许多程序中暴露了相同的动作
地点。 你写一个命令定义。 口哨有六个不可改变的观点
从它:密钥绑定、命令调色板、斜线命令、帮助、菜单和代理
查看 AI 工具。 每个呼叫者,从按键到代理,都执行命令
通过一个键入 execute 门并获得一个输入结果返回.
当键盘快捷键、调色板条目、a /slash 命令和一个
代理工具应该都意味着相同的动作,显示相同的可用性,并且失败
同样的方式。 Whistle拥有命令图. 您的主机保持密钥匹配,
制作、运输、授权和真实工作.
安装或设置
npm install @fungi.computer/whistle
口哨是一个以Zod为唯一运行时间依赖的无害环境管理软件包. 它运行在 节点,浏览器,和工人。 它有五个切入点:
-
@fungi.computer/whistle运行时间,scopeSnapshot,键图适配器, 和命令类型. -
@fungi.computer/whistle/wire: Zod schemas and 剖析命令 定义、提示、通知和迅速回答. -
@fungi.computer/whistle/interaction:一个渲染器中性交互存储. -
@fungi.computer/whistle/catalog:解析后端whistle.catalog/v1命令目录并将其变成命令. -
@fungi.computer/whistle/html:将命令预测绑定在一个 DOM 键图上. HTML 用户安装可选端口@opentui/[email protected]; 核心用户这样做 不需要这个.
快开始
这将创建运行时间, 向调色板和斜线贡献一个命令 表面,读取快照,并运行命令:
import { createWhistle } from "@fungi.computer/whistle";
const whistle = createWhistle();
const lease = whistle.contribute({
sourceId: "editor",
namespace: "editor",
commands: [
{
id: "save",
title: "Save file",
category: "File",
projections: {
palette: {},
slash: { aliases: ["save"] },
bindings: { values: ["mod+s"] },
},
handler: () => ({ saved: true }),
},
],
});
const snapshot = whistle.snapshot();
console.log(snapshot.palette.map((row) => row.command)); // [{ namespace: "editor", id: "save" }]
console.log(snapshot.slash.map((row) => row.alias)); // ["save"]
const outcome = await whistle.execute(
{ namespace: "editor", id: "save" },
{ origin: "palette" },
);
if (outcome.status === "handled") console.log(outcome.result); // { saved: true }
lease.dispose();
whistle.close();
命令以其输入的密钥命名, { namespace, id }。每个投影行
带着那个键作为 command,所以点击调色板、按键和斜线
命令全部调用相同的 execute 带着它 一行货 commandId 是一个不透明
制取密钥 Whistle mints: 用于反键或去dupe, 绝不构建或解析
这个 命名空间可能包含冒号( App 表面, 如
installation:inkcap);命令id可能不会.
捆绑浏览器键盘
npm install @opentui/[email protected]
import { createWhistle } from "@fungi.computer/whistle";
import { bindHtmlKeymap } from "@fungi.computer/whistle/html";
const whistle = createWhistle();
whistle.contribute({
sourceId: "browser",
namespace: "browser",
commands: [
{
id: "save",
title: "Save",
category: "File",
projections: { bindings: { values: ["mod+s"] } },
handler: () => ({ saved: true }),
},
],
});
const keyboard = bindHtmlKeymap(document.body, whistle, "keyboard");
// Contributions and availability updates refresh the projected key layer.
keyboard.dispose();
whistle.close();
dispose 删除 Whistle 层和订阅,可以两次调用.
HTML 密钥映射主机遵循文档的寿命; 此帮助器
不删除它的 DOM 听器。 命令仍然通过Whistle执行.
核心概念
指挥、捐助和租赁
命令有一个 id页:1 title页:1 category,可选 description页:1
handler选择 projections, (中文(简体) ), action 财务报告和审定财务报表 availability命令
只出现在表面 projections 名称.
contribute 接受一组命令 sourceId 变成一个
namespace,可选择在焦点内 scope 例如打开一个窗口.
招生是原子:全部贡献在任何贡献去之前都经过验证
活着 当一个命名空间已经属于另一个源, 当一个
同一范围内的犬形ID或斜口别名重复,或当一个字段是
空无一物.
每一笔捐款都为这确切的一代人提供租约. lease.update
从解剖角度交换命令. lease.dispose 将取去. 处理废品
租借从不取消替换。 两通电话都是一流的.
一. 快照和预测
snapshot() 返回六个被冻结的数组: bindings, (中文(简体) ), palette, (中文(简体) ), slash, (中文(简体) ), help, (中文(简体) ),
menu 财务报告和审定财务报表 agent。行是不可改变的副本。 每个表面的种类 依其
明确 order,然后根据UTF-8字节顺序中的命令ID,所以命令永远不会
取决于插入。 一个快照保持其身份直到图表或一些
可用性变化. subscribe(listener) 当图表改变时告诉你.
代理视图下降 presentation 元数据并保留语义
inputSchema 财务报告和审定财务报表 outputSchema,所以一个代理人得到工具描述,从不
a 用户界面布局. scopeSnapshot(snapshot, scope) 给人类和代理人
一个重点范围:全球行外加范围行外加重点斜线
化名或绑定同一全球的阴影。 代理行携带其范围
遵循相同的过滤器。 完整的运行时间快照仍然列出所有瞄准镜.
执行
execute(command, input) 只有这样 操作员才能运行. input.origin 是一个
八个封闭的起源: agent, (中文(简体) ), browser, (中文(简体) ), keyboard, (中文(简体) ), menu, (中文(简体) ), palette, (中文(简体) ),
programmatic, (中文(简体) ), slash 财务报告和审定财务报表 terminal其结果是:
handled:处理器完成. 返回值为result这样做 并非意味着实际工作或完成的持久工作流程.unavailable: 命令内容availability()说不。 其由回还.failed: 处理器扔了.error这是JSON:{ code, message, details? }。 。 。 .missing:没有现场指挥部有身份证.invalid:命令ID为空白.closed:运行时间先被关闭.
可用性
命令可以提供同步 availability() 浏览当前主机
事实. snapshot() 将无法使用的命令按其理由显示,以及
execute() 在拨打处理器前, 重新读取可用性 .
可用性是一种便利检查。 这不是授权,所以处理者
还是接受真正的要求 主持人更新自己的事实并出版
自己联系的快照 口哨没有投票或广播.
失败
掷出 WhistleActionError(code, message, details?) 选择失败代码
并附上JSON的细节。 任何其他 Error 变成 command_failed堆叠和
非错误抛出值从不交叉执行结果.
互动
运行中的命令可以和它的人通话 通过 input.interaction。 。 。 .
prompt 询问一个 choice, (中文(简体) ), text (可选择) secret)或(或) confirmation
问. notify 显示 info, (中文(简体) ), progress, (中文(简体) ), handoff (可选择打开URL)
输入代码)或 success。词汇名称是答案的类型,从不
部件.
呼叫者可以通过自己的交互. 否则 present(presenter) 登记册
上面的客户端 打开一个 用于每次人种处决。 探员和
计划性处决没有结果。 口哨解决会话 handled, (中文(简体) ),
cancelled 或者说 failed 当处理器结束。 最新介绍者获胜直到
其处置者运行.
createWhistleInteractionHost 从 ./interaction 是一个渲染器中性存储
一个互动。 它分析每一个迅速,注意和回答 ./wire
schemas,以及一个终端或浏览器渲染器绘制它的快照.
在回答或再次尝试后,主机重用当前进度消息,或 当制片人不提供时, 命令标题位于已运行行中. 这不会为内在操作打开一个对话 从未要求过 问或显示进展/移交.
interaction.run(operation) 标记一个序列操作为可重试。 一旦
对话已经提问或显示进度/交接,失败是
显示并等待 host.retry() 或开除。 在此之前,它传播
通常:失败的内线动作不会突然打开对话. 重试
仅以抓取的输入重复此操作; 原处理器保留
暂停,所以没有重播先前成功的步骤和问题。 开除
中止等待。 外部失败 run 正常结束处理器而不结束
广告整个命令的回放。 仅通知的相互作用
执行 run 一次并传播失败, 不进行交互重试等待 .
斜线参数
斜线投影可以携带 arguments,然后是文本的仅数据语法
还有别名 固定 words 用于完成参数对象的映射. text 剩下的放进去
字段,可选择与请求
身份重复的ID字段。 客户端分析, 完整并提示从此
数据,所以没有客户端在代码中命名命令.
目录
后端将其命令作为广告 whistle.catalog/v1 相同的目录
数据定义. parseCatalog 在客户端边界验证它.
catalogCommands(catalog, invoke) 把它变成命令 处理者呼唤
invoke 函数。 每项答复都是一个步骤: done (非强制性通知和
结果)或 prompt (一个问题加上下一个电话的全部论点,
答案将输入一个指定字段。 多步骤流动仍然处于无国籍状态
电线和运行 通过呼叫者的互动。 每个目录引用是
一个可重试的操作。 保留非秘密答复和请求身份;
秘密答案不会 续作保留了秘密问题,
在每次尝试中询问他们,并删除他们的价值观
有效载荷一旦解决,无论是成功还是失败。 只重试重试
那些秘密问题,不是早期的非秘密选择。 如果稍后的后端步骤
仍然需要一个秘密,它必须再次输入 引用这一点.
闭幕
close() 是同步的,是一元的,返回 undefined。第一个电话
释放每一个活性贡献,并留下共享的空快照。 租赁
之前的发行 变得无害。 一个 execute 答应你回来之前 close
仍然安顿下来,可能安顿下来 handled。稍后时间 execute 返回时 closed,以及
后一个有效 contribute 掷出 WhistleClosedError。 。 。 .
打入可重复使用的处理器
使用索引访问于 WhistleCommand 在命令之外声明处理器
字义 :
import type { WhistleCommand } from "@fungi.computer/whistle";
type SaveArguments = Readonly<{ path: string }>;
type SaveResult = Readonly<{ saved: string }>;
const save: WhistleCommand<SaveArguments, SaveResult>["handler"] = ({
arguments: input,
}) => (input === undefined ? undefined : { saved: input.path });
处理者收到 WhistleExecutionInput<SaveArguments>:来源,
可选参数、取消信号和可选互动。 这个
返回时 SaveResult, (中文(简体) ), undefined或承诺其中之一.
WhistleCommandHandler 名称此召回类型。 不同命令
参数和结果类型可共享 readonly WhistleCommand<never>[]
捐款。 贡献在运行时去掉这些普通字,所以处理器
仍然验证它实际得到的论据.
演示文稿提示
action.inputSchema 财务报告和审定财务报表 action.outputSchema 描述结构数据并保存
不透明的JSON说明. action.presentation 是一个可选的本地提示 :
action, (中文(简体) ), choice, (中文(简体) ), form, (中文(简体) ), confirmation,或 custom 以不透明 JSON 表示
拥有客户端自己的字段或手势绑定. 服务器能力不能
指定其形式、菜单或其他渲染。 人类的预测能保持暗示.
代理投影器放弃.
API 参考
见生成 API 参考覆盖根部
入境点和入境点 wire, (中文(简体) ), interaction 财务报告和审定财务报表 catalog 潜行道.
实例
显示命令为不可用
import { createWhistle } from "@fungi.computer/whistle";
let signedIn = false;
const whistle = createWhistle();
whistle.contribute({
sourceId: "account",
namespace: "account",
commands: [
{
id: "sync",
title: "Sync now",
category: "Account",
projections: { palette: {}, agent: {} },
availability: () =>
signedIn
? { status: "available" }
: { status: "unavailable", reason: "Sign in first." },
handler: () => undefined,
},
],
});
console.log(whistle.snapshot().palette[0]?.availability);
// { status: "unavailable", reason: "Sign in first." }
console.log(
await whistle.execute(
{ namespace: "account", id: "sync" },
{ origin: "agent" },
),
);
// { status: "unavailable", reason: "Sign in first." }
signedIn = true;
代码失败
import { WhistleActionError, createWhistle } from "@fungi.computer/whistle";
const whistle = createWhistle();
whistle.contribute({
sourceId: "files",
namespace: "files",
commands: [
{
id: "open",
title: "Open file",
category: "File",
handler: () => {
throw new WhistleActionError("not_found", "No such file", {
path: "a.txt",
});
},
},
],
});
const outcome = await whistle.execute(
{ namespace: "files", id: "open" },
{ origin: "terminal" },
);
// { status: "failed", error: { code: "not_found", message: "No such file", details: { path: "a.txt" } } }
装入后端目录
import { createWhistle } from "@fungi.computer/whistle";
import { catalogCommands, parseCatalog } from "@fungi.computer/whistle/catalog";
const catalog = parseCatalog(await (await fetch("/commands")).json());
const whistle = createWhistle();
whistle.contribute({
sourceId: "backend",
namespace: "backend",
commands: catalogCommands(catalog, async (commandId, args, signal) => {
const response = await fetch(`/commands/${commandId}`, {
method: "POST",
body: JSON.stringify(args),
signal,
});
return response.json();
}),
});
检查
从一次检查中, 单元测试覆盖运行时间, 电线计划, 交互 主机和目录 :
pnpm --filter @fungi.computer/whistle test
限制和错误
- 运行时间最多可维持64个现场贡献,256个现场指挥.
- 一个命令最多有16个绑定值和16个斜切别名. 斜线语法 最多32个字.
- 一个目录最多有128个指令,一个目录流在16个步骤后停止
与
flow_too_long。需要快捷键的目录命令失败interaction_required当没有互动时,总是如此 用于代理和方案来源. - 即时通知文本的上限为8192个字符。 选择提示 1 到
256个选项。 通知 URL 必须是
http或者说https。 。 。 . - 命名空间不得含有冒号,并命令ID、标题、类别和
标签不得为空白。 违章行为
Error从contribute或者说lease.update。 。 。 . - 这些界限是执行过程中的防御. 他们不是稳定的API 承诺.
口哨不解析原始文本中的按键或斜线输入, 渲染 UI, 运行
运输,或保持耐用交货状态。 它没有浏览器,终端,DOM,
反应、产品或授权状态,没有定时器或背景工作。 使用
页:1 ./wire 解析未信任的输入, 然后再成为命令、 即时或
备注.
贡献和许可证
麻省理工学院. 见 自由主义。 。 。 .