tool-registry.js
2.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
* DevLoop Tool Registry — loadAllTools()
*
* 把 server/src/tools/devloop/ 下所有 *-tools.js 加载并按工具 name 聚合到 Map。
* tool-manager / ClaudeCliProtocol 启动 Agent 时通过 toolPolicy 取这里的 ToolDef。
*
* 规格: docs/prd/newfunction/_spec/arch-tool-registry.md · §④
*/
const path = require('path');
const fs = require('fs');
// 工具模块清单(手动 require,避免动态 require 出错)
const TOOL_MODULES = [
'./persistence-tools',
'./doc-tools',
'./coder-tools',
'./tester-tools',
'./integrator-tools',
'./triager-tools',
];
let cached = null;
function loadAllTools() {
if (cached) return cached;
const registry = new Map();
for (const mod of TOOL_MODULES) {
const fullPath = path.join(__dirname, mod + '.js');
if (!fs.existsSync(fullPath)) continue;
const tools = require(mod);
for (const key of Object.keys(tools)) {
const tool = tools[key];
if (tool && typeof tool === 'object' && tool.name && typeof tool.execute === 'function') {
if (registry.has(tool.name)) {
throw new Error(`Duplicate tool name: ${tool.name} (in ${mod})`);
}
registry.set(tool.name, tool);
}
}
}
cached = registry;
return registry;
}
/**
* 列工具名(用于 _registry.json toolPolicy 校验)
*/
function listToolNames() {
return Array.from(loadAllTools().keys()).sort();
}
/**
* 按 toolPolicy 取一组工具(含 LemonBoss 既有工具是允许的,本函数只过滤 devloop 的)
*/
function getDevLoopTools(toolPolicy) {
const all = loadAllTools();
const out = [];
for (const name of toolPolicy) {
if (all.has(name)) out.push(all.get(name));
}
return out;
}
/**
* 调度:根据 name 找工具,执行(中央调用入口,外部 ACP 调用方使用)。
* - 校验工具存在
* - 校验 input 符合 inputSchema(简化版:只校验 required + type)
* - 调 tool.execute(input, ctx)
* - 错误标准化
*/
async function dispatch(name, input, ctx) {
const all = loadAllTools();
const tool = all.get(name);
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
_validateInput(input, tool.inputSchema, name);
return tool.execute(input, ctx);
}
function _validateInput(input, schema, name) {
if (!schema || typeof schema !== 'object') return;
if (schema.required && Array.isArray(schema.required)) {
for (const k of schema.required) {
if (input == null || input[k] === undefined) {
const e = new Error(`Tool ${name}: missing required field "${k}"`);
e.code = 'TOOL_INPUT_INVALID';
throw e;
}
}
}
}
function _resetForTesting() {
cached = null;
}
module.exports = {
loadAllTools,
listToolNames,
getDevLoopTools,
dispatch,
_resetForTesting,
TOOL_MODULES,
};