@kai-workflow-buildinger/cli-sdk (英语)
作者原子,TypeScript中的可代理执行的CLI.
一个小的,有观点的SDK,用于定义特工运行时间引用的CLI. 每个工具都代表一个原子操作——一般是单个HTTP调用,但一个工具可以包裹任何单个可观察到的操作. SDK给你一个沙盒 fetch,范围机密,打入输入+输出计划,以及每个工具的 Egress 允许列表. 主机运行时间处理输入验证,秘密范围界定,网络沙箱,以及用于写作的"人入"审批门.
状态 : 预v0.1. 写作的表面
tool, (中文(简体) ),defineCLI, (中文(简体) ),buildCtx, (中文(简体) ),ctx.fetch, (中文(简体) ),ctx.secrets, (中文(简体) ),ctx.properties)是稳定的.
安装
npm install @akai-workflow-builder/cli-sdk zod
所需资源
| 节点 | ≥ 24 |
| 类型脚本 | ≥ 5.0 (用户数) const 字元键推论的类型参数) |
zod | ^4.4.1 (同类依赖) |
tsconfig.json | 需求 Response 编号 : RequestInit 编号 : AbortSignal添加 "DOM" 给您现有的 lib 条目(例如). "lib": ["ES2023", "DOM"])或安装 @types/node@^18+ 并列出在 "types" (例如:. "types": ["node"]) — TypeScript 6 不再装入 @types/* 软件包 types 没有名称 |
快速启动
import { tool, defineCLI } from '@akai-workflow-builder/cli-sdk';
import { z } from 'zod';
// Define the response shape once; reuse for `jsonOutput` and runtime parsing.
const Response = z.object({
user: z.object({ id: z.number(), email: z.string() }),
});
const usersMe = tool({
name: 'Authenticated user',
description: 'Return the authenticated user.',
jsonOutput: Response, // input defaults to z.object({}) when omitted
options: {
network: { egress: 'zendesk.com' },
secretKeys: ['ZENDESK_SUBDOMAIN', 'ZENDESK_API_TOKEN'],
isReadonly: true,
},
handler: async ({ ctx, isJson }) => {
const sub = ctx.secrets.ZENDESK_SUBDOMAIN!;
const tok = ctx.secrets.ZENDESK_API_TOKEN!;
// Quickstart trusts `sub` is a workspace shortname ("acme"); the worked
// example validates it before constructing the URL.
const res = await ctx.fetch(`https://${sub}.zendesk.com/api/v2/users/me.json`, {
signal: ctx.signal,
headers: { Authorization: `Bearer ${tok}` },
});
if (!res.ok) throw new Error(`fetch authenticated user failed: ${res.status}`);
// Parse, don't cast — strips unknown fields, throws if the shape changes.
const body = Response.parse(await res.json());
return isJson ? body : `User #${body.user.id} <${body.user.email}>`;
},
});
export default defineCLI({
id: 'zendesk',
name: 'Zendesk Support',
summary: 'Read tickets, users, and search results from Zendesk Support.',
description:
'Connect to a Zendesk Support workspace via OAuth bearer token. Read-only surface: fetch the authenticated user, fetch tickets and users by id, list tickets, and run search queries.',
secrets: [
{ key: 'ZENDESK_SUBDOMAIN', name: 'Workspace subdomain', description: '...', required: true },
{ key: 'ZENDESK_API_TOKEN', name: 'API access token', description: '...', required: true },
],
tools: {
'users.me': usersMe,
},
});
一个有五个只读工具的完整实例和一个烟跑者住在库内 examples/zendesk/ (未被捆绑在已发布的包中;参见源树).
定义连接
A 类 连接 是一组共享一个认证表面的CLI的伞形特性——例如. google 组合 Gmail 和工作表。 声明用 defineConnection,对称到 defineCLI数字 :
import { defineConnection } from '@akai-workflow-builder/cli-sdk';
export default defineConnection({
slug: 'google', // groups CLIs that set connectionSlug: 'google'
name: 'Google Workspace', // umbrella display name
vendor: 'Google',
blurb: 'Gmail, Sheets, Docs, Drive and Calendar in one connection.',
longDesc: 'Connect once with OAuth; every Google CLI shares the authorization.',
authKind: 'oauth2',
oauth: { /* see below */ },
});
CLIs通过 现有 defineCLI 设置 connectionSlug数字 :
const gmail = defineCLI({ id: 'gmail', connectionSlug: 'google', summary: '…', tools: { … } });
const gsheet = defineCLI({ id: 'gsheet', connectionSlug: 'google', summary: '…', tools: { … } });
何时宣布: 只有当2 CLIs共享一发子弹,需要单一的伞形身份时. A 类 单 CLI 连接不需要 defineConnection 它的特性来自该国际中心自己的元数据(pdf, (中文(简体) ), sandbox 朋友不变.
defineConnection 拥有一个关切: 身份它不拥有工具来源、OAuth秘密胶水或UI化妆品——这些化妆品按来源划分:
| 关切 | 来源 |
|---|---|
| 身份( 名称 / 模糊 / 长 Desc / 指令 / 供应商 / authKind) | defineConnection |
| OAuth 形状( 授权/ 移动 URL、 范围、 pkce、 参数、 刷新、 管理字段) | defineConnection.oauth |
| OAuth 秘密 VALUES, 令牌存储, 加密, 刷新钩子 | akai-app, 由弹子键入 |
| UI化妆品(花相/标记/标志) | akai-app, 由弹子键入 |
一个完整的可运行示例在 examples/google —— 说 defineConnection 加上两个国家综合指数(gmail, (中文(简体) ), gsheet页:1.
OAuth 连接
何时 authKind 实值 oauth2,声明 oauth 块. 显示的宣告形状; akai-app 拥有值 :
oauth: {
authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenUrl: 'https://oauth2.googleapis.com/token',
scopes: [ // OAuth authorize scopes (≥1); distinct from per-CLI vendorScopes
'https://mail.google.com/',
'https://www.googleapis.com/auth/drive',
],
pkce: true, // Google requires PKCE; defaults to false
tokenEndpointAuthMethod: 'client_secret_post', // default 'client_secret_basic'
extraAuthorizeParams: { access_type: 'offline', prompt: 'consent' },
refreshStrategy: 'cron', // 'cron' (background refresh) | 'jit' (refresh on next use); default 'cron'
adminFields: [
{ key: 'GMAIL_CLIENT_ID', label: 'Client ID' },
{ key: 'GMAIL_CLIENT_SECRET', label: 'Client Secret', secret: true },
],
}
scopes是连接级 OAuth 授权范围设定 (% 1) , 引擎会加入到授权的 URL 上, 不同于 CLI 的每个工具vendorScopes。 。 。 .- 设定
pkce当提供者需要时; 设置extraAuthorizeParams用于静态授权参数( Google )access_type: 'offline'; 离开refreshStrategy默认时cron(背景工作在符号过期前刷新)或设置jit在下次使用时及时刷新. adminFields声明 哪个 OAuth- 应用程序证书必须输入( env 样式)key+UI 组合键label) (中文(简体) ). 这个 VALUES在akai-app中被加密存储,从未在清单或租户图像中被加密.- 标记字段
secret: true所以akai-app 掩盖了管理员格式中的输入,并从日志中修改它.
见 connection-identity 财务报告和审定财务报表 define-connection-oauth 迁移规格( akai- app) app/connections/migration-specs/用于每个外地的指导.
托管的 MCP 服务器连接
连接可以代表卖家 主机 MCP 服务器 (Notion, Linear, Strape,.)代替了一套CLI. 设定 mcpServer数字 :
export default defineConnection({
slug: 'notion-mcp',
name: 'Notion',
vendor: 'Notion',
blurb: 'Search and edit Notion pages through the Notion MCP server.',
longDesc: 'Sign in with Notion. Akai talks to the server directly; no CLI tools are involved.',
authKind: 'oauth2', // or 'none' for an open server
mcpServer: { url: 'https://mcp.notion.com/mcp' }, // must be https
});
- 主机(akai-app)将连接转换为MCP连接. 与
authKind: 'oauth2'它在登录时发现服务器的 OAuth 元数据,并通过 RFC 7591 动态客户端注册自行注册,所以有 无静态 OAuth 客户端和无管理员字段数字 :oauth绝不能设置. authKind必须是oauth2或者说none(单位:千美元)apiKey尚未支持.secrets, (中文(简体) ),apiKeyHeader财务报告和审定财务报表jwtRefresh不得设置,以及nativeBuiltin绝对不是true。 。 。 .- 它是独立的:没有
defineCLI链接到它的 swit, 登记簿仍然列出它。 打开/tools/list它出现在connections与mcpServer: { url }(字段在所有其他连接上被省略).
核心概念
原子工具
每个 tool() 显示 一次原子操作 ——一般是单个HTTP呼叫,但它可以包接任何单个可观察到的操作(本地计算,一个已读文件,一个DB查询). 组成是计划者的责任——代理人计划运行时间所暴露的原子工具;SDK故意不是连锁步骤的地方.
三种后果:
- 每个工具的权限都有意义 − 一个工具=一个可观察到的副作用=一个批准表面.
- 司马氏要紧 每个工具都记录一个操作,而不是工作流程.
- 没有隐藏的副作用 ——计划描述的是执行什么.
SDK不静地执行步数——原子性是设计指南. 如果一个逻辑步骤需要三通电话,就把三通工具船上,让计划者来作曲.
defineCLI()
CLI是一组工具 共享一个秘密声明集:
defineCLI({
id: 'zendesk',
name: 'Zendesk Support',
summary: 'Read tickets, users, and search results from Zendesk Support.',
description: 'Connect to a Zendesk Support workspace via OAuth bearer token …',
secrets: [/* ... */],
tools: {
'users.me': usersMe,
'tickets.get': getTicket,
'tickets.list': listTickets,
},
});
需求
| 外地 | 作用 |
|---|---|
id | 机器子弹 用于有线标识<id>.<toolKey>)和 x-akai-cli 头曰. 模式 : ^[a-z][a-z0-9_-]*$。 。 。 . |
name | 人类展示标签. |
summary | 单罚短标签. |
tools | 地图 tool() 结果。 键跟随下方的kebab + 点会. |
可选
| 外地 | 作用 |
|---|---|
vendor | 品牌/公司. 默认为 name,当显示("JPMorgan Access Portal")与供应商("JPMorgan")不同时有用. |
description | 长式描述. 消费者在省略时决定如何制作. |
instructions | 命令设置步骤向操作员显示 . |
secrets | 矩阵 SecretDecl (关键工具参考). |
properties | 矩阵 PropertyDecl 非敏感租户配置(基准 URL,区域代码)。 与 secrets[]。 。 。 . |
defineCLI() 交叉验证 secretKeys 财务报告和审定财务报表 propertyKeys 参照国家综合倡议一级的声明。 投错 AkaiSpecError 在模块加载时——没有无声故障.
工具键
工具键为 弹匣,可选择用于分组的点名:
chat-post-message
users.me
tickets.search-by-query
activations.contract-benefit.mark-enrolled-ahead-of-ingest
每个点分隔的片段匹配 [a-zA-Z][a-zA-Z0-9_]*(-[a-zA-Z0-9_]+)* ——第一个char字母,连字符后必须至少有一个单词char(所以 --,引导/跟踪 -和空块。 电线标识是 <cliId>.<toolKey>。 。 。 .
强调和骆驼Case仍然被接受,因此现有的工具继续发挥作用,但新的工具应当是kebab,以便与公约相匹配 akai-cli-tools 复发.
这个 ctx 合同
工具处理器接收被冻结 ctx 与这些成员:
interface ToolCtx<S extends string, P extends string> {
fetch: (url: string, init?: RequestInit) => Promise<Response>;
secrets: Readonly<Record<S, string | undefined>>;
properties: Readonly<Record<P, string | undefined>>;
logger: ToolLogger;
signal: AbortSignal;
workdir: string;
safePath: (userPath: string) => string;
readFile: (value: string) => Promise<Buffer>;
}
你得到的东西
fetch- 装有沙盒fetch绑在工具的 egress 允许列表.secrets——只有工具声明的密钥secretKeys. 其他工作空间秘密无法获取.properties——只有工具所声明的属性键propertyKeys非敏感租户配置(基准 URL, 区域) 住在这里, 不在secrets。 。 。 .logger结构化日志; 写入 stderr 。 stdout是为工具的结果而保留的.signal请求AbortSignal。转发给ctx.fetch取消和暂停.workdir——可书写划痕目录范围为此引用.safePath——在工具允许的根下解决提供代理路径(默认:workdir+ 键os.tmpdir(); 条file://,然后丢出AkaiPathError逃跑时.readFile——读取本地路径(通过safePath或一个http(s)://URL; 有对filePath()从SDK的处理器透明地支持跨波德文件发送.
什麽 ctx 故意省略
- 没有
process.env访问者——通过ctx.secrets所以运行时间可以 范围并审计它们. - 没有
fs, (中文(简体) ),child_process,或worker_threads手柄——ctx不预发信号 SDK不 sandbox 为节点运行时间;如果一个处理器直接从这些模块导入,则由主机负责限制该模块脱离波段. - 没有手柄可以引用兄弟会的工具——组成是规划者的工作.
秘密——声明与参考
机密在CLI一级宣布一次,并在需要这些机密的每个工具上用密钥引用:
defineCLI({
secrets: [
{ key: 'ZENDESK_API_TOKEN', name: 'API token', description: '...', required: true },
],
tools: {
getTicket: tool({
options: {
secretKeys: ['ZENDESK_API_TOKEN'],
/* ... */
},
/* ... */
}),
},
});
| 外地 | 类型 | 作用 | |
|---|---|---|---|
| 国 际 | secrets | SecretDecl[] | 接线员申报 |
| 工具 | options.secretKeys | string[] | 每个工具子集 |
| 运行时间 | ctx.secrets | Readonly<Record<S, string | undefined>> | 处理者读到的 |
建设时 ctx,运行时间走 工具宣布 secretKeys。每个密钥都必须在“CLI”中宣布 secrets[]; 未申报的引用抛出 AkaiSpecError 在舱载。 援引时a required: true 无值丢出的秘密 AkaiSecretError打电话者没有提供的可选秘密 ctx.secrets 作为 undefined 因此,处理者可以分支.
ctx.secrets[K] 输入 string | undefined 向所有人开放 K ——可能没有可选的秘密,因此处理器在使用前会缩小. 即使当 required: true,SDK无法静态地证明运行时的机体运出值,所以该类型保持了结合.
属性——非敏感租户配置
房客特有价值,没有资格 properties[]没有 secrets[]。示例:工作空间子域名,自托管的基础URL,区域代码。 主机运行时会储存这些没有加密的保存处理适用于秘密的,并可能在管理员UIs解码后露出.
defineCLI({
secrets: [{ key: 'JIRA_API_TOKEN', name: 'API token', description: '...', required: true }],
properties: [{ key: 'JIRA_BASE_URL', name: 'Workspace URL', description: '...', required: true }],
tools: {
getIssue: tool({
options: {
secretKeys: ['JIRA_API_TOKEN'],
propertyKeys: ['JIRA_BASE_URL'],
network: { egress: { from: 'property:JIRA_BASE_URL' } },
isReadonly: true,
},
handler: async ({ ctx }) => {
const base = ctx.properties.JIRA_BASE_URL!;
const tok = ctx.secrets.JIRA_API_TOKEN!;
return ctx.fetch(`https://${base}/rest/api/3/issue/...`, {
headers: { Authorization: `Bearer ${tok}` },
});
},
/* ... */
}),
},
});
| 外地 | 类型 | 作用 | |
|---|---|---|---|
| 国 际 | properties | PropertyDecl[] | 接线员申报 |
| 工具 | options.propertyKeys | string[] | 每个工具子集 |
| 运行时间 | ctx.properties | Readonly<Record<P, string | undefined>> | 处理者读到的 |
AkaiPropertyError 当一个 required: true 财产在引用时丢失。 与 AkaiSecretError,不同类别,因为故障源在概念上有所不同.
动态相继器(多租位连接器)
对于每个租户主机不同(Jira Cloud, Zendesk, Looker自发托管,GHES, Salesforce)的连接器,宣布主机为地产并参考来源于 egress数字 :
options: {
propertyKeys: ['JIRA_BASE_URL'],
network: { egress: { from: 'property:JIRA_BASE_URL' } },
}
defineCLI() 交叉检查引用的密钥在 properties[] 并纳入工具 propertyKeys。 。 。 . buildCtx 解决引用本身——从绑定的密件或属性值中取出主机名——因此,进程步骤fetcher路径和执行器都得出相同的允许列表主机. 分辨率抛出 AkaiSpecError 在不支持的前缀上, AkaiSecretError编号 :AkaiPropertyError 当值未设置或不是有效主机时.
进取许可列表
每个工具声明其允许的出站主机 :
options: {
network: { egress: 'api.example.com' }, // single host (shorthand)
// or: { egress: ['api.example.com', 'cdn.example.com'] },
}
ctx.fetch 强制执行,以便:
- 议定书 ——只生产HTTPS. 仅允许在
NODE_ENV === 'development'。 。 。 . - 东道主 - 准确的或后缀的点.
api.example.com匹配本身和*.api.example.com· ;evil.api.example.com.attacker.tld没有. - DNS 飞行前 ——主机名在请求前解决. 在制作中,拒绝私人地址(RFC 1918),回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回(回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回 在开发过程中,私人-IP拒绝被跳过,以便处理者能够针对当地服务。 一个生产例外是由主机运行时间传递的接线针——见 操作员固定的私人入侵。 。 。 .
- 重定向 SDK从不跟踪方向,所以..
Location:无法通过egress检查将请求偷运到另一个主机.redirect: 'error'是默认,并且'follow'被拒绝(降为'error'),因为下面是检查通过后可以离开所允许的列表主机的一种模式.redirect: 'manual'因此,处理者收到3xx本身——即表格后端门户报告成功的方式——并到达Location:开新价ctx.fetch重新运行整个飞行前. - 身份头 —— 说
user-agent, (中文(简体) ),x-akai-cli, (中文(简体) ),x-akai-tool, (中文(简体) ),x-akai-request-id每个请求都盖上印章.x-akai-tenant只在主机运行时间通过租户代号时盖章buildCtx;否则即使操作者试图设置它也会被移除. 其中任何一个的处理器提供值总是被覆盖.
协议、宿主和 DNS 飞行前失败以 AkaiEgressError 在任何网络请求提出之前。 下 redirect: 'error' 违约,以及在何处 'follow' 被降级为- 重定向表面取而代之 fetch 初始响应后拒绝:相同的结果(没有后续),不同的错误类型。 下 'manual' 表面没有出故障,因为3xx是一个成功的结果:它作为普通反应被还给处理器.
Spec验证还拒绝没有通过综合DNS-hostname检查的Egress主机—— localhost (单标签),IP字形,主机有空格或被鞭打——所以一开始不能被宣布. 检查是只用形状的;它不验证名称是公开可解的,因此内部DNS区仍然通过光谱,然后在DNS-飞行前时间处理.
操作员固定的自带( 只有主机运行时间)
DNS预飞拒绝每个私人地址,因为一个工具的主机可以来自一个租户用户控制的连接秘密. 在一个平台所属内部服务旁边部署的主机运行时间可以为其打开一个完全的例外. 已经过去了 privateEgress 改为 executeTool (或 buildCtx,或 createSafeFetch:
import { executeTool } from '@akai-workflow-builder/cli-sdk/runtime';
await executeTool({
/* …registry, body, logger, signal, workdir… */
privateEgress: [
{
cli: 'grafana', // the CLI id; no other CLI may use this entry
host: 'grafana.shared.akai', // exact hostname; no suffix match, never an IP literal
cidrs: ['10.33.0.0/16'], // the host must resolve inside these (each at most /16)
port: 443, // optional, default 443
caPem: '-----BEGIN CERTIFICATE-----…', // optional CA for this connection; default trust store otherwise
},
],
});
默认关闭:没有 privateEgress 行为正是上述所述。 值出自主机运行时间的自有配置,出自别处——一个工具定义,它的选项,秘密,属性,请求正体和进程环境从不读作一针.
针头只允许调用到一个私人地址, 只有在所有这些都持有时; 否则不变 resolved to private address 丢出错误 :
- 调用 CLI id 等于
cli· ; - uRL 主机等于
host准确(对案件不敏感);x.host财务报告和审定财务报表evilhost(b) 不匹配),也出现在工具本身的 " 后入 " 许可列表中; - 计划是
https:和端口相等port· ; - 所有已解析的地址都在里面
cidrs,且没有回放,链接-本地(包括云元地址)或未说明。 IPv4- 缩放的 IPv6 表格 (::ffff:127.0.0.1, (中文(简体) ),::ffff:7f00:1)被判定为IPv4地址.
被允许的电话会过去 node:https,坚持检查所见: 套接字只连接到经过的地址(一个后来的DNS答案无法重定向),TLS名称是被绑定的主机, caPem ——在设定时——取代连接的信托商店,证书的核查继续,并且 Host 头部被强迫到被绑住的主机 任何处理器设置。 重新定向遵循通常规则:除非 redirect: 'manual',然后 Location: 是新鲜的 ctx.fetch 和完整的支票.
条目被忽略( 一次通过 logger.warn而不是半应用当其 cli 这不是一个CLI id,它的 host 是一个IP字形或不是一个完全合格的名称, cidrs 空的或畸形的, CIDR 宽于 16 或重叠回路、 链接- 局部或未指定空间(127.0.0.0/8, (中文(简体) ), 169.254.0.0/16, (中文(简体) ), 0.0.0.0/8, (中文(简体) ), ::1, (中文(简体) ), fe80::/10,IPv6元数据地址), port 不是1 - 65535,或 caPem 不是一个 PEM 证书.
isReadonly
每个工具必须声明它是否变异了远程状态:
options: {
isReadonly: true, // reads, queries — no approval gate
// isReadonly: false, // writes — runtime pauses for human approval
}
两个角色:
- 计划提示 ——在工具列表中被曝光,因此计划UI会显著地使写出步骤.
- 运行时门 —— 写入工具不能执行,除非设置了确认标记,运行时间会翻转到批准流返回批准后才执行.
无缺省. SDK抛出"省略"——沉默的默认会冒着写作只读的风险. 当有疑问时,选择 false ——假阳性者要花费一次批准点击;假阴性者要引起无声突变.
输出模式: JSON vs 文本
每个操作员收到一个 isJson: boolean 旗帜。 打电话者选择 format=json 或者说 format=text; 处理器返回任意形状:
handler: async ({ input, ctx, isJson }) => {
const body = await fetchTheThing(/* ... */);
return isJson ? body : `Thing #${body.id} (${body.status})`;
}
何时 jsonOutput 声明,处理器返回类型是 string | ZInfer<jsonOutput>时 jsonOutput 中省略,处理器总是返回字符串.
工作实例: Zendesk
仓库的 examples/zendesk/ 文件夹传输一个只读的 Zendesk 支持连接器(未在已发布的软件包中捆绑) :
| 工具 | 终点 | 演示 |
|---|---|---|
users.me | GET /users/me | Auth 探测器; 最简单的工具 |
users.get | GET /users/{id} | 路径参数 |
tickets.get | GET /tickets/{id} | 路径参数, 404 映射到打出错误 |
tickets.list | GET /tickets | 光标标记 |
tickets.search | GET /search | 查询成分,422表面 |
每个工具分支在 isJson 并使用结构化的日志。 文件夹中包含一个能反映运行时的每次调用调度的烟雾跑道(registry ~) buildCtx() 这样你就能从终端打出真正的工作空间.
错误
| 类 | 何时 |
|---|---|
AkaiSpecError | 无效 tool() 编号 : defineCLI() spec- 扔入模块负载 |
AkaiInputError | 工具输入失败 |
AkaiSecretError | A 类 required: true 在引用时, 秘密缺失—— 由 buildCtx |
AkaiPropertyError | A 类 required: true 财产在援引时失踪 buildCtx |
AkaiEgressError | ctx.fetch 拒绝目标 |
AkaiToolError | 一个处理器扔出一个打字的,调用器的失败——主机地图 kind 到 HTTP 状态并逐一显示信件 |
每个错误都会延伸 AkaiError; 运行时将每个图到结构化错误信封.
输入工具出错
掷出 AkaiToolError 从一个处理者,给预期失败者一个稳定的分类和经作者审查的措辞。 主机运行时间品牌检查其外形上的财产访问所抛出的价值—— { name: 'AkaiToolError', kind, message, meta? } (全文为: message 通过财产进入,而不是JSON序列化,因为 Error.message ——没有SDK依赖,地图 kind 返回 HTTP 状态,并返回 message 致打电话者 逐字记录。保持信息保密.
import { AkaiToolError } from '@akai-workflow-builder/cli-sdk';
const res = await ctx.fetch(`https://api.example.com/tickets/${id}`);
if (res.status === 404) throw AkaiToolError.notFound(`No ticket ${id}`, { id });
if (res.status === 429) throw AkaiToolError.rateLimited('Slow down', { retryAfterMs: 1000 });
工厂——每个厂家设置匹配 kind数字 :
| 工厂 | kind | 东道国地位 |
|---|---|---|
AkaiToolError.notFound(msg, meta?) | not_found | 404 (韩语) |
AkaiToolError.forbidden(msg, meta?) | forbidden | 403 (韩语) |
AkaiToolError.authExpired(msg, meta?) | auth_expired | 401 (中文(简体) ) |
AkaiToolError.invalidInput(msg, meta?) | invalid_input | 422个 |
AkaiToolError.conflict(msg, meta?) | conflict | 409个 |
AkaiToolError.rateLimited(msg, meta?) | rate_limited | 429 (可重试) |
AkaiToolError.unavailable(msg, meta?) | unavailable | 503(可重试) |
未打入的抛出物一般被分类(一个500个有衍生消息).
解决问题
Cannot find name 'Response' —— 说 tsconfig.json 缺少网络平台类型。 添加 "DOM" 给您现有的 lib 条目(例如). "lib": ["ES2023", "DOM"]——设置 lib 覆盖对象的默认值, 所以既包括—— 也包括安装 @types/node@^18+ 添加 "types": ["node"] (脚本 6 默认值) types 改为 []仅仅安装它是不够的.
AkaiSpecError: undeclared secret 'X' a 包括: secretKeys 条目引用未在 defineCLI({ secrets: [...] })。 。 。 .
AkaiSpecError: undeclared property 'X' a 包括: propertyKeys 条目引用未在 defineCLI({ properties: [...] })。 。 。 .
AkaiSpecError: invalid id "..." ——CLI id 必须是小写 sattle 匹配 ^[a-z][a-z0-9_-]*$。显示标签位于 name。 。 。 .
AkaiSpecError: invalid egress host 页:1 localhost 编号 : 127.0.0.1 —— 说 tool() 拒绝 IP 字元和单标签主机名(包括 localhost)在光谱时间,在 ctx.fetch 永远跑。 为本地开发,使用一条隧道(ngrok, Cloudflare Tunnel)解决一个公共主机名.
AkaiEgressError 对一个无人知晓的宿主 ——运行时 Egress 检查失败. 将主机添加到 options.network.egress (相匹配的精确或后缀与点.
类型错误: 处理器返回不匹配 jsonOutput 何时 jsonOutput 声明,处理器返回类型是 string | <structured shape>部门 isJson。 。 。 .
更改日志
未释放
- 添加
defineConnection——声明连接的伞形身份(slug, (中文(简体) ),name, (中文(简体) ),vendor, (中文(简体) ),blurb, (中文(简体) ),longDesc, (中文(简体) ),instructions, (中文(简体) ),authKind加上其内容oauth形状。 返回一个被冻结的,WeakSet- 开花ConnectionDef。对称到defineCLI。 。 。 . - 添加数据
oauth块 :authorizeUrl, (中文(简体) ),tokenUrl, (中文(简体) ),scopes, (中文(简体) ),pkce, (中文(简体) ),tokenEndpointAuthMethod, (中文(简体) ),extraAuthorizeParams, (中文(简体) ),refreshStrategy,以及adminFields(OAuth- app certain a admin 输入; 值留在 akai- app). 验证有效的 URL, 非空scopes,以env 样式管理键,以及adminFields存在时authKind实值oauth2。 。 。 . - 新出口:
defineConnection, (中文(简体) ),isAkaiConnection, (中文(简体) ),AkaiConnectionError类型ConnectionSpec, (中文(简体) ),ConnectionDef, (中文(简体) ),ConnectionOAuthConfig, (中文(简体) ),OAuthAdminField, (中文(简体) ),ConnectionAuthKind, (中文(简体) ),RefreshStrategy, (中文(简体) ),TokenEndpointAuthMethod。 。 。 . - 新的可运行示例 :
examples/google(连接分组)gmail+ 键gsheet) (中文(简体) ).
许可证
麻省理工学院——见 LICENSE。 。 。 .