webtape-receiver 1.3.0 → 1.4.3
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 +66 -83
- package/dist/analyzer.d.ts +17 -13
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +94 -76
- package/dist/analyzer.js.map +1 -1
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +66 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +4 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +287 -0
- package/dist/context.js.map +1 -0
- package/dist/index.js +169 -43
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +9 -22
- package/dist/server.js.map +1 -1
- package/dist/storage.d.ts +29 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +149 -25
- package/dist/storage.js.map +1 -1
- package/dist/templates/context.md.ejs +63 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace.d.ts +2 -3
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +110 -9
- package/dist/workspace.js.map +1 -1
- package/dist/workspace.zip +0 -0
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,116 +1,99 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🚀 WebTape Receiver:让网页自动化像录屏一样简单
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**WebTape Receiver** 是 [WebTape](https://github.com/FurtherBank/WebTape) 生态中的核心动力引擎。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
它能将你在浏览器中的操作,瞬间转化为可直接运行且**不依赖网站 Open API 和浏览器手动操作**的自动化脚本。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm install -g webtape-receiver
|
|
9
|
-
```
|
|
7
|
+
不再需要抓包、不再需要分析复杂的 API 文档、不再需要手动处理 Cookie。你只需在浏览器里点一点,剩下的交给 WebTape。
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
---
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
# 启动 webhook 接收服务器
|
|
15
|
-
webtape-receiver serve
|
|
11
|
+
## ✨ 核心价值:录制即自动化集成
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
# http://localhost:5643/webhook
|
|
19
|
-
```
|
|
13
|
+
通过 WebTape 插件录制你的业务操作,Receiver 会自动接收并保存所有网络请求。
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
配合 AI 分析,它能直接理解你在网站上的操作,并将这些操作流程编写成**可以自动化运行的**`request.js`脚本,用于快速将网页操作以命令行、Agent 等形式自动化。
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
其中:
|
|
18
|
+
- 可自动复用你电脑 Chrome 浏览器的登录状态(Cookie),个人场景下运行无需额外网站登录过程。
|
|
19
|
+
- 对于上层业务流程,AI 会深度梳理接口间的调用链路,及其中输入和流转过程中的参数字段关系,帮你从繁琐的逆向工程和拼代码中解脱出来。
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
webtape-receiver serve [options]
|
|
27
|
-
```
|
|
21
|
+
这使得你可以将生成的函数快速集成到自己的工具、爬虫或 Agent 中:
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
| `-p, --port <number>` | 监听端口 | `5643` |
|
|
32
|
-
| `-w, --workspace <path>` | 工作区路径 | `~/Desktop/WebTape` |
|
|
33
|
-
| `--no-auto-analyze` | 接收数据后不自动运行 AI 分析 | — |
|
|
34
|
-
| `--backend <name>` | AI 分析后端 | `cursor` |
|
|
35
|
-
| `--model <name>` | AI 模型名称(例如 `kimi-k2.5`) | — |
|
|
23
|
+
让需要打开浏览器的操作可以**完全自动化**,
|
|
24
|
+
让自动化 API 调用可以**不限于网站 Open API**,**只要日常浏览器使用能通的 API,自动化都可以通**
|
|
36
25
|
|
|
37
|
-
|
|
26
|
+
无论是获取私有接口数据,还是执行复杂的业务流程,都能像调用本地函数一样简单。
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
webtape-receiver serve -p 8080 --model kimi-k2.5
|
|
41
|
-
webtape-receiver serve --no-auto-analyze
|
|
42
|
-
```
|
|
28
|
+
---
|
|
43
29
|
|
|
44
|
-
|
|
30
|
+
## 🛠️ 快速上手
|
|
31
|
+
|
|
32
|
+
### 第一步:安装
|
|
33
|
+
> 如果你还没有安装 Node.js,请先参考 [Node.js 安装指南](https://nodejs.org/zh-cn/download/package-manager) 安装 Node 和 npm。
|
|
34
|
+
|
|
35
|
+
只需一行命令,开启你的自动化之旅:
|
|
45
36
|
|
|
46
37
|
```bash
|
|
47
|
-
|
|
38
|
+
npm install -g webtape-receiver
|
|
48
39
|
```
|
|
49
40
|
|
|
50
|
-
###
|
|
51
|
-
|
|
41
|
+
### 第二步:启动服务
|
|
42
|
+
启动你的专属 Webhook 接收服务器:
|
|
52
43
|
```bash
|
|
53
|
-
webtape-receiver
|
|
44
|
+
webtape-receiver serve
|
|
54
45
|
```
|
|
46
|
+
*首次启动,将自动创建默认工作区在你的**桌面 `WebTape` 文件夹**下。*
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| `--backend <name>` | AI 分析后端 | `cursor` |
|
|
60
|
-
| `--model <name>` | AI 模型名称 | — |
|
|
61
|
-
| `--prompt-only` | 仅生成提示词文件,不执行分析 | `false` |
|
|
48
|
+
### 第三步:配置插件
|
|
49
|
+
在 WebTape Chrome 插件中,将 Webhook 地址设置为:
|
|
50
|
+
`http://localhost:5643/webhook`
|
|
62
51
|
|
|
63
|
-
|
|
52
|
+
---
|
|
64
53
|
|
|
65
|
-
|
|
66
|
-
webtape-receiver analyze 2024-01-14_12-45-30 --model kimi-k2.5
|
|
67
|
-
webtape-receiver analyze 2024-01-14_12-45-30 --prompt-only
|
|
68
|
-
```
|
|
54
|
+
## WIP 视频演示
|
|
69
55
|
|
|
70
|
-
|
|
56
|
+
### 场景:从网页操作到自动化脚本生成
|
|
71
57
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
│ ├── prompt.md # AI 分析提示词
|
|
79
|
-
│ ├── requests/ # 请求详情
|
|
80
|
-
│ │ ├── req_0001_body.json
|
|
81
|
-
│ │ └── ...
|
|
82
|
-
│ └── responses/ # 响应详情
|
|
83
|
-
│ ├── req_0001_res.json
|
|
84
|
-
│ └── ...
|
|
85
|
-
└── analyses/
|
|
86
|
-
└── 2024-01-14_12-45-30.md # AI 分析报告
|
|
87
|
-
```
|
|
58
|
+
| 阶段 | 操作 | 核心产出 |
|
|
59
|
+
| :--- | :--- | :--- |
|
|
60
|
+
| **1. 接收** | 终端运行 `webtape-receiver serve`,插件端点击导出。 | 录制数据自动进入 `recordings/` 目录。 |
|
|
61
|
+
| **2. 分析** | 运行 `webtape-receiver analyze <session>`。 | AI 自动生成 `analysis_report.md` 业务文档。 |
|
|
62
|
+
| **3. 生成** | AI 根据 `AGENTS.md` 规则编写代码。 | 自动生成 `request.js`,包含封装好的业务函数。 |
|
|
63
|
+
| **4. 集成** | 在你的项目中 `import { xxx } from './request.js'`。 | **无需逆向工程**,直接实现网页功能自动化。 |
|
|
88
64
|
|
|
89
|
-
##
|
|
65
|
+
## 📂 你的自动化工作区 (Workspace)
|
|
90
66
|
|
|
91
|
-
|
|
67
|
+
当你运行 `serve` 命令时,系统会自动为你准备好一切:
|
|
68
|
+
- **recordings/**:安全存储你所有的录制会话。
|
|
69
|
+
- **AGENTS.md**:内置 AI 分析规则,确保你的 AI 助手(如 Cursor)能精准理解业务逻辑。
|
|
70
|
+
- **自动环境配置**:自动初始化 Node.js 环境并安装必要依赖,确保生成的脚本开箱即用。
|
|
92
71
|
|
|
93
|
-
|
|
94
|
-
|------|------|------|
|
|
95
|
-
| `GET` | `/` 或 `/health` | 健康检查 |
|
|
96
|
-
| `POST` | `/` 或 `/webhook` | 接收 WebTape webhook 数据 |
|
|
97
|
-
| `POST` | `/analyze/<session>` | 触发指定会话的 AI 分析 |
|
|
98
|
-
| `POST` | `/prompt/<session>` | 生成提示词文件 |
|
|
72
|
+
---
|
|
99
73
|
|
|
100
|
-
##
|
|
74
|
+
## 💡 使用场景
|
|
101
75
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
4. 自动或手动触发 AI 分析(通过 Cursor Agent CLI)
|
|
106
|
-
5. 分析报告保存到 `analyses/` 目录
|
|
76
|
+
- **数据采集**:无需研究复杂的反爬和登录校验,直接复用浏览器的登录态获取数据。
|
|
77
|
+
- **业务自动化**:将重复的网页操作转化为脚本,定时运行或集成到你的工作流中。
|
|
78
|
+
- **AI Agent 增强**:为你的 AI 助手提供直接操作真实业务接口的能力。
|
|
107
79
|
|
|
108
|
-
|
|
80
|
+
---
|
|
109
81
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
82
|
+
## ⚠️ 温馨提示 (macOS 用户)
|
|
83
|
+
生成的 request 代码最终基于`chrome-cookies-secure`库获取本机 Chrome 浏览器的 Cookie 信息,
|
|
84
|
+
macOS 在第一次运行时会弹出安全提示。
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
- **操作**:请输入你的 **Mac 开机密码**。
|
|
89
|
+
- **建议**:请务必点击 **“始终允许” (Always Allow)**,以获得最流畅的自动化体验。
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📄 开源协议
|
|
94
|
+
基于 **MIT** 协议开源。
|
|
113
95
|
|
|
114
|
-
|
|
96
|
+
---
|
|
115
97
|
|
|
116
|
-
|
|
98
|
+
**准备好释放网页自动化的潜力了吗?**
|
|
99
|
+
[立即开始使用 WebTape](https://github.com/FurtherBank/WebTape)
|
package/dist/analyzer.d.ts
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import type { WorkspacePaths } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export declare function buildPrompt(sessionDir: string): string;
|
|
6
|
-
export type AnalyzerBackend = 'cursor';
|
|
1
|
+
import type { WorkspacePaths } from "./workspace.js";
|
|
2
|
+
export type AnalyzerBackend = "cursor" | "claude";
|
|
3
|
+
export declare const VALID_BACKENDS: readonly AnalyzerBackend[];
|
|
7
4
|
export interface AnalyzeOptions {
|
|
8
5
|
backend: AnalyzerBackend;
|
|
9
6
|
workspace: WorkspacePaths;
|
|
10
7
|
sessionDir: string;
|
|
11
8
|
model?: string;
|
|
9
|
+
/** Callback for real-time log output */
|
|
10
|
+
onLog?: (line: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface AnalyzeResult {
|
|
13
|
+
/** Whether the analysis report was successfully created */
|
|
14
|
+
success: boolean;
|
|
15
|
+
/** Expected report path */
|
|
16
|
+
reportPath: string;
|
|
17
|
+
/** Session name */
|
|
18
|
+
sessionName: string;
|
|
19
|
+
/** Analysis duration in milliseconds */
|
|
20
|
+
duration?: number;
|
|
12
21
|
}
|
|
13
22
|
/**
|
|
14
23
|
* Run AI analysis on a recording session.
|
|
15
|
-
* Returns
|
|
16
|
-
*/
|
|
17
|
-
export declare function analyzeRecording(opts: AnalyzeOptions): Promise<string>;
|
|
18
|
-
/**
|
|
19
|
-
* Generate (or refresh) the prompt.md file inside the session directory
|
|
20
|
-
* for manual use (e.g. paste into Cursor chat).
|
|
24
|
+
* Returns an AnalyzeResult indicating whether the report was created.
|
|
21
25
|
*/
|
|
22
|
-
export declare function
|
|
26
|
+
export declare function analyzeRecording(opts: AnalyzeOptions): Promise<AnalyzeResult>;
|
|
23
27
|
//# sourceMappingURL=analyzer.d.ts.map
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAElD,eAAO,MAAM,cAAc,EAAE,SAAS,eAAe,EAG3C,CAAC;AASX,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,cAAc,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,aAAa,CAAC,CAsBxB"}
|
package/dist/analyzer.js
CHANGED
|
@@ -1,109 +1,127 @@
|
|
|
1
|
-
import { execFile } from
|
|
2
|
-
import {
|
|
3
|
-
import { join } from
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
1. **梳理用户操作流程**:根据 index.json 中的 action 序列,还原用户的完整操作路径。
|
|
9
|
-
2. **分析接口调用链路**:
|
|
10
|
-
- 列出每个用户操作触发的所有 API 请求
|
|
11
|
-
- 标注请求方法、URL、状态码
|
|
12
|
-
- 分析请求之间的依赖关系(例如:登录后拿到 token,后续请求携带 token)
|
|
13
|
-
3. **识别业务模块**:根据 URL 模式和请求内容,划分业务模块(如:用户认证、数据查询、表单提交等)
|
|
14
|
-
4. **生成接口文档概要**:对每个接口给出简要说明,包括用途、请求参数、响应结构
|
|
15
|
-
5. **绘制链路图**:用 Mermaid 序列图描述核心业务流程的接口调用时序
|
|
16
|
-
|
|
17
|
-
## 录制数据
|
|
18
|
-
|
|
19
|
-
以下是 WebTape 录制的 index.json 内容:
|
|
20
|
-
|
|
21
|
-
\`\`\`json
|
|
22
|
-
{{INDEX_JSON}}
|
|
23
|
-
\`\`\`
|
|
24
|
-
|
|
25
|
-
## 请求详情目录
|
|
26
|
-
|
|
27
|
-
录制的请求详情文件位于:{{SESSION_DIR}}
|
|
28
|
-
|
|
29
|
-
请详细分析并输出 Markdown 格式的报告。`;
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
export const VALID_BACKENDS = [
|
|
5
|
+
"cursor",
|
|
6
|
+
"claude",
|
|
7
|
+
];
|
|
30
8
|
/**
|
|
31
|
-
* Build the analysis
|
|
9
|
+
* Build the analysis instruction for a specific recording session.
|
|
32
10
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!existsSync(indexPath)) {
|
|
36
|
-
throw new Error(`index.json not found in ${sessionDir}`);
|
|
37
|
-
}
|
|
38
|
-
const indexContent = readFileSync(indexPath, 'utf-8');
|
|
39
|
-
return ANALYSIS_PROMPT_TEMPLATE
|
|
40
|
-
.replace('{{INDEX_JSON}}', indexContent)
|
|
41
|
-
.replace('{{SESSION_DIR}}', sessionDir);
|
|
11
|
+
function buildInstruction(sessionName) {
|
|
12
|
+
return `"请先阅读 recordings/${sessionName}/_context.md 了解录制数据全貌,然后按照 AGENTS.md 中的指示完成分析,并将报告文件保存到对应位置。如需查看被截断的完整请求/响应体,请查阅 requests/ 和 responses/ 目录下的原始文件。"`;
|
|
42
13
|
}
|
|
43
14
|
/**
|
|
44
15
|
* Run AI analysis on a recording session.
|
|
45
|
-
* Returns
|
|
16
|
+
* Returns an AnalyzeResult indicating whether the report was created.
|
|
46
17
|
*/
|
|
47
18
|
export async function analyzeRecording(opts) {
|
|
48
19
|
const { backend, workspace, sessionDir, model } = opts;
|
|
49
|
-
const sessionName =
|
|
50
|
-
const reportPath = join(workspace.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
20
|
+
const sessionName = relative(workspace.recordings, sessionDir);
|
|
21
|
+
const reportPath = join(workspace.recordings, sessionName, "analysis_report.md");
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
if (backend === "cursor") {
|
|
24
|
+
await runCursor(workspace.root, sessionName, model, opts.onLog);
|
|
25
|
+
}
|
|
26
|
+
else if (backend === "claude") {
|
|
27
|
+
await runClaude(workspace.root, sessionName, opts.onLog);
|
|
56
28
|
}
|
|
57
|
-
|
|
58
|
-
|
|
29
|
+
else {
|
|
30
|
+
throw new Error(`Unsupported analyzer backend: ${backend}`);
|
|
59
31
|
}
|
|
60
|
-
|
|
32
|
+
const duration = Date.now() - startTime;
|
|
33
|
+
const success = existsSync(reportPath);
|
|
34
|
+
return { success, reportPath, sessionName, duration };
|
|
61
35
|
}
|
|
62
36
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* Command: cursor agent prompt "<instruction>" --model "<model>"
|
|
37
|
+
* Run analysis via `cursor agent` CLI.
|
|
38
|
+
* cwd is the workspace root so cursor reads AGENTS.md.
|
|
66
39
|
*/
|
|
67
|
-
async function
|
|
68
|
-
const instruction = '请阅读当前目录下的 prompt.md 文件,按照其中的指示完成分析任务,并将分析报告输出为 Markdown 格式。';
|
|
40
|
+
async function runCursor(workspaceRoot, sessionName, model, onLog) {
|
|
69
41
|
return new Promise((resolve, reject) => {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
instruction, '--yolo'
|
|
73
|
-
];
|
|
42
|
+
const instruction = buildInstruction(sessionName);
|
|
43
|
+
const args = ["agent", instruction, "--print", "--trust", "--yolo"];
|
|
74
44
|
if (model) {
|
|
75
|
-
args.push(
|
|
45
|
+
args.push("--model", model);
|
|
76
46
|
}
|
|
77
|
-
const child = execFile(
|
|
78
|
-
cwd:
|
|
47
|
+
const child = execFile("cursor", args, {
|
|
48
|
+
cwd: workspaceRoot,
|
|
79
49
|
maxBuffer: 10 * 1024 * 1024,
|
|
80
50
|
timeout: 5 * 60 * 1000,
|
|
81
|
-
}, (error,
|
|
51
|
+
}, (error, _stdout, stderr) => {
|
|
82
52
|
if (error) {
|
|
83
53
|
if (stderr) {
|
|
84
|
-
console.error(
|
|
54
|
+
console.error("[analyzer] cursor stderr:", stderr);
|
|
85
55
|
}
|
|
86
56
|
reject(new Error(`cursor agent failed: ${error.message}`));
|
|
87
57
|
return;
|
|
88
58
|
}
|
|
89
|
-
|
|
90
|
-
writeFileSync(reportPath, output, 'utf-8');
|
|
91
|
-
resolve(reportPath);
|
|
59
|
+
resolve();
|
|
92
60
|
});
|
|
93
|
-
|
|
61
|
+
if (onLog) {
|
|
62
|
+
child.stdout?.on("data", (data) => {
|
|
63
|
+
const lines = data.toString().split("\n");
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
if (line.trim())
|
|
66
|
+
onLog(line.trim());
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
child.stderr?.on("data", (data) => {
|
|
70
|
+
const lines = data.toString().split("\n");
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
if (line.trim())
|
|
73
|
+
onLog(line.trim());
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
child.on("error", (err) => {
|
|
94
78
|
reject(new Error(`Failed to launch cursor agent: ${err.message}. ` +
|
|
95
|
-
|
|
79
|
+
"Ensure Cursor is installed and the `cursor` CLI is on your PATH."));
|
|
96
80
|
});
|
|
97
81
|
});
|
|
98
82
|
}
|
|
99
83
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
84
|
+
* Run analysis via `claude` CLI (Claude Code).
|
|
85
|
+
* cwd is the workspace root so claude reads AGENTS.md.
|
|
102
86
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
87
|
+
async function runClaude(workspaceRoot, sessionName, onLog) {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const instruction = buildInstruction(sessionName);
|
|
90
|
+
const args = [instruction, "--dangerously-skip-permissions"];
|
|
91
|
+
const child = execFile("claude", args, {
|
|
92
|
+
cwd: workspaceRoot,
|
|
93
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
94
|
+
timeout: 5 * 60 * 1000,
|
|
95
|
+
}, (error, _stdout, stderr) => {
|
|
96
|
+
if (error) {
|
|
97
|
+
if (stderr) {
|
|
98
|
+
console.error("[analyzer] claude stderr:", stderr);
|
|
99
|
+
}
|
|
100
|
+
reject(new Error(`claude failed: ${error.message}`));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
resolve();
|
|
104
|
+
});
|
|
105
|
+
if (onLog) {
|
|
106
|
+
child.stdout?.on("data", (data) => {
|
|
107
|
+
const lines = data.toString().split("\n");
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
if (line.trim())
|
|
110
|
+
onLog(line.trim());
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
child.stderr?.on("data", (data) => {
|
|
114
|
+
const lines = data.toString().split("\n");
|
|
115
|
+
for (const line of lines) {
|
|
116
|
+
if (line.trim())
|
|
117
|
+
onLog(line.trim());
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
child.on("error", (err) => {
|
|
122
|
+
reject(new Error(`Failed to launch claude: ${err.message}. ` +
|
|
123
|
+
"Ensure Claude Code is installed and the `claude` CLI is on your PATH."));
|
|
124
|
+
});
|
|
125
|
+
});
|
|
108
126
|
}
|
|
109
127
|
//# sourceMappingURL=analyzer.js.map
|
package/dist/analyzer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AnalyzerBackend } from './analyzer.js';
|
|
2
|
+
export interface WebtapeConfig {
|
|
3
|
+
aiBackend?: AnalyzerBackend;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Load the persisted configuration, returning an empty object if none exists.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadConfig(): WebtapeConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Save the configuration to disk (merges with existing).
|
|
11
|
+
*/
|
|
12
|
+
export declare function saveConfig(partial: Partial<WebtapeConfig>): WebtapeConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Interactive prompt: ask the user to choose an AI backend.
|
|
15
|
+
* Uses Node.js built-in readline so we don't add any dependencies.
|
|
16
|
+
*/
|
|
17
|
+
export declare function promptAiBackend(): Promise<AnalyzerBackend>;
|
|
18
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAKrD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAQ1C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAQzE;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,eAAe,CAAC,CA6BhE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
const CONFIG_DIR = join(homedir(), '.webtape-receiver');
|
|
5
|
+
const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
|
|
6
|
+
/**
|
|
7
|
+
* Load the persisted configuration, returning an empty object if none exists.
|
|
8
|
+
*/
|
|
9
|
+
export function loadConfig() {
|
|
10
|
+
if (!existsSync(CONFIG_PATH))
|
|
11
|
+
return {};
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
console.warn('Failed to parse config file, using defaults:', err.message);
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Save the configuration to disk (merges with existing).
|
|
22
|
+
*/
|
|
23
|
+
export function saveConfig(partial) {
|
|
24
|
+
const current = loadConfig();
|
|
25
|
+
const merged = { ...current, ...partial };
|
|
26
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
27
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(merged, null, 2), 'utf-8');
|
|
30
|
+
return merged;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Interactive prompt: ask the user to choose an AI backend.
|
|
34
|
+
* Uses Node.js built-in readline so we don't add any dependencies.
|
|
35
|
+
*/
|
|
36
|
+
export async function promptAiBackend() {
|
|
37
|
+
const { createInterface } = await import('node:readline');
|
|
38
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log(' 请选择 AI 分析后端 / Select AI analysis backend:');
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(' 1) cursor — Cursor Agent (cursor agent …)');
|
|
44
|
+
console.log(' 2) claude — Claude Code (claude … --dangerously-skip-permissions)');
|
|
45
|
+
console.log('');
|
|
46
|
+
const ask = () => {
|
|
47
|
+
rl.question(' 请输入选项 (1/2): ', (answer) => {
|
|
48
|
+
const trimmed = answer.trim();
|
|
49
|
+
if (trimmed === '1' || trimmed === 'cursor') {
|
|
50
|
+
rl.close();
|
|
51
|
+
resolve('cursor');
|
|
52
|
+
}
|
|
53
|
+
else if (trimmed === '2' || trimmed === 'claude') {
|
|
54
|
+
rl.close();
|
|
55
|
+
resolve('claude');
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log(' 无效输入,请输入 1、2、cursor 或 claude');
|
|
59
|
+
ask();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
ask();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,YAAY,CAAC;AAiSpB,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAK9D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAatF"}
|