# @north-light/plugin 中文文档

规范地址：https://npmcn.com/package/%40north-light/plugin/

版本：0\.3\.573
npm 元数据更新时间：2026-11-01T23:59:11.960Z
中文译文更新时间：2026-11-02T01:15:40.336Z
软件包许可：UNLICENSED

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

原始 npm 页面：https://www.npmjs.com/package/%40north-light/plugin
原文对照：https://npmcn.com/package/%40north-light/plugin/?lang=original
项目主页：https://github.com/Northlight-Systems/crouter#readme
源码仓库：https://github.com/Northlight-Systems/crouter

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

- 包体积（解压后）：147 kB，35 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：2 个
- 首次发布：2026-10-09T21:01:52.299Z
- 最新版本发布：2026-11-01T23:59:11.786Z
- 已发布版本数：155（近 12 个月 155 个）

## 中文简介

![crouter](https://raw.githubusercontent.com/Northlight-Systems/crouter/main/assets/banner.svg)

## 安装

```sh
npm install @north-light/plugin
```

也可以使用：`pnpm add @north-light/plugin`、`yarn add @north-light/plugin`、`bun add @north-light/plugin`

## README

# @ northlight /plugin -在TypeScript中定义一次crtr命令，并通过HTTP提供它们

![crouter](https://raw.githubusercontent.com/Northlight-Systems/crouter/main/assets/banner.svg)

[![npm](https://img.shields.io/npm/v/@north-light/plugin?label=npm)](https://www.npmjs.com/package/@north-light/plugin)

`@north-light/plugin` 控件中添加命令 `crtr` 对运行它的每个代理也是如此。您编写一个键入的命令树。包将其转换为一个Fetch处理程序 `commands.json` 清单，然后存档 `crtr pkg plugin install --endpoint` 读取。您不需要编写清单、路由表或 `plugin.json`.

它是一个服务器端创作工具包，而不是运行时：处理程序是一个标准的Fetch `{ fetch }` 您部署在任何提供导出的主机上的默认导出。它不运行代理；代理调用您的命令的方式与调用其他命令的方式相同 `crtr` 命令, `crtr` 向端点发出HTTP请求。不需要服务器的插件，比如本地可执行文件，可以从市场安装；看到 [插件文档](https://crouter.docs.northlight.ai/docs/plugin).

[文档](https://crouter.docs.northlight.ai/docs/plugin) · [命令](https://crouter.docs.northlight.ai/docs/plugin/commands) · [部署](https://crouter.docs.northlight.ai/docs/plugin/deploying) · [官方市场](https://github.com/crouton-labs/crouter-official-marketplace) · [克劳特库](https://github.com/Northlight-Systems/crouter)

## 安装

```bash
npm install @north-light/plugin
```

该软件包仅支持esm。从ES模块导入它或使用动态 `import()` CommonJS。要尝试插件，您还需要 `crtr` CLI: `npm install -g @north-light/cli`，然后跑 `crtr` 一次登录到模型提供程序。

## 定义一个插件

创建 `src/crtr-plugin.ts`：

```ts
import {
  createFetchHandler,
  defineBranch,
  defineLeaf,
  definePlugin,
  field,
  LeafError,
  param,
} from '@north-light/plugin';

export const plugin = definePlugin({
  name: 'acme',
  description: 'Manage Acme applications.',
  whenToUse: 'you need to create or inspect an Acme application.',
  summary: 'Acme application management',
  rootEntry: {
    concept: 'an Acme application',
    description: 'Create and inspect applications.',
    whenToUse: 'the task is about an Acme application.',
  },
  commands: {
    app: defineBranch({
      description: 'Create and inspect applications.',
      whenToUse: 'you are working with an application.',
      summary: 'application operations',
      children: {
        create: defineLeaf({
          description: 'Create an Acme application.',
          whenToUse: 'an application does not exist yet.',
          summary: 'create an application',
          params: {
            name: param.positional('The application name.', { required: true }),
            region: param.enum(['us-east', 'eu-west'], 'The region for the application.', { default: 'us-east' }),
          },
          output: {
            appId: field.string('The created application id.'),
            url: field.string('The application URL.'),
          },
          effects: ['Creates an Acme application.'],
          handler(input) {
            if (input.name === 'taken') {
              throw new LeafError({
                code: 'name_taken',
                message: 'That application name is already in use.',
                status: 409,
                field: 'name',
              });
            }
            return {
              appId: `app_${input.name}`,
              url: `https://${input.name}.${input.region ?? 'us-east'}.acme.example.com`,
            };
          },
        }),
      },
    }),
  },
});

