ai-worktool 1.0.18 → 1.0.34

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.
@@ -20,7 +20,7 @@ const path_1 = __importDefault(require("path"));
20
20
  */
21
21
  async function detectProjectConfig(projectRoot, defaultConfig) {
22
22
  defaultConfig = defaultConfig || {};
23
- const config = {
23
+ let config = {
24
24
  projectRoot: defaultConfig.projectRoot || projectRoot,
25
25
  projectType: defaultConfig.projectType || 'unknown',
26
26
  packageManager: defaultConfig.packageManager || 'unknown',
@@ -33,22 +33,32 @@ async function detectProjectConfig(projectRoot, defaultConfig) {
33
33
  testConfigFile: defaultConfig.testConfigFile || undefined,
34
34
  isInstalled: defaultConfig.isInstalled || undefined
35
35
  };
36
+ if (!projectRoot) {
37
+ throw new Error('项目文件夹未知.');
38
+ }
39
+ // 检查文件夹是否存在
40
+ if (!(await directoryExists(projectRoot))) {
41
+ throw new Error(`项目文件夹 "${projectRoot}" 不存在.`);
42
+ }
43
+ // 检测包管理工具
44
+ await detectPackageManager(projectRoot, config);
36
45
  // 首先检查项目是否已安装
37
46
  await checkProjectInstalled(projectRoot, config);
38
- // 只有项目已安装的情况下才进行其他检测
39
- if (config.isInstalled) {
40
- // 检测包管理工具
41
- await detectPackageManager(projectRoot, config);
42
- // 检测使用的开发语言
43
- await detectLanguages(projectRoot, config);
44
- // 检测源代码目录
45
- await detectSrcDir(projectRoot, config);
46
- // 检测测试相关配置
47
- await detectTestConfig(projectRoot, config);
48
- // 检测项目类型(前端/后端/全栈)
49
- await detectProjectType(projectRoot, config);
50
- // 检测框架信息
51
- await detectFramework(projectRoot, config);
47
+ // 检测使用的开发语言
48
+ await detectLanguages(projectRoot, config);
49
+ // 检测源代码目录
50
+ await detectSrcDir(projectRoot, config);
51
+ // 检测测试相关配置
52
+ await detectTestConfig(projectRoot, config);
53
+ // 检测项目类型(前端/后端/全栈)
54
+ await detectProjectType(projectRoot, config);
55
+ // 检测框架信息
56
+ await detectFramework(projectRoot, config);
57
+ // 检查项目目录中是否有aicoder.config.json有的直接加载作为配置信息
58
+ if (await fileExists(path_1.default.join(projectRoot, 'aicoder.config.json'))) {
59
+ const aicoderConfig = JSON.parse(await promises_1.default.readFile(path_1.default.join(projectRoot, 'aicoder.config.json'), 'utf8'));
60
+ // 合并配置信息
61
+ config = { ...config, ...aicoderConfig };
52
62
  }
53
63
  return config;
54
64
  }
@@ -81,7 +91,7 @@ async function detectPackageManager(projectRoot, config) {
81
91
  return;
82
92
  }
83
93
  }
84
- // config.packageManager = 'unknown';
94
+ config.packageManager = 'npm';
85
95
  }
86
96
  /**
87
97
  * 检测开发语言 - 排除node_modules和隐藏目录
@@ -257,7 +267,7 @@ async function detectTestConfig(projectRoot, config) {
257
267
  const testDirPath = path_1.default.join(projectRoot, config.testDir);
258
268
  if (await directoryExists(testDirPath)) {
259
269
  // 检测测试目录中实际存在的文件模式
260
- const detectedPatterns = await detectExistingTestPatterns(testDirPath, projectRoot, extensions);
270
+ const detectedPatterns = await detectExistingTestPatterns(testDirPath, projectRoot, Array.from(extensions));
261
271
  config.testFilePatterns = detectedPatterns;
262
272
  }
263
273
  }
@@ -271,6 +281,7 @@ async function detectTestConfig(projectRoot, config) {
271
281
  */
272
282
  async function detectExistingTestPatterns(testDir, projectRoot, extensions) {
273
283
  const patterns = new Set();
284
+ const extensionSets = new Set(Array.isArray(extensions) ? extensions : extensions.split(','));
274
285
  const relativeTestDir = path_1.default.relative(projectRoot, testDir);
275
286
  // 递归检查测试目录中的文件
276
287
  async function checkDir(currentDir) {
@@ -283,7 +294,7 @@ async function detectExistingTestPatterns(testDir, projectRoot, extensions) {
283
294
  else if (entry.isFile()) {
284
295
  // 检查文件是否匹配测试文件命名规范
285
296
  const extMatch = entry.name.match(/\.([^.]+)$/);
286
- if (extMatch && extensions.has(extMatch[1])) {
297
+ if (extMatch && extensionSets.has(extMatch[1])) {
287
298
  const fileName = entry.name.replace(/\.[^.]+$/, '');
288
299
  if (fileName.endsWith('.test') || fileName.endsWith('.spec')) {
289
300
  // 生成对应的glob模式
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
  }
@@ -440,6 +440,17 @@ async function getUserData() {
440
440
  }
441
441
  return null;
442
442
  }
443
+ function isTokenExpired(payload) {
444
+ if (!payload || !payload.exp) {
445
+ // 没有过期时间字段,视为过期
446
+ return true;
447
+ }
448
+ // JWT的exp是秒级时间戳,需要转换为毫秒
449
+ const expirationTime = payload.exp * 1000;
450
+ const currentTime = Date.now();
451
+ // 检查是否已过期(提前30秒过期,避免网络延迟问题)
452
+ return currentTime >= expirationTime - 30000;
453
+ }
443
454
  /**
444
455
  * 解析JWT获取其中的信息
445
456
  * @param token JWT字符串
@@ -449,6 +460,9 @@ function decodeJwt(token) {
449
460
  try {
450
461
  // 不验证签名,仅解码获取信息
451
462
  const decoded = jsonwebtoken_1.default.decode(token);
463
+ if (isTokenExpired(decoded)) {
464
+ return null;
465
+ }
452
466
  return decoded;
453
467
  }
454
468
  catch (error) {
package/dist/view.js CHANGED
@@ -7,10 +7,11 @@ 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;
10
11
  const fs_1 = __importDefault(require("fs"));
11
12
  const path_1 = __importDefault(require("path"));
12
13
  const table_1 = require("table");
13
- const chalk_1 = __importDefault(require("chalk"));
14
+ const cli_color_1 = __importDefault(require("cli-color"));
14
15
  let REFRESH_INTERVAL = 3000;
15
16
  // 存储上一次的覆盖率数据
16
17
  let previousCoverage = null;
@@ -25,7 +26,7 @@ exports.COVERAGE_FILE_PATH = 'coverage-summary.json';
25
26
  // 主函数
26
27
  async function showCoverageView(rootDir, coverageFilePath = exports.COVERAGE_FILE_PATH) {
27
28
  const coveragePath = path_1.default.join(rootDir, coverageFilePath);
28
- console.log(chalk_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
29
+ console.log(cli_color_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
29
30
  // 初始加载
30
31
  await loadAndDisplayCoverage(coveragePath);
31
32
  // 设置文件监听
@@ -50,6 +51,7 @@ function stopMonitoring() {
50
51
  refreshTimer = null;
51
52
  }
52
53
  }
54
+ let timer = null;
53
55
  // 加载并显示覆盖率数据
54
56
  async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INTERVAL) {
55
57
  try {
@@ -61,7 +63,8 @@ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INT
61
63
  changedCells = compareCoverage(previousCoverage, coverage);
62
64
  // 如果有变化,3秒后清除标记
63
65
  if (changedCells.length > 0) {
64
- setTimeout(() => {
66
+ timer = setTimeout(() => {
67
+ timer = null;
65
68
  changedCells = [];
66
69
  loadAndDisplayCoverage(coveragePath);
67
70
  }, refreshTimeout);
@@ -73,7 +76,7 @@ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INT
73
76
  renderTable(tableData);
74
77
  }
75
78
  catch (error) {
76
- console.error(chalk_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
79
+ console.error(cli_color_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
77
80
  }
78
81
  }
79
82
  // 读取覆盖率文件
@@ -102,7 +105,7 @@ function generateTableData(coverage) {
102
105
  filePaths.sort();
103
106
  // 添加总计行
104
107
  tableData.push([
105
- chalk_1.default.bold('总计'),
108
+ cli_color_1.default.bold('总计'),
106
109
  formatPercentage(coverage.total.lines.pct),
107
110
  formatPercentage(coverage.total.statements.pct),
108
111
  formatPercentage(coverage.total.functions.pct),
@@ -126,17 +129,17 @@ function generateTableData(coverage) {
126
129
  // 格式化百分比并应用颜色
127
130
  function formatPercentage(pct) {
128
131
  if (pct === 'Unknown')
129
- return chalk_1.default.gray('Unknown');
132
+ return cli_color_1.default.italic('Unknown');
130
133
  let formatted = pct.toFixed(2) + '%';
131
134
  // 根据覆盖率设置颜色
132
135
  if (pct < 50) {
133
- formatted = chalk_1.default.red(formatted);
136
+ formatted = cli_color_1.default.red(formatted);
134
137
  }
135
138
  else if (pct < 80) {
136
- formatted = chalk_1.default.yellow(formatted);
139
+ formatted = cli_color_1.default.yellow(formatted);
137
140
  }
138
141
  else {
139
- formatted = chalk_1.default.green(formatted);
142
+ formatted = cli_color_1.default.green(formatted);
140
143
  }
141
144
  return formatted;
142
145
  }
@@ -176,7 +179,7 @@ function renderTable(tableData) {
176
179
  return row.map((cell, colIndex) => {
177
180
  // 检查是否是变化的单元格
178
181
  if (changedCells.some(([r, c]) => r === rowIndex && c === colIndex)) {
179
- return chalk_1.default.bgRed.white(cell);
182
+ return cli_color_1.default.bgRed.white(cell);
180
183
  }
181
184
  return cell;
182
185
  });
@@ -210,7 +213,13 @@ function renderTable(tableData) {
210
213
  };
211
214
  // 清除控制台并打印表格
212
215
  console.clear();
213
- console.log(chalk_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
216
+ console.log(cli_color_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
214
217
  output((0, table_1.table)(markedData, tableConfig));
215
218
  }
219
+ function disposeTimer() {
220
+ if (timer) {
221
+ clearTimeout(timer);
222
+ timer = null;
223
+ }
224
+ }
216
225
  //# 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.18",
4
+ "version": "1.0.34",
5
5
  "description": "单元测试智能体,帮你开发单元测试用例代码直到100%单测覆盖通过。",
6
6
  "author": "jianguoke.cn",
7
7
  "license": "MIT",
@@ -23,9 +23,11 @@
23
23
  "restore-deps": "node ./restore-deps.js",
24
24
  "pub": "yarn remove-deps && yarn changelog && vsce publish --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
25
25
  "pubovsx": "yarn remove-deps && ovsx publish -p 47621ff6-be56-4814-865e-d2a8e8a76f86 --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
26
- "puball": "yarn ver && yarn pub && yarn pubovsx && npm publish",
26
+ "puball": "yarn ver && yarn pub && yarn pubovsx && yarn puboss && npm publish --registry=https://registry.npmjs.org",
27
27
  "patch": "yarn remove-deps && vsce publish patch --yarn && yarn restore-deps",
28
- "vspack": "yarn remove-deps && vsce package -o aicoder.vsix --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
28
+ "vspack": "yarn remove-deps && vsce package -o aicoder-1.0.34.vsix --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
29
+ "uposs": "node ./uposs.js",
30
+ "puboss": "yarn vspack && yarn uposs",
29
31
  "prepare": "husky"
30
32
  },
31
33
  "publisher": "JianGuoKe",
@@ -275,6 +277,7 @@
275
277
  "@yao-pkg/pkg": "^6.6.0",
276
278
  "@yao-pkg/pkg-fetch": "^3.5.24",
277
279
  "adm-zip": "^0.5.16",
280
+ "ali-oss": "^6.23.0",
278
281
  "conventional-changelog-cli": "^2.2.2",
279
282
  "cross-env": "^10.0.0",
280
283
  "dotenv": "^17.2.1",
Binary file