deepfish-ai 1.0.29 → 2.0.2
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 +126 -368
- package/dist/index.js +23320 -0
- package/dist/serve/client/assets/index-DUHYC91l.js +1 -0
- package/dist/serve/client/assets/index-zcrdNLZs.css +1 -0
- package/dist/serve/client/index.html +14 -0
- package/dist/serve/pm2-server.js +21015 -0
- package/dist/serve/server/entry-server.mjs +224 -0
- package/package.json +61 -83
- package/LICENSE +0 -21
- package/README_CN.md +0 -460
- package/src/AgentRobot/AgentRobotFactory/MainAgentRobot.js +0 -88
- package/src/AgentRobot/AgentRobotFactory/SubAgentRobot.js +0 -45
- package/src/AgentRobot/AgentRobotFactory/SubSkillAgentRobot.js +0 -45
- package/src/AgentRobot/AgentRobotFactory/index.js +0 -48
- package/src/AgentRobot/BaseAgentRobot/Brain.js +0 -210
- package/src/AgentRobot/BaseAgentRobot/BrainEvent.js +0 -17
- package/src/AgentRobot/BaseAgentRobot/Hand.js +0 -119
- package/src/AgentRobot/BaseAgentRobot/Heart.js +0 -0
- package/src/AgentRobot/BaseAgentRobot/Logger.js +0 -114
- package/src/AgentRobot/BaseAgentRobot/ScreenPrinter.js +0 -49
- package/src/AgentRobot/BaseAgentRobot/index.js +0 -266
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/AliBailian.js +0 -997
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/doc-transform.js +0 -204
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/docx.js +0 -1286
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/img.js +0 -765
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/pdf.js +0 -736
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/pptx.js +0 -567
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/video.js +0 -36
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/xlsx.js +0 -611
- package/src/AgentRobot/BaseAgentRobot/tools/BaseTools.js +0 -13
- package/src/AgentRobot/BaseAgentRobot/tools/CreateAgentTools.js +0 -158
- package/src/AgentRobot/BaseAgentRobot/tools/FileTools.js +0 -768
- package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +0 -441
- package/src/AgentRobot/BaseAgentRobot/tools/InquirerTools.js +0 -244
- package/src/AgentRobot/BaseAgentRobot/tools/SystemTools.js +0 -424
- package/src/AgentRobot/BaseAgentRobot/tools/TaskTools.js +0 -211
- package/src/AgentRobot/BaseAgentRobot/tools/TestTools.js +0 -100
- package/src/AgentRobot/BaseAgentRobot/tools/UserTool.js +0 -87
- package/src/AgentRobot/BaseAgentRobot/tools/WebTools.js +0 -257
- package/src/AgentRobot/BaseAgentRobot/utils/AIRequest.js +0 -297
- package/src/AgentRobot/BaseAgentRobot/utils/AIToolManager.js +0 -131
- package/src/AgentRobot/BaseAgentRobot/utils/AgentTree.js +0 -66
- package/src/AgentRobot/BaseAgentRobot/utils/AttachmentToolScanner.js +0 -235
- package/src/AgentRobot/BaseAgentRobot/utils/MessageCompresser.js +0 -236
- package/src/AgentRobot/BaseAgentRobot/utils/aiConsole.js +0 -74
- package/src/AgentRobot/BaseAgentRobot/utils/aiInquirer.js +0 -78
- package/src/AgentRobot/BaseAgentRobot/utils/analyzeReturn.test.js +0 -104
- package/src/AgentRobot/BaseAgentRobot/utils/copilot.js +0 -117
- package/src/AgentRobot/BaseAgentRobot/utils/node-root.js +0 -138
- package/src/AgentRobot/BaseAgentRobot/utils/normal.js +0 -399
- package/src/cli/ConfigManager.js +0 -283
- package/src/cli/DefaultConfig.js +0 -98
- package/src/cli/GlobalVariable.js +0 -8
- package/src/cli/MemoryManager.js +0 -53
- package/src/cli/SkillConfigManager.js +0 -354
- package/src/cli/SkillParser.js +0 -58
- package/src/cli/ai-auth.js +0 -231
- package/src/cli/ai-config.js +0 -225
- package/src/cli/ai-memory.js +0 -22
- package/src/cli/ai-skill.js +0 -65
- package/src/cli/index.js +0 -77
- package/src/index.js +0 -54
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// 执行测试任务
|
|
2
|
-
function executeTestTask(subGoalPrompt = "") {
|
|
3
|
-
// 调用子工作流完成目标
|
|
4
|
-
return this.Tools.createSubAgent(subGoalPrompt)
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// 生成测试任务
|
|
8
|
-
function generateTestTaskRule(goal) {
|
|
9
|
-
return `
|
|
10
|
-
你是资深测试工程师。用户测试任务目标如下:${goal}
|
|
11
|
-
|
|
12
|
-
请在当前目录下生成一个 Markdown 格式的“程序功能测试说明文档”,并严格遵循以下要求:
|
|
13
|
-
1. 文档标题
|
|
14
|
-
- 一级标题固定格式:# xxx测试任务
|
|
15
|
-
- 其中“xxx”必须与用户提供的测试任务名称保持一致,不得改写。
|
|
16
|
-
2. 先分析再输出
|
|
17
|
-
- 在正文最前面先给出“测试范围与模块拆分”小节,明确:
|
|
18
|
-
- 待测功能模块
|
|
19
|
-
- 核心测试点
|
|
20
|
-
- 不在本轮测试范围的内容(如有)
|
|
21
|
-
- 然后再输出详细测试用例。
|
|
22
|
-
3. 测试用例组织方式
|
|
23
|
-
- 按“功能模块”分组(每个模块一个二级标题)。
|
|
24
|
-
- 模块内按优先级排序:P0 > P1 > P2。
|
|
25
|
-
- 每个用例必须有唯一编号,建议格式:TC-模块缩写-序号(如 TC-LOGIN-001)。
|
|
26
|
-
4. 每条测试用例必须包含以下字段(缺一不可)
|
|
27
|
-
- 用例编号
|
|
28
|
-
- 所属模块
|
|
29
|
-
- 优先级(P0/P1/P2)
|
|
30
|
-
- 测试场景
|
|
31
|
-
- 前置条件
|
|
32
|
-
- 测试数据
|
|
33
|
-
- 操作步骤(可逐条执行、无歧义、可落地)
|
|
34
|
-
- 预期结果(与步骤一一对应、可验证)
|
|
35
|
-
5. 覆盖性要求
|
|
36
|
-
- 必须覆盖:主流程、异常流程、边界场景。
|
|
37
|
-
- 不得遗漏核心功能,不得加入与目标无关的测试内容。
|
|
38
|
-
- 期望结果必须具体到可观察的页面状态、返回结果、数据变化或提示信息。
|
|
39
|
-
6. 输出格式要求
|
|
40
|
-
- 全文仅输出 Markdown 正文,不要输出解释性前言。
|
|
41
|
-
- 标题层级清晰:文档标题为一级、模块为二级、测试用例为三级(或有序列表)。
|
|
42
|
-
- 文档末尾增加“测试执行任务清单”章节,使用可勾选任务列表(- [ ])逐条列出所有测试用例,便于执行与追踪。
|
|
43
|
-
- 文档末尾增加“测试结果评估”章节,输出测试结果和未通过的测试用例,并评估评估测试结果的有效性、全面性、及时性。
|
|
44
|
-
|
|
45
|
-
请根据以上要求直接生成完整测试文档。
|
|
46
|
-
`}
|
|
47
|
-
|
|
48
|
-
const descriptions = [
|
|
49
|
-
{
|
|
50
|
-
type: "function",
|
|
51
|
-
function: {
|
|
52
|
-
name: "generateTestTaskRule",
|
|
53
|
-
description:
|
|
54
|
-
"根据用户提供的程序功能测试目标,生成一份标准化的Markdown格式功能测试说明文档的规则提示词。返回的提示词将指导AI生成包含完整测试用例(测试步骤、期望结果、用例编号)的测试文档。适用于用户需要对程序进行功能测试等场景。",
|
|
55
|
-
parameters: {
|
|
56
|
-
type: "object",
|
|
57
|
-
properties: {
|
|
58
|
-
goal: {
|
|
59
|
-
type: "string",
|
|
60
|
-
description: "程序功能测试目标描述,需明确待测试的功能模块、测试范围和测试重点",
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
required: ["goal"],
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
type: "function",
|
|
69
|
-
function: {
|
|
70
|
-
name: "executeTestTask",
|
|
71
|
-
description:
|
|
72
|
-
"执行测试任务,启动一个专用的AI子工作流来完成指定的某一项测试目标。子工作流将根据测试任务描述,自动执行测试步骤并验证测试结果。适用于需要AI自动化执行功能测试的场景。",
|
|
73
|
-
parameters: {
|
|
74
|
-
type: "object",
|
|
75
|
-
properties: {
|
|
76
|
-
subGoalPrompt: {
|
|
77
|
-
type: "string",
|
|
78
|
-
description: "测试子工作流需要完成的目标描述,描述具体的测试任务内容",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
required: ["subGoalPrompt"],
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
const functions = {
|
|
88
|
-
executeTestTask,
|
|
89
|
-
generateTestTaskRule,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const TestTools = {
|
|
93
|
-
name: 'TestTools',
|
|
94
|
-
extensionDescription: "提供程序功能测试任务的生成和执行功能,支持自动化测试工作流",
|
|
95
|
-
descriptions,
|
|
96
|
-
functions,
|
|
97
|
-
isSystem: true
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
module.exports = TestTools
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra')
|
|
2
|
-
|
|
3
|
-
function readUserInfo() {
|
|
4
|
-
const userInfoFilePath = this.agentRobot.userInfoFilePath
|
|
5
|
-
if (!fs.existsSync(userInfoFilePath)) {
|
|
6
|
-
return '暂无用户信息记录';
|
|
7
|
-
}
|
|
8
|
-
return fs.readFileSync(userInfoFilePath, 'utf-8') || '暂无用户信息记录'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function writeUserInfo(info) {
|
|
12
|
-
const userInfoFilePath = this.agentRobot.userInfoFilePath
|
|
13
|
-
const oldUserInfo = this.Tools.readUserInfo()
|
|
14
|
-
const normalizedOldUserInfo = oldUserInfo === '暂无用户信息记录' ? '' : oldUserInfo
|
|
15
|
-
let mergedInfo = (info || '').trim()
|
|
16
|
-
|
|
17
|
-
const systemDescription = `你是用户信息整理助手。请将已有用户信息与新增用户信息合并为一份Markdown文本,只保留非敏感信息并去重,优先保留更新、更准确的信息。记录内容应简洁、可检索、可复用。`
|
|
18
|
-
const prompt = `请整合以下两部分用户信息,输出最终Markdown内容(仅输出正文,不要解释):\n\n【已有用户信息】\n${normalizedOldUserInfo || '(空)'}\n\n【新增用户信息】\n${mergedInfo || '(空)'}\n\n要求:\n1. 仅保留非敏感信息。\n2. 相同信息去重,冲突时以新增信息为准。\n3. 不确定内容标注“待确认”。\n4. 按“信息类型: 信息内容”格式整理。`
|
|
19
|
-
|
|
20
|
-
if (this.Tools.requestAI && mergedInfo) {
|
|
21
|
-
try {
|
|
22
|
-
const aiMerged = await this.Tools.requestAI(systemDescription, prompt, 0.2)
|
|
23
|
-
if (typeof aiMerged === 'string' && aiMerged.trim()) {
|
|
24
|
-
mergedInfo = aiMerged.trim()
|
|
25
|
-
}
|
|
26
|
-
} catch (error) {
|
|
27
|
-
// AI合并失败时回退为直接写入新增信息,保证写入流程可用
|
|
28
|
-
mergedInfo = normalizedOldUserInfo + '\n' + info
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
fs.writeFileSync(userInfoFilePath, mergedInfo, 'utf-8')
|
|
32
|
-
// 更新系统提示词中的用户信息区块
|
|
33
|
-
this.agentRobot.systemPrompt = this.agentRobot.systemPrompt.replace(
|
|
34
|
-
/----user info start----([\s\S]*?)----user info end----/,
|
|
35
|
-
`----user info start----\n${mergedInfo}\n----user info end----`
|
|
36
|
-
)
|
|
37
|
-
return true
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const descriptions = [
|
|
41
|
-
{
|
|
42
|
-
type: 'function',
|
|
43
|
-
function: {
|
|
44
|
-
name: 'writeUserInfo',
|
|
45
|
-
description: '写入用户信息内容。记录规则:1. 仅记录非敏感信息:个人基础信息(如姓名、年龄、职业、兴趣、性格特征)、操作习惯、代码习惯、阅读习惯、常用目录、文档收藏夹目录等。2. 禁止记录敏感信息:密码、密钥、令牌、身份证号、银行卡号、详细住址、联系方式、精确定位、财务/医疗等个人隐私数据。3. 已有同类信息时优先更新,不重复堆叠;存在不确定信息时需标注“待确认”,不得臆测补全。4. 记录内容应简洁、可检索、可复用,避免写入一次性上下文和与任务无关的噪音信息。5. 无需询问,自动写入文件。6. 使用markdown文件格式记录,按照“信息类型: 信息内容”的格式进行记录,如“兴趣: 阅读、旅行、编程”。7. 只记录当前用户信息中没有的内容,避免重复记录相同信息。',
|
|
46
|
-
parameters: {
|
|
47
|
-
type: 'object',
|
|
48
|
-
properties: {
|
|
49
|
-
info: {
|
|
50
|
-
type: 'string',
|
|
51
|
-
description: '当前用户信息中没有的内容',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
required: ['info'],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
type: 'function',
|
|
60
|
-
function: {
|
|
61
|
-
name: 'readUserInfo',
|
|
62
|
-
description: '读取已记录的用户信息内容。如果文件不存在,则返回 "暂无用户信息记录"。',
|
|
63
|
-
parameters: {
|
|
64
|
-
type: 'object',
|
|
65
|
-
properties: {},
|
|
66
|
-
required: [],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
const functions = {
|
|
74
|
-
readUserInfo,
|
|
75
|
-
writeUserInfo,
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const UserTool = {
|
|
79
|
-
name: 'UserTool',
|
|
80
|
-
description: '提供用户信息读写功能,用于维护用户偏好、习惯和常用目录信息',
|
|
81
|
-
platform: 'all',
|
|
82
|
-
descriptions,
|
|
83
|
-
functions,
|
|
84
|
-
isSystem: true
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
module.exports = UserTool
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
const axios = require('axios')
|
|
2
|
-
const cheerio = require('cheerio')
|
|
3
|
-
const puppeteer = require('puppeteer')
|
|
4
|
-
|
|
5
|
-
function ok(data = null) {
|
|
6
|
-
return { success: true, data }
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function fail(error, data = null) {
|
|
10
|
-
return { success: false, error: error?.message || String(error), data }
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function normalizeWhitespace(text = '') {
|
|
14
|
-
return String(text || '').replace(/\s+/g, ' ').trim()
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function absoluteUrl(baseUrl, href = '') {
|
|
18
|
-
try {
|
|
19
|
-
return new URL(href, baseUrl).toString()
|
|
20
|
-
} catch {
|
|
21
|
-
return href
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function extractByCheerio(html = '', url = '') {
|
|
26
|
-
const $ = cheerio.load(html)
|
|
27
|
-
$('script, style, noscript').remove()
|
|
28
|
-
|
|
29
|
-
const title = normalizeWhitespace($('title').first().text())
|
|
30
|
-
const bodyText = normalizeWhitespace($('body').text())
|
|
31
|
-
const metaDescription = normalizeWhitespace(
|
|
32
|
-
$('meta[name="description"]').attr('content') ||
|
|
33
|
-
$('meta[property="og:description"]').attr('content') ||
|
|
34
|
-
'',
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const links = []
|
|
38
|
-
$('a[href]').each((_, el) => {
|
|
39
|
-
const href = $(el).attr('href') || ''
|
|
40
|
-
const text = normalizeWhitespace($(el).text())
|
|
41
|
-
if (!href) return
|
|
42
|
-
links.push({
|
|
43
|
-
href: absoluteUrl(url, href),
|
|
44
|
-
text,
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
title,
|
|
50
|
-
description: metaDescription,
|
|
51
|
-
content: bodyText,
|
|
52
|
-
links,
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function fetchStatic(url, timeout = 15000) {
|
|
57
|
-
const response = await axios.get(url, {
|
|
58
|
-
timeout,
|
|
59
|
-
headers: {
|
|
60
|
-
'User-Agent':
|
|
61
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130 Safari/537.36 DeepFish-MCP-Web/1.0',
|
|
62
|
-
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
return response.data
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function fetchDynamic(url, waitUntil = 'networkidle2', timeout = 30000) {
|
|
69
|
-
const browser = await puppeteer.launch({
|
|
70
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
71
|
-
})
|
|
72
|
-
try {
|
|
73
|
-
const page = await browser.newPage()
|
|
74
|
-
await page.goto(url, { waitUntil, timeout })
|
|
75
|
-
return await page.content()
|
|
76
|
-
} finally {
|
|
77
|
-
await browser.close()
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function mcpBrowseWebpage(url, mode = 'auto', maxChars = 4000) {
|
|
82
|
-
try {
|
|
83
|
-
if (!url) {
|
|
84
|
-
return fail('url is required')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let html = ''
|
|
88
|
-
let resolvedMode = mode
|
|
89
|
-
|
|
90
|
-
if (mode === 'dynamic') {
|
|
91
|
-
html = await fetchDynamic(url)
|
|
92
|
-
} else if (mode === 'static') {
|
|
93
|
-
html = await fetchStatic(url)
|
|
94
|
-
} else {
|
|
95
|
-
try {
|
|
96
|
-
html = await fetchStatic(url)
|
|
97
|
-
resolvedMode = 'static'
|
|
98
|
-
} catch {
|
|
99
|
-
html = await fetchDynamic(url)
|
|
100
|
-
resolvedMode = 'dynamic'
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const parsed = extractByCheerio(html, url)
|
|
105
|
-
const content = parsed.content.slice(0, Number(maxChars) > 0 ? Number(maxChars) : 4000)
|
|
106
|
-
const links = parsed.links.slice(0, 50)
|
|
107
|
-
|
|
108
|
-
return ok({
|
|
109
|
-
url,
|
|
110
|
-
mode: resolvedMode,
|
|
111
|
-
title: parsed.title,
|
|
112
|
-
description: parsed.description,
|
|
113
|
-
content,
|
|
114
|
-
contentLength: parsed.content.length,
|
|
115
|
-
links,
|
|
116
|
-
linkCount: parsed.links.length,
|
|
117
|
-
})
|
|
118
|
-
} catch (error) {
|
|
119
|
-
return fail(error, { url, mode, maxChars })
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function mcpFetchWebpageByQuery(url, query, mode = 'auto', limit = 20) {
|
|
124
|
-
try {
|
|
125
|
-
if (!url) {
|
|
126
|
-
return fail('url is required')
|
|
127
|
-
}
|
|
128
|
-
if (!query) {
|
|
129
|
-
return fail('query is required')
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const browseResult = await mcpBrowseWebpage(url, mode, 200000)
|
|
133
|
-
if (!browseResult.success) {
|
|
134
|
-
return browseResult
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const keyword = String(query).toLowerCase().trim()
|
|
138
|
-
const rawContent = browseResult.data.content || ''
|
|
139
|
-
const sentences = rawContent
|
|
140
|
-
.split(/[。!?.!?\n]/)
|
|
141
|
-
.map((item) => normalizeWhitespace(item))
|
|
142
|
-
.filter(Boolean)
|
|
143
|
-
|
|
144
|
-
const matchedSentences = sentences
|
|
145
|
-
.map((text) => {
|
|
146
|
-
const lower = text.toLowerCase()
|
|
147
|
-
let score = 0
|
|
148
|
-
let fromIndex = 0
|
|
149
|
-
while (true) {
|
|
150
|
-
const idx = lower.indexOf(keyword, fromIndex)
|
|
151
|
-
if (idx < 0) break
|
|
152
|
-
score += 1
|
|
153
|
-
fromIndex = idx + keyword.length
|
|
154
|
-
}
|
|
155
|
-
return { text, score }
|
|
156
|
-
})
|
|
157
|
-
.filter((item) => item.score > 0)
|
|
158
|
-
.sort((a, b) => b.score - a.score)
|
|
159
|
-
.slice(0, Number(limit) > 0 ? Number(limit) : 20)
|
|
160
|
-
|
|
161
|
-
const matchedLinks = (browseResult.data.links || [])
|
|
162
|
-
.filter((item) => {
|
|
163
|
-
const href = String(item.href || '').toLowerCase()
|
|
164
|
-
const text = String(item.text || '').toLowerCase()
|
|
165
|
-
return href.includes(keyword) || text.includes(keyword)
|
|
166
|
-
})
|
|
167
|
-
.slice(0, Number(limit) > 0 ? Number(limit) : 20)
|
|
168
|
-
|
|
169
|
-
return ok({
|
|
170
|
-
url,
|
|
171
|
-
query,
|
|
172
|
-
mode: browseResult.data.mode,
|
|
173
|
-
title: browseResult.data.title,
|
|
174
|
-
matchedSentenceCount: matchedSentences.length,
|
|
175
|
-
matchedLinkCount: matchedLinks.length,
|
|
176
|
-
matchedSentences,
|
|
177
|
-
matchedLinks,
|
|
178
|
-
})
|
|
179
|
-
} catch (error) {
|
|
180
|
-
return fail(error, { url, query, mode, limit })
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const descriptions = [
|
|
185
|
-
{
|
|
186
|
-
type: 'function',
|
|
187
|
-
function: {
|
|
188
|
-
name: 'mcpBrowseWebpage',
|
|
189
|
-
description:
|
|
190
|
-
'MCP网页浏览: 打开并抓取任意网页的标题、摘要正文与链接列表。适合快速浏览网页内容。',
|
|
191
|
-
parameters: {
|
|
192
|
-
type: 'object',
|
|
193
|
-
properties: {
|
|
194
|
-
url: {
|
|
195
|
-
type: 'string',
|
|
196
|
-
description: '要浏览的网页地址(http或https)。',
|
|
197
|
-
},
|
|
198
|
-
mode: {
|
|
199
|
-
type: 'string',
|
|
200
|
-
description: '抓取模式:auto|static|dynamic。默认auto。',
|
|
201
|
-
},
|
|
202
|
-
maxChars: {
|
|
203
|
-
type: 'number',
|
|
204
|
-
description: '返回正文最大字符数,默认4000。',
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
required: ['url'],
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
type: 'function',
|
|
213
|
-
function: {
|
|
214
|
-
name: 'mcpFetchWebpageByQuery',
|
|
215
|
-
description:
|
|
216
|
-
'MCP网页抓取: 按关键词从网页正文和链接中提取匹配内容,返回高相关片段。',
|
|
217
|
-
parameters: {
|
|
218
|
-
type: 'object',
|
|
219
|
-
properties: {
|
|
220
|
-
url: {
|
|
221
|
-
type: 'string',
|
|
222
|
-
description: '目标网页地址(http或https)。',
|
|
223
|
-
},
|
|
224
|
-
query: {
|
|
225
|
-
type: 'string',
|
|
226
|
-
description: '要匹配的关键词。',
|
|
227
|
-
},
|
|
228
|
-
mode: {
|
|
229
|
-
type: 'string',
|
|
230
|
-
description: '抓取模式:auto|static|dynamic。默认auto。',
|
|
231
|
-
},
|
|
232
|
-
limit: {
|
|
233
|
-
type: 'number',
|
|
234
|
-
description: '返回匹配结果上限,默认20。',
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
required: ['url', 'query'],
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
]
|
|
242
|
-
|
|
243
|
-
const functions = {
|
|
244
|
-
mcpBrowseWebpage,
|
|
245
|
-
mcpFetchWebpageByQuery,
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const MCPWebTool = {
|
|
249
|
-
name: 'MCPWebTool',
|
|
250
|
-
description: '提供网页浏览与内容抓取能力,支持任意网页的读取与关键词提取。',
|
|
251
|
-
platform: 'all',
|
|
252
|
-
descriptions,
|
|
253
|
-
functions,
|
|
254
|
-
isSystem: true
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
module.exports = MCPWebTool
|