export default {
  fetch: createFetchHandler(plugin, { token: process.env.ACME_CRTR_TOKEN }),
};
```

处理程序输入是从 `params`： `name` 是必需的 `region` 是可选的。在crtr到达的URL上部署默认导出。相同的URL句柄 `GET` 存档请求和 `POST` 命令请求。

## 安装到crtr中

在运行crtr的机器上设置相同的承载令牌，然后安装处理程序的实际挂载路径：

```bash
export ACME_CRTR_TOKEN='replace-with-a-secret'
crtr pkg plugin install --endpoint https://acme.example.com/crtr --name acme --auth-env ACME_CRTR_TOKEN
crtr acme app create my-app --region eu-west
```

`export` 涵盖您自己键入的命令。代理的 `crtr acme …` 在它的代理中运行，其env来自配置文件env存储，而不是来自您的shell -所以对于代理，也将令牌存储在那里： `echo -n "$ACME_CRTR_TOKEN" | crtr profile env set <profile> --name ACME_CRTR_TOKEN`． 从那时起，在该配置文件下启动的节点携带它。

使用中的实际挂载路径 `--endpoint`． 生成的归档文件在该路径下声明命令路径，而crtr存储以后命令调用的源文件。只安装 `https://acme.example.com` 会发送呼叫到原始根而不是 `/crtr`.

## 作为在目录中注册的应用程序

向连接它的人提供命令的应用程序通过目录身份验证，而不是静态令牌：

```ts
import { createFetchHandler, createNodeHandler } from '@north-light/plugin';

const handler = createFetchHandler(plugin, {
  auth: { issuer: 'https://directory.example', audience: 'https://acme.example.com/crtr', providerId: CLIENT_ID },
  requests,
});
```

`providerId` 是应用程序的客户端id（它的OAuth客户端id），裸或作为 `app:<id>`． 目录将范围划分为 `<provider id>:<group>`，所以工具包要求 `<providerId>:<group>` 在每一片叶子上。没有 `providerId` 它需要 `<plugin name>:<group>`只有名字等于其id的第一方提供商才会携带这个号码——来自任何其他应用程序的每个呼叫都会回答403 `scope_missing`，以及误差 `next` 命名要设置的id。

声明应用的工具组在它的叶子旁边 `definePlugin({ provider: true, version, groups })`. `groups` 由组id作为关键字；每个值都有 `consentLine` 和(可选) `who` （`own`， `user`， `allowlist`， `any`， `team:<id>`;省略手段 `user`), `allowlist` （`app:<id>` 校长，只与 `who: 'allowlist'`）. 每一片叶子的 `group` 必须为声明的组命名，并且每个声明的组都必须由叶子使用，或者 `definePlugin` 抛出。的服务 `bundle.json` 将其作为 `"groups": {"<id>": {"consent_line": "...", "who"?: "...", "allowlist"?: [...]}}` 目录在刷新提供商的清单时应用它。请参阅文档中的命令。

