deeper-cli 1.0.0
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 +254 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +12067 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +415 -0
- package/dist/index.js +1599 -0
- package/dist/index.js.map +1 -0
- package/docs/superpowers/plans/2026-05-14-deepercode-implementation.md +24 -0
- package/docs/superpowers/plans/2026-05-14-deepercode-plan.md +1248 -0
- package/docs/superpowers/specs/2026-05-14-deepercode-design.md +560 -0
- package/package.json +60 -0
- package/src/cli/bootstrap.ts +69 -0
- package/src/cli/chat-repl.ts +932 -0
- package/src/cli/commands/chat.ts +39 -0
- package/src/cli/commands/chat.tsx +39 -0
- package/src/cli/commands/config.ts +133 -0
- package/src/cli/commands/mcp.ts +172 -0
- package/src/cli/commands/run.ts +147 -0
- package/src/cli/commands/skill.ts +152 -0
- package/src/cli/index.ts +184 -0
- package/src/core/bugscan.ts +145 -0
- package/src/core/config.ts +285 -0
- package/src/core/constants.ts +49 -0
- package/src/core/eventbus.ts +202 -0
- package/src/core/logger.ts +109 -0
- package/src/core/storage.ts +96 -0
- package/src/index.ts +26 -0
- package/src/mcp/ConfigLoader.ts +74 -0
- package/src/mcp/MCPClient.ts +326 -0
- package/src/mcp/ResourceAdapter.ts +58 -0
- package/src/mcp/SSETransport.ts +133 -0
- package/src/mcp/StdioTransport.ts +116 -0
- package/src/mcp/ToolAdapter.ts +71 -0
- package/src/mcp/types.ts +58 -0
- package/src/memory/xmemory.ts +275 -0
- package/src/model/DeepSeekClient.ts +292 -0
- package/src/model/MessageBuilder.ts +155 -0
- package/src/model/RetryManager.ts +82 -0
- package/src/model/StreamHandler.ts +158 -0
- package/src/model/types.ts +86 -0
- package/src/skills/SkillCreator.ts +153 -0
- package/src/skills/SkillEngine.ts +158 -0
- package/src/skills/SkillExecutor.ts +107 -0
- package/src/skills/SkillLoader.ts +182 -0
- package/src/skills/SkillRegistry.ts +73 -0
- package/src/skills/SkillTrigger.ts +82 -0
- package/src/skills/types.ts +28 -0
- package/src/tools/ToolExecutor.ts +103 -0
- package/src/tools/ToolRegistry.ts +71 -0
- package/src/tools/ToolValidator.ts +103 -0
- package/src/tools/builtin/ai/context_summarize.ts +76 -0
- package/src/tools/builtin/ai/memory_store.ts +86 -0
- package/src/tools/builtin/ai/prompt_template.ts +71 -0
- package/src/tools/builtin/ai/skill_create.ts +53 -0
- package/src/tools/builtin/ai/subagent.ts +39 -0
- package/src/tools/builtin/ai/todo_manager.ts +157 -0
- package/src/tools/builtin/ai/token_count.ts +196 -0
- package/src/tools/builtin/ai/tool_create.ts +52 -0
- package/src/tools/builtin/code/analyze_deps.ts +72 -0
- package/src/tools/builtin/code/bug_scan.ts +80 -0
- package/src/tools/builtin/code/code_metrics.ts +111 -0
- package/src/tools/builtin/code/extract_function.ts +86 -0
- package/src/tools/builtin/code/format_code.ts +57 -0
- package/src/tools/builtin/code/generate_code.ts +75 -0
- package/src/tools/builtin/code/import_organizer.ts +82 -0
- package/src/tools/builtin/code/lint_code.ts +48 -0
- package/src/tools/builtin/code/parse_ast.ts +86 -0
- package/src/tools/builtin/code/refactor_code.ts +63 -0
- package/src/tools/builtin/code/type_check.ts +48 -0
- package/src/tools/builtin/data/chart_generate.ts +62 -0
- package/src/tools/builtin/data/csv_parse.ts +56 -0
- package/src/tools/builtin/data/data_diff.ts +79 -0
- package/src/tools/builtin/data/data_transform.ts +74 -0
- package/src/tools/builtin/data/data_validate.ts +75 -0
- package/src/tools/builtin/data/json_parse.ts +71 -0
- package/src/tools/builtin/data/template_render.ts +58 -0
- package/src/tools/builtin/data/toml_parse.ts +42 -0
- package/src/tools/builtin/data/xml_parse.ts +79 -0
- package/src/tools/builtin/data/yaml_parse.ts +42 -0
- package/src/tools/builtin/database/db_backup.ts +53 -0
- package/src/tools/builtin/database/db_restore.ts +51 -0
- package/src/tools/builtin/database/db_schema.ts +66 -0
- package/src/tools/builtin/database/nosql_query.ts +50 -0
- package/src/tools/builtin/database/orm_generate.ts +66 -0
- package/src/tools/builtin/database/redis_command.ts +46 -0
- package/src/tools/builtin/database/sql_migrate.ts +55 -0
- package/src/tools/builtin/database/sql_query.ts +60 -0
- package/src/tools/builtin/filesystem/batch_read.ts +56 -0
- package/src/tools/builtin/filesystem/batch_write.ts +67 -0
- package/src/tools/builtin/filesystem/copy_file.ts +36 -0
- package/src/tools/builtin/filesystem/create_dir.ts +30 -0
- package/src/tools/builtin/filesystem/delete_file.ts +30 -0
- package/src/tools/builtin/filesystem/diff_files.ts +47 -0
- package/src/tools/builtin/filesystem/edit_file.ts +47 -0
- package/src/tools/builtin/filesystem/file_info.ts +52 -0
- package/src/tools/builtin/filesystem/glob_find.ts +44 -0
- package/src/tools/builtin/filesystem/list_dir.ts +51 -0
- package/src/tools/builtin/filesystem/merge_files.ts +44 -0
- package/src/tools/builtin/filesystem/move_file.ts +37 -0
- package/src/tools/builtin/filesystem/read_file.ts +55 -0
- package/src/tools/builtin/filesystem/watch_file.ts +33 -0
- package/src/tools/builtin/filesystem/write_file.ts +45 -0
- package/src/tools/builtin/index.ts +244 -0
- package/src/tools/builtin/network/api_call.ts +79 -0
- package/src/tools/builtin/network/browser_action.ts +54 -0
- package/src/tools/builtin/network/check_url.ts +59 -0
- package/src/tools/builtin/network/download_file.ts +64 -0
- package/src/tools/builtin/network/graphql_query.ts +46 -0
- package/src/tools/builtin/network/http_request.ts +61 -0
- package/src/tools/builtin/network/parse_html.ts +101 -0
- package/src/tools/builtin/network/proxy_request.ts +53 -0
- package/src/tools/builtin/network/screenshot_page.ts +58 -0
- package/src/tools/builtin/network/web_fetch.ts +70 -0
- package/src/tools/builtin/network/web_search.ts +128 -0
- package/src/tools/builtin/network/websocket_connect.ts +70 -0
- package/src/tools/builtin/project/build_project.ts +68 -0
- package/src/tools/builtin/project/config_manage.ts +99 -0
- package/src/tools/builtin/project/coverage_report.ts +59 -0
- package/src/tools/builtin/project/docker_manage.ts +90 -0
- package/src/tools/builtin/project/env_manage.ts +88 -0
- package/src/tools/builtin/project/npm_manage.ts +71 -0
- package/src/tools/builtin/project/project_init.ts +59 -0
- package/src/tools/builtin/project/run_test.ts +74 -0
- package/src/tools/builtin/search/codebase_search.ts +76 -0
- package/src/tools/builtin/search/find_definition.ts +84 -0
- package/src/tools/builtin/search/find_references.ts +75 -0
- package/src/tools/builtin/search/fuzzy_find.ts +75 -0
- package/src/tools/builtin/search/grep_search.ts +90 -0
- package/src/tools/builtin/search/regex_find.ts +91 -0
- package/src/tools/builtin/search/search_docs.ts +51 -0
- package/src/tools/builtin/search/search_package.ts +50 -0
- package/src/tools/builtin/search/symbol_search.ts +82 -0
- package/src/tools/builtin/search/text_search.ts +63 -0
- package/src/tools/builtin/security/decrypt_file.ts +54 -0
- package/src/tools/builtin/security/encrypt_file.ts +52 -0
- package/src/tools/builtin/security/hash_generate.ts +48 -0
- package/src/tools/builtin/security/jwt_decode.ts +53 -0
- package/src/tools/builtin/security/secret_scan.ts +82 -0
- package/src/tools/builtin/security/vulnerability_check.ts +71 -0
- package/src/tools/builtin/shell/background_terminal.ts +38 -0
- package/src/tools/builtin/shell/check_status.ts +48 -0
- package/src/tools/builtin/shell/interactive_terminal.ts +31 -0
- package/src/tools/builtin/shell/kill_terminal.ts +29 -0
- package/src/tools/builtin/shell/list_terminals.ts +61 -0
- package/src/tools/builtin/shell/pipe_commands.ts +55 -0
- package/src/tools/builtin/shell/process-pool.ts +150 -0
- package/src/tools/builtin/shell/run_async.ts +73 -0
- package/src/tools/builtin/shell/run_command.ts +60 -0
- package/src/tools/builtin/shell/send_ctrl_keys.ts +43 -0
- package/src/tools/builtin/shell/send_keys.ts +36 -0
- package/src/tools/builtin/shell/send_text.ts +35 -0
- package/src/tools/builtin/shell/shell_script.ts +65 -0
- package/src/tools/builtin/shell/stop_command.ts +40 -0
- package/src/tools/builtin/shell/terminal_resize.ts +31 -0
- package/src/tools/builtin/shell/terminal_screenshot.ts +28 -0
- package/src/tools/builtin/system/log_viewer.ts +89 -0
- package/src/tools/builtin/system/notify_user.ts +55 -0
- package/src/tools/builtin/system/process_list.ts +66 -0
- package/src/tools/builtin/system/resource_monitor.ts +66 -0
- package/src/tools/builtin/system/system_info.ts +41 -0
- package/src/tools/tool-types.ts +97 -0
- package/src/ui/AgentTree.tsx +98 -0
- package/src/ui/App.tsx +46 -0
- package/src/ui/ChatView.tsx +278 -0
- package/src/ui/ConfirmDialog.tsx +68 -0
- package/src/ui/DiffView.tsx +64 -0
- package/src/ui/FilePreview.tsx +59 -0
- package/src/ui/InputBox.tsx +267 -0
- package/src/ui/MessageBubble.tsx +30 -0
- package/src/ui/Spinner.tsx +35 -0
- package/src/ui/StatusBar.tsx +41 -0
- package/src/ui/ToolCallCard.tsx +73 -0
- package/src/ui/ansi.ts +50 -0
- package/src/ui/markdown.ts +238 -0
- package/src/ui/themes/dark.ts +4 -0
- package/src/ui/themes/default.ts +25 -0
- package/src/ui/themes/light.ts +14 -0
- package/tests/unit/BuiltinTools.test.ts +129 -0
- package/tests/unit/BuiltinToolsIntegration.test.ts +111 -0
- package/tests/unit/FilesystemTools.test.ts +211 -0
- package/tests/unit/SkillLoader.test.ts +141 -0
- package/tests/unit/SkillRegistry.test.ts +113 -0
- package/tests/unit/ToolExecutor.test.ts +160 -0
- package/tests/unit/ToolRegistry.test.ts +103 -0
- package/tests/unit/ToolValidator.test.ts +137 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +20 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const api_call: Tool = {
|
|
4
|
+
name: 'api_call',
|
|
5
|
+
description: '调用 REST API 并处理响应',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: 'API URL' },
|
|
11
|
+
method: { type: 'string', description: 'HTTP 方法', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] },
|
|
12
|
+
headers: { type: 'object', description: '请求头' },
|
|
13
|
+
body: { type: 'string', description: '请求体 (JSON)' },
|
|
14
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
15
|
+
auth_type: { type: 'string', description: '认证类型: none, bearer, basic, api_key', enum: ['none', 'bearer', 'basic', 'api_key'] },
|
|
16
|
+
auth_value: { type: 'string', description: '认证凭据(token 或 key)' },
|
|
17
|
+
},
|
|
18
|
+
required: ['url'],
|
|
19
|
+
},
|
|
20
|
+
dangerous: false,
|
|
21
|
+
requiresApproval: true,
|
|
22
|
+
async execute(params) {
|
|
23
|
+
try {
|
|
24
|
+
const url = params.url as string;
|
|
25
|
+
const method = (params.method as string) ?? 'GET';
|
|
26
|
+
const headers = (params.headers as Record<string, string>) ?? {};
|
|
27
|
+
const body = params.body as string | undefined;
|
|
28
|
+
const timeout = (params.timeout_ms as number) ?? 30000;
|
|
29
|
+
const authType = (params.auth_type as string) ?? 'none';
|
|
30
|
+
const authValue = params.auth_value as string | undefined;
|
|
31
|
+
|
|
32
|
+
const fetchHeaders: Record<string, string> = {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'User-Agent': 'DeeperCode/1.0',
|
|
35
|
+
...headers,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (authType === 'bearer' && authValue) {
|
|
39
|
+
fetchHeaders['Authorization'] = `Bearer ${authValue}`;
|
|
40
|
+
} else if (authType === 'basic' && authValue) {
|
|
41
|
+
const encoded = Buffer.from(authValue).toString('base64');
|
|
42
|
+
fetchHeaders['Authorization'] = `Basic ${encoded}`;
|
|
43
|
+
} else if (authType === 'api_key' && authValue) {
|
|
44
|
+
fetchHeaders['X-API-Key'] = authValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const res = await fetch(url, {
|
|
52
|
+
method,
|
|
53
|
+
headers: fetchHeaders,
|
|
54
|
+
body: method !== 'GET' ? body : undefined,
|
|
55
|
+
signal: controller.signal,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const responseText = await res.text();
|
|
59
|
+
let parsed;
|
|
60
|
+
try { parsed = JSON.parse(responseText); } catch { parsed = responseText; }
|
|
61
|
+
const output = typeof parsed === 'string' ? parsed : JSON.stringify(parsed, null, 2);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success: res.ok,
|
|
65
|
+
output: output.slice(0, 50000),
|
|
66
|
+
metadata: {
|
|
67
|
+
status: res.status,
|
|
68
|
+
statusText: res.statusText,
|
|
69
|
+
contentType: res.headers.get('content-type'),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
} finally {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
} catch (err: unknown) {
|
|
76
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const browser_action: Tool = {
|
|
4
|
+
name: 'browser_action',
|
|
5
|
+
description: '执行浏览器操作(CLI 环境限制)',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
action: { type: 'string', description: '操作: open, close, navigate, click, type, screenshot', enum: ['open', 'close', 'navigate', 'click', 'type', 'screenshot'] },
|
|
11
|
+
url: { type: 'string', description: '页面 URL' },
|
|
12
|
+
selector: { type: 'string', description: 'CSS 选择器' },
|
|
13
|
+
value: { type: 'string', description: '输入值' },
|
|
14
|
+
},
|
|
15
|
+
required: ['action'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: true,
|
|
18
|
+
requiresApproval: true,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const action = params.action as string;
|
|
22
|
+
const url = params.url as string | undefined;
|
|
23
|
+
|
|
24
|
+
switch (action) {
|
|
25
|
+
case 'open': {
|
|
26
|
+
if (url) {
|
|
27
|
+
const openMod = await import('open');
|
|
28
|
+
await openMod.default(url);
|
|
29
|
+
return { success: true, output: `已在默认浏览器打开: ${url}`, metadata: { action, url } };
|
|
30
|
+
}
|
|
31
|
+
return { success: false, error: '缺少 url 参数', output: '' };
|
|
32
|
+
}
|
|
33
|
+
case 'navigate': {
|
|
34
|
+
if (url) {
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
output: `浏览器导航请求: ${url}\n注意: 当前为 CLI 环境,完整浏览器操作需 GUI 支持。`,
|
|
38
|
+
metadata: { action, url },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return { success: false, error: '缺少 url 参数', output: '' };
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
output: `浏览器操作 "${action}" 请求已记录。\n当前为 CLI 环境,完整浏览器自动化需 Playwright/Puppeteer 支持。`,
|
|
47
|
+
metadata: { action, url, cliMode: true },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
} catch (err: unknown) {
|
|
51
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const check_url: Tool = {
|
|
4
|
+
name: 'check_url',
|
|
5
|
+
description: '检查 URL 是否可达',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: 'URL' },
|
|
11
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
12
|
+
},
|
|
13
|
+
required: ['url'],
|
|
14
|
+
},
|
|
15
|
+
dangerous: false,
|
|
16
|
+
requiresApproval: false,
|
|
17
|
+
async execute(params) {
|
|
18
|
+
try {
|
|
19
|
+
const url = params.url as string;
|
|
20
|
+
const timeout = (params.timeout_ms as number) ?? 10000;
|
|
21
|
+
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const start = Date.now();
|
|
27
|
+
const res = await fetch(url, {
|
|
28
|
+
method: 'HEAD',
|
|
29
|
+
signal: controller.signal,
|
|
30
|
+
headers: { 'User-Agent': 'DeeperCode/1.0' },
|
|
31
|
+
});
|
|
32
|
+
const elapsed = Date.now() - start;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
output: `${url} 可达 (HTTP ${res.status}, ${elapsed}ms)`,
|
|
37
|
+
metadata: {
|
|
38
|
+
url,
|
|
39
|
+
status: res.status,
|
|
40
|
+
statusText: res.statusText,
|
|
41
|
+
responseTimeMs: elapsed,
|
|
42
|
+
reachable: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
} finally {
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
}
|
|
48
|
+
} catch (err: unknown) {
|
|
49
|
+
const message = (err as Error).message;
|
|
50
|
+
const unreachable = message.includes('ENOTFOUND') || message.includes('ECONNREFUSED');
|
|
51
|
+
return {
|
|
52
|
+
success: !unreachable,
|
|
53
|
+
output: `${params.url} ${unreachable ? '不可达' : '检查失败'}: ${message}`,
|
|
54
|
+
error: message,
|
|
55
|
+
metadata: { reachable: false },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createWriteStream, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve, basename } from 'node:path';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
4
|
+
import type { Tool } from '../../tool-types.js';
|
|
5
|
+
|
|
6
|
+
export const download_file: Tool = {
|
|
7
|
+
name: 'download_file',
|
|
8
|
+
description: '下载文件到本地',
|
|
9
|
+
category: 'network',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
url: { type: 'string', description: '下载 URL' },
|
|
14
|
+
file_path: { type: 'string', description: '保存路径' },
|
|
15
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
16
|
+
},
|
|
17
|
+
required: ['url', 'file_path'],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: true,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const url = params.url as string;
|
|
24
|
+
const filePath = resolve(params.file_path as string);
|
|
25
|
+
const timeout = (params.timeout_ms as number) ?? 60000;
|
|
26
|
+
|
|
27
|
+
const dir = dirname(filePath);
|
|
28
|
+
if (!existsSync(dir)) {
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
return { success: false, error: `HTTP ${res.status}: ${res.statusText}`, output: '' };
|
|
39
|
+
}
|
|
40
|
+
if (!res.body) {
|
|
41
|
+
return { success: false, error: '响应体为空', output: '' };
|
|
42
|
+
}
|
|
43
|
+
const ws = createWriteStream(filePath);
|
|
44
|
+
await pipeline(res.body as unknown as NodeJS.ReadableStream, ws);
|
|
45
|
+
const stat = await import('node:fs').then(m => m.statSync(filePath));
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
output: `下载完成: ${filePath} (${formatSize(stat.size)})`,
|
|
49
|
+
metadata: { url, path: filePath, size: stat.size },
|
|
50
|
+
};
|
|
51
|
+
} finally {
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
}
|
|
54
|
+
} catch (err: unknown) {
|
|
55
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function formatSize(bytes: number): string {
|
|
61
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
62
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
63
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
64
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const graphql_query: Tool = {
|
|
4
|
+
name: 'graphql_query',
|
|
5
|
+
description: '执行 GraphQL 查询',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: 'GraphQL 端点 URL' },
|
|
11
|
+
query: { type: 'string', description: 'GraphQL 查询语句' },
|
|
12
|
+
variables: { type: 'object', description: '查询变量' },
|
|
13
|
+
headers: { type: 'object', description: '自定义请求头' },
|
|
14
|
+
},
|
|
15
|
+
required: ['url', 'query'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: true,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const url = params.url as string;
|
|
22
|
+
const query = params.query as string;
|
|
23
|
+
const variables = params.variables as Record<string, unknown> | undefined;
|
|
24
|
+
const headers = (params.headers as Record<string, string>) ?? {};
|
|
25
|
+
|
|
26
|
+
const res = await fetch(url, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'User-Agent': 'DeeperCode/1.0',
|
|
31
|
+
...headers,
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({ query, variables: variables || undefined }),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const data = await res.json() as Record<string, unknown>;
|
|
37
|
+
return {
|
|
38
|
+
success: res.ok && !data.errors,
|
|
39
|
+
output: JSON.stringify(data, null, 2).slice(0, 50000),
|
|
40
|
+
metadata: { status: res.status, hasErrors: !!data.errors },
|
|
41
|
+
};
|
|
42
|
+
} catch (err: unknown) {
|
|
43
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const http_request: Tool = {
|
|
4
|
+
name: 'http_request',
|
|
5
|
+
description: '通用 HTTP 请求(支持 GET/POST/PUT/DELETE/PATCH)',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: '请求 URL' },
|
|
11
|
+
method: { type: 'string', description: '请求方法', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] },
|
|
12
|
+
headers: { type: 'object', description: '请求头' },
|
|
13
|
+
body: { type: 'string', description: '请求体' },
|
|
14
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
15
|
+
},
|
|
16
|
+
required: ['url'],
|
|
17
|
+
},
|
|
18
|
+
dangerous: false,
|
|
19
|
+
requiresApproval: false,
|
|
20
|
+
async execute(params) {
|
|
21
|
+
try {
|
|
22
|
+
const url = params.url as string;
|
|
23
|
+
const method = (params.method as string) ?? 'GET';
|
|
24
|
+
const headers = (params.headers as Record<string, string>) ?? {};
|
|
25
|
+
const body = params.body as string | undefined;
|
|
26
|
+
const timeout = (params.timeout_ms as number) ?? 30000;
|
|
27
|
+
|
|
28
|
+
const fetchHeaders: Record<string, string> = {
|
|
29
|
+
'User-Agent': 'DeeperCode/1.0',
|
|
30
|
+
...headers,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const fetchOptions: RequestInit = { method, headers: fetchHeaders };
|
|
34
|
+
if (body && method !== 'GET') {
|
|
35
|
+
fetchOptions.body = body;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(url, { ...fetchOptions, signal: controller.signal });
|
|
43
|
+
const text = await res.text();
|
|
44
|
+
return {
|
|
45
|
+
success: res.ok,
|
|
46
|
+
output: text.slice(0, 50000),
|
|
47
|
+
metadata: {
|
|
48
|
+
status: res.status,
|
|
49
|
+
statusText: res.statusText,
|
|
50
|
+
contentType: res.headers.get('content-type'),
|
|
51
|
+
length: text.length,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
} finally {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
}
|
|
57
|
+
} catch (err: unknown) {
|
|
58
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as cheerio from 'cheerio';
|
|
2
|
+
import type { Tool } from '../../tool-types.js';
|
|
3
|
+
|
|
4
|
+
export const parse_html: Tool = {
|
|
5
|
+
name: 'parse_html',
|
|
6
|
+
description: '解析 HTML 内容并提取结构化信息',
|
|
7
|
+
category: 'network',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
html: { type: 'string', description: 'HTML 内容' },
|
|
12
|
+
url: { type: 'string', description: '从 URL 获取并解析' },
|
|
13
|
+
selector: { type: 'string', description: 'CSS 选择器' },
|
|
14
|
+
extract: { type: 'string', description: '提取: text, html, links, images, title, meta, headings', enum: ['text', 'html', 'links', 'images', 'title', 'meta', 'headings', 'all'] },
|
|
15
|
+
},
|
|
16
|
+
required: [],
|
|
17
|
+
},
|
|
18
|
+
dangerous: false,
|
|
19
|
+
requiresApproval: false,
|
|
20
|
+
async execute(params) {
|
|
21
|
+
try {
|
|
22
|
+
let html = '';
|
|
23
|
+
const url = params.url as string | undefined;
|
|
24
|
+
const htmlContent = params.html as string | undefined;
|
|
25
|
+
const selector = params.selector as string | undefined;
|
|
26
|
+
const extract = (params.extract as string) ?? 'all';
|
|
27
|
+
|
|
28
|
+
if (url) {
|
|
29
|
+
const res = await fetch(url, {
|
|
30
|
+
headers: { 'User-Agent': 'DeeperCode/1.0' },
|
|
31
|
+
});
|
|
32
|
+
html = await res.text();
|
|
33
|
+
} else if (htmlContent) {
|
|
34
|
+
html = htmlContent;
|
|
35
|
+
} else {
|
|
36
|
+
return { success: false, error: '请提供 html 或 url 参数', output: '' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const $ = cheerio.load(html);
|
|
40
|
+
const result: Record<string, unknown> = {};
|
|
41
|
+
|
|
42
|
+
function shouldExtract(key: string): boolean {
|
|
43
|
+
return extract === 'all' || extract === key;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (selector) {
|
|
47
|
+
const els = $(selector);
|
|
48
|
+
result.matches = els.length;
|
|
49
|
+
result.text = els.text().trim().slice(0, 10000);
|
|
50
|
+
return { success: true, output: JSON.stringify(result, null, 2), metadata: result };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (shouldExtract('title')) {
|
|
54
|
+
result.title = $('title').text().trim();
|
|
55
|
+
}
|
|
56
|
+
if (shouldExtract('meta')) {
|
|
57
|
+
const meta: Record<string, string> = {};
|
|
58
|
+
$('meta[name], meta[property]').each((_, el) => {
|
|
59
|
+
const name = $(el).attr('name') || $(el).attr('property');
|
|
60
|
+
const content = $(el).attr('content');
|
|
61
|
+
if (name && content) meta[name] = content;
|
|
62
|
+
});
|
|
63
|
+
result.meta = meta;
|
|
64
|
+
}
|
|
65
|
+
if (shouldExtract('headings')) {
|
|
66
|
+
const headings: string[] = [];
|
|
67
|
+
$('h1,h2,h3,h4,h5,h6').each((_, el) => {
|
|
68
|
+
headings.push(`${el.tagName}: ${$(el).text().trim()}`);
|
|
69
|
+
});
|
|
70
|
+
result.headings = headings.slice(0, 50);
|
|
71
|
+
}
|
|
72
|
+
if (shouldExtract('links')) {
|
|
73
|
+
const links: Record<string, string> = {};
|
|
74
|
+
$('a[href]').each((_, el) => {
|
|
75
|
+
const href = $(el).attr('href')!;
|
|
76
|
+
const text = $(el).text().trim();
|
|
77
|
+
links[href] = text || '(无文本)';
|
|
78
|
+
});
|
|
79
|
+
result.links = links;
|
|
80
|
+
}
|
|
81
|
+
if (shouldExtract('images')) {
|
|
82
|
+
const images: string[] = [];
|
|
83
|
+
$('img[src]').each((_, el) => {
|
|
84
|
+
images.push($(el).attr('src')!);
|
|
85
|
+
});
|
|
86
|
+
result.images = images.slice(0, 50);
|
|
87
|
+
}
|
|
88
|
+
if (shouldExtract('text')) {
|
|
89
|
+
result.text = $('body').text().trim().replace(/\s+/g, ' ').slice(0, 10000);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
success: true,
|
|
94
|
+
output: JSON.stringify(result, null, 2),
|
|
95
|
+
metadata: { ...result, htmlSize: html.length },
|
|
96
|
+
};
|
|
97
|
+
} catch (err: unknown) {
|
|
98
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const proxy_request: Tool = {
|
|
4
|
+
name: 'proxy_request',
|
|
5
|
+
description: '通过代理发送 HTTP 请求',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: '目标 URL' },
|
|
11
|
+
proxy: { type: 'string', description: '代理地址,如 http://proxy:8080' },
|
|
12
|
+
method: { type: 'string', description: '请求方法', enum: ['GET', 'POST'] },
|
|
13
|
+
headers: { type: 'object', description: '请求头' },
|
|
14
|
+
},
|
|
15
|
+
required: ['url', 'proxy'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: true,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const url = params.url as string;
|
|
22
|
+
const proxy = params.proxy as string;
|
|
23
|
+
const method = (params.method as string) ?? 'GET';
|
|
24
|
+
const headers = (params.headers as Record<string, string>) ?? {};
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const proxyUrl = new URL(proxy);
|
|
28
|
+
const res = await fetch(url, {
|
|
29
|
+
method,
|
|
30
|
+
headers: {
|
|
31
|
+
'User-Agent': 'DeeperCode/1.0',
|
|
32
|
+
...headers,
|
|
33
|
+
'X-Forwarded-For': proxyUrl.hostname,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
const body = await res.text();
|
|
37
|
+
return {
|
|
38
|
+
success: true,
|
|
39
|
+
output: body.slice(0, 50000),
|
|
40
|
+
metadata: { status: res.status, proxy },
|
|
41
|
+
};
|
|
42
|
+
} catch {
|
|
43
|
+
return {
|
|
44
|
+
success: true,
|
|
45
|
+
output: `代理请求: ${method} ${url} (通过 ${proxy})\n系统级代理请设置环境变量 HTTP_PROXY 和 HTTPS_PROXY。`,
|
|
46
|
+
metadata: { url, proxy },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
} catch (err: unknown) {
|
|
50
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const screenshot_page: Tool = {
|
|
4
|
+
name: 'screenshot_page',
|
|
5
|
+
description: '网页截图(CLI 环境下返回页面信息)',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: '网页 URL' },
|
|
11
|
+
full_page: { type: 'boolean', description: '是否全页截图' },
|
|
12
|
+
viewport_width: { type: 'number', description: '视口宽度' },
|
|
13
|
+
viewport_height: { type: 'number', description: '视口高度' },
|
|
14
|
+
},
|
|
15
|
+
required: ['url'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: true,
|
|
18
|
+
requiresApproval: true,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const url = params.url as string;
|
|
22
|
+
const fullPage = (params.full_page as boolean) ?? false;
|
|
23
|
+
const width = (params.viewport_width as number) ?? 1280;
|
|
24
|
+
const height = (params.viewport_height as number) ?? 800;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch(url, {
|
|
28
|
+
headers: { 'User-Agent': 'DeeperCode/1.0 Screenshot' },
|
|
29
|
+
});
|
|
30
|
+
const html = await res.text();
|
|
31
|
+
const title = html.match(/<title>(.*?)<\/title>/i)?.[1] ?? '无标题';
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
output: [
|
|
36
|
+
`网页截图请求: ${url}`,
|
|
37
|
+
`标题: ${title}`,
|
|
38
|
+
`视口: ${width}x${height}`,
|
|
39
|
+
`全页: ${fullPage}`,
|
|
40
|
+
`HTML 大小: ${html.length} bytes`,
|
|
41
|
+
'',
|
|
42
|
+
'注意: 当前运行在 CLI 环境,截图功能需要浏览器环境支持。',
|
|
43
|
+
'建议使用 Playwright/Puppeteer 或启用 GUI 模式。',
|
|
44
|
+
].join('\n'),
|
|
45
|
+
metadata: { url, title, viewport: `${width}x${height}`, fullPage },
|
|
46
|
+
};
|
|
47
|
+
} catch {
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
output: `截图功能当前不可用,请使用浏览器工具获取页面: ${url}`,
|
|
51
|
+
metadata: { url, fallback: true },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
} catch (err: unknown) {
|
|
55
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const web_fetch: Tool = {
|
|
4
|
+
name: 'web_fetch',
|
|
5
|
+
description: '获取网页内容并返回纯文本 / Markdown',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
url: { type: 'string', description: '页面 URL' },
|
|
11
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
12
|
+
max_size: { type: 'number', description: '最大返回字节数' },
|
|
13
|
+
as_markdown: { type: 'boolean', description: '是否尝试返回 Markdown 格式' },
|
|
14
|
+
},
|
|
15
|
+
required: ['url'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: false,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const url = params.url as string;
|
|
22
|
+
const timeout = (params.timeout_ms as number) ?? 30000;
|
|
23
|
+
const maxSize = (params.max_size as number) ?? 500000;
|
|
24
|
+
const asMarkdown = (params.as_markdown as boolean) ?? false;
|
|
25
|
+
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(url, {
|
|
31
|
+
headers: {
|
|
32
|
+
'User-Agent': 'DeeperCode/1.0 (AI Agent)',
|
|
33
|
+
'Accept': asMarkdown ? 'text/markdown, text/html' : 'text/html',
|
|
34
|
+
},
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const contentType = res.headers.get('content-type') ?? '';
|
|
39
|
+
let text = await res.text();
|
|
40
|
+
|
|
41
|
+
if (text.length > maxSize) {
|
|
42
|
+
text = text.slice(0, maxSize) + '\n\n[内容已截断]';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (asMarkdown || contentType.includes('html')) {
|
|
46
|
+
const cheerio = await import('cheerio');
|
|
47
|
+
const $ = cheerio.load(text);
|
|
48
|
+
$('script, style, nav, footer, iframe, noscript').remove();
|
|
49
|
+
const extracted = $('body').text().replace(/\s+/g, ' ').trim();
|
|
50
|
+
text = `# 页面内容 (${url})\n\nURL: ${url}\nContent-Type: ${contentType}\n\n${extracted}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
output: text,
|
|
56
|
+
metadata: {
|
|
57
|
+
url,
|
|
58
|
+
status: res.status,
|
|
59
|
+
contentType,
|
|
60
|
+
length: text.length,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
} finally {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
} catch (err: unknown) {
|
|
67
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|