# @bpinternal/overwatch 中文文档

规范地址：https://npmcn.com/package/%40bpinternal/overwatch/

版本：0\.3\.7
npm 元数据更新时间：2026-09-02T19:11:32.073Z
中文译文更新时间：2026-11-08T13:14:31.845Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40bpinternal/overwatch
原文对照：https://npmcn.com/package/%40bpinternal/overwatch/?lang=original
项目主页：https://github.com/botpress/packages#readme
源码仓库：https://github.com/botpress/packages

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

- 包体积（解压后）：331 kB，87 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：7 个
- peer 依赖：typescript ^5
- 首次发布：2026-07-16T17:26:47.443Z
- 最新版本发布：2026-08-07T17:50:23.315Z
- 近 30 天下载量：10724（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：10（近 12 个月 10 个）

## 中文简介

用于构建控制回路的无头库:用于扫描一个类异常回路的预定bots,用编码代理来修复几个实例,并打开一个PR——没有服务器,UI,或持续过程.

## 安装

```sh
npm install @bpinternal/overwatch
```

也可以使用：`pnpm add @bpinternal/overwatch`、`yarn add @bpinternal/overwatch`、`bun add @bpinternal/overwatch`

## README

# 监督

用于建筑的无头图书馆 **控制循环**: 计划抽查一个重播 类异常,用编码代理修正几个实例,并打开一个PR. 没有服务器或 UI - 你打电话 `loop.run()` 从您已有的调度器( cron, CI, 无服务器) 中 每个电话都有一个完整的周期:

```
sense → filter claimed → pick → actuator acts
                                 └─ default: agent per signal → verify (re-sense) → commit → PR
```

一切都在新鲜中运行 [代顿a](https://daytona.io/) 沙盒:repo被复制在, 一个代理CLI(Claude Code或Codex)被安装和驱动非交互,而沙盒 在运行结束时被撕毁.

## 安装

```bash
pnpm add @bpinternal/overwatch     # or: npm install / yarn add
```

船舶作为汇编的建筑物(CommonJS, 附有类型申报),从两者一起运作 `require` 财务报告和审定财务报表 `import` 在节点18+。 要黑进图书馆本身 复制回放并运行 `pnpm install` (pnpm是支持的软件包管理器;该版本通过 `packageManager`) (中文(简体) ).

起码你需要:

-   代托纳 API 密钥——通过时为 `config.sandbox.apiKey` 每个循环,或通过 `DAYTONA_API_KEY` env var (法语)`DAYTONA_API_URL` 编号 : `DAYTONA_TARGET` 如有需要,也可作为 vars 工作)
-   Anthropic 或 OpenAI API 密钥 — 以您使用的代理
-   GitHub 令牌, 如果想要打开循环, 在目标 repo 上带有 push + PR 权限 公关而不是仅仅报告

## 快开始

```ts
import { ControlLoop, Github, Codex, AgentPrActuator, sensors } from "@bpinternal/overwatch";

const loop = new ControlLoop({
  // Slugified and applied as a label on every PR this loop opens; also what
  // config.maxOpenPrCount counts against.
  label: "Naked Error Should be ServiceError",

  config: {
    maxOpenPrCount: 1,
    git: new Github({
      repo: "https://github.com/my-org/my-repo.git",
      branch: "main",
      key: process.env.GITHUB_TOKEN,
    }),
    agent: new Codex({ apiKey: process.env.OPENAI_API_KEY!, model: "gpt-5.5" }),
    hooks: {
      setup: "bun install",
      preCommit: "bun run fix:format",
    },
    sandbox: { apiKey: process.env.DAYTONA_API_KEY! },
  },

  // Detects anomalies. Scans src/ for raw `throw new Error(...)`.
  sensor: sensors.astGrep({
    pattern: "throw new Error($$$)",
    language: "ts",
    paths: ["src/"],
  }),

  // The actuator decides what to do with the picked signals. The default AgentPrActuator
  // runs the agent per signal, verifies by re-sensing, then commits and opens a PR. Its
  // `instructions` builder turns one signal into the agent's prompt — see "Writing a
  // custom actuator" below for the built-in builders and for how to swap in an actuator
  // that does something other than open a PR.
  actuator: new AgentPrActuator({
    instructions: (signal) => ({
      instructions: [
        `Fix this in ${signal.location?.file ?? "the codebase"}.`,
        `Issue: ${signal.message}`,
        "Make the smallest change that resolves it — no unrelated refactors.",
      ].join("\n"),
    }),
  }),
});

// Expose the built-in CLI (see "Running from CI" below). Or call loop.run() directly.
loop.cli();
```