在Node、Express或Nest上，用 `createNodeHandler(handler, { bodyLimit?, origin? })`． 它将请求转换为Fetch `Request` (使用 `req.rawBody` 当框架对其进行缓冲时（否则流），将拒绝body over `bodyLimit` （默认65536字节）与413处理程序运行之前，流响应与重复的头和 `set-cookie`，如果处理程序抛出，则回答500。在Nest中，控制器方法是 `@All(['', '*path']) serve(@Req() req, @Res() res) { return node(req, res); }`;与 `node:http`， `createServer((req, res) => void node(req, res))`。通过 `origin` 当公共源与Host头不同时。

## 计量提供商工具

使用目录身份验证，配置 `createFetchHandler(plugin, { auth, receipts: { provider: 'app:acme', outbox } })`. `provider` 必须是提供者的身份验证 `app:<id>` Principal，而不是插件名。发出收据 `tool` 值，以及 `tool` 那 `ctx.checkSpend()` 发送，是命令路径 **后** 插件名称： `send` 用于 `crtr acme send`，或者 `messages send` 用于 `crtr acme messages send`． 处理程序调用 `ctx.meter({ units, unit })` 发出 `Crtr-Receipt`;提供者持久化并将相同的收据提交到目录分类账。 `ctx.meter` 可以分别叫一次吗 `unit` 在调用中（重复单元抛出）；通过 `receiptId` 为收据命名（默认是请求id，调用中的每个收据都需要自己的id）。一张收据进来了 `Crtr-Receipt` 作为一个对象，几个作为一个JSON数组，收据在一个 `LeafError` 利用错误响应。

该工具包没有价格：目录拥有每一个价格，最高限额和津贴(设置在控制台或与 `crtr cloud apps provider pricing`)，并从收据中收取费用 `units` 或者它的报告成本。 `ctx.meter` 设置收据 `usd_equivalent` 致第一个 `usdEquivalent` 你通过了（一个报告其成本的工具通过了它）； `units` 这个请求的价格行 `ctx.checkSpend()` 回答的 `unit` 火柴，用于单价工具；否则 `0`． 它永远不会因为丢失的价格而抛出。这个数字是运行时显示的调用成本；目录自己重新计算费用，并且从不信任它。 `ReceiptOptions.rates` 不再存在：当你升级时删除它（TypeScript标记它；JavaScript配置中仍然通过它会被忽略）。

`ctx.causedBy` 是 `request:<this request's Crtr-Request-Id>` （当请求不携带时不存在）。调用运行时的处理程序本身会传递它，因此这些调用的花费被跟踪到这个调用： `new Crouter({ causedBy: ctx.causedBy })`，或者 `causedBy` 在 `runs.start`.

`ctx.caller.payer` 这通电话是收费的吗？ `user:<id>` 或者 `app:<id>`：代币 `payer` 要求,或 `sub` 当令牌没有时。 `ctx.meter` 在收据上盖章。在收费电话之前， `await ctx.checkSpend()` 询问目录(`POST <auth.issuer>/v1/billing/check`（发送请求的出站令牌）付款人是否可以在此命令上花费（它发布） `{ token, tool }`， `tool` 作为上面的命令路径)，并保留它所应答的价格行 `ctx.meter`;它不保留任何东西，用它来解决 `{ prices }` (每一行组的价格， `[price]` 当目录应答时 `price`（未定价时为空）。它需要 `receipts.ledger: { clientId, privateKey, keyId? }`和你传递的证书一样 `postReceipt`，并抛出一个平原 `Error` 没有他们或没有经过验证的来电者。它解决了调用何时可以继续，抛出 `SpendRefusedError { code, message, next }` 拒绝(`payment_required` 或者 `limit_reached`, `next` 修复它的页面)，并抛出 `ReceiptPostError` 当支票无法完成时。要拒绝调用，请将其重新抛出为 `new LeafError({ code: e.code, message: e.message, status: 402, next: e.next })`.

回答一个速率限制，扔一个 `LeafError` 与 `retryAfter` (秒，或一 `Date`): `throw new LeafError({ code: 'rate_limited', status: 429, message: 'Slow down.', retryAfter: 30 })` 回答429 `Retry-After: 30`. `headers: { 'X-RateLimit-Reset': '…' }` 设置任何其他响应头； `Content-Type`， `Content-Length`， `Content-Encoding`， `Transfer-Encoding` 和 `Crtr-Receipt` 属于工具箱。对于副作用叶，套件将头传递给 `requests.store` 作为 `answer.headers` (小写的名字);存储并返回它们的存储将使用相同的标头重复请求回答。

对于副作用的叶子，提供 `requests: { record, read, store, delete }`. `record(requestId, identity)` 自动插入id和 `{sub, grantee}` 来自已验证的调用方(或 `null` 对于遗留的static-token插件)； `read(requestId)` 返回 `{identity, recordedAt, answer}`． 该工具拒绝来自不同调用者的重复id，而不是重播另一个人的结果。 `store` 用发件箱以事务方式保存结果和收据。 `answer.receipt` 是来电的第一个收条，一如既往；当一个电话超过一个时， `answer.receipts` 还列出了所有这些（首先包括）和商店必须保存和返回它 `read` (重播重建 `Crtr-Receipt` 从 `receipts` 在场时，否则 `receipt`）. 未记录的请求将每个收据保存为自己的收据 `outbox.save` 呼叫。 `onError` 得到 `(error, path, receipt, receipts?)` 和 `onAnswer` 得到 `receiptIds` 在同样的规则下。对于提供者日志记录， `onAnswer(event)` 每个应答的叶子POST接收一个仅元数据事件（主链字段、请求/运行id、相对工具、状态、持续时间、接收id、重放标志）；它从不接收承载令牌、参数或结果体，日志记录失败也不会改变答案。 `createCallerVerifier(auth)` 为在Fetch处理程序之外验证请求的提供程序导出。

## 参考

这个 [插件指导](https://crouter.docs.northlight.ai/docs/plugin) 涵盖了 [命令](https://crouter.docs.northlight.ai/docs/plugin/commands)， [参数](https://crouter.docs.northlight.ai/docs/plugin/parameters)， [输出](https://crouter.docs.northlight.ai/docs/plugin/output)， [错误和流](https://crouter.docs.northlight.ai/docs/plugin/errors)， [部署](https://crouter.docs.northlight.ai/docs/plugin/deploying), [捆包和文档](https://crouter.docs.northlight.ai/docs/plugin/bundles-and-memory).

## 相关

-   [`@north-light/api`](https://www.npmjs.com/package/@north-light/api)：定义这个包发出的命令清单模式。
-   [`@north-light/identity`](https://www.npmjs.com/package/@north-light/identity)：验证处理程序检查的目录生成令牌 `auth`.
-   [`@north-light/sdk`](https://www.npmjs.com/package/@north-light/sdk)：应用客户端；插件和SDK应用通常是相同的服务。
-   [主要存储库](https://github.com/Northlight-Systems/crouter): `crtr` CLI, `crtrd` 守护进程。

## 许可证

专有的。版权所有2026 Northlight Systems版权所有。看到 `LICENSE`.

## 常见问题

### 如何安装 @north-light/plugin？

在项目目录运行 npm install @north-light/plugin，或使用 pnpm add / yarn add / bun add。

### @north-light/plugin 支持 TypeScript 吗？

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

### @north-light/plugin 支持 ESM 还是 CommonJS？

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

### @north-light/plugin 可以商用吗？

@north-light/plugin 使用 UNLICENSED 许可。作者声明为 UNLICENSED，即不授予公开使用许可（与公共领域的 Unlicense 不同）。使用前需获得作者授权。以上为概括，不构成法律意见，请以许可原文为准。

### @north-light/plugin 还在维护吗？

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

