ai-worktool 1.0.17 → 1.0.18

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/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ## 1.0.17 (2025-08-06)
1
+ ## 1.0.18 (2025-08-09)
2
2
 
3
3
 
4
4
  ### Bug Fixes
@@ -138,6 +138,7 @@
138
138
  * **agent:** 增加工具调用回调并优化测试流程 ([1cae77e](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/1cae77e7bf05c3f72771d29568034eaecfea3c8e))
139
139
  * **agent:** 增加智能体增量回复功能 ([edd406b](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/edd406b905b930a7943c9697248150f9a4505e08))
140
140
  * **ai-worktool:** 重构 Webview 交互逻辑 ([d118dec](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/d118dec14d3ca3644f41a34db4a5b028987df2db))
141
+ * **commands:** 在打开订单和使用页面时添加 token 参数 ([c20e809](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/c20e809cec5a0c2c1ca886c5414730ce4893fabd))
141
142
  * **configuration:** 增加包管理器、测试框架和编程语言选择配置项 ([ec3cceb](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/ec3cceba695485010c5c710894ea2e49f57737f8))
142
143
  * **dify-plugin:** 新增重命名文件或目录的功能 ([9bb6961](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/9bb69613a28aba6859f72b14b2aa1a586ac9ca70))
143
144
  * **extension:** 重构 Webview 并添加覆盖率报告功能 ([fd8a62b](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/fd8a62b79aff4c03af8b56779d607f01d65c175a))