用您的 TypeScript 跑取器运行它—— 例如 . `pnpm dlx tsx my-loop.ts run` - 随便怎么说 您喜欢( 线圈触发器, 时间表上的 GitHub 动作等) ; 循环本身不会停留 在运行之间居住.

见 `examples/` 更完整、更现实的配置(共享配置于 `examples/common.ts`, (中文(简体) ), 上面的循环.

## 从中央情报局逃跑

循环是一个平版脚本,所以任何TypeScript跑者都可以运行. 而不是把论据串起来 处理您自己,结束文件 **`loop.cli()`** 它把剧本变成一个小 带有两个子命令的命令行工具 :

```ts
const loop = new ControlLoop({ /* ... */ });

loop.cli(); // parses process.argv and runs the matching subcommand
```

```bash
tsx my-loop.ts run                    # one full cycle: sense → pick → actuate (→ PR)
tsx my-loop.ts apply-comments 1234    # apply new review comments on PR #1234, push in place
tsx my-loop.ts --help                 # usage (works on each subcommand too)
```

-   **`run`** 输出 **非零** 如果一个固定点无法被验证(`fix-failed`)或任何掷出 错误, 因此 CI 步骤会大声失败 。 所有其他结果—— `clean`, (中文(简体) ), `skipped`, (中文(简体) ), `pr-opened` —— 说 输出 `0`。结果也作为一行摘要印发.
-   **`apply-comments <pr>`** 阅读比公关头条更新的评论并将其应用于 公关的分支(参见: **公关审查意见** (见上文)。 把它连接到一个 `pull_request_review_comment` 触发,通过PR号码。 它首先检查 PR 带有这个循环的标签;如果它 不——来自另一个循环或人类的公关—— 它什么都不做,而且退出 **非零** (单位:千美元)`wrong-loop`),所以错误的接线触发器会响亮地失败,而不是推动这个循环的配置 在外国分支.

帮助和论证 [指挥官](https://github.com/tj/commander.js)这样 `--help`,未知命令错误,每个命令的使用都免费.

GitHub Action 工作流程运行回路时

```yaml
name: control-loop
on:
  schedule:
    - cron: "0 * * * *"           # hourly
  pull_request_review_comment:
    types: [created]

jobs:
  loop:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install
      - name: run
        if: github.event_name == 'schedule'
        run: pnpm dlx tsx my-loop.ts run
        env:
          DAYTONA_API_KEY: ${{ secrets.DAYTONA_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GH_PUSH_TOKEN }}
      - name: apply review comments
        if: github.event_name == 'pull_request_review_comment'
        run: pnpm dlx tsx my-loop.ts apply-comments ${{ github.event.pull_request.number }}
        env:
          DAYTONA_API_KEY: ${{ secrets.DAYTONA_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GH_PUSH_TOKEN }}
```

过 `config.maxOpenPrCount` 保持预定时间 `run` 从堆积起来的公关 比它们合并的速度快.

### 多个循环, 一个切入点

运行许多循环意味着许多接近相同的工作流程——a `pull_request_review_comment` 触发器不能知道 _哪个_ 循环打开正在评论的 PR . **`LoopOrchestrator`** 崩溃 将它们编成单一的脚本: 记录循环, 结尾为 `orchestrator.run()`,和一个CLI战线 他们全部.

```ts
import { LoopOrchestrator } from "@bpinternal/overwatch";
import { nakedErrorLoop, reactDoctorLoop } from "./loops";

new LoopOrchestrator()
  .register(nakedErrorLoop)
  .register(reactDoctorLoop)
  .run();
```

```bash
tsx loops.ts list                     # the registered loops and their routing slugs
tsx loops.ts run react-doctor-issues  # one cycle of a specific loop (by slug)
tsx loops.ts apply-comments 1234      # route the event to whichever loop owns PR #1234
```

`apply-comments` 将 PR 与每个循环的标签相匹配,并将注释应用到该循环的标签上 拥有它——所以一个评论的webhook服务于每一个循环. 如果没有注册的循环拥有 PR,它 输出 **非零** (单位:千美元)`no-matching-loop`) (中文(简体) ). 循环被他们的标签弹出键入,所以他们的标签必须 以相隔(登记相撞抛出物),它们应瞄准相同的还原物(a) 评论 Webhook 是每repo 。 在以上工作流程中,将两个评论/时间表步骤指向: `loops.ts apply-comments …` 编号 : `loops.ts run <slug>` 而不是每页脚本.

## 核心概念

控制回路是五块有线的配置 `ControlLoop`数字 :

| 块 | 类型 | 目的 |
| --- | --- | --- |
| `sensor` | `Sensor` | 扫描 repo, 返回 `Signal[]` 发现的异常现象 |
| `picker` | `Picker` (可选) | 选择要操作的信号 _此运行_ |
| `actuator` | `Actuator` | 对所选信号采取行动; 默认运行代理并打开 PR |
| `config.agent` | `Agent` | 在沙盒内驱动编码代理 CLI |
| `config.git` | `GitSource` | 在哪里复制,在哪里推和打开 PR |

A 类 **`Signal`** 单位是所有其他操作:

```ts
interface Signal<TData = unknown> {
  // Where the anomaly is. Optional — some anomalies are repo-wide. When present,
  // `location.file` anchors the signal's identity (see below).
  location?: { file: string; line?: number };
  message: string;
  priority?: "low" | "medium" | "high";
  data?: TData; // anything sensor-specific; carried through to picker and actuator untouched
}
```

信号通过运行来匹配 **`location.file + message` 仅限** —— 说 `line` 是信息和 自然改变为固定土地,所以永远不要烤出行号或时间戳入 `message`这个 身份是让循环在验证时从"固定"中分辨出"仍然被打破,重试"的,以及 是什么让它跳过信号 一个已经打开的公关声称.

### 哪个 `run()` 已经,顺序

1.  如果 `config.maxOpenPrCount` 打开的 PR 已经带有此循环的标签 .
2.  旋转沙盒,复制回波,运行 `config.hooks.setup`。 。 。 .
3.  运行传感器。 无信号 ~ 完成 (`status: "clean"`) (中文(简体) ).
4.  丢弃此循环的另一个开放 PR 已声称的信号( 见 **内存声称( C)** 页:1.
5.  运行拾取器( 默认 ): `pickers.count(1)`,即传感器顺序的第一个信号). 没有 信号已选中 .
6.  把选中的信号交给 `actuator.act(...)` 并归还它所报告的一切。 步骤6a-6c 下面是 **默认 `AgentPrActuator`** 是否 —— 由不同的驱动器拥有此步骤 他们中的任何人不得:
    -   **第6条a款.** 检查一个分支,运行一个代理 每一个选定的信号使用该信号 指示,然后重新运行传感器,以检查哪些信号已经消失。 还有什么 重试(用“ 尝试不同的方法” ) 至 `config.maxFixAttempts` 时间( 默认 3).
    -   **第6条b款.** 如果在一切尝试之后 事情仍未解决 `status: "fix-failed"`, (中文(简体) ), 无所取舍.
    -   **第6条c款.** 否则:运行 `config.hooks.preCommit`,承诺一切 `--no-verify`, (中文(简体) ), 推出,打开公关(贴上标签,在正文中贴有索赔标记) `status: "pr-opened"`。 。 。 .

`run()` 决定: `ControlLoopRunResult` - 总是检查 `.status` 而不是假设 成功 干净的感应器 和"我们尝试和失败"都是正常的结果 例外情况.

### 公关审查意见

`loop.applyPrComments(prNumber)` 是另一个切入点——将它接通您的 GitHub webhook(或 民意调查),用于对打开的环路的公关评论活动. 它获取的评论比 PR的头部承诺,让特工在PR现有的分支上应用,并推进到位. 比头部年长的评论被当做已经处理过, 所以重新触发在同一方面 线程是安全的 (`status: "no-new-comments"`) (中文(简体) ).

以 `/feedback` 与其他注释一样应用,但文本(前缀) (被剥去) `.github/control-loop/memory-<label>.md` 在回购。 那个 文件在同一个循环的每个未来运行中被读取,并附在每一个代理指令之后,作为 常年指导——这是你如何纠正一个循环的一次行为,并把它粘住,因为 记忆文件船与 PR 记录它.

### 通知

`config.notifications` 是循环报告最终结果的目的地列表. `Slack` 带有 lib 的船舶; 抽象的下级 `Notification` (一个) `send(text, state?)` 方法),用于其他一切.

