@cicctencent/agent-midway 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -0
- package/dist/adapters/express.d.ts +8 -0
- package/dist/adapters/express.js +91 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/koa.d.ts +3 -0
- package/dist/adapters/koa.js +75 -0
- package/dist/adapters/midway.d.ts +5 -0
- package/dist/adapters/midway.js +11 -0
- package/dist/adapters/next.d.ts +12 -0
- package/dist/adapters/next.js +89 -0
- package/dist/adapters/shared.d.ts +4 -0
- package/dist/adapters/shared.js +31 -0
- package/dist/channel/dingtalk.d.ts +18 -0
- package/dist/channel/dingtalk.js +68 -0
- package/dist/channel/feishu.d.ts +20 -0
- package/dist/channel/feishu.js +96 -0
- package/dist/channel/index.d.ts +46 -0
- package/dist/channel/index.js +311 -0
- package/dist/channel/types.d.ts +77 -0
- package/dist/channel/types.js +7 -0
- package/dist/channel/wecom.d.ts +22 -0
- package/dist/channel/wecom.js +106 -0
- package/dist/component.d.ts +49 -0
- package/dist/component.js +129 -0
- package/dist/connector/calendar-adapter.d.ts +19 -0
- package/dist/connector/calendar-adapter.js +236 -0
- package/dist/connector/db-adapter.d.ts +28 -0
- package/dist/connector/db-adapter.js +193 -0
- package/dist/connector/email-adapter.d.ts +23 -0
- package/dist/connector/email-adapter.js +192 -0
- package/dist/connector/fs-adapter.d.ts +15 -0
- package/dist/connector/fs-adapter.js +199 -0
- package/dist/connector/http-adapter.d.ts +29 -0
- package/dist/connector/http-adapter.js +181 -0
- package/dist/connector/index.d.ts +24 -0
- package/dist/connector/index.js +454 -0
- package/dist/connector/mcp-adapter.d.ts +27 -0
- package/dist/connector/mcp-adapter.js +156 -0
- package/dist/connector/mq-adapter.d.ts +25 -0
- package/dist/connector/mq-adapter.js +181 -0
- package/dist/connector/types.d.ts +205 -0
- package/dist/connector/types.js +9 -0
- package/dist/controller/a2a.controller.d.ts +41 -0
- package/dist/controller/a2a.controller.js +150 -0
- package/dist/controller/agent-profile.controller.d.ts +97 -0
- package/dist/controller/agent-profile.controller.js +200 -0
- package/dist/controller/agent.controller.d.ts +199 -0
- package/dist/controller/agent.controller.js +414 -0
- package/dist/controller/application.controller.d.ts +113 -0
- package/dist/controller/application.controller.js +217 -0
- package/dist/controller/automation.controller.d.ts +113 -0
- package/dist/controller/automation.controller.js +246 -0
- package/dist/controller/channel.controller.d.ts +73 -0
- package/dist/controller/channel.controller.js +183 -0
- package/dist/controller/chat.controller.d.ts +188 -0
- package/dist/controller/chat.controller.js +375 -0
- package/dist/controller/connector.controller.d.ts +134 -0
- package/dist/controller/connector.controller.js +257 -0
- package/dist/controller/knowledge-base.controller.d.ts +157 -0
- package/dist/controller/knowledge-base.controller.js +278 -0
- package/dist/controller/mcp-server.controller.d.ts +115 -0
- package/dist/controller/mcp-server.controller.js +236 -0
- package/dist/controller/model-config.controller.d.ts +139 -0
- package/dist/controller/model-config.controller.js +274 -0
- package/dist/controller/observability.controller.d.ts +124 -0
- package/dist/controller/observability.controller.js +142 -0
- package/dist/controller/security.controller.d.ts +91 -0
- package/dist/controller/security.controller.js +172 -0
- package/dist/controller/settings.controller.d.ts +83 -0
- package/dist/controller/settings.controller.js +280 -0
- package/dist/core/ai-workstation.d.ts +17 -0
- package/dist/core/ai-workstation.js +129 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +20 -0
- package/dist/core/service-container.d.ts +12 -0
- package/dist/core/service-container.js +54 -0
- package/dist/core/sse.d.ts +6 -0
- package/dist/core/sse.js +56 -0
- package/dist/core/types.d.ts +72 -0
- package/dist/core/types.js +2 -0
- package/dist/dto/agent.dto.d.ts +21 -0
- package/dist/dto/agent.dto.js +79 -0
- package/dist/dto/ai-config.dto.d.ts +67 -0
- package/dist/dto/ai-config.dto.js +249 -0
- package/dist/dto/chat.dto.d.ts +40 -0
- package/dist/dto/chat.dto.js +122 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +195 -0
- package/dist/memory/db-store.d.ts +33 -0
- package/dist/memory/db-store.js +143 -0
- package/dist/memory/index.d.ts +187 -0
- package/dist/memory/index.js +443 -0
- package/dist/model/ai-agent-profile.entity.d.ts +32 -0
- package/dist/model/ai-agent-profile.entity.js +289 -0
- package/dist/model/ai-application.entity.d.ts +20 -0
- package/dist/model/ai-application.entity.js +166 -0
- package/dist/model/ai-chat-memory.entity.d.ts +16 -0
- package/dist/model/ai-chat-memory.entity.js +123 -0
- package/dist/model/ai-chat-message.entity.d.ts +16 -0
- package/dist/model/ai-chat-message.entity.js +122 -0
- package/dist/model/ai-chat-skill.entity.d.ts +19 -0
- package/dist/model/ai-chat-skill.entity.js +155 -0
- package/dist/model/ai-chat-thread.entity.d.ts +15 -0
- package/dist/model/ai-chat-thread.entity.js +113 -0
- package/dist/model/ai-chat-workspace.entity.d.ts +17 -0
- package/dist/model/ai-chat-workspace.entity.js +136 -0
- package/dist/model/ai-kb-document.entity.d.ts +16 -0
- package/dist/model/ai-kb-document.entity.js +122 -0
- package/dist/model/ai-knowledge-base.entity.d.ts +22 -0
- package/dist/model/ai-knowledge-base.entity.js +185 -0
- package/dist/model/ai-mcp-server.entity.d.ts +23 -0
- package/dist/model/ai-mcp-server.entity.js +198 -0
- package/dist/model/ai-model-config.entity.d.ts +24 -0
- package/dist/model/ai-model-config.entity.js +200 -0
- package/dist/service/a2a.service.d.ts +142 -0
- package/dist/service/a2a.service.js +537 -0
- package/dist/service/agent-profile.service.d.ts +34 -0
- package/dist/service/agent-profile.service.js +110 -0
- package/dist/service/agent-server.service.d.ts +91 -0
- package/dist/service/agent-server.service.js +634 -0
- package/dist/service/agent-task-queue.service.d.ts +98 -0
- package/dist/service/agent-task-queue.service.js +283 -0
- package/dist/service/ai-chat.service.d.ts +103 -0
- package/dist/service/ai-chat.service.js +431 -0
- package/dist/service/ai-skill.service.d.ts +116 -0
- package/dist/service/ai-skill.service.js +457 -0
- package/dist/service/application.service.d.ts +42 -0
- package/dist/service/application.service.js +139 -0
- package/dist/service/automation.service.d.ts +37 -0
- package/dist/service/automation.service.js +196 -0
- package/dist/service/connector.service.d.ts +136 -0
- package/dist/service/connector.service.js +524 -0
- package/dist/service/knowledge-base.service.d.ts +138 -0
- package/dist/service/knowledge-base.service.js +528 -0
- package/dist/service/mcp-server.service.d.ts +39 -0
- package/dist/service/mcp-server.service.js +143 -0
- package/dist/service/model-config.service.d.ts +57 -0
- package/dist/service/model-config.service.js +168 -0
- package/dist/service/observability.service.d.ts +145 -0
- package/dist/service/observability.service.js +281 -0
- package/dist/service/openai.service.d.ts +88 -0
- package/dist/service/openai.service.js +406 -0
- package/dist/service/prompt-builder.service.d.ts +50 -0
- package/dist/service/prompt-builder.service.js +246 -0
- package/dist/tools/code-exec.tool.d.ts +37 -0
- package/dist/tools/code-exec.tool.js +162 -0
- package/dist/tools/datetime.tool.d.ts +21 -0
- package/dist/tools/datetime.tool.js +379 -0
- package/dist/tools/http-request.tool.d.ts +43 -0
- package/dist/tools/http-request.tool.js +455 -0
- package/dist/tools/registry.d.ts +71 -0
- package/dist/tools/registry.js +77 -0
- package/dist/tools/text-process.tool.d.ts +7 -0
- package/dist/tools/text-process.tool.js +366 -0
- package/dist/tools/web-search.tool.d.ts +28 -0
- package/dist/tools/web-search.tool.js +304 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.js +7 -0
- package/package.json +69 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.computeHashTool = exports.encodeDecodeTool = exports.textProcessTool = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* 文本处理工具
|
|
39
|
+
*
|
|
40
|
+
* 让 Agent 能够执行各种文本处理操作:正则匹配/替换、编解码、Hash 计算、文本统计等。
|
|
41
|
+
*
|
|
42
|
+
* 工具说明:
|
|
43
|
+
* - text_process:综合文本处理(正则匹配、替换、提取、统计)
|
|
44
|
+
* - encode_decode:编码/解码(Base64、URL、Unicode、Hex)
|
|
45
|
+
* - compute_hash:计算文本 Hash(MD5、SHA1、SHA256、SHA512)
|
|
46
|
+
*
|
|
47
|
+
* 使用场景:
|
|
48
|
+
* - 从大段文本中提取结构化信息(邮箱、电话、URL 等)
|
|
49
|
+
* - 数据格式转换与编解码
|
|
50
|
+
* - 文本清洗与预处理
|
|
51
|
+
* - 内容完整性校验(Hash)
|
|
52
|
+
*/
|
|
53
|
+
const crypto = __importStar(require("crypto"));
|
|
54
|
+
// ===================== 正则匹配/替换 =====================
|
|
55
|
+
/** 内置常用正则模板 */
|
|
56
|
+
const BUILTIN_PATTERNS = {
|
|
57
|
+
email: /[\w.+-]+@[\w-]+\.[\w.-]+/g,
|
|
58
|
+
url: /https?:\/\/[^\s<>"{}|\\^`[\]]+/g,
|
|
59
|
+
phone_cn: /1[3-9]\d{9}/g,
|
|
60
|
+
ipv4: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
61
|
+
date_iso: /\d{4}-\d{2}-\d{2}/g,
|
|
62
|
+
number: /-?\d+(?:\.\d+)?/g,
|
|
63
|
+
chinese: /[\u4e00-\u9fa5]+/g,
|
|
64
|
+
json_block: /\{[\s\S]*?\}|\[[\s\S]*?\]/g,
|
|
65
|
+
};
|
|
66
|
+
function processText(text, operation, pattern, replacement, flags, template) {
|
|
67
|
+
switch (operation) {
|
|
68
|
+
case 'match': {
|
|
69
|
+
const regex = pattern ? new RegExp(pattern, flags || 'g') : null;
|
|
70
|
+
if (!regex)
|
|
71
|
+
return { error: 'match 操作需要提供 pattern 参数' };
|
|
72
|
+
const matches = [];
|
|
73
|
+
let m;
|
|
74
|
+
while ((m = regex.exec(text)) !== null && matches.length < 100) {
|
|
75
|
+
matches.push({
|
|
76
|
+
value: m[0],
|
|
77
|
+
index: m.index,
|
|
78
|
+
groups: m.groups ? { ...m.groups } : undefined,
|
|
79
|
+
});
|
|
80
|
+
if (!regex.global)
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
operation: 'match',
|
|
85
|
+
totalMatches: matches.length,
|
|
86
|
+
matches,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
case 'replace': {
|
|
90
|
+
if (!pattern)
|
|
91
|
+
return { error: 'replace 操作需要提供 pattern 参数' };
|
|
92
|
+
const regex = new RegExp(pattern, flags || 'g');
|
|
93
|
+
const result = text.replace(regex, replacement || '');
|
|
94
|
+
return { operation: 'replace', result, changed: result !== text };
|
|
95
|
+
}
|
|
96
|
+
case 'extract': {
|
|
97
|
+
// 使用内置模板快速提取
|
|
98
|
+
const templateName = (template || pattern || '').toLowerCase();
|
|
99
|
+
const regex = BUILTIN_PATTERNS[templateName] ||
|
|
100
|
+
(pattern ? new RegExp(pattern, flags || 'g') : null);
|
|
101
|
+
if (!regex)
|
|
102
|
+
return {
|
|
103
|
+
error: 'extract 操作需要提供 pattern 或 template 参数',
|
|
104
|
+
};
|
|
105
|
+
const items = [];
|
|
106
|
+
let m;
|
|
107
|
+
const r = new RegExp(regex.source, regex.flags.includes('g') ? regex.flags : regex.flags + 'g');
|
|
108
|
+
while ((m = r.exec(text)) !== null && items.length < 200) {
|
|
109
|
+
if (!items.includes(m[0]))
|
|
110
|
+
items.push(m[0]);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
operation: 'extract',
|
|
114
|
+
template: templateName,
|
|
115
|
+
totalUnique: items.length,
|
|
116
|
+
items,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
case 'split': {
|
|
120
|
+
if (!pattern)
|
|
121
|
+
return { error: 'split 操作需要提供 pattern(分隔符)参数' };
|
|
122
|
+
const parts = text.split(new RegExp(pattern, flags || ''));
|
|
123
|
+
return {
|
|
124
|
+
operation: 'split',
|
|
125
|
+
totalParts: parts.length,
|
|
126
|
+
parts: parts.slice(0, 200),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
case 'stats': {
|
|
130
|
+
const chars = text.length;
|
|
131
|
+
const lines = text.split('\n').length;
|
|
132
|
+
const words = text.trim().split(/\s+/).filter(Boolean).length;
|
|
133
|
+
const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
|
|
134
|
+
const englishWords = (text.match(/[a-zA-Z]+/g) || []).length;
|
|
135
|
+
const numbers = (text.match(/\d+/g) || []).length;
|
|
136
|
+
const sentences = (text.match(/[.!?。!?]+/g) || []).length;
|
|
137
|
+
return {
|
|
138
|
+
operation: 'stats',
|
|
139
|
+
characters: chars,
|
|
140
|
+
charactersNoSpaces: chars - (text.match(/\s/g) || []).length,
|
|
141
|
+
lines,
|
|
142
|
+
words,
|
|
143
|
+
sentences,
|
|
144
|
+
chineseCharacters: chineseChars,
|
|
145
|
+
englishWords,
|
|
146
|
+
numbers,
|
|
147
|
+
paragraphs: text.split(/\n\s*\n/).filter(p => p.trim()).length,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
default:
|
|
151
|
+
return { error: `不支持的操作: ${operation}` };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function encodeDecode(text, operation) {
|
|
155
|
+
try {
|
|
156
|
+
switch (operation) {
|
|
157
|
+
case 'base64_encode':
|
|
158
|
+
return {
|
|
159
|
+
operation,
|
|
160
|
+
result: Buffer.from(text, 'utf-8').toString('base64'),
|
|
161
|
+
};
|
|
162
|
+
case 'base64_decode':
|
|
163
|
+
return {
|
|
164
|
+
operation,
|
|
165
|
+
result: Buffer.from(text, 'base64').toString('utf-8'),
|
|
166
|
+
};
|
|
167
|
+
case 'url_encode':
|
|
168
|
+
return { operation, result: encodeURIComponent(text) };
|
|
169
|
+
case 'url_decode':
|
|
170
|
+
return { operation, result: decodeURIComponent(text) };
|
|
171
|
+
case 'unicode_escape':
|
|
172
|
+
return {
|
|
173
|
+
operation,
|
|
174
|
+
result: text.replace(/[^\x20-\x7E]/g, ch => `\\u${ch
|
|
175
|
+
.charCodeAt(0)
|
|
176
|
+
.toString(16)
|
|
177
|
+
.padStart(4, '0')}`),
|
|
178
|
+
};
|
|
179
|
+
case 'unicode_unescape':
|
|
180
|
+
return {
|
|
181
|
+
operation,
|
|
182
|
+
result: text.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))),
|
|
183
|
+
};
|
|
184
|
+
case 'hex_encode':
|
|
185
|
+
return {
|
|
186
|
+
operation,
|
|
187
|
+
result: Buffer.from(text, 'utf-8').toString('hex'),
|
|
188
|
+
};
|
|
189
|
+
case 'hex_decode':
|
|
190
|
+
return {
|
|
191
|
+
operation,
|
|
192
|
+
result: Buffer.from(text, 'hex').toString('utf-8'),
|
|
193
|
+
};
|
|
194
|
+
case 'html_escape':
|
|
195
|
+
return {
|
|
196
|
+
operation,
|
|
197
|
+
result: text
|
|
198
|
+
.replace(/&/g, '&')
|
|
199
|
+
.replace(/</g, '<')
|
|
200
|
+
.replace(/>/g, '>')
|
|
201
|
+
.replace(/"/g, '"')
|
|
202
|
+
.replace(/'/g, '''),
|
|
203
|
+
};
|
|
204
|
+
case 'html_unescape':
|
|
205
|
+
return {
|
|
206
|
+
operation,
|
|
207
|
+
result: text
|
|
208
|
+
.replace(/&/g, '&')
|
|
209
|
+
.replace(/</g, '<')
|
|
210
|
+
.replace(/>/g, '>')
|
|
211
|
+
.replace(/"/g, '"')
|
|
212
|
+
.replace(/'/g, "'"),
|
|
213
|
+
};
|
|
214
|
+
default:
|
|
215
|
+
return {
|
|
216
|
+
operation,
|
|
217
|
+
result: '',
|
|
218
|
+
error: `不支持的编解码操作: ${operation}`,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
return {
|
|
224
|
+
operation,
|
|
225
|
+
result: '',
|
|
226
|
+
error: `${operation} 失败: ${e.message}`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// ===================== 工具定义 =====================
|
|
231
|
+
/** 文本处理工具 */
|
|
232
|
+
exports.textProcessTool = {
|
|
233
|
+
id: 'text_process',
|
|
234
|
+
name: '文本处理',
|
|
235
|
+
description: '对文本执行各种处理操作。支持:match(正则匹配)、replace(正则替换)、extract(提取邮箱/URL/电话等)、split(分割)、stats(文本统计:字数/行数/词数等)。可使用内置模板(email/url/phone_cn/ipv4/date_iso/number/chinese)或自定义正则。',
|
|
236
|
+
category: 'utility',
|
|
237
|
+
parameters: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
text: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
description: '待处理的文本内容',
|
|
243
|
+
},
|
|
244
|
+
operation: {
|
|
245
|
+
type: 'string',
|
|
246
|
+
description: '操作类型:match(正则匹配)、replace(替换)、extract(提取)、split(分割)、stats(统计)',
|
|
247
|
+
enum: ['match', 'replace', 'extract', 'split', 'stats'],
|
|
248
|
+
},
|
|
249
|
+
pattern: {
|
|
250
|
+
type: 'string',
|
|
251
|
+
description: '正则表达式或分隔符。extract 操作时也可使用内置模板名:email/url/phone_cn/ipv4/date_iso/number/chinese',
|
|
252
|
+
},
|
|
253
|
+
replacement: {
|
|
254
|
+
type: 'string',
|
|
255
|
+
description: '替换字符串(replace 操作使用),支持 $1、$2 等捕获组引用',
|
|
256
|
+
},
|
|
257
|
+
flags: {
|
|
258
|
+
type: 'string',
|
|
259
|
+
description: '正则标志,如 g(全局)、i(忽略大小写)、m(多行)。默认 g',
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
required: ['text', 'operation'],
|
|
263
|
+
},
|
|
264
|
+
async execute(input) {
|
|
265
|
+
const text = input.text || '';
|
|
266
|
+
if (!text)
|
|
267
|
+
return { error: 'text 参数不能为空' };
|
|
268
|
+
return processText(text, input.operation, input.pattern, input.replacement, input.flags);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
/** 编解码工具 */
|
|
272
|
+
exports.encodeDecodeTool = {
|
|
273
|
+
id: 'encode_decode',
|
|
274
|
+
name: '编码/解码',
|
|
275
|
+
description: '对文本执行编码或解码操作。支持:Base64 编解码、URL 编解码、Unicode 转义/反转义、Hex 编解码、HTML 实体转义/反转义。',
|
|
276
|
+
category: 'utility',
|
|
277
|
+
parameters: {
|
|
278
|
+
type: 'object',
|
|
279
|
+
properties: {
|
|
280
|
+
text: {
|
|
281
|
+
type: 'string',
|
|
282
|
+
description: '待编码/解码的文本',
|
|
283
|
+
},
|
|
284
|
+
operation: {
|
|
285
|
+
type: 'string',
|
|
286
|
+
description: '编解码操作类型',
|
|
287
|
+
enum: [
|
|
288
|
+
'base64_encode',
|
|
289
|
+
'base64_decode',
|
|
290
|
+
'url_encode',
|
|
291
|
+
'url_decode',
|
|
292
|
+
'unicode_escape',
|
|
293
|
+
'unicode_unescape',
|
|
294
|
+
'hex_encode',
|
|
295
|
+
'hex_decode',
|
|
296
|
+
'html_escape',
|
|
297
|
+
'html_unescape',
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
required: ['text', 'operation'],
|
|
302
|
+
},
|
|
303
|
+
async execute(input) {
|
|
304
|
+
const text = input.text || '';
|
|
305
|
+
return encodeDecode(text, input.operation);
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
/** Hash 计算工具 */
|
|
309
|
+
exports.computeHashTool = {
|
|
310
|
+
id: 'compute_hash',
|
|
311
|
+
name: '计算 Hash',
|
|
312
|
+
description: '计算文本的哈希摘要值。支持 MD5、SHA1、SHA256、SHA512 算法。可用于数据完整性校验、签名生成等场景。可选输出 Hex 或 Base64 格式。',
|
|
313
|
+
category: 'utility',
|
|
314
|
+
parameters: {
|
|
315
|
+
type: 'object',
|
|
316
|
+
properties: {
|
|
317
|
+
text: {
|
|
318
|
+
type: 'string',
|
|
319
|
+
description: '待计算 Hash 的文本内容',
|
|
320
|
+
},
|
|
321
|
+
algorithm: {
|
|
322
|
+
type: 'string',
|
|
323
|
+
description: '哈希算法',
|
|
324
|
+
enum: ['md5', 'sha1', 'sha256', 'sha512'],
|
|
325
|
+
default: 'sha256',
|
|
326
|
+
},
|
|
327
|
+
encoding: {
|
|
328
|
+
type: 'string',
|
|
329
|
+
description: '输出编码格式:hex(默认)或 base64',
|
|
330
|
+
enum: ['hex', 'base64'],
|
|
331
|
+
default: 'hex',
|
|
332
|
+
},
|
|
333
|
+
secret: {
|
|
334
|
+
type: 'string',
|
|
335
|
+
description: '可选的密钥,提供时将计算 HMAC 而非普通 Hash',
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
required: ['text'],
|
|
339
|
+
},
|
|
340
|
+
async execute(input) {
|
|
341
|
+
const algorithm = input.algorithm || 'sha256';
|
|
342
|
+
const encoding = (input.encoding ||
|
|
343
|
+
'hex');
|
|
344
|
+
const text = input.text || '';
|
|
345
|
+
try {
|
|
346
|
+
let hash;
|
|
347
|
+
if (input.secret) {
|
|
348
|
+
hash = crypto.createHmac(algorithm, input.secret);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
hash = crypto.createHash(algorithm);
|
|
352
|
+
}
|
|
353
|
+
hash.update(text, 'utf-8');
|
|
354
|
+
const digest = hash.digest(encoding);
|
|
355
|
+
return {
|
|
356
|
+
algorithm: input.secret ? `hmac-${algorithm}` : algorithm,
|
|
357
|
+
encoding,
|
|
358
|
+
hash: digest,
|
|
359
|
+
inputLength: text.length,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
return { error: `Hash 计算失败: ${e.message}` };
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../types';
|
|
2
|
+
/** 网络搜索结果 */
|
|
3
|
+
interface SearchResult {
|
|
4
|
+
title: string;
|
|
5
|
+
url: string;
|
|
6
|
+
snippet: string;
|
|
7
|
+
publishedAt?: string;
|
|
8
|
+
source?: string;
|
|
9
|
+
}
|
|
10
|
+
/** 网络搜索工具 */
|
|
11
|
+
export declare const webSearchTool: ToolDefinition<{
|
|
12
|
+
query: string;
|
|
13
|
+
maxResults?: number;
|
|
14
|
+
timeRange?: string;
|
|
15
|
+
}, {
|
|
16
|
+
query: string;
|
|
17
|
+
total: number;
|
|
18
|
+
results: SearchResult[];
|
|
19
|
+
}>;
|
|
20
|
+
/** 获取网页内容 */
|
|
21
|
+
export declare const fetchWebPageTool: ToolDefinition<{
|
|
22
|
+
url: string;
|
|
23
|
+
}, {
|
|
24
|
+
url: string;
|
|
25
|
+
title: string;
|
|
26
|
+
content: string;
|
|
27
|
+
}>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchWebPageTool = exports.webSearchTool = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* 网络搜索工具
|
|
9
|
+
*
|
|
10
|
+
* 让 Agent 能够搜索互联网信息(预留接口,可对接各类搜索 API)。
|
|
11
|
+
*
|
|
12
|
+
* 工具说明:
|
|
13
|
+
* - web_search:搜索互联网获取最新信息、新闻、研究报告
|
|
14
|
+
* - fetch_web_page:获取指定 URL 的网页正文内容
|
|
15
|
+
*
|
|
16
|
+
* 对接方案(待实现):
|
|
17
|
+
* - SerpAPI / Tavily / Bing Search API —— 用于 web_search
|
|
18
|
+
* - Cheerio / Puppeteer / Jina Reader API —— 用于 fetch_web_page
|
|
19
|
+
*
|
|
20
|
+
* 当前状态:返回模拟数据,接入外部 API 后替换 execute 实现。
|
|
21
|
+
*/
|
|
22
|
+
const axios_1 = __importDefault(require("axios"));
|
|
23
|
+
/** 网络搜索工具 */
|
|
24
|
+
exports.webSearchTool = {
|
|
25
|
+
id: 'web_search',
|
|
26
|
+
name: '网络搜索',
|
|
27
|
+
description: '搜索互联网获取最新信息、新闻、研究报告等。当需要实时数据或外部知识时使用此工具。',
|
|
28
|
+
category: 'search',
|
|
29
|
+
parameters: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
query: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: '搜索关键词',
|
|
35
|
+
},
|
|
36
|
+
maxResults: {
|
|
37
|
+
type: 'integer',
|
|
38
|
+
description: '最大返回结果数,默认 5,最大 20',
|
|
39
|
+
minimum: 1,
|
|
40
|
+
maximum: 20,
|
|
41
|
+
default: 5,
|
|
42
|
+
},
|
|
43
|
+
timeRange: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: '时间范围筛选: day/week/month/year/any',
|
|
46
|
+
enum: ['day', 'week', 'month', 'year', 'any'],
|
|
47
|
+
default: 'any',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ['query'],
|
|
51
|
+
},
|
|
52
|
+
async execute(input) {
|
|
53
|
+
const provider = process.env.SEARCH_PROVIDER || 'bing';
|
|
54
|
+
const apiKey = process.env.SEARCH_API_KEY || '';
|
|
55
|
+
const query = input.query.trim();
|
|
56
|
+
const maxResults = Math.min(Math.max(Number(input.maxResults) || 5, 1), 20);
|
|
57
|
+
console.log(`[WebSearch] 搜索: "${query}" (provider=${provider}, maxResults=${maxResults})`);
|
|
58
|
+
if (!apiKey && provider !== 'searxng') {
|
|
59
|
+
console.warn(`[WebSearch] 未配置 SEARCH_API_KEY,provider=${provider} 需要 API 密钥`);
|
|
60
|
+
return {
|
|
61
|
+
query,
|
|
62
|
+
total: 0,
|
|
63
|
+
results: [],
|
|
64
|
+
hint: `搜索功能未配置。请设置环境变量 SEARCH_API_KEY 和 SEARCH_PROVIDER(可选值: tavily/serpapi/bing/searxng)。当前 provider=${provider}。`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
switch (provider) {
|
|
69
|
+
case 'tavily':
|
|
70
|
+
return await searchTavily(query, maxResults, input.timeRange, apiKey);
|
|
71
|
+
case 'serpapi':
|
|
72
|
+
return await searchSerpApi(query, maxResults, apiKey);
|
|
73
|
+
case 'bing':
|
|
74
|
+
return await searchBing(query, maxResults, apiKey);
|
|
75
|
+
case 'searxng':
|
|
76
|
+
return await searchSearxng(query, maxResults, process.env.SEARCH_BASE_URL);
|
|
77
|
+
default:
|
|
78
|
+
return {
|
|
79
|
+
query,
|
|
80
|
+
total: 0,
|
|
81
|
+
results: [],
|
|
82
|
+
hint: `未知的搜索 provider: ${provider}。支持的值: tavily, serpapi, bing, searxng。`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
const message = error?.response?.data?.message ||
|
|
88
|
+
error?.message ||
|
|
89
|
+
String(error);
|
|
90
|
+
console.error(`[WebSearch] ${provider} 搜索失败:`, message);
|
|
91
|
+
return {
|
|
92
|
+
query,
|
|
93
|
+
total: 0,
|
|
94
|
+
results: [],
|
|
95
|
+
error: `搜索失败: ${message}`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
// ===================== 搜索后端实现 =====================
|
|
101
|
+
/** Tavily 搜索 —— 专为 AI Agent 设计的高质量搜索 API */
|
|
102
|
+
async function searchTavily(query, maxResults, timeRange, apiKey) {
|
|
103
|
+
const response = await axios_1.default.post('https://api.tavily.com/search', {
|
|
104
|
+
query,
|
|
105
|
+
search_depth: 'advanced',
|
|
106
|
+
include_answer: false,
|
|
107
|
+
include_images: false,
|
|
108
|
+
max_results: maxResults,
|
|
109
|
+
time_range: timeRange && timeRange !== 'any' ? timeRange : undefined,
|
|
110
|
+
}, {
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: `Bearer ${apiKey}`,
|
|
113
|
+
'Content-Type': 'application/json',
|
|
114
|
+
},
|
|
115
|
+
timeout: 30000,
|
|
116
|
+
});
|
|
117
|
+
const results = (response.data?.results || []).map((r) => ({
|
|
118
|
+
title: r.title || '',
|
|
119
|
+
url: r.url || '',
|
|
120
|
+
snippet: r.content || r.snippet || '',
|
|
121
|
+
publishedAt: r.published_date,
|
|
122
|
+
source: new URL(r.url).hostname,
|
|
123
|
+
}));
|
|
124
|
+
return { query, total: results.length, results };
|
|
125
|
+
}
|
|
126
|
+
/** SerpAPI —— Google 搜索结果 */
|
|
127
|
+
async function searchSerpApi(query, maxResults, apiKey) {
|
|
128
|
+
const response = await axios_1.default.get('https://serpapi.com/search', {
|
|
129
|
+
params: {
|
|
130
|
+
q: query,
|
|
131
|
+
engine: 'google',
|
|
132
|
+
api_key: apiKey,
|
|
133
|
+
num: maxResults,
|
|
134
|
+
},
|
|
135
|
+
timeout: 30000,
|
|
136
|
+
});
|
|
137
|
+
const organic = response.data?.organic_results || [];
|
|
138
|
+
const results = organic
|
|
139
|
+
.slice(0, maxResults)
|
|
140
|
+
.map((r) => ({
|
|
141
|
+
title: r.title || '',
|
|
142
|
+
url: r.link || r.url || '',
|
|
143
|
+
snippet: r.snippet || '',
|
|
144
|
+
publishedAt: r.date,
|
|
145
|
+
source: new URL(r.link || r.url || 'http://example.com').hostname,
|
|
146
|
+
}));
|
|
147
|
+
return { query, total: results.length, results };
|
|
148
|
+
}
|
|
149
|
+
/** Bing Search API */
|
|
150
|
+
async function searchBing(query, maxResults, apiKey) {
|
|
151
|
+
const response = await axios_1.default.get('https://api.bing.microsoft.com/v7.0/search', {
|
|
152
|
+
headers: { 'Ocp-Apim-Subscription-Key': apiKey },
|
|
153
|
+
params: { q: query, count: maxResults, mkt: 'zh-CN' },
|
|
154
|
+
timeout: 30000,
|
|
155
|
+
});
|
|
156
|
+
const items = response.data?.webPages?.value || [];
|
|
157
|
+
const results = items.map((r) => ({
|
|
158
|
+
title: r.name || '',
|
|
159
|
+
url: r.url || '',
|
|
160
|
+
snippet: r.snippet || '',
|
|
161
|
+
source: new URL(r.url || 'http://example.com').hostname,
|
|
162
|
+
}));
|
|
163
|
+
return { query, total: results.length, results };
|
|
164
|
+
}
|
|
165
|
+
/** SearXNG —— 自托管搜索聚合器(无需 API Key) */
|
|
166
|
+
async function searchSearxng(query, maxResults, baseUrl) {
|
|
167
|
+
if (!baseUrl) {
|
|
168
|
+
throw new Error('使用 searxng 搜索必须配置 SEARCH_BASE_URL 环境变量');
|
|
169
|
+
}
|
|
170
|
+
const response = await axios_1.default.get(`${baseUrl.replace(/\/$/, '')}/search`, {
|
|
171
|
+
params: { q: query, format: 'json', pageno: 1 },
|
|
172
|
+
timeout: 30000,
|
|
173
|
+
});
|
|
174
|
+
const items = (response.data?.results || []).slice(0, maxResults);
|
|
175
|
+
const results = items.map((r) => ({
|
|
176
|
+
title: r.title || '',
|
|
177
|
+
url: r.url || '',
|
|
178
|
+
snippet: r.content || r.snippet || '',
|
|
179
|
+
publishedAt: r.publishedDate,
|
|
180
|
+
source: r.engine || new URL(r.url || 'http://example.com').hostname,
|
|
181
|
+
}));
|
|
182
|
+
return { query, total: results.length, results };
|
|
183
|
+
}
|
|
184
|
+
// ===================== 网页抓取后端实现 =====================
|
|
185
|
+
/** Jina Reader —— 免费网页内容提取(无需 API Key) */
|
|
186
|
+
async function fetchJinaReader(url) {
|
|
187
|
+
const response = await axios_1.default.get(`https://r.jina.ai/http://${url.replace(/^https?:\/\//, '')}`, {
|
|
188
|
+
headers: { Accept: 'text/plain' },
|
|
189
|
+
timeout: 30000,
|
|
190
|
+
});
|
|
191
|
+
const text = String(response.data || '');
|
|
192
|
+
const lines = text.split('\n').filter(Boolean);
|
|
193
|
+
const title = lines[0] || '';
|
|
194
|
+
const content = lines.slice(1).join('\n').trim();
|
|
195
|
+
return { url, title, content };
|
|
196
|
+
}
|
|
197
|
+
/** Firecrawl —— 高质量网页抓取服务 */
|
|
198
|
+
async function fetchFirecrawl(url, apiKey) {
|
|
199
|
+
if (!apiKey) {
|
|
200
|
+
throw new Error('使用 firecrawl 抓取必须配置 FIRECRAWL_API_KEY 环境变量');
|
|
201
|
+
}
|
|
202
|
+
const response = await axios_1.default.post('https://api.firecrawl.dev/v1/scrape', { url, formats: ['markdown'] }, {
|
|
203
|
+
headers: {
|
|
204
|
+
Authorization: `Bearer ${apiKey}`,
|
|
205
|
+
'Content-Type': 'application/json',
|
|
206
|
+
},
|
|
207
|
+
timeout: 60000,
|
|
208
|
+
});
|
|
209
|
+
const data = response.data?.data || {};
|
|
210
|
+
return {
|
|
211
|
+
url,
|
|
212
|
+
title: data.metadata?.title || '',
|
|
213
|
+
content: data.markdown || data.content || '',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** Cheerio —— 服务器端网页解析(Node.js 环境) */
|
|
217
|
+
async function fetchCheerio(url) {
|
|
218
|
+
// 动态导入 cheerio,避免未安装时的硬依赖
|
|
219
|
+
let cheerio;
|
|
220
|
+
try {
|
|
221
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
222
|
+
const mod = require('cheerio');
|
|
223
|
+
cheerio = mod;
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
throw new Error('cheerio 未安装。请运行 npm install cheerio 后重试。');
|
|
227
|
+
}
|
|
228
|
+
const response = await axios_1.default.get(url, {
|
|
229
|
+
headers: {
|
|
230
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.0',
|
|
231
|
+
},
|
|
232
|
+
timeout: 30000,
|
|
233
|
+
});
|
|
234
|
+
const $ = cheerio.load(response.data);
|
|
235
|
+
// 移除脚本、样式、导航等噪音元素
|
|
236
|
+
$('script, style, nav, header, footer, aside, .advertisement, .ads').remove();
|
|
237
|
+
const title = $('title').text().trim() || $('h1').first().text().trim() || '';
|
|
238
|
+
// 优先取 article 或 main,否则取 body 中的段落
|
|
239
|
+
const articleText = $('article, main').first().text();
|
|
240
|
+
const bodyText = $('body').text();
|
|
241
|
+
const content = (articleText.length > 50 ? articleText : bodyText)
|
|
242
|
+
.replace(/\s+/g, ' ')
|
|
243
|
+
.trim();
|
|
244
|
+
return { url, title, content };
|
|
245
|
+
}
|
|
246
|
+
/** 获取网页内容 */
|
|
247
|
+
exports.fetchWebPageTool = {
|
|
248
|
+
id: 'fetch_web_page',
|
|
249
|
+
name: '获取网页内容',
|
|
250
|
+
description: '获取指定 URL 的网页正文内容。用于深入阅读搜索结果中的某篇文章。',
|
|
251
|
+
category: 'search',
|
|
252
|
+
parameters: {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
url: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: '网页 URL 地址',
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
required: ['url'],
|
|
261
|
+
},
|
|
262
|
+
async execute(input) {
|
|
263
|
+
const url = input.url.trim();
|
|
264
|
+
const provider = process.env.WEB_FETCH_PROVIDER || 'jina';
|
|
265
|
+
console.log(`[FetchWebPage] 获取: ${url} (provider=${provider})`);
|
|
266
|
+
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
267
|
+
return {
|
|
268
|
+
url,
|
|
269
|
+
title: '',
|
|
270
|
+
content: '',
|
|
271
|
+
error: '无效的 URL,必须以 http:// 或 https:// 开头',
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
switch (provider) {
|
|
276
|
+
case 'jina':
|
|
277
|
+
return await fetchJinaReader(url);
|
|
278
|
+
case 'firecrawl':
|
|
279
|
+
return await fetchFirecrawl(url, process.env.FIRECRAWL_API_KEY || '');
|
|
280
|
+
case 'cheerio':
|
|
281
|
+
return await fetchCheerio(url);
|
|
282
|
+
default:
|
|
283
|
+
return {
|
|
284
|
+
url,
|
|
285
|
+
title: '',
|
|
286
|
+
content: '',
|
|
287
|
+
hint: `未知的抓取 provider: ${provider}。支持的值: jina, firecrawl, cheerio。`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
const message = error?.response?.data?.message ||
|
|
293
|
+
error?.message ||
|
|
294
|
+
String(error);
|
|
295
|
+
console.error('[FetchWebPage] 抓取失败:', message);
|
|
296
|
+
return {
|
|
297
|
+
url,
|
|
298
|
+
title: '',
|
|
299
|
+
content: '',
|
|
300
|
+
error: `网页抓取失败: ${message}`,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
};
|