deepspider 0.3.1 → 0.4.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.
Files changed (87) hide show
  1. package/.env.example +3 -0
  2. package/README.md +21 -15
  3. package/package.json +9 -7
  4. package/src/agent/core/PanelBridge.js +56 -78
  5. package/src/agent/core/StreamHandler.js +244 -20
  6. package/src/agent/index.js +120 -23
  7. package/src/agent/logger.js +183 -8
  8. package/src/agent/middleware/memoryFlush.js +48 -0
  9. package/src/agent/middleware/report.js +95 -37
  10. package/src/agent/middleware/subagent.js +236 -0
  11. package/src/agent/middleware/toolAvailability.js +37 -0
  12. package/src/agent/middleware/toolGuard.js +187 -0
  13. package/src/agent/middleware/validationWorkflow.js +171 -0
  14. package/src/agent/prompts/system.js +310 -59
  15. package/src/agent/run.js +168 -20
  16. package/src/agent/sessions.js +88 -0
  17. package/src/agent/skills/anti-detect/SKILL.md +89 -14
  18. package/src/agent/skills/captcha/SKILL.md +93 -19
  19. package/src/agent/skills/crawler/SKILL.md +64 -3
  20. package/src/agent/skills/crawler/evolved.md +9 -1
  21. package/src/agent/skills/dynamic-analysis/SKILL.md +74 -7
  22. package/src/agent/skills/env/SKILL.md +75 -0
  23. package/src/agent/skills/js2python/evolved.md +5 -1
  24. package/src/agent/skills/sandbox/SKILL.md +35 -0
  25. package/src/agent/skills/static-analysis/SKILL.md +98 -2
  26. package/src/agent/skills/static-analysis/evolved.md +5 -1
  27. package/src/agent/subagents/anti-detect.js +36 -24
  28. package/src/agent/subagents/captcha.js +35 -28
  29. package/src/agent/subagents/crawler.js +40 -105
  30. package/src/agent/subagents/factory.js +129 -9
  31. package/src/agent/subagents/index.js +4 -13
  32. package/src/agent/subagents/js2python.js +25 -35
  33. package/src/agent/subagents/reverse.js +180 -0
  34. package/src/agent/tools/analysis.js +101 -8
  35. package/src/agent/tools/anti-detect.js +5 -2
  36. package/src/agent/tools/browser.js +186 -13
  37. package/src/agent/tools/capture.js +24 -3
  38. package/src/agent/tools/correlate.js +129 -15
  39. package/src/agent/tools/crawler.js +3 -2
  40. package/src/agent/tools/crawlerGenerator.js +90 -0
  41. package/src/agent/tools/debug.js +43 -6
  42. package/src/agent/tools/evolve.js +5 -2
  43. package/src/agent/tools/extractor.js +5 -1
  44. package/src/agent/tools/file.js +14 -5
  45. package/src/agent/tools/generateHook.js +66 -0
  46. package/src/agent/tools/hookManager.js +19 -9
  47. package/src/agent/tools/index.js +36 -21
  48. package/src/agent/tools/nodejs.js +41 -6
  49. package/src/agent/tools/patch.js +1 -1
  50. package/src/agent/tools/sandbox.js +21 -1
  51. package/src/agent/tools/scratchpad.js +70 -0
  52. package/src/agent/tools/store.js +1 -1
  53. package/src/agent/tools/tracing.js +26 -0
  54. package/src/agent/tools/verifyAlgorithm.js +117 -0
  55. package/src/browser/EnvBridge.js +27 -13
  56. package/src/browser/client.js +128 -18
  57. package/src/browser/collector.js +101 -22
  58. package/src/browser/defaultHooks.js +3 -1
  59. package/src/browser/hooks/index.js +5 -0
  60. package/src/browser/interceptors/AntiDebugInterceptor.js +132 -0
  61. package/src/browser/interceptors/NetworkInterceptor.js +76 -12
  62. package/src/browser/interceptors/ScriptInterceptor.js +32 -7
  63. package/src/browser/interceptors/index.js +1 -0
  64. package/src/browser/ui/analysisPanel.js +541 -464
  65. package/src/cli/commands/config.js +11 -3
  66. package/src/config/paths.js +9 -1
  67. package/src/config/settings.js +7 -1
  68. package/src/core/PatchGenerator.js +24 -4
  69. package/src/core/Sandbox.js +140 -3
  70. package/src/env/EnvCodeGenerator.js +60 -88
  71. package/src/env/modules/bom/history.js +6 -0
  72. package/src/env/modules/bom/location.js +6 -0
  73. package/src/env/modules/bom/navigator.js +13 -0
  74. package/src/env/modules/bom/screen.js +6 -0
  75. package/src/env/modules/bom/storage.js +7 -0
  76. package/src/env/modules/dom/document.js +14 -0
  77. package/src/env/modules/dom/event.js +4 -0
  78. package/src/env/modules/index.js +27 -10
  79. package/src/env/modules/webapi/fetch.js +4 -0
  80. package/src/env/modules/webapi/url.js +4 -0
  81. package/src/env/modules/webapi/xhr.js +8 -0
  82. package/src/store/DataStore.js +125 -42
  83. package/src/store/Store.js +2 -1
  84. package/src/agent/subagents/dynamic.js +0 -64
  85. package/src/agent/subagents/env-agent.js +0 -82
  86. package/src/agent/subagents/sandbox.js +0 -55
  87. package/src/agent/subagents/static.js +0 -66