```ts
import { Slack } from "@bpinternal/overwatch";

notifications: [
  new Slack({
    auth: { token: process.env.SLACK_BOT_TOKEN! }, // bot token with chat:write
    channel: "#overwatch",
    onSuccess: (context) => `A new PR was open: ${context.url}`,
    onFailure: (reason) => reason,
  }),
],
```

语言完全属于你——每个处理者将消息发回,然后返回 `null`编号 :`undefined` 所以一个目的地只说起事件 它有一个处理器:

| 处理器 | 着火了 | 接收 |
| --- | --- | --- |
| `onSuccess` | `run()` 打开 PR | `{ label, url, signals }` |
| `onFailure` | `run()` 已返回 `fix-failed`输入点 | `reason: string` (已经命名循环) |
| `onCommentsApplied` | `applyPrComments()` 被推入公关 | `{ label, branch, comments }` |

`clean` 财务报告和审定财务报表 `skipped` 运行——并评论意味着另一个循环的事件——不通知: 大部分滴滴的循环回报率是多少 并宣布这些滴滴值 看着 无法到达的目的地( 已撤销的令牌, 不在频道中) 是 登录为警告并跳过; 它从未失败过运行, 也从未替换运行本身的错误 .

在第一次公关之后,每条关于同一公关的信息都与该公关相接: `Slack` 员额 “应用的评论”更新(以及任何后来的失败) **在线索中回复** 信息内容 所以公关读作谈话.

