# @akai-workflow-builder/cli-sdk 中文文档

规范地址：https://npmcn.com/package/%40akai-workflow-builder/cli-sdk/

版本：0\.15\.1
npm 元数据更新时间：2026-10-29T08:07:31.107Z
中文译文更新时间：2026-11-06T17:51:22.864Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40akai-workflow-builder/cli-sdk
原文对照：https://npmcn.com/package/%40akai-workflow-builder/cli-sdk/?lang=original
项目主页：https://github.com/akai-workflow-builder/akai-cli-sdk#readme
源码仓库：https://github.com/akai-workflow-builder/akai-cli-sdk

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：527 kB，113 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.0\.0
- 运行依赖：1 个
- peer 依赖：zod ^4\.4\.1，sharp 0\.34\.5，heic-convert 2\.1\.0
- 首次发布：2026-05-26T20:31:36.899Z
- 最新版本发布：2026-10-29T08:07:30.848Z
- 近 30 天下载量：2541（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：39（近 12 个月 39 个）

## 中文简介

授权SDK用于原子,代理可执行的CLI和工具.

## 安装

```sh
npm install @akai-workflow-builder/cli-sdk
```

也可以使用：`pnpm add @akai-workflow-builder/cli-sdk`、`yarn add @akai-workflow-builder/cli-sdk`、`bun add @akai-workflow-builder/cli-sdk`

## README

# @kai-workflow-buildinger/cli-sdk (英语)

> 作者原子,TypeScript中的可代理执行的CLI.

一个小的,有观点的SDK,用于定义特工运行时间引用的CLI. 每个工具都代表一个原子操作——一般是单个HTTP调用,但一个工具可以包裹任何单个可观察到的操作. SDK给你一个沙盒 `fetch`,范围机密,打入输入+输出计划,以及每个工具的 Egress 允许列表. 主机运行时间处理输入验证,秘密范围界定,网络沙箱,以及用于写作的"人入"审批门.

> **状态 :** 预v0.1. 写作的表面`tool`, (中文(简体) ), `defineCLI`, (中文(简体) ), `buildCtx`, (中文(简体) ), `ctx.fetch`, (中文(简体) ), `ctx.secrets`, (中文(简体) ), `ctx.properties`)是稳定的.

* * *

## 安装

```bash
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` 没有名称 |

* * *

## 快速启动

```ts
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`数字 :

```ts
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`数字 :

```ts
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`](https://npmcn.com/package/%40akai-workflow-builder/cli-sdk/examples/google) —— 说 `defineConnection` 加上两个国家综合指数(`gmail`, (中文(简体) ), `gsheet`页:1.

### OAuth 连接

何时 `authKind` 实值 `oauth2`,声明 `oauth` 块. **显示的宣告形状; akai-app 拥有值 :**

```ts
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`数字 :

```ts
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是一组工具 共享一个秘密声明集:

```ts
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`](https://github.com/akai-workflow-builder/akai-cli-tools) 复发.

### 这个 `ctx` 合同

工具处理器接收被冻结 `ctx` 与这些成员:

```ts
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一级宣布一次,并在需要这些机密的每个工具上用密钥引用:

```ts
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解码后露出.

```ts
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`数字 :

```ts
options: {
  propertyKeys: ['JIRA_BASE_URL'],
  network:      { egress: { from: 'property:JIRA_BASE_URL' } },
}
```

`defineCLI()` 交叉检查引用的密钥在 `properties[]` 并纳入工具 `propertyKeys`。 。 。 . `buildCtx` 解决引用本身——从绑定的密件或属性值中取出主机名——因此,进程步骤fetcher路径和执行器都得出相同的允许列表主机. 分辨率抛出 `AkaiSpecError` 在不支持的前缀上, `AkaiSecretError`编号 :`AkaiPropertyError` 当值未设置或不是有效主机时.

### 进取许可列表

每个工具声明其允许的出站主机 :

```ts
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拒绝被跳过,以便处理者能够针对当地服务。 一个生产例外是由主机运行时间传递的接线针——见 [操作员固定的私人入侵](https://npmcn.com/package/%40akai-workflow-builder/cli-sdk/#readme-operator-pinned-private-egress-host-runtime-only)。 。 。 .
-   **重定向** 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`:

```ts
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`

每个工具必须声明它是否变异了远程状态:

```ts
options: {
  isReadonly: true,   // reads, queries — no approval gate
  // isReadonly: false, // writes — runtime pauses for human approval
}
```

两个角色:

1.  **计划提示** ——在工具列表中被曝光,因此计划UI会显著地使写出步骤.
2.  **运行时门** —— 写入工具不能执行,除非设置了确认标记,运行时间会翻转到批准流返回批准后才执行.

无缺省. SDK抛出"省略"——沉默的默认会冒着写作只读的风险. **当有疑问时,选择 `false`** ——假阳性者要花费一次批准点击;假阴性者要引起无声突变.

### 输出模式: JSON vs 文本

每个操作员收到一个 `isJson: boolean` 旗帜。 打电话者选择 `format=json` 或者说 `format=text`; 处理器返回任意形状:

```ts
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` 致打电话者 **逐字记录**。保持信息保密.

```ts
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`](https://npmcn.com/package/%40akai-workflow-builder/cli-sdk/LICENSE)。 。 。 .

## 常见问题

### @akai-workflow-builder/cli-sdk 是什么？

授权SDK用于原子,代理可执行的CLI和工具\.

### 如何安装 @akai-workflow-builder/cli-sdk？

在项目目录运行 npm install @akai-workflow-builder/cli-sdk，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：zod（^4\.4\.1）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。它还提供命令行工具 akai，可以用 npx akai 直接运行。

### @akai-workflow-builder/cli-sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @akai-workflow-builder/cli-sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @akai-workflow-builder/cli-sdk 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @akai-workflow-builder/cli-sdk 可以商用吗？

@akai-workflow-builder/cli-sdk 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @akai-workflow-builder/cli-sdk 的下载量有多少？

根据 npm 官方下载统计，@akai-workflow-builder/cli-sdk 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,541 次，日均约 85 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @akai-workflow-builder/cli-sdk 还在维护吗？

最新版本 0\.15\.1 发布于 2026/10/29（9 天前）；至今共发布 39 个版本，其中过去 12 个月发布了 39 个；首次发布于 2026/05/27。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

