@chamvi/频道-openclaw
Chamvi代理的 OpenClaw 频道.
在 OpenClaw 聊天中,信件和批准请求到达所有者手中,
每个消息一次,即使引擎重试发送.
目录
安装
npm install @chamvi/channel-openclaw @chamvi/core --save
仅限无害环境管理,节点.js >=26.0.0。 。 。 . @chamvi/core 是一个对等依赖; 频道使用全局 fetch 并且没有运行时间依赖.
chamvi 和 OpenClaw 对话 "MCP,钩出": OpenClaw 启动并读取通过应用程序的 MCP 端点运行(@chamvi/http)),而chamvi通过这个频道将消息推到OpenClaw的钩子端点. 频道只停播:没有 in 并且不订阅.
OpenClaw 设置
在 OpenClaw 网关配置中:
- 启用钩( E)
hooks.enabled: true他们将被弃绝 专用hooks.token。它不是网关符; 频道发送它作为Authorization: Bearer <token>。 。 。 . - 保留
hooks.path时间/hooks或传递与hooksPath。 。 。 . - 当你通过
agentId列表中hooks.allowedAgentIds。 。 。 . - 添加 chamvi app的 MCP 端点(
http://<service>/mcp(到OpenClaw'smcp.servers持有主要持证agent:<name>:run开始运行。 永远不要给它agent:<name>:approve: 主人通过approvalUrl链接,作为本单位的负责人签名(参见 答复批准) (中文(简体) ).
使用量
openclawChannel() 是一个 IChannel; 在引擎上注册(channels并用一个工具通过它 ctx.channels.findOne('openclaw')数字 : emit() 为了一个消息, approve() 暂停工具呼叫,直到所有者回答 :
import type { LanguageModel } from 'ai';
import { z } from 'zod';
import {
Engine,
createMemoryStore,
defineModelAgent,
defineTool,
} from '@chamvi/core';
import type { IEngine } from '@chamvi/core';
import { openclawChannel } from '@chamvi/channel-openclaw';
import type { OpenclawChannelOptions } from '@chamvi/channel-openclaw';
export function createApprovalEngine(
model: LanguageModel,
options: Pick<OpenclawChannelOptions, 'fetch' | 'clock'> = {},
): IEngine {
const {
OPENCLAW_URL,
OPENCLAW_HOOKS_TOKEN,
OPENCLAW_CHANNEL,
OPENCLAW_TO,
APP_URL,
} = process.env;
if (!OPENCLAW_URL || !OPENCLAW_HOOKS_TOKEN || !OPENCLAW_CHANNEL || !OPENCLAW_TO || !APP_URL) {
throw new Error('Set OPENCLAW_URL, OPENCLAW_HOOKS_TOKEN, OPENCLAW_CHANNEL, OPENCLAW_TO and APP_URL.');
}
const openclaw = openclawChannel({
baseUrl: OPENCLAW_URL,
// OpenClaw's hooks.token, not the gateway token
hooksToken: OPENCLAW_HOOKS_TOKEN,
deliver: { channel: OPENCLAW_CHANNEL, to: OPENCLAW_TO },
// a link to the approval in the chamvi UI, without the token
approvalUrl: ({ runId, approvalId }) => `${APP_URL}/ui/approvals?run=${encodeURIComponent(runId)}&approval=${encodeURIComponent(approvalId)}`,
...options,
});
const deploy = defineTool({
description: 'Deploy the current release to an environment.',
inputSchema: z.object({ env: z.enum(['staging', 'prod']) }),
execute: async ({ env }, { context }) => {
// every deploy waits for the owner, who is asked in their chat
const { approved } = await context.channels.findOneOrFail('openclaw').approve({
title: `Deploy to ${env}?`,
body: 'The current release goes out now.',
address: OPENCLAW_TO,
});
return approved ? `deployed to ${env}` : 'the owner said no';
},
});
const deployer = defineModelAgent({
name: 'deployer',
model,
instructions: 'Deploy the release the user asks for.',
tools: { deploy },
});
return new Engine({
store: createMemoryStore({ clock: options.clock }),
agents: [deployer],
channels: [openclaw],
clock: options.clock,
});
}
每个消息都变成一个 POST <baseUrl><hooksPath>/agent 与身体 { message, name: 'chamvi', agentId?, deliver: true, channel, to, accountId? },钩子符号为无记名符号,消息密钥为 Idempotency-Key (单位:千美元)<runId>:<approvalId> 用于批准,内容地址发送消息的密钥). channel 实值 deliver.channel· ; to 是工具通过的地址,或 deliver.to (频道的) defaultAddress当工具通过无。 文本中保留了信息的标题,其正文(被切成8000个字符),代理和运行代号;对于批准,也保留了过期和 approvalUrl 链接在配置时( 没有它, 在 chamvi UI 中表示要回答的行) POST /runs/:id/approve) (中文(简体) ).
2xx 回复按发送数计算( OpenClaw 回答) 200 { ok: true, runId } 一旦它承认钩子。 每一个回答都说 ChamviError 有代码 openclaw_request (单位:千美元)OpenClaw <status>: <response text>,与 retry after <s> (单位:千美元) Retry-After),如网络失败(OpenClaw request failed: <message> (<cause>),例如命名原因. ECONNREFUSED 或者说 ENOTFOUND)和一个不回答内部问题的网关 timeoutMs (30 s); 被中止的发送(运行被取消,引擎被排出) 拒绝与中止原因. 失败 emit() 是一个工具错误, 记录什么也没有, 所以下次相同的发送会再次发送 。 一个失败的审批消息会转到引擎的 onError,而引擎在下一次民意测验中再次发送, 键和字节相同, 直到一个发送成功.
答复批准
该消息没有符号,也没有MCP呼叫,只有批准链接:
- 一个链接 (单位:千美元)
approvalUrl({ runId, approvalId, agent })),在配置时,在 chamvi UI中,如:.https://<app>/ui/approvals?run=<runId>&approval=<approvalId>。链接只持有ID;所有者打开它,通过无符号签名并回答POST /runs/:id/approve。签字的主要需要agent:<agent>:approve回答和run:read或者说agent:<agent>:run用于 UI 装入运行并显示其批准. - 无
approvalUrl,该消息说答案 在chamviUI或通过POST /runs/:id/approve。 。 。 .
OpenClaw 代理无法批准 . 批准的标题和正文是工具所写的,它可能重复未经信任的文本(一个发文机构,一个邮件),所以被注入的指令会到达OpenClaw代理. 信息没有标语 和中继代理 其MCP主要持有 agent:<name>:run 既无象征,也无权回答:无论尸体怎么说,只有业主,签入UI,批准.
洞穴
- OpenClaw运行代理打开消息(它不张贴原始文本),并将其作为外部内容处理,因此聊天中的措辞是OpenClaw代理的.
- OpenClaw的"一能"重放生活于内存中,运行完成后持续了5分钟;一个重启即可清除. Chamvi的记录(释放效应、批准)
waiting.notifiedAt和它的每个批准锁)是主去dupe;密钥只覆盖在这些分钟内丢失的响应. - 错误的钩子( 400, 401, 404) 每次发送失败, 并收到一个批准信息
onError在每次民意调查中, 直到它固定或批准时间结束. - OpenClaw 忽略一个
Idempotency-Key超过 256 个字符; 这种密钥( 以及带有白空或非 ASCII 字符的密钥) 以sha256:<hex>。 。 。 . - 错误从不带钩子符号: 它被替换为
[redacted]在响应文本和网络失败消息中,每个字被切为500个字符。 A 类hooksToken那个fetch无法作为信头发送( 控制字符、 U+00FF 上方字符、 周围的白空)openclawChannel()名为.
使用@ chamvi/ app
openclawModule() 从 @chamvi/channel-openclaw/module 读取 OPENCLAW_BASE_URL, (中文(简体) ), OPENCLAW_HOOKS_TOKEN, (中文(简体) ), OPENCLAW_CHANNEL, (中文(简体) ), OPENCLAW_TO, (中文(简体) ), OPENCLAW_HOOKS_PATH, (中文(简体) ), OPENCLAW_AGENT_ID 财务报告和审定财务报表 OPENCLAW_ACCOUNT_ID 当它被调用时,链接批准与仪表板从 CHAMVI_PUBLIC_URL 并贡献频道(模块) chamvi:channel:<name>);工具通过 ctx.channels.findOne(name)。 。 。 . @chamvi/app 是可选的对等点;主条目从不导入.
import { createApp } from '@chamvi/app';
import { openclawModule } from '@chamvi/channel-openclaw/module';
export default createApp({ agents, modules: [openclawModule(), openclawModule({ name: 'ops' })] });
选项
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
baseUrl | string | 所需经费 | 网关,例如:. http://openclaw:18789; 绝对值 http:编号 :https: 没有证书、查询或散列的 URL 。 保存一个基础路径 |
hooksToken | string | 所需经费 | OpenClaw 软件 hooks.token; 一个有效的标题值( 无控制字符, U+00FF 上方没有, 周围没有白空格) |
deliver | { channel, to, accountId? } | 所需经费 | OpenClaw提供答案; channel 财务报告和审定财务报表 to 必须是具体的(last - 也拒绝 defaultAddress,因此工具可能会将地址留出;一个地址通过该工具替换 to |
name | string | 'openclaw' | 频道名称 ctx.channels.findOne(name) 二审需要自己的 |
hooksPath | string | '/hooks' | OpenClaw 软件 hooks.path; 从 /没有 / 本身;后起倒挂 |
agentId | string | 无 | 定义信件的 OpenClaw 代理 |
approvalUrl | ({ runId, approvalId, agent }) => string | 无 | 与Chamvi UI中批准内容的链接,添加到批准信息中;它没有符号(见 答复批准(中文(简体) ) |
timeoutMs | number | 30_000 | 一发即发 clock |
clock | IClock | systemClock | 超时的时间来源 |
fetch | typeof fetch | globalThis.fetch | HTTP 客户端( 测试通过一个假的) |
每个选项都会在 openclawChannel() 被叫作;一个被弃置的 InvalidAgentDefinitionError。 。 。 .
API 密码
| 导出 | 仁爱 | 目的 |
|---|---|---|
openclawChannel | 函数 | openclawChannel(options): 仅出局 IChannel 张贴到 OpenClaw 的钩子端点 |
OPENCLAW_CHANNEL_NAME | 常数 | 'openclaw'默认值 name |
DEFAULT_HOOKS_PATH | 常数 | '/hooks' |
OPENCLAW_TIMEOUT_MS | 常数 | 30_000默认值 timeoutMs |
OPENCLAW_REQUEST_ERROR_CODE | 常数 | 'openclaw_request',则 code 每次发送失败 |
MESSAGE_BODY_MAX_LENGTH | 常数 | 8_000,电文正文的切口 |
类型 : OpenclawChannelOptions, (中文(简体) ), OpenclawApprovalRef, (中文(简体) ), OpenclawDelivery, (中文(简体) ), OpenclawHookBody。 。 。 .
@chamvi/channel-openclaw/module数字 : openclawModule, (中文(简体) ), OpenclawModule类型 OpenclawModuleOptions。 。 。 .
许可证
由 所制
原载 Apache 许可证 2.0。 。 。 .