这在程序上是可行的 打开公关的运行 以及更新它的评论 除了公关之外 `NotifyState`: 随便 `send` 返回存储在 PR 正文中的隐形标记(紧接索赔标记)并移交 回到同一个目的地 在下一个活动。 斯莱克只是第一个信息 `ts`。 。 。 . 它从来没有到达你的处理器; 它是交付力学,不是措辞。 为了运送你 本身: 返回状态来记录它, 不返回任何内容来保存已存储的内容( 这就是 Slack 如何保存) 持续回答线根而不是自己最后的回覆).

### 索赔(安全货币)

每个打开的PR嵌入了一个隐形标记,列出它修补的信号密钥. 在接下来的比赛中, 已声明的信号 _打开_ 同一循环的PR在选择之前被排除在外——所以 在先前的 PR 合并之前再次运行回路不会产生相同的重复修复 问题。 合并的PR删除源信号(传感器停止报告); a 关闭的不合并的PR会自动释放其索赔,因为它不再开放.

## 写入自定义传感器

传感器要么是一种平庸的功能,要么是一个上传到沙盒中的脚本. 使用 `sensors.*` 页:1 普通案件; 不适合时写自己的案件.

**主机侧函数** (单位:千美元)`SensorFn`)——运行在你的机器/CI跑道上,有一个 `RepoHandle` 那个 代理人 `exec`, (中文(简体) ), `readFile`, (中文(简体) ), `glob`,以及 `grep` 进入沙盒:

