MVP底座⑤:自定义工具导入路由 —— 与第一方同门两审

顺手补两处底座:试运行审此前传空连接,靠 endpoint 干活的工具一律过不了门(这道门
只对不需要连接的工具有效);现传本次申报的连接值。未注册第一方处理器的工具走通用
HTTP 实现 —— 导入的工具由此可真调、可真审:端点不通就是不通,不入目录。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-28 01:58:38 +08:00
co-authored by Claude Fable 5
parent 0fe5222f79
commit 8e7509f65b
9 changed files with 292 additions and 4 deletions
+69
View File
@@ -0,0 +1,69 @@
/**
* 导入工具的通用实现 —— 「实现对系统」的兜底一件(编码规范 §3;语义出处 OS 7.3)。
*
* 第一方工具各有各的处理器;**导入进来的自定义工具**没有处理器,
* 但它在契约里申报了 `endpoint` 连接键 —— 这个处理器就把入参 POST 到那个端点。
*
* 运行时受限照旧:只拿得到申报的连接目标,没有别的环境能力。
* 端点不通 = 试运行审不过 = 进不了目录 —— 这是**门**,不是障碍:
* 一件调不通的工具入了目录,活动调它时才失败,那才是真的坏。
*/
import type { ToolHandler } from '@modules/toolbox';
/** 未在第一方注册的工具:申报了 endpoint 就按 HTTP 调,没申报就如实说没有实现 */
export const httpToolHandler: ToolHandler = async ({ input, connection, operation, idempotencyKey }) => {
const endpoint = connection['endpoint'];
if (endpoint === undefined || endpoint.trim() === '') {
return {
ok: false,
error: {
code: 'tool-unimplemented',
retryable: false,
message: '工具无实现:未申报 endpoint 连接键,系统侧也没有第一方处理器',
},
};
}
if (!/^https?:\/\//.test(endpoint)) {
return {
ok: false,
error: { code: 'endpoint-invalid', retryable: false, message: `端点须是 http(s) 地址:${endpoint}` },
};
}
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'content-type': 'application/json',
// 幂等键透传给对端:写类动作重投时由对端去重(契约声明 idempotent 才有意义)
...(idempotencyKey === undefined ? {} : { 'idempotency-key': idempotencyKey }),
},
body: JSON.stringify({ input, ...(operation === undefined ? {} : { operation }) }),
});
if (!response.ok) {
return {
ok: false,
error: {
code: `http-${response.status}`,
// 5xx 可重试,4xx 是我们自己请求得不对,重试没用
retryable: response.status >= 500,
message: `端点返回 ${response.status}`,
},
};
}
const body: unknown = await response.json();
if (typeof body !== 'object' || body === null || Array.isArray(body)) {
return { ok: false, error: { code: 'output-invalid', retryable: false, message: '端点返回的不是 JSON 对象' } };
}
return { ok: true, output: body as Record<string, unknown> };
} catch (error) {
return {
ok: false,
error: {
code: 'endpoint-unreachable',
retryable: true,
message: `端点调用失败:${error instanceof Error ? error.message : String(error)}`,
},
};
}
};