@@ -3,136 +3,71 @@
3
3
  * 智能调度、流程规划、脚本生成
4
4
  */
5
5
 
6
- import { createSkillsMiddleware } from 'deepagents';
7
- import { SKILLS, skillsBackend } from '../skills/config.js';
8
- import { createFilterToolsMiddleware } from '../middleware/filterTools.js';
6
+ import { createSubagent } from './factory.js';
9
7
 
10
8
  import { crawlerTools } from '../tools/crawler.js';
11
9
  import { fileTools } from '../tools/file.js';
12
- import { evolveTools } from '../tools/evolve.js';
13
10
  import { storeTools } from '../tools/store.js';
11
+ import { getPageSource, getElementHtml } from '../tools/browser.js';
14
12
 
15
- export const crawlerSubagent = {
13
+ export const crawlerSubagent = createSubagent({
16
14
  name: 'crawler',
17
- description: '爬虫编排专家。当需要规划完整爬虫流程、生成爬虫脚本、进行端到端测试时使用。负责分析目标网站复杂度,按需调度其他子代理,输出完整可运行的爬虫代码。',
15
+ description: '爬虫编排专家。适用于:规划完整爬虫流程、整合各模块生成完整 Python 爬虫脚本。不能做加密分析、不能反混淆、不能控制浏览器。依赖其他子代理提供加密/验证码等模块。',
18
16
  systemPrompt: `你是 DeepSpider 的爬虫编排专家,负责生成完整可运行的 Python 爬虫脚本。
19
17
 
20
18
  ## 核心职责
21
- **最终目标:输出一份用户可以直接 python crawler.py 运行的完整爬虫代码**
19
+ 输出用户可以直接 \`python crawler.py\` 运行的完整爬虫代码。
22
20
 
23
- 1. 分析目标网站,识别需要处理的环节
24
- 2. 调度其他子代理获取各模块代码
25
- 3. 整合所有模块,生成完整 Python 爬虫脚本
26
- 4. E2E 测试验证脚本可运行
27
- 5. 输出最终代码文件
21
+ ## 输入来源
22
+ agent 在 task description 中提供:
23
+ - 接口分析结果(URL、方法、参数、Headers)
24
+ - 已验证的加密代码文件路径(如有)
25
+ - 用户选择的框架(requests / scrapy / playwright 等)
28
26
 
29
- ## 网站复杂度分级
27
+ 你不需要自己分析加密或调度其他子代理。
28
+ 如需查看已有的加密代码,用 \`query_store\` 或 \`artifact_load\` 读取。
30
29
 
31
- ### Level 1 - 简单
32
- - 无加密或简单加密
33
- - 无验证码
34
- - 无登录要求
35
- - 无风控检测
36
-
37
- ### Level 2 - 中等
38
- - 有加密参数
39
- - 可能有简单验证码
40
- - 可能需要登录
41
- - 基础风控
42
-
43
- ### Level 3 - 复杂
44
- - 复杂加密 + 多重风控
45
- - 多种验证码
46
- - 设备指纹检测
47
- - 行为分析
48
-
49
- ## 调度策略
50
-
51
- 根据网站特征,按需调用子代理获取代码模块:
30
+ ## 工作流程
52
31
 
53
- | 网站特征 | 调用子代理 | 获取模块 |
54
- |----------|-----------|----------|
55
- | 有加密参数 | static → js2python | crypto.py |
56
- | 有验证码 | captcha 分析 | 生成验证码处理代码 |
57
- | 有风控 | anti-detect 分析 | 生成反检测配置代码 |
58
- | 需要登录 | dynamic 分析 | 生成登录流程代码 |
32
+ 1. **读取输入** task description 和 store 中获取分析结果、加密代码
33
+ 2. **生成代码** — 整合为完整可运行的 Python 爬虫脚本
34
+ 3. **自验证** 检查代码完整性:
35
+ - 所有 import 是否齐全
36
+ - 加密模块是否正确整合(路径、函数名、参数)
37
+ - Headers/Cookies 是否从分析结果中完整复制
38
+ - if __name__ 入口是否可运行
39
+ 4. **保存** — artifact_save 保存文件 + requirements.txt
40
+ 5. **输出路径** — 告知文件保存位置
59
41
 
60
42
  ## 输出规范
61
43
 
62
- **重要:必须输出完整可运行的 Python 代码文件**
63
-
64
- ### 输出要求
65
- 1. 使用 artifact_save 保存完整 .py 文件
44
+ 1. 使用 artifact_save 保存 .py 文件
66
45
  2. 代码必须可以直接 \`python xxx.py\` 运行
67
- 3. 包含所有依赖的 import
68
- 4. 包含使用示例(if __name__ == "__main__")
69
- 5. 包含 requirements.txt
46
+ 3. 包含完整 import、使用示例(if __name__)、requirements.txt
47
+ 4. 禁止在对话中输出大段代码片段代替文件
70
48
 
71
- ### 复杂网站 - 项目结构
49
+ ### 复杂网站 多文件结构
72
50
  \`\`\`
73
- <domain>_crawler/
74
- ├── config.py # 配置(可选的代理、账号等)
51
+ {domain}_crawler/
52
+ ├── config.py # 配置
75
53
  ├── crypto.py # 加密模块(来自 js2python)
76
- ├── captcha.py # 验证码处理(如需要)
77
54
  ├── crawler.py # 主爬虫逻辑
78
- └── requirements.txt # 依赖列表
55
+ └── requirements.txt
79
56
  \`\`\`
80
57
 
81
- ### 代码模板
82
-
83
- \`\`\`python
84
- """
85
- <domain> 爬虫 - 由 DeepSpider 生成
86
- """
87
- import requests
88
-
89
- class Crawler:
90
- def __init__(self):
91
- self.session = requests.Session()
92
- self.session.headers.update({...})
93
-
94
- def encrypt(self, data):
95
- # 加密逻辑
96
- ...
97
-
98
- def login(self, username, password):
99
- # 登录流程(如需要)
100
- ...
101
-
102
- def fetch(self, params):
103
- # 请求逻辑
104
- encrypted = self.encrypt(params)
105
- resp = self.session.post(url, data=encrypted)
106
- return resp.json()
107
-
108
- if __name__ == "__main__":
109
- c = Crawler()
110
- # c.login("user", "pass") # 如需要
111
- data = c.fetch({"page": 1})
112
- print(data)
113
- \`\`\`
114
-
115
- ## 工作流程
116
- 1. 分析网站特征
117
- 2. 调度子代理获取模块
118
- 3. 整合为完整脚本
119
- 4. E2E 验证
120
- 5. 输出文件
121
-
122
- ## 经验记录
123
- 完成爬虫编排后,如发现有价值的经验,使用 evolve_skill 记录:
124
- - skill: "crawler"`,
58
+ ## 常见问题处理
59
+ - 加密代码路径找不到 → query_store 搜索,或返回告知主 agent
60
+ - 分析结果中缺少关键 Headers → 从原始请求详情中补全,不要猜测
61
+ - 框架不熟悉 → 用 requests 作为降级方案,说明原因
62
+ `,
125
63
  tools: [
126
64
  ...crawlerTools,
127
65
  ...fileTools,
128
- ...evolveTools,
129
66
  ...storeTools,
67
+ // 页面结构分析(主 agent 不持有,防止拉 HTML 自己分析 JS)
68
+ getPageSource,
69
+ getElementHtml,
130
70
  ],
131
- middleware: [
132
- createFilterToolsMiddleware(),
133
- createSkillsMiddleware({
134
- backend: skillsBackend,
135
- sources: [SKILLS.crawler, SKILLS.xpath],
136
- }),
137
- ],
138
- };
71
+ skills: ['crawler', 'xpath'],
72
+ evolveSkill: 'crawler',
73
+ });
@@ -3,19 +3,127 @@
3
3
  * 统一子代理创建,自动注入公共配置
4
4
  */
5
5
 
6
+ import { createMiddleware, toolRetryMiddleware, contextEditingMiddleware, ClearToolUsesEdit } from 'langchain';
6
7
  import { createSkillsMiddleware } from 'deepagents';
7
8
  import { SKILLS, skillsBackend } from '../skills/config.js';
8
9
  import { createFilterToolsMiddleware } from '../middleware/filterTools.js';
9
10
  import { evolveTools } from '../tools/evolve.js';
10
11
 
12
+ /**
13
+ * 子代理工具调用上限
14
+ * 正常任务 30-50 次足够,80 留有余量但能防止无限循环
15
+ */
16
+ const SUBAGENT_RUN_LIMIT = 80;
17
+
18
+ /**
19
+ * 子代理执行纪律提示
20
+ * 注入所有子代理的公共行为规范
21
+ */
22
+ export const SUBAGENT_DISCIPLINE_PROMPT = `
23
+
24
+ ## 执行纪律
25
+
26
+ ### 先验证再展开
27
+ - 先用最小代价验证假设(一次工具调用),确认可行后再展开
28
+ - 不要一上来就拉全量数据或启动完整流程
29
+
30
+ ### Think/Reflect — 异常时暂停
31
+ 遇到以下情况,先输出思考过程再行动:
32
+ - 执行结果与预期不符
33
+ - 连续 2 次工具调用失败
34
+ - 需要在多个方案中选择
35
+
36
+ ### 循环检测
37
+ 同一操作最多重试 3 次。第 3 次失败后:
38
+ 1. 分析失败模式,不要简单重试
39
+ 2. 尝试替代方案(换工具、换参数、换思路)
40
+ 3. 替代方案也失败 → 总结当前进度和卡点,返回给主 agent
41
+
42
+ ### 信息优先级
43
+ 1. 已捕获的数据(请求/响应/Hook 记录)— 最可靠
44
+ 2. 工具实时获取的结果 — 需验证
45
+ 3. 模型推断 — 仅作参考,必须验证后才能作为结论`;
46
+
47
+ /**
48
+ * 创建工具调用次数限制中间件
49
+ * - wrapToolCall 计数
50
+ * - wrapModelCall 达到上限后注入提示引导模型总结返回
51
+ * - beforeAgent 每次子代理被调用时重置计数器,避免跨任务累积
52
+ *
53
+ * 注意:callCount 通过闭包持有,假设同一子代理不会被并行调用。
54
+ * deepagents 当前是串行调度子代理,如果未来支持并行需改为 per-invocation 计数。
55
+ */
56
+ export function createToolCallLimitMiddleware(runLimit = SUBAGENT_RUN_LIMIT) {
57
+ let callCount = 0;
58
+
59
+ return createMiddleware({
60
+ name: 'toolCallLimitMiddleware',
61
+
62
+ beforeAgent: async () => {
63
+ callCount = 0;
64
+ },
65
+
66
+ wrapToolCall: async (request, handler) => {
67
+ callCount++;
68
+
69
+ // 超过上限直接阻止,不经过 LLM
70
+ if (callCount > runLimit) {
71
+ return {
72
+ type: 'tool',
73
+ name: request.tool?.name || request.toolCall?.name || 'unknown',
74
+ content: JSON.stringify({
75
+ success: false,
76
+ error: `工具调用次数已达上限 (${runLimit})。请总结当前发现并返回。`,
77
+ callCount,
78
+ runLimit,
79
+ }),
80
+ tool_call_id: request.toolCall?.id || `limit_${callCount}`,
81
+ status: 'error',
82
+ };
83
+ }
84
+
85
+ return handler(request);
86
+ },
87
+ });
88
+ }
89
+
90
+ /**
91
+ * 创建子代理基础中间件数组
92
+ * 包含:工具错误兜底 + 工具过滤 + 调用次数限制 + 技能注入
93
+ * @param {Array} skillsSources - 技能源列表
94
+ */
95
+ export function createBaseMiddleware(skillsSources = []) {
96
+ return [
97
+ toolRetryMiddleware({ // 工具错误 → ToolMessage,LLM 自我修正
98
+ maxRetries: 0, // schema 错误是确定性的,不重试
99
+ onFailure: (err) => `Tool call failed: ${err.message}\nPlease fix the arguments and retry.`,
100
+ }),
101
+ createFilterToolsMiddleware(),
102
+ createToolCallLimitMiddleware(),
103
+ contextEditingMiddleware({ // 清理旧工具结果,防止上下文膨胀
104
+ edits: [new ClearToolUsesEdit({
105
+ trigger: { tokens: 80000 }, // 80k token 触发(子代理上下文较短)
106
+ keep: { messages: 5 }, // 保留最近 5 条工具结果
107
+ excludeTools: ['save_memo'], // scratchpad 内容不清理
108
+ })],
109
+ }),
110
+ createSkillsMiddleware({
111
+ backend: skillsBackend,
112
+ sources: skillsSources,
113
+ }),
114
+ ];
115
+ }
116
+
11
117
  /**
12
118
  * 创建子代理配置
119
+ * 自动注入 evolveTools + createBaseMiddleware,减少子代理定义中的重复代码
13
120
  * @param {Object} config - 子代理配置
14
121
  * @param {string} config.name - 子代理名称
15
122
  * @param {string} config.description - 子代理描述
16
123
  * @param {string} config.systemPrompt - 系统提示
17
124
  * @param {Array} config.tools - 工具列表
18
- * @param {Array} [config.skills] - 技能源列表(SKILLS 枚举值)
125
+ * @param {Array<string>} [config.skills] - SKILLS 的 key 列表(如 ['static', 'xpath']),自动映射为路径
126
+ * @param {string|string[]} [config.evolveSkill='general'] - evolve_skill 的目标 skill(对应 evolve.js skillMap key),支持数组表示多领域
19
127
  * @param {Array} [config.middleware] - 额外的中间件
20
128
  * @param {boolean} [config.includeEvolve=true] - 是否包含 evolve 工具
21
129
  */
@@ -26,29 +134,41 @@ export function createSubagent(config) {
26
134
  systemPrompt,
27
135
  tools,
28
136
  skills = [],
137
+ evolveSkill = 'general',
29
138
  middleware = [],
30
139
  includeEvolve = true,
31
140
  } = config;
32
141
 
33
- // 合并工具列表
34
142
  const finalTools = includeEvolve
35
143
  ? [...tools, ...evolveTools]
36
144
  : tools;
37
145
 
38
- // 构建中间件列表
146
+ // skills key → SKILLS 路径值
147
+ const skillsSources = skills.map((key) => {
148
+ const source = SKILLS[key];
149
+ if (!source) throw new Error(`Unknown skill key: "${key}". Valid keys: ${Object.keys(SKILLS).join(', ')}`);
150
+ return source;
151
+ });
152
+
39
153
  const finalMiddleware = [
40
- createFilterToolsMiddleware(),
41
- createSkillsMiddleware({
42
- backend: skillsBackend,
43
- sources: skills,
44
- }),
154
+ ...createBaseMiddleware(skillsSources),
45
155
  ...middleware,
46
156
  ];
47
157
 
158
+ // 自动拼接通用 prompt 段落:经验记录 + 执行纪律
159
+ let evolvePrompt;
160
+ if (Array.isArray(evolveSkill)) {
161
+ const list = evolveSkill.map(s => ` - "${s}"`).join('\n');
162
+ evolvePrompt = `\n\n## 经验记录\n完成任务后,如发现有价值的经验,使用 evolve_skill 记录。根据经验所属领域选择对应 skill:\n${list}`;
163
+ } else {
164
+ evolvePrompt = `\n\n## 经验记录\n完成任务后,如发现有价值的经验,使用 evolve_skill 记录:\n- skill: "${evolveSkill}"`;
165
+ }
166
+ const fullPrompt = systemPrompt + evolvePrompt + SUBAGENT_DISCIPLINE_PROMPT;
167
+
48
168
  return {
49
169
  name,
50
170
  description,
51
- systemPrompt,
171
+ systemPrompt: fullPrompt,
52
172
  tools: finalTools,
53
173
  middleware: finalMiddleware,
54
174
  };
@@ -8,33 +8,24 @@ export { createSubagent, SKILLS } from './factory.js';
8
8
  // 编排层
9
9
  export { crawlerSubagent } from './crawler.js';
10
10
 
11
- // 逆向分析
12
- export { staticSubagent } from './static.js';
13
- export { dynamicSubagent } from './dynamic.js';
14
- export { sandboxSubagent } from './sandbox.js';
11
+ // 逆向分析(合并原 static + dynamic + sandbox + env-agent)
12
+ export { reverseSubagent } from './reverse.js';
15
13
  export { js2pythonSubagent } from './js2python.js';
16
- export { envAgentSubagent } from './env-agent.js';
17
14
 
18
15
  // 爬虫能力
19
16
  export { captchaSubagent } from './captcha.js';
20
17
  export { antiDetectSubagent } from './anti-detect.js';
21
18
 
22
19
  import { crawlerSubagent } from './crawler.js';
23
- import { staticSubagent } from './static.js';
24
- import { dynamicSubagent } from './dynamic.js';
25
- import { sandboxSubagent } from './sandbox.js';
20
+ import { reverseSubagent } from './reverse.js';
26
21
  import { js2pythonSubagent } from './js2python.js';
27
- import { envAgentSubagent } from './env-agent.js';
28
22
  import { captchaSubagent } from './captcha.js';
29
23
  import { antiDetectSubagent } from './anti-detect.js';
30
24
 
31
25
  export const allSubagents = [
32
26
  crawlerSubagent,
33
- staticSubagent,
34
- dynamicSubagent,
35
- sandboxSubagent,
27
+ reverseSubagent,
36
28
  js2pythonSubagent,
37
- envAgentSubagent,
38
29
  captchaSubagent,
39
30
  antiDetectSubagent,
40
31
  ];
@@ -3,70 +3,60 @@
3
3
  * 将 JS 加密逻辑转换为 Python 代码
4
4
  */
5
5
 
6
- import { createSkillsMiddleware } from 'deepagents';
7
- import { SKILLS, skillsBackend } from '../skills/config.js';
8
- import { createFilterToolsMiddleware } from '../middleware/filterTools.js';
6
+ import { createSubagent } from './factory.js';
9
7
 
10
8
  import { pythonTools } from '../tools/python.js';
11
9
  import { nodejsTools } from '../tools/nodejs.js';
12
10
  import { analyzerTools } from '../tools/analyzer.js';
13
11
  import { fileTools } from '../tools/file.js';
14
- import { evolveTools } from '../tools/evolve.js';
15
12
 
16
- export const js2pythonSubagent = {
13
+ export const js2pythonSubagent = createSubagent({
17
14
  name: 'js2python',
18
- description: 'JS转Python专家。当需要将JS加密代码转换为Python时使用,适用于:爬虫项目需要Python实现、标准加密算法转换、复杂算法使用execjs方案。',
15
+ description: 'JS转Python专家。适用于:将已还原的 JS 加密代码转换为 Python 实现、标准加密算法转换、复杂算法 execjs 方案。输入必须是已分析清楚的 JS 代码。不能做代码分析、不能反混淆、不能控制浏览器。',
19
16
  systemPrompt: `你是 DeepSpider 的 JS 转 Python 专家,负责将 JS 加密逻辑转换为 Python 代码。
20
17
 
21
18
  ## 核心职责
22
- 将 JS 加密算法转换为 Python 实现,保证可以成功运行。
19
+ 将 JS 加密算法转换为 Python 实现,保证输出与原始 JS 结果完全一致。
23
20
 
24
21
  ## 转换策略
25
22
 
26
23
  ### 策略一:纯 Python 重写(优先)
27
- 适用:标准加密算法(AES、MD5、SHA、RSA、国密)
24
+ 适用:标准加密算法(AES、MD5、SHA、HMAC、RSA、国密 SM2/SM3/SM4)
25
+ 常用库:pycryptodome、hashlib、hmac、gmssl
28
26
 
29
27
  ### 策略二:execjs 执行原始 JS
30
- 适用:复杂自定义算法、混淆代码难还原
28
+ 适用:复杂自定义算法、混淆代码难还原、位运算密集型
31
29
 
32
30
  ## 工作流程
33
- 1. 分析 JS 代码,识别加密算法类型
34
- 2. 使用 run_node_code 执行原始 JS 获取基准结果
35
- 3. 选择转换策略
36
- 4. 生成 Python 代码
37
- 5. 验证结果一致性
38
- 6. 使用 artifact_save 保存文件
39
31
 
40
- ## 输出规范
32
+ 1. **获取基准** — run_node_code 执行原始 JS,用固定输入获取基准输出
33
+ 2. **识别算法** — 分析 JS 代码,判断加密类型和关键参数(key、iv、mode、padding)
34
+ 3. **选择策略** — 标准算法走纯 Python,复杂算法走 execjs
35
+ 4. **生成代码** — 编写 Python 实现
36
+ 5. **验证一致性** — 用相同输入运行 Python 代码,对比输出是否与基准完全一致
37
+ 6. **保存文件** — artifact_save 保存 .py 文件
41
38
 
42
- **重要:必须输出完整可运行的 Python 文件**
39
+ ## 常见坑
40
+ - AES padding 差异:JS 的 CryptoJS 默认 PKCS7,Python 需手动实现
41
+ - 编码差异:JS 的 toString() 可能是 hex/base64,注意对齐
42
+ - 字节序:JS 的 charCodeAt 返回 UTF-16,Python 的 ord 返回 Unicode code point
43
+ - 大数运算:JS 的位运算是 32 位有符号,Python 是任意精度,需要 & 0xFFFFFFFF 截断
43
44
 
45
+ ## 输出规范
44
46
  1. 使用 artifact_save 保存 .py 文件
45
47
  2. 文件必须可以直接 python xxx.py 运行
46
48
  3. 包含完整 import、函数定义、使用示例
47
- 4. 禁止在对话中输出大段代码片段代替完整文件
49
+ 4. 禁止在对话中输出大段代码片段代替文件
48
50
 
49
51
  ## 降级策略
50
-
51
- 纯 Python 转换失败 3 次 → 改用 execjs 方案
52
-
53
- 目标是保证最终输出可用的代码。
54
-
55
- ## 经验记录
56
- 完成转换后,如发现有价值的经验,使用 evolve_skill 记录:
57
- - skill: "js2python"`,
52
+ 纯 Python 转换失败 3 次 → 改用 execjs 方案。目标是保证最终输出可用的代码。
53
+ `,
58
54
  tools: [
59
55
  ...pythonTools,
60
56
  ...nodejsTools,
61
57
  ...analyzerTools,
62
58
  ...fileTools,
63
- ...evolveTools,
64
- ],
65
- middleware: [
66
- createFilterToolsMiddleware(),
67
- createSkillsMiddleware({
68
- backend: skillsBackend,
69
- sources: [SKILLS.js2python],
70
- }),
71
59
  ],
72
- };
60
+ skills: ['js2python'],
61
+ evolveSkill: 'js2python',
62
+ });
@@ -0,0 +1,180 @@
1
+ /**
2
+ * DeepSpider - 逆向分析子代理
3
+ * 合并原 static + dynamic + sandbox + env-agent 的核心能力
4
+ */
5
+
6
+ import { createSubagent } from './factory.js';
7
+
8
+ // 数据查询(读脚本、搜索请求,不含 clear 工具)
9
+ import {
10
+ getSiteList, searchInResponses, getRequestDetail, getRequestList,
11
+ getRequestInitiator, getScriptList, getScriptSource, searchInScripts,
12
+ } from '../tools/tracing.js';
13
+ // 静态分析
14
+ import { preprocessTools } from '../tools/preprocess.js';
15
+ import { webcrackTools } from '../tools/webcrack.js';
16
+ import { analyzerTools } from '../tools/analyzer.js';
17
+ import { deobfuscatorTools } from '../tools/deobfuscator.js';
18
+ import { correlateTools } from '../tools/correlate.js';
19
+ import { traceTools } from '../tools/trace.js';
20
+ import { extractorTools } from '../tools/extractor.js';
21
+ // 动态分析
22
+ import { debugTools } from '../tools/debug.js';
23
+ import { captureTools } from '../tools/capture.js';
24
+ import { hookManagerTools } from '../tools/hookManager.js';
25
+ import { generateHookTools } from '../tools/generateHook.js';
26
+ // 沙箱 + 补环境
27
+ import { sandboxTools } from '../tools/sandbox.js';
28
+ import { envTools } from '../tools/env.js';
29
+ import { envDumpTools } from '../tools/envdump.js';
30
+ import { extractTools } from '../tools/extract.js';
31
+ import { patchTools } from '../tools/patch.js';
32
+ // 验证 + 执行
33
+ import { verifyAlgorithmTools } from '../tools/verifyAlgorithm.js';
34
+ import { nodejsTools } from '../tools/nodejs.js';
35
+ // 输出
36
+ import { fileTools } from '../tools/file.js';
37
+ import { storeTools } from '../tools/store.js';
38
+ // 页面交互(仅断点触发 + Cookie 采集所需的最小集)
39
+ import { reloadPage, clickElement, scrollPage, getCookies, getPageSource } from '../tools/browser.js';
40
+ // 工作记忆
41
+ import { scratchpadTools } from '../tools/scratchpad.js';
42
+
43
+ export const reverseSubagent = createSubagent({
44
+ name: 'reverse-agent',
45
+ description: '逆向分析专家。适用于:分析加密参数、反混淆、定位加密入口、还原算法、浏览器断点调试、运行时变量采集、Hook注入、沙箱执行验证、补环境。覆盖逆向分析全流程,无需在多个子代理间切换。不能生成 Python 代码(用 js2python)、不能编排爬虫(用 crawler)。',
46
+ systemPrompt: `你是 DeepSpider 的逆向分析专家,覆盖从代码分析到验证的完整逆向流程。
47
+
48
+ ## 核心能力
49
+ - **请求追溯**:从目标请求的 initiator(调用栈)直接定位发起请求的代码位置
50
+ - **静态分析**:预处理、解包、反混淆、AST 分析、加密入口定位
51
+ - **动态分析**:断点调试、Hook 注入、运行时变量采集、加密调用追踪
52
+ - **沙箱执行**:补环境、沙箱运行、算法验证
53
+ - **关联分析**:请求参数与加密函数的关联、Cookie/Header 加密定位
54
+
55
+ ## 工作流程
56
+
57
+ ### 标准逆向路径(请求驱动,必须遵守)
58
+
59
+ **核心原则:从请求出发,沿调用链向上追溯,精准定位加密入口。**
60
+
61
+ 1. **找请求** — get_request_list 找到目标 API 请求(主 agent 通常会在任务描述中提供 site 和 requestId)
62
+ 2. **看调用栈** — get_request_initiator 获取请求的 initiator(调用栈)
63
+ - 有 callFrames → 直接定位到发起请求的函数(脚本URL + 行号)→ 跳到第 3 步
64
+ - 没有 initiator → 走退化路径(见下方)
65
+ 3. **定位函数** — 根据调用栈中的脚本 URL 和行号:
66
+ - get_script_source 获取对应脚本(只取相关片段,用 offset/limit 控制,不要全量拉取)
67
+ - get_function_code 提取目标函数及其依赖
68
+ 4. **分析加密** — 在定位到的函数范围内分析加密逻辑
69
+ - 代码混淆严重 → preprocess_code + deobfuscate
70
+ - 引用运行时变量(window.x、document.x 等)→ **必须用断点采集,禁止猜测**:
71
+ a. set_breakpoint 在目标函数入口设断点
72
+ b. 触发目标代码执行(根据场景选择触发方式,见「断点触发」)
73
+ c. evaluate_at_breakpoint 采集所有运行时变量值
74
+ d. resume_execution 继续执行
75
+ - collect_property 返回 undefined 时,说明变量仅在函数执行时存在,必须走断点路径
76
+ - **禁止用 search_in_scripts 搜索变量定义来猜测值,禁止用 run_node_code 穷举尝试**
77
+ 5. **验证** — sandbox_execute 或 run_node_code 验证算法正确性
78
+
79
+ ### 退化路径(initiator 不可用时)
80
+ **触发条件:get_request_initiator 返回 error,或返回的 callFrames 为空数组。**
81
+ 1. analyze_request_params 快速识别可疑加密参数(自动检测 hex/base64/hash 模式,不依赖 Hook)
82
+ 2. search_in_scripts 搜索参数名或参数赋值位置
83
+ 3. 如果搜不到(参数名被混淆)→ set_xhr_breakpoint + get_call_stack 动态获取调用栈
84
+ 4. 定位到函数后回到标准路径第 3 步
85
+
86
+ ### 何时使用断点(优先于 Hook)
87
+ - 需要获取函数入参/返回值 → set_breakpoint + evaluate_at_breakpoint
88
+ - 需要追踪调用链 → set_xhr_breakpoint + get_call_stack
89
+ - 断点是 CDP 原生能力,不修改页面 JS,不触发反调试检测
90
+
91
+ ### 何时使用 Hook
92
+ - 需要持续监控(如观察多次请求的加密参数变化)→ Hook
93
+ - 断点无法使用时(如异步回调链复杂)→ Hook
94
+ - 注意:inject_hook 可能触发反调试检测,优先用内置 Hook(enable_hook)
95
+ - 需要生成 Hook 代码时,使用 generate_hook(type) 生成,再通过 inject_hook 注入
96
+
97
+ ### 补环境路径(算法复杂难还原时)
98
+ 1. generate_env_dump_code 生成环境自吐代码
99
+ 2. collect_env / collect_property 采集真实环境
100
+ 3. generate_patch / load_env_module 生成补丁
101
+ 4. sandbox_inject + sandbox_execute 运行
102
+
103
+ ### 禁止行为
104
+ - 禁止在有目标请求的情况下,跳过 initiator 直接拉全量源码
105
+ - 禁止把超过 5000 字符的混淆代码塞入分析上下文
106
+ - 禁止在没有明确目标函数的情况下使用 inject_hook
107
+
108
+ ## 浏览器状态
109
+ - 浏览器生命周期由主 agent 管理,你没有 launch_browser / navigate_to 工具
110
+ - 如果任务描述中包含"浏览器已就绪",你可以直接使用断点、Hook、采集工具、页面交互工具
111
+ - 如果浏览器未启动,返回结果告知主 agent 需要先启动浏览器
112
+ - 你有 reload_page、click_element、scroll_page 用于触发断点,get_cookies 用于采集 Cookie,get_page_source 用于获取页面源码(定位内联脚本等)
113
+
114
+ ### 断点触发(必须掌握)
115
+ 设置断点后,断点不会立即命中 — 需要目标代码被执行。根据目标代码的执行时机选择触发方式:
116
+ - 代码在页面加载时执行(如初始化、首次请求)→ **reload_page**
117
+ - 代码在用户交互时执行(如翻页、点击按钮、提交表单)→ **click_element**
118
+ - 代码在滚动时执行(如懒加载、无限滚动)→ **scroll_page**
119
+ - 不确定触发时机 → 先看调用栈上下文判断,或用 **reload_page** 尝试
120
+
121
+ 断点命中后,用 **evaluate_at_breakpoint** 采集运行时变量,用 **resume_execution** 继续执行
122
+
123
+ ## 工作记忆
124
+ 多步分析时,用 save_memo 记录关键发现,防止上下文丢失:
125
+ - 定位到加密函数后 → save_memo("crypto-func", "函数名 + 位置 + 算法类型")
126
+ - 找到 key/iv 来源后 → save_memo("key-source", "来源 + 值")
127
+ - 验证成功后 → save_memo("verified-code", "可运行的完整代码")
128
+ 分析开始时用 list_memo 检查是否有之前的记录可以复用。
129
+
130
+ ## 输出要求
131
+ - 返回加密算法的完整分析(入口函数、参数来源、算法类型)
132
+ - 如已验证成功,返回可独立运行的 JS 代码片段
133
+ - 如需转 Python,明确告知主 agent 委托 js2python
134
+
135
+ ### 保存前置条件(必须遵守)
136
+ - **只有通过 run_node_code 或 sandbox_execute 验证算法正确后,才能调用 artifact_save 保存代码**
137
+ - 未验证成功时,返回文本说明当前进度和卡点,不保存文件
138
+ - 禁止保存"框架代码"或"需要动态采集"的半成品代码
139
+
140
+ ## 能力边界
141
+ - 你不能生成 Python 代码,需要转换时返回结果让主 agent 委托 js2python
142
+ - 你不能编排完整爬虫流程,那是 crawler 的工作
143
+ - 你不能启动/关闭浏览器或导航到新 URL,需要时返回让主 agent 处理`,
144
+ tools: [
145
+ // 数据查询(不含 clear_site_data / clear_all_data)
146
+ getSiteList, searchInResponses, getRequestDetail, getRequestList,
147
+ getRequestInitiator, getScriptList, getScriptSource, searchInScripts,
148
+ // 静态分析
149
+ ...preprocessTools,
150
+ ...webcrackTools,
151
+ ...analyzerTools,
152
+ ...deobfuscatorTools,
153
+ ...correlateTools,
154
+ ...traceTools,
155
+ ...extractorTools,
156
+ // 动态分析
157
+ ...debugTools,
158
+ ...captureTools,
159
+ ...hookManagerTools,
160
+ ...generateHookTools,
161
+ // 沙箱 + 补环境
162
+ ...sandboxTools,
163
+ ...envTools,
164
+ ...envDumpTools,
165
+ ...extractTools,
166
+ ...patchTools,
167
+ // 验证 + 执行
168
+ ...verifyAlgorithmTools,
169
+ ...nodejsTools,
170
+ // 页面交互(断点触发 + Cookie 采集 + 页面源码)
171
+ reloadPage, clickElement, scrollPage, getCookies, getPageSource,
172
+ // 输出
173
+ ...fileTools,
174
+ ...storeTools,
175
+ // 工作记忆
176
+ ...scratchpadTools,
177
+ ],
178
+ skills: ['static', 'dynamic', 'sandbox', 'env'],
179
+ evolveSkill: ['static-analysis', 'dynamic-analysis', 'sandbox', 'env'],
180
+ });