```ts
import type { RepoHandle } from "@bpinternal/overwatch";
import type { Signal } from "@bpinternal/overwatch";

const myTodoSensor = async (repo: RepoHandle): Promise<Signal[]> => {
  const matches = await repo.grep("FIXME:", ["src/"]);
  return matches.map((match) => ({
    location: { file: match.file, line: match.line },
    message: `unresolved FIXME: ${match.content.trim()}`,
    priority: "low",
  }));
};

const loop = new ControlLoop({ /* ... */ sensor: myTodoSensor, /* ... */ });
```

当检查便宜且可表达时使用此形状 `exec`编号 :`grep`编号 :`glob` ——关闭 和东道方的进口正常工作,因为它永远不会离开你的流程.

**沙盒脚本** (单位:千美元)`sensors.script(localPath)`——用于更重的扫描仪(海关静态) 应运行的项目工具 _内部_ 以还原根作为沙盒 cwd, 而不是通过主机进行圆通:

```ts
// sensors/find-unhandled-promises.ts
export default async function (): Promise<import("@bpinternal/overwatch").Signal[]> {
  // this file is uploaded and run with `bun` inside the sandbox; cwd is the repo root
  // ... your scanning logic ...
  return [];
}
```

```ts
sensor: sensors.script("./sensors/find-unhandled-promises.ts"),
```

默认导出必须是 `() => Promise<Signal[]>`循环将它包裹在一个小跑道上 将结果写入 JSON 文件, 而不是 stdout, 因此脚本可以自由使用 `console.log` 页:1 它自己的调试.