package/README.md CHANGED
@@ -1,67 +1,67 @@
1
- # 🎮 吃豆豆:单测智能体
2
-
3
- <p align="center">
4
- <img src="https://aicoder.jianguoke.cn/assets/images/logo.png" alt="吃豆豆logo" width="120" height="120">
5
- </p>
6
-
7
- <p align="center">
8
- <strong>自动生成高质量单元测试,让代码覆盖率轻松达到100% 🚀</strong>
9
- </p>
10
-
11
- <p align="center">
12
- <em>你负责编写源代码,我负责编写测试代码,解放双手,专注核心业务逻辑开发 💻</em>
13
- </p>
14
-
15
- ---
16
-
17
- ## 目录
18
- - [🎮 吃豆豆:单测智能体](#-吃豆豆单测智能体)
19
- - [目录](#目录)
20
- - [🌟 特性亮点](#-特性亮点)
21
- - [🚀 核心功能](#-核心功能)
22
- - [💎 核心价值](#-核心价值)
23
- - [📝 使用指南](#-使用指南)
24
- - [🔧 适用范围](#-适用范围)
25
- - [🚀 开始使用](#-开始使用)
26
- - [💬 联系我们](#-联系我们)
27
-
28
- ## 🌟 特性亮点
29
- - 🔍 **智能代码分析**:深入理解代码结构和逻辑关系
30
- - 📝 **精准测试生成**:针对不同代码场景生成最佳测试策略
31
- - 🔄 **持续优化**:根据测试结果不断完善测试用例
32
- - 💡 **AI驱动**:利用先进的大语言模型提升测试质量
33
-
34
- ## 🚀 核心功能
35
- - 🤖 **自动化测试生成**:自动识别未覆盖代码,生成完整测试用例,直到100%代码覆盖通过
36
- - 🛠️ **智能错误修复**:检测并修复单元测试中的语法错误、逻辑错误及断言失败
37
- - 📊 **覆盖率可视化**:实时展示行/语句/函数/分支覆盖率,通过颜色编码直观呈现测试状态
38
- - 🔄 **全流程自动化**:从环境初始化、依赖安装到持续测试优化,全程无需人工干预
39
-
40
- ## 💎 核心价值
41
- - ⚡ **提升开发效率**:再也**不用手写**单测代码,专注核心业务逻辑开发
42
- - 🛡️ **保障代码质量**:确保测试覆盖率达标,降低线上bug风险
43
- - 🧩 **简化测试流程**:无需深入掌握测试框架细节,一键启动全自动化测试流程
44
- - 👁️ **可视化反馈**:通过直观的覆盖率报表和进度展示,让测试状态一目了然
45
-
46
- ## 📝 使用指南
47
- 1. 📂 打开项目文件夹
48
- 2. ▶️ 启动吃豆豆智能体
49
- 3. 📈 查看实时测试进度和覆盖率报告
50
- 4. 🧠 智能体自动编写代码
51
-
52
- ## 🔧 适用范围
53
- - 🔌 支持多种调用方式:VSCode插件、CLI命令
54
- - 💻 支持多种编程语言:TypeScript、JavaScript等
55
- - 🧪 支持主流测试框架:Jest、Vitest等
56
-
57
- ---
58
-
59
- ## 🚀 开始使用
60
- 准备好提升你的开发效率了吗?立即安装吃豆豆单测智能体,让测试工作变得轻松简单!
61
-
62
- ## 💬 联系我们
63
- 如有任何问题或建议,可以给我发邮件 9160294@qq.com。
64
-
65
- <p align="center">
66
- <strong>© 2025 吃豆豆单测智能体 - 让单元测试自动化、智能化</strong>
1
+ # 🎮 吃豆豆:单测智能体
2
+
3
+ <p align="center">
4
+ <img src="https://aicoder.jianguoke.cn/assets/images/logo.png" alt="吃豆豆logo" width="120" height="120">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>自动生成高质量单元测试,让代码覆盖率轻松达到100% 🚀</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <em>你负责编写源代码,我负责编写测试代码,解放双手,专注核心业务逻辑开发 💻</em>
13
+ </p>
14
+
15
+ ---
16
+
17
+ ## 目录
18
+ - [🎮 吃豆豆:单测智能体](#-吃豆豆单测智能体)
19
+ - [目录](#目录)
20
+ - [🌟 特性亮点](#-特性亮点)
21
+ - [🚀 核心功能](#-核心功能)
22
+ - [💎 核心价值](#-核心价值)
23
+ - [📝 使用指南](#-使用指南)
24
+ - [🔧 适用范围](#-适用范围)
25
+ - [🚀 开始使用](#-开始使用)
26
+ - [💬 联系我们](#-联系我们)
27
+
28
+ ## 🌟 特性亮点
29
+ - 🔍 **智能代码分析**:深入理解代码结构和逻辑关系
30
+ - 📝 **精准测试生成**:针对不同代码场景生成最佳测试策略
31
+ - 🔄 **持续优化**:根据测试结果不断完善测试用例
32
+ - 💡 **AI驱动**:利用先进的大语言模型提升测试质量
33
+
34
+ ## 🚀 核心功能
35
+ - 🤖 **自动化测试生成**:自动识别未覆盖代码,生成完整测试用例,直到100%代码覆盖通过
36
+ - 🛠️ **智能错误修复**:检测并修复单元测试中的语法错误、逻辑错误及断言失败
37
+ - 📊 **覆盖率可视化**:实时展示行/语句/函数/分支覆盖率,通过颜色编码直观呈现测试状态
38
+ - 🔄 **全流程自动化**:从环境初始化、依赖安装到持续测试优化,全程无需人工干预
39
+
40
+ ## 💎 核心价值
41
+ - ⚡ **提升开发效率**:再也**不用手写**单测代码,专注核心业务逻辑开发
42
+ - 🛡️ **保障代码质量**:确保测试覆盖率达标,降低线上bug风险
43
+ - 🧩 **简化测试流程**:无需深入掌握测试框架细节,一键启动全自动化测试流程
44
+ - 👁️ **可视化反馈**:通过直观的覆盖率报表和进度展示,让测试状态一目了然
45
+
46
+ ## 📝 使用指南
47
+ 1. 📂 打开项目文件夹
48
+ 2. ▶️ 启动吃豆豆智能体
49
+ 3. 📈 查看实时测试进度和覆盖率报告
50
+ 4. 🧠 智能体自动编写代码
51
+
52
+ ## 🔧 适用范围
53
+ - 🔌 支持多种调用方式:VSCode插件、CLI命令
54
+ - 💻 支持多种编程语言:TypeScript、JavaScript等
55
+ - 🧪 支持主流测试框架:Jest、Vitest等
56
+
57
+ ---
58
+
59
+ ## 🚀 开始使用
60
+ 准备好提升你的开发效率了吗?立即安装吃豆豆单测智能体,让测试工作变得轻松简单!
61
+
62
+ ## 💬 联系我们
63
+ 如有任何问题或建议,可以给我发邮件 9160294@qq.com。
64
+
65
+ <p align="center">
66
+ <strong>© 2025 吃豆豆单测智能体 - 让单元测试自动化、智能化</strong>
67
67
  </p>
Binary file
@@ -47,7 +47,7 @@ const setConversationId = (id) => {
47
47
  };
48
48
  exports.setConversationId = setConversationId;
49
49
  async function postData(url, data) {
50
- const token = await (0, user_1.getTokenStorage)().get();
50
+ const token = (0, user_1.getTokenStorage)().get();
51
51
  try {
52
52
  // 发送POST请求
53
53
  const response = await fetch(url, {
@@ -109,7 +109,7 @@ async function startAgent(name, question, vars, withConversation, ctrl, onMessag
109
109
  let msg = '';
110
110
  let lastMsgNo = 0;
111
111
  const baseURL = process.env.AI_SERVER || DEFAULT_SERVER;
112
- const token = await (0, user_1.getTokenStorage)().get();
112
+ const token = (0, user_1.getTokenStorage)().get();
113
113
  console.log(`Starting agent: ${baseURL}, token: ${token}`);
114
114
  await (0, fetch_sse_1.fetchEventData)(`${baseURL}/api/agent/exec/aicoder`, {
115
115
  method: 'POST',
@@ -10,32 +10,32 @@ const path_1 = __importDefault(require("path"));
10
10
  const tools_1 = require("../tools");
11
11
  const MAXLEN = 10000;
12
12
  function fixErrorPrompt(error) {
13
- return `单测执行失败,日志如下:
14
- \`\`\`
15
- ${error.slice(0, MAXLEN)}
16
- \`\`\`
13
+ return `单测执行失败,日志如下:
14
+ \`\`\`
15
+ ${error.slice(0, MAXLEN)}
16
+ \`\`\`
17
17
  `;
18
18
  }
19
19
  // 提示词管理,根据任务合成特定提示词
20
20
  async function fixCodePrompt(failedTest) {
21
- return `测试文件"${path_1.default.resolve(failedTest.testFilePath)}"${failedTest.testName ? '里的"' + failedTest.testName + '"' : ''}执行失败.
22
-
23
- 错误信息为:
24
- \`\`\`
25
- ${failedTest.errorMessages.join('\n')}
26
- \`\`\`
27
-
28
- 测试文件内容如下:
29
- \`\`\`
30
- ${await (0, tools_1.readFile)(failedTest.testFilePath, 'utf8', true)}
21
+ return `测试文件"${path_1.default.resolve(failedTest.testFilePath)}"${failedTest.testName ? '里的"' + failedTest.testName + '"' : ''}执行失败.
22
+
23
+ 错误信息为:
24
+ \`\`\`
25
+ ${failedTest.errorMessages.join('\n')}
26
+ \`\`\`
27
+
28
+ 测试文件内容如下:
29
+ \`\`\`
30
+ ${await (0, tools_1.readFile)(failedTest.testFilePath, 'utf8', true)}
31
31
  \`\`\``;
32
32
  }
33
33
  async function addUncoveredLinePrompt(srcPath, report) {
34
- return `源文件"${path_1.default.resolve(srcPath)}"的单元测试覆盖率是${report.coverage.lines.pct}%, 还有"${report.uncoveredLines.join(',')}"行未覆盖单测。
35
-
36
- 源文件内容如下:
37
- \`\`\`
38
- ${await (0, tools_1.readFile)(srcPath, 'utf8', true)}
34
+ return `源文件"${path_1.default.resolve(srcPath)}"的单元测试覆盖率是${report.coverage.lines.pct}%, 还有"${report.uncoveredLines.join(',')}"行未覆盖单测。
35
+
36
+ 源文件内容如下:
37
+ \`\`\`
38
+ ${await (0, tools_1.readFile)(srcPath, 'utf8', true)}
39
39
  \`\`\``;
40
40
  }
41
41
  //# sourceMappingURL=prompt.js.map
package/dist/cli.js CHANGED
File without changes
package/dist/program.js CHANGED
@@ -12,13 +12,12 @@ const testAgent_1 = require("./testAgent");
12
12
  const tools_1 = require("./tools");
13
13
  const view_1 = require("./view");
14
14
  const user_1 = require("./user");
15
+ // import { showCoverageView } from './view';
15
16
  const program = new commander_1.Command();
16
17
  program.name('testAgent').description('吃豆豆:单测智能体').version('1.0.1');
17
18
  // 屏蔽调试日志
18
19
  const logHandler = console.log;
19
- if (process.env.NODE_ENV !== 'test') {
20
- console.log = () => { };
21
- }
20
+ console.log = () => { };
22
21
  const tokenFile = path_1.default.join(os_1.default.homedir(), '.aiworktools');
23
22
  // token存储在系统用户文件夹的.ai-worktools
24
23
  (0, user_1.setTokenStorage)({
@@ -82,19 +81,13 @@ program
82
81
  });
83
82
  });
84
83
  program
85
- .command('show [projectRoot]')
84
+ .command('showCoverage [projectRoot]')
86
85
  .description('监控代码覆盖率报告')
87
86
  .option('-r, --projectRoot <directory>', '源代码文件夹', (txt) => path_1.default.resolve(txt), process.cwd())
88
87
  .option('-c, --coverageDir <string>', '覆盖率报告文件夹', 'coverage')
89
- .option('-w watch <boolean>', '是否监控代码覆盖率报告变化', false)
90
88
  .action(async (projectRoot, options) => {
91
89
  await (0, tools_1.runJestTests)(projectRoot || options.projectRoot);
92
- if (options.watch) {
93
- await (0, view_1.showCoverageView)(path_1.default.join(projectRoot || options.projectRoot, options.coverageDir));
94
- }
95
- else {
96
- await (0, view_1.loadAndDisplayCoverage)(path_1.default.join(projectRoot || options.projectRoot, options.coverageDir, view_1.COVERAGE_FILE_PATH));
97
- }
90
+ // await showCoverageView(path.join(options.projectRoot, options.coverageDir));
98
91
  });
99
92
  exports.default = program;
100
93
  //# sourceMappingURL=program.js.map
package/dist/tools/git.js CHANGED
@@ -43,7 +43,7 @@ async function clone(repoPath, remoteUrl, localPath, branch) {
43
43
  console.log(`成功克隆仓库: ${remoteUrl} 到 ${clonePath}`);
44
44
  }
45
45
  catch (error) {
46
- console.log(`克隆仓库失败: ${error}`);
46
+ console.error(`克隆仓库失败: ${error}`);
47
47
  throw error;
48
48
  }
49
49
  }
@@ -59,7 +59,7 @@ async function add(files = '.', repoPath) {
59
59
  console.log(`成功添加文件到暂存区: ${Array.isArray(files) ? files.join(', ') : files}`);
60
60
  }
61
61
  catch (error) {
62
- console.log(`添加文件失败: ${error}`);
62
+ console.error(`添加文件失败: ${error}`);
63
63
  throw error;
64
64
  }
65
65
  }
@@ -76,7 +76,7 @@ async function commit(repoPath, message, files) {
76
76
  console.log(`提交成功: ${result?.summary}`);
77
77
  }
78
78
  catch (error) {
79
- console.log(`提交失败: ${error}`);
79
+ console.error(`提交失败: ${error}`);
80
80
  throw error;
81
81
  }
82
82
  }
@@ -94,7 +94,7 @@ async function pull(repoPath, remote = 'origin', branch) {
94
94
  console.log(`拉取成功: ${result?.summary}`);
95
95
  }
96
96
  catch (error) {
97
- console.log(`拉取失败: ${error}`);
97
+ console.error(`拉取失败: ${error}`);
98
98
  throw error;
99
99
  }
100
100
  }
@@ -112,7 +112,7 @@ async function push(repoPath, remote = 'origin', branch) {
112
112
  console.log(`推送成功: ${remote}/${currentBranch}`);
113
113
  }
114
114
  catch (error) {
115
- console.log(`推送失败: ${error}`);
115
+ console.error(`推送失败: ${error}`);
116
116
  // 处理常见的推送失败情况(如远程分支有新提交)
117
117
  if (error.message.includes('non-fast-forward')) {
118
118
  console.log('提示: 远程分支有新提交,建议先拉取再推送');
@@ -132,7 +132,7 @@ async function createBranch(branchName, repoPath) {
132
132
  console.log(`成功创建并切换到分支: ${branchName}`);
133
133
  }
134
134
  catch (error) {
135
- console.log(`创建分支失败: ${error}`);
135
+ console.error(`创建分支失败: ${error}`);
136
136
  throw error;
137
137
  }
138
138
  }
@@ -148,7 +148,7 @@ async function switchBranch(branchName, repoPath) {
148
148
  console.log(`成功切换到分支: ${branchName}`);
149
149
  }
150
150
  catch (error) {
151
- console.log(`切换分支失败: ${error}`);
151
+ console.error(`切换分支失败: ${error}`);
152
152
  throw error;
153
153
  }
154
154
  }
@@ -162,7 +162,7 @@ async function getStatus(repoPath) {
162
162
  return await git.status();
163
163
  }
164
164
  catch (error) {
165
- console.log(`获取仓库状态失败: ${error}`);
165
+ console.error(`获取仓库状态失败: ${error}`);
166
166
  throw error;
167
167
  }
168
168
  }
@@ -179,7 +179,7 @@ async function addRemote(remoteName, remoteUrl, repoPath) {
179
179
  console.log(`成功添加远程仓库: ${remoteName} -> ${remoteUrl}`);
180
180
  }
181
181
  catch (error) {
182
- console.log(`添加远程仓库失败: ${error}`);
182
+ console.error(`添加远程仓库失败: ${error}`);
183
183
  throw error;
184
184
  }
185
185
  }
@@ -197,7 +197,7 @@ async function setUserInfo(username, email, repoPath) {
197
197
  console.log(`已设置用户信息: ${username} <${email}>`);
198
198
  }
199
199
  catch (error) {
200
- console.log(`设置用户信息失败: ${error}`);
200
+ console.error(`设置用户信息失败: ${error}`);
201
201
  throw error;
202
202
  }
203
203
  }
@@ -279,8 +279,7 @@ async function checkDependency(projectRoot, packageName, isDev = false) {
279
279
  async function setupTestEnvironment(projectRoot, packageManager, devLanguage, testFramework) {
280
280
  // 验证参数
281
281
  validateParameters(packageManager, devLanguage, testFramework);
282
- const resolvedPath = path_1.default.resolve(projectRoot);
283
- const packageJsonPath = path_1.default.join(resolvedPath, 'package.json');
282
+ const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
284
283
  // 异步检查并初始化package.json
285
284
  try {
286
285
  await promises_1.default.access(packageJsonPath);
@@ -288,29 +287,29 @@ async function setupTestEnvironment(projectRoot, packageManager, devLanguage, te
288
287
  catch {
289
288
  console.log('未找到package.json,初始化项目...');
290
289
  const initCommand = packageManager === 'yarn' ? 'yarn init -y' : `${packageManager} init -y`;
291
- await executeCommand(resolvedPath, initCommand, '初始化项目');
290
+ await executeCommand(projectRoot, initCommand, '初始化项目');
292
291
  }
293
292
  // 如果是TypeScript项目,检查并安装TypeScript
294
293
  if (devLanguage === 'typescript') {
295
294
  console.log('检查TypeScript是否安装...');
296
- const hasTypeScript = await checkDependency(resolvedPath, 'typescript', true);
295
+ const hasTypeScript = await checkDependency(projectRoot, 'typescript', true);
297
296
  if (!hasTypeScript) {
298
297
  const installCmd = getInstallCommand(['typescript', '@types/node'], true, packageManager);
299
- await executeCommand(resolvedPath, installCmd, '安装TypeScript及类型定义');
298
+ await executeCommand(projectRoot, installCmd, '安装TypeScript及类型定义');
300
299
  // 异步检查并创建tsconfig.json
301
- const tsconfigPath = path_1.default.join(resolvedPath, 'tsconfig.json');
300
+ const tsconfigPath = path_1.default.join(projectRoot, 'tsconfig.json');
302
301
  try {
303
302
  await promises_1.default.access(tsconfigPath);
304
303
  }
305
304
  catch {
306
- await executeCommand(resolvedPath, 'npx tsc --init', '创建tsconfig.json');
305
+ await executeCommand(projectRoot, 'npx tsc --init', '创建tsconfig.json');
307
306
  }
308
307
  }
309
308
  }
310
309
  // 检查并安装测试框架
311
310
  console.log(`检查${testFramework}是否安装...`);
312
- const hasTestFramework = await checkDependency(resolvedPath, testFramework, true);
313
- const hasTestTypes = devLanguage === 'typescript' ? await checkDependency(resolvedPath, `@types/${testFramework}`, true) : true;
311
+ const hasTestFramework = await checkDependency(projectRoot, testFramework, true);
312
+ const hasTestTypes = devLanguage === 'typescript' ? await checkDependency(projectRoot, `@types/${testFramework}`, true) : true;
314
313
  const packagesToInstall = [];
315
314
  if (!hasTestFramework) {
316
315
  packagesToInstall.push(testFramework);
package/dist/user.js CHANGED
@@ -115,54 +115,54 @@ function startCallbackServer(state, port) {
115
115
  if (callbackState !== state) {
116
116
  // eslint-disable-next-line @typescript-eslint/naming-convention
117
117
  res.writeHead(403, { 'Content-Type': 'text/html; charset=utf-8' });
118
- res.end(`
119
- <html>
120
- <head>
121
- <style>
122
- body {
123
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
124
- display: flex;
125
- flex-direction: column;
126
- align-items: center;
127
- justify-content: center;
128
- height: 100vh;
129
- margin: 0;
130
- background-color: #f8f9fa;
131
- color: #333;
132
- }
133
- .card {
134
- background-color: white;
135
- border-radius: 12px;
136
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
137
- padding: 2rem;
138
- text-align: center;
139
- max-width: 400px;
140
- width: 90%;
141
- border-left: 4px solid #dc3545;
142
- }
143
- h1 {
144
- color: #dc3545;
145
- margin-bottom: 1rem;
146
- font-size: 1.8rem;
147
- }
148
- p {
149
- margin: 0.5rem 0;
150
- color: #6c757d;
151
- }
152
- .countdown {
153
- font-weight: bold;
154
- color: #dc3545;
155
- font-size: 1.2rem;
156
- }
157
- </style>
158
- </head>
159
- <body>
160
- <div class="card">
161
- <h1>登录验证失败</h1>
162
- <p>无效的状态参数,可能存在安全风险</p>
163
- </div>
164
- </body>
165
- </html>
118
+ res.end(`
119
+ <html>
120
+ <head>
121
+ <style>
122
+ body {
123
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
124
+ display: flex;
125
+ flex-direction: column;
126
+ align-items: center;
127
+ justify-content: center;
128
+ height: 100vh;
129
+ margin: 0;
130
+ background-color: #f8f9fa;
131
+ color: #333;
132
+ }
133
+ .card {
134
+ background-color: white;
135
+ border-radius: 12px;
136
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
137
+ padding: 2rem;
138
+ text-align: center;
139
+ max-width: 400px;
140
+ width: 90%;
141
+ border-left: 4px solid #dc3545;
142
+ }
143
+ h1 {
144
+ color: #dc3545;
145
+ margin-bottom: 1rem;
146
+ font-size: 1.8rem;
147
+ }
148
+ p {
149
+ margin: 0.5rem 0;
150
+ color: #6c757d;
151
+ }
152
+ .countdown {
153
+ font-weight: bold;
154
+ color: #dc3545;
155
+ font-size: 1.2rem;
156
+ }
157
+ </style>
158
+ </head>
159
+ <body>
160
+ <div class="card">
161
+ <h1>登录验证失败</h1>
162
+ <p>无效的状态参数,可能存在安全风险</p>
163
+ </div>
164
+ </body>
165
+ </html>
166
166
  `);
167
167
  reject(new Error('登录验证失败:无效的状态参数'));
168
168
  server.close();
@@ -177,121 +177,121 @@ function startCallbackServer(state, port) {
177
177
  // 返回包含自动刷新和关闭逻辑的HTML
178
178
  // eslint-disable-next-line @typescript-eslint/naming-convention
179
179
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
180
- res.end(`
181
- <html>
182
- <head>
183
- <title>登录成功</title>
184
- <style>
185
- body {
186
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
187
- display: flex;
188
- flex-direction: column;
189
- align-items: center;
190
- justify-content: center;
191
- height: 100vh;
192
- margin: 0;
193
- background-color: #f8f9fa;
194
- color: #333;
195
- }
196
- .card {
197
- background-color: white;
198
- border-radius: 12px;
199
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
200
- padding: 2rem;
201
- text-align: center;
202
- max-width: 400px;
203
- width: 90%;
204
- border-left: 4px solid #28a745;
205
- }
206
- h1 {
207
- color: #28a745;
208
- margin-bottom: 1rem;
209
- font-size: 1.8rem;
210
- }
211
- p {
212
- margin: 0.5rem 0;
213
- color: #6c757d;
214
- }
215
- .countdown {
216
- font-weight: bold;
217
- color: #28a745;
218
- font-size: 1.2rem;
219
- }
220
- .checkmark {
221
- font-size: 3rem;
222
- color: #28a745;
223
- margin-bottom: 1rem;
224
- }
225
- </style>
226
- </head>
227
- <body>
228
- <div class="card">
229
- <div class="checkmark">✓</div>
230
- <h1>登录成功!</h1>
231
- <p>您可以关闭此窗口返回应用。</p>
232
- </div>
233
- </body>
234
- </html>
180
+ res.end(`
181
+ <html>
182
+ <head>
183
+ <title>登录成功</title>
184
+ <style>
185
+ body {
186
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
187
+ display: flex;
188
+ flex-direction: column;
189
+ align-items: center;
190
+ justify-content: center;
191
+ height: 100vh;
192
+ margin: 0;
193
+ background-color: #f8f9fa;
194
+ color: #333;
195
+ }
196
+ .card {
197
+ background-color: white;
198
+ border-radius: 12px;
199
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
200
+ padding: 2rem;
201
+ text-align: center;
202
+ max-width: 400px;
203
+ width: 90%;
204
+ border-left: 4px solid #28a745;
205
+ }
206
+ h1 {
207
+ color: #28a745;
208
+ margin-bottom: 1rem;
209
+ font-size: 1.8rem;
210
+ }
211
+ p {
212
+ margin: 0.5rem 0;
213
+ color: #6c757d;
214
+ }
215
+ .countdown {
216
+ font-weight: bold;
217
+ color: #28a745;
218
+ font-size: 1.2rem;
219
+ }
220
+ .checkmark {
221
+ font-size: 3rem;
222
+ color: #28a745;
223
+ margin-bottom: 1rem;
224
+ }
225
+ </style>
226
+ </head>
227
+ <body>
228
+ <div class="card">
229
+ <div class="checkmark">✓</div>
230
+ <h1>登录成功!</h1>
231
+ <p>您可以关闭此窗口返回应用。</p>
232
+ </div>
233
+ </body>
234
+ </html>
235
235
  `);
236
236
  resolve(token);
237
237
  }
238
238
  else {
239
239
  // eslint-disable-next-line @typescript-eslint/naming-convention
240
240
  res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
241
- res.end(`
242
- <html>
243
- <head>
244
- <style>
245
- body {
246
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
247
- display: flex;
248
- flex-direction: column;
249
- align-items: center;
250
- justify-content: center;
251
- height: 100vh;
252
- margin: 0;
253
- background-color: #f8f9fa;
254
- color: #333;
255
- }
256
- .card {
257
- background-color: white;
258
- border-radius: 12px;
259
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
260
- padding: 2rem;
261
- text-align: center;
262
- max-width: 400px;
263
- width: 90%;
264
- border-left: 4px solid #dc3545;
265
- }
266
- h1 {
267
- color: #dc3545;
268
- margin-bottom: 1rem;
269
- font-size: 1.8rem;
270
- }
271
- p {
272
- margin: 0.5rem 0;
273
- color: #6c757d;
274
- }
275
- .countdown {
276
- font-weight: bold;
277
- color: #dc3545;
278
- font-size: 1.2rem;
279
- }
280
- .error-icon {
281
- font-size: 3rem;
282
- color: #dc3545;
283
- margin-bottom: 1rem;
284
- }
285
- </style>
286
- </head>
287
- <body>
288
- <div class="card">
289
- <div class="error-icon">✗</div>
290
- <h1>登录失败</h1>
291
- <p>未收到有效的认证信息</p>
292
- </div>
293
- </body>
294
- </html>
241
+ res.end(`
242
+ <html>
243
+ <head>
244
+ <style>
245
+ body {
246
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
247
+ display: flex;
248
+ flex-direction: column;
249
+ align-items: center;
250
+ justify-content: center;
251
+ height: 100vh;
252
+ margin: 0;
253
+ background-color: #f8f9fa;
254
+ color: #333;
255
+ }
256
+ .card {
257
+ background-color: white;
258
+ border-radius: 12px;
259
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
260
+ padding: 2rem;
261
+ text-align: center;
262
+ max-width: 400px;
263
+ width: 90%;
264
+ border-left: 4px solid #dc3545;
265
+ }
266
+ h1 {
267
+ color: #dc3545;
268
+ margin-bottom: 1rem;
269
+ font-size: 1.8rem;
270
+ }
271
+ p {
272
+ margin: 0.5rem 0;
273
+ color: #6c757d;
274
+ }
275
+ .countdown {
276
+ font-weight: bold;
277
+ color: #dc3545;
278
+ font-size: 1.2rem;
279
+ }
280
+ .error-icon {
281
+ font-size: 3rem;
282
+ color: #dc3545;
283
+ margin-bottom: 1rem;
284
+ }
285
+ </style>
286
+ </head>
287
+ <body>
288
+ <div class="card">
289
+ <div class="error-icon">✗</div>
290
+ <h1>登录失败</h1>
291
+ <p>未收到有效的认证信息</p>
292
+ </div>
293
+ </body>
294
+ </html>
295
295
  `);
296
296
  reject(new Error('登录失败:未收到token'));
297
297
  }
package/dist/view.js CHANGED
@@ -7,11 +7,10 @@ exports.COVERAGE_FILE_PATH = void 0;
7
7
  exports.showCoverageView = showCoverageView;
8
8
  exports.stopMonitoring = stopMonitoring;
9
9
  exports.loadAndDisplayCoverage = loadAndDisplayCoverage;
10
- exports.disposeTimer = disposeTimer;
11
10
  const fs_1 = __importDefault(require("fs"));
12
11
  const path_1 = __importDefault(require("path"));
13
12
  const table_1 = require("table");
14
- const cli_color_1 = __importDefault(require("cli-color"));
13
+ const chalk_1 = __importDefault(require("chalk"));
15
14
  let REFRESH_INTERVAL = 3000;
16
15
  // 存储上一次的覆盖率数据
17
16
  let previousCoverage = null;
@@ -26,7 +25,7 @@ exports.COVERAGE_FILE_PATH = 'coverage-summary.json';
26
25
  // 主函数
27
26
  async function showCoverageView(rootDir, coverageFilePath = exports.COVERAGE_FILE_PATH) {
28
27
  const coveragePath = path_1.default.join(rootDir, coverageFilePath);
29
- console.log(cli_color_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
28
+ console.log(chalk_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
30
29
  // 初始加载
31
30
  await loadAndDisplayCoverage(coveragePath);
32
31
  // 设置文件监听
@@ -51,7 +50,6 @@ function stopMonitoring() {
51
50
  refreshTimer = null;
52
51
  }
53
52
  }
54
- let timer = null;
55
53
  // 加载并显示覆盖率数据
56
54
  async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INTERVAL) {
57
55
  try {
@@ -63,8 +61,7 @@ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INT
63
61
  changedCells = compareCoverage(previousCoverage, coverage);
64
62
  // 如果有变化,3秒后清除标记
65
63
  if (changedCells.length > 0) {
66
- timer = setTimeout(() => {
67
- timer = null;
64
+ setTimeout(() => {
68
65
  changedCells = [];
69
66
  loadAndDisplayCoverage(coveragePath);
70
67
  }, refreshTimeout);
@@ -76,7 +73,7 @@ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INT
76
73
  renderTable(tableData);
77
74
  }
78
75
  catch (error) {
79
- console.error(cli_color_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
76
+ console.error(chalk_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
80
77
  }
81
78
  }
82
79
  // 读取覆盖率文件
@@ -105,7 +102,7 @@ function generateTableData(coverage) {
105
102
  filePaths.sort();
106
103
  // 添加总计行
107
104
  tableData.push([
108
- cli_color_1.default.bold('总计'),
105
+ chalk_1.default.bold('总计'),
109
106
  formatPercentage(coverage.total.lines.pct),
110
107
  formatPercentage(coverage.total.statements.pct),
111
108
  formatPercentage(coverage.total.functions.pct),
@@ -129,17 +126,17 @@ function generateTableData(coverage) {
129
126
  // 格式化百分比并应用颜色
130
127
  function formatPercentage(pct) {
131
128
  if (pct === 'Unknown')
132
- return cli_color_1.default.italic('Unknown');
129
+ return chalk_1.default.gray('Unknown');
133
130
  let formatted = pct.toFixed(2) + '%';
134
131
  // 根据覆盖率设置颜色
135
132
  if (pct < 50) {
136
- formatted = cli_color_1.default.red(formatted);
133
+ formatted = chalk_1.default.red(formatted);
137
134
  }
138
135
  else if (pct < 80) {
139
- formatted = cli_color_1.default.yellow(formatted);
136
+ formatted = chalk_1.default.yellow(formatted);
140
137
  }
141
138
  else {
142
- formatted = cli_color_1.default.green(formatted);
139
+ formatted = chalk_1.default.green(formatted);
143
140
  }
144
141
  return formatted;
145
142
  }
@@ -179,7 +176,7 @@ function renderTable(tableData) {
179
176
  return row.map((cell, colIndex) => {
180
177
  // 检查是否是变化的单元格
181
178
  if (changedCells.some(([r, c]) => r === rowIndex && c === colIndex)) {
182
- return cli_color_1.default.bgRed.white(cell);
179
+ return chalk_1.default.bgRed.white(cell);
183
180
  }
184
181
  return cell;
185
182
  });
@@ -213,13 +210,7 @@ function renderTable(tableData) {
213
210
  };
214
211
  // 清除控制台并打印表格
215
212
  console.clear();
216
- console.log(cli_color_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
213
+ console.log(chalk_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
217
214
  output((0, table_1.table)(markedData, tableConfig));
218
215
  }
219
- function disposeTimer() {
220
- if (timer) {
221
- clearTimeout(timer);
222
- timer = null;
223
- }
224
- }
225
216
  //# sourceMappingURL=view.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-worktool",
3
3
  "displayName": "吃豆豆:单测智能体",
4
- "version": "1.0.17",
4
+ "version": "1.0.18",
5
5
  "description": "单元测试智能体,帮你开发单元测试用例代码直到100%单测覆盖通过。",
6
6
  "author": "jianguoke.cn",
7
7
  "license": "MIT",
@@ -14,7 +14,8 @@
14
14
  "build": "tsc",
15
15
  "test": "jest test/tools",
16
16
  "coverage": "jest --coverage test/tools",
17
- "plugin": "ts-node ./tools.ts && dify plugin package plugins/dify-plugin",
17
+ "tools": "ts-node ./tools.ts",
18
+ "plugin": "yarn tools && dify plugin package plugins/dify-plugin",
18
19
  "package": "cross-env PKG_CACHE_PATH=./binaries pkg . --targets node20-win-x64 --out-path aicoder ",
19
20
  "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
20
21
  "ver": "node ./updatever.js",
@@ -276,6 +277,7 @@
276
277
  "adm-zip": "^0.5.16",
277
278
  "conventional-changelog-cli": "^2.2.2",
278
279
  "cross-env": "^10.0.0",
280
+ "dotenv": "^17.2.1",
279
281
  "eslint": "^7.32.0",
280
282
  "husky": "^9.1.7",
281
283
  "jest": "^29.7.0",
@@ -288,6 +290,9 @@
288
290
  "vsce": "^2.6.4"
289
291
  },
290
292
  "jest": {
293
+ "setupFiles": [
294
+ "dotenv/config"
295
+ ],
291
296
  "preset": "ts-jest",
292
297
  "testEnvironment": "node"
293
298
  }