**内置传感器** (单位:千美元)`sensors.grep`, (中文(简体) ), `sensors.astGrep`, (中文(简体) ), `sensors.reactDoctor`) 覆盖正文 搜索,结构AST搜索,以及 [反应医生](https://react.doctor/) 诊断 检查 `src/sensors/` 用于在写自定义形状前的完整选项; (单位:千美元) `grep`编号 :`astGrep` 用自定义调用 `message` 函数经常取代原本的 一个感应器.

无论你选择的形状: 保留 `paths` (或等同范围)故意. 无瞄准传感器 将"修复这件事情"变成"修复一切,无所不在,永远"

## 写入自定义拾取器

一个拾取者将感知(和无感知)信号缩小到运行时应该采取的行动:

```ts
type Picker = (signals: Signal[]) => Promise<Signal[]> | Signal[];
```

```ts
import type { Signal } from "@bpinternal/overwatch";

const highPriorityFirst = (max: number) => (signals: Signal[]): Signal[] =>
  [...signals]
    .sort((a, b) => rank(b.priority) - rank(a.priority))
    .slice(0, max);

function rank(p: Signal["priority"]): number {
  return { high: 2, medium: 1, low: 0 }[p];
}
```

内设: `pickers.count(n)` (第一届) `n` 按传感器顺序排列——默认)和 `pickers.busiestFile(maxCount?)` (无论哪个文件的信号都最多,所以运行 在一个连贯的公关中共同修复土地。 摸摸感应器,或者写一个像那个 改变"第一"的意思.

## 演员

这个 **演员** 选择后拥有所有的东西——它决定了实际是什么 _发生_ 页:1 并报告结果。 这是一个抽象的阶级 只有一个方法:

```ts
abstract class Actuator<TData = unknown> {
  abstract act(input: ActuatorInput<TData>): Promise<ControlLoopRunResult<TData>>;
}
```

`act` 接收选中的 `signals`,提供 `sandbox`,一个 `AgentContext` (单位:千美元)`ctx`用于 运行命令/ 在其中写入文件, a `sense()` 回调以重运行传感器,循环 `config`页:1 `label`编号 :`displayLabel`运行 `log` - 做这些事的所有事情 返回 a `ControlLoopRunResult`。 。 。 .

### 默认 : `AgentPrActuator`

大多数循环想要内置行为:在每个信号上运行代理,通过再感应(与 重复,然后承诺并打开一个公关。 这是 `AgentPrActuator`,它需要一个 **指令构建器** 函数将一个信号变成代理的提示:

```ts
type InstructionBuilder<TData = unknown> =
  (signal: Signal<TData>) => Promise<{ instructions: string }> | { instructions: string };
```

```ts
import { AgentPrActuator, actuators } from "@bpinternal/overwatch";

new AgentPrActuator({
  instructions: (signal) => ({
    instructions: [
      `Fix this in ${signal.location?.file ?? "the codebase"}.`,
      `Issue: ${signal.message}`,
      "Make the smallest change that resolves it — no unrelated refactors.",
    ].join("\n"),
  }),
});
```

到达 `signal.data` 用于任何特定传感器——例如:. `sensors.reactDoctor` 附上 全面诊断(包括其 `help` 这里,和 `sensors.astGrep`编号 :`sensors.grep` 附 录 原始的比赛.

两艘建造船 `actuators` (通过一个作为 `instructions`,或者省略它——默认是 `fromSignal()`:

-   `actuators.fromSignal()` ——仅从信号中创建的最小指令;一个好的默认.
-   `actuators.fromFile(localPath)` ——读取本地标记下/文本文件一次(已刻出)并用作 说明书,并附上信号的细节。 当固定跟随一个 作为医生写作比重建每个符号更容易的房子风格——见: `examples/blank-errors-instructions.md` 真实的(带有地图的完整移徙指南) 表格和片断前后.

`AgentPrActuator` 在每个指令中自动附加两件事: 重试调整 重复尝试,这个循环积累 `/feedback` 内存( 见 **公关审查意见** (见上文)。 不要重复 任何关心 在您的指令构建者.

### 写入自定义驱动程序

子类 `Actuator` 当你想逃跑的时候 _其他_ 打开 PR — 张贴报告到 Slack,写一个摘要文件,呼叫一个API,打开一个问题而不是一个PR. 开动器不需要 触摸编码代理; 它只需要返回一个 `ControlLoopRunResult`数字 :

```ts
import { Actuator, type ActuatorInput, type ControlLoopRunResult } from "@bpinternal/overwatch";

class ReportOnly extends Actuator {
  async act({ signals, log }: ActuatorInput): Promise<ControlLoopRunResult> {
    log.info(`found ${signals.length} signal(s) — reporting without touching the repo`);
    await postToSlack(signals.map((s) => `• ${s.location?.file ?? "repo"}: ${s.message}`).join("\n"));
    return { status: "clean" }; // nothing was changed or opened
  }
}
```

循环仍然处理感知, 索赔过滤,采摘 `maxOpenPrCount` 大门周围 您的驱动器 —— 带有循环标签的 PR 键, 所以它们只是不给驱动器 从来没有打开一个.

## 配置引用( E)`ControlLoopConfig`(中文(简体) )

静态的,每升起的设置 `config`数字 :

-   **`git`** (单位:千美元)`GitSource`——使用 `new Github({ repo, branch, key })`。 。 。 . `key` 实值 用于克隆/计算公共回购的PR的可选性,要求推取或打开PR.
-   **`agent`** (单位:千美元)`Agent`—— `new Claude({ apiKey, model?, timeoutSec? })` 或者说 `new Codex({ apiKey, model?, timeoutSec? })`扩展抽象 `Agent` 类 (单位:千美元)`prepare`, (中文(简体) ), `executeAgent`插入另一个CLI.
-   **`maxOpenPrCount`** ——跳出直接跑道,如果这些带有循环标签的PR是 已经打开.
-   **`notifications`** (单位:千美元)`Notification[]`——目的地告知跑道结果 **通知** 见上文.
-   **`branchPrefix`** ——默认 `"control-loop"`;被推的树枝看起来像 `<prefix>/<label-slug>-<run-id>`。 。 。 .
-   **`hooks.setup`** —— shell 命令在克隆后立即运行(例如. `"bun install"`非零级 退出中止运行 .
-   **`hooks.preCommit`** ——贝壳命令追随代理人,在犯罪前(例如: repo的自动处理器). 规范代理编辑和任何循环,所以 PR 通过 CI 格式化。 非零退出中止运行 .
-   **`env`** - 创建时在沙盒上设置了环境 vars(可用 `hooks` 和代理人.
-   **`maxFixAttempts`** ——默认3;见上文步骤6.
-   **`sandbox.apiKey`** —— Daytona API 密钥沙盒是用创建的,所以在 一个过程可以使用不同的代托纳账户. 掉回原点 `DAYTONA_API_KEY` 环境 var 当省略时.
-   **`sandbox.snapshot`** 来创建沙盒而非默认的 Daytona 快照 图像。 预装工具链和代理CLI在此跳过每个运行的安装. 说明: 资源大小必须被烤入快照本身——无法在创建时要求它 时间用一个.

## 日志

每跑步都会说出步道 `RunLog` (沙盒的创造、克隆、感知、, 每个信号代理运行,承诺/推出,公关打开)——无可配置,仅当观察stderr时 手动运行一个循环。 设定 `CONTROL_LOOP_SILENT=1` 变音; 否则颜色会随之而来 `NO_COLOR` 和TTY检测.

## 扩展

-   **新传感器口味**: 将文件添加到 `src/sensors/` 返回时 `SensorFn` 再从 `src/sensors/index.ts` (或使用) `sensors.script`原始函数内含——你不需要 用于一次性扫描的内置).
-   **新拾取器**: 任意 `(signals: Signal[]) => Signal[] | Promise<Signal[]>` 工作内置; 添加一个 文件为 `src/pickers/` (从其 `index.ts`),但需广泛再用.
-   **新指令样式**: 添加一个 `InstructionBuilder` 工厂改为 `src/actuators/instructions.ts` (比如说) `fromFile`编号 :`fromSignal`),或通过一个内线 `AgentPrActuator`。 。 。 .
-   **新建起动器类型** (不打开 PR / 不运行代理): 添加文件到 `src/actuators/` 一个分类抽象的类 `Actuator`,从 `src/actuators/index.ts`。 。 。 .
-   **新剂CLI**: 扩展抽象 `Agent` 类 `src/agents.ts`。 。 。 .
-   **新的通知目的地**: 将文件添加到 `src/notifications/` 与一个类 子分类抽象 `Notification` 执行 `send(text, state?)`,从 `src/notifications/index.ts`。基地处理哪些事件起火和它们说什么;返回 a `NotifyState` 从 `send` 在下一次关于公关的事件上 将循环的手拿回来.
-   **新伪造** (吉特拉布等):执行 `GitSource` 界面从 `src/github.ts`。 。 。 .

## 免责声明

此软件包将在 `@bpinternal` 组织。 这个组织的所有软件包都用于 [瓶装](https://github.com/botpress/botpress) 内部的团队 并不适合我们的社区。 由于这些软件包适合我们自己的使用情况,它们可能不稳定的API,在没有警告的情况下接受中断的更改,文件很少,缺乏以社区为重点的支持。 然而,这些软件包仍然被故意公开,其重要原因如下: 我们爱开源. 因此,如果您想要安装或叉出这个软件包,就完全可以自由操作。 我们强烈建议你正确标记你的版本.

博讯工程组.

## 常见问题

### @bpinternal/overwatch 是什么？

用于构建控制回路的无头库:用于扫描一个类异常回路的预定bots,用编码代理来修复几个实例,并打开一个PR——没有服务器,UI,或持续过程\.

### 如何安装 @bpinternal/overwatch？

在项目目录运行 npm install @bpinternal/overwatch，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：typescript（^5）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @bpinternal/overwatch 支持 TypeScript 吗？

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

### @bpinternal/overwatch 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @bpinternal/overwatch 需要什么版本的 Node\.js？

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

### @bpinternal/overwatch 可以商用吗？

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

### @bpinternal/overwatch 的下载量有多少？

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

### @bpinternal/overwatch 还在维护吗？

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

