ai-worktool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +1 -0
- package/README.md +67 -0
- package/dist/agents/chanjet.js +382 -0
- package/dist/agents/index.js +73 -0
- package/dist/agents/jianguoke.js +202 -0
- package/dist/agents/prompt.js +41 -0
- package/dist/agents/toolCall.js +140 -0
- package/dist/cli.js +9 -0
- package/dist/loging.js +110 -0
- package/dist/program.js +114 -0
- package/dist/testAgent.js +141 -0
- package/dist/tools/file.js +543 -0
- package/dist/tools/git.js +204 -0
- package/dist/tools/index.js +71 -0
- package/dist/tools/interface.js +3 -0
- package/dist/tools/javascript.js +183 -0
- package/dist/tools/jest.js +484 -0
- package/dist/tools/mocha.js +107 -0
- package/dist/tools/package.js +381 -0
- package/dist/tools/project.js +561 -0
- package/dist/tools/typescript.js +242 -0
- package/dist/tools/vitest.js +115 -0
- package/dist/user.js +505 -0
- package/dist/view.js +225 -0
- package/package.json +302 -0
package/dist/user.js
ADDED
@@ -0,0 +1,505 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
19
|
+
var ownKeys = function(o) {
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
21
|
+
var ar = [];
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
23
|
+
return ar;
|
24
|
+
};
|
25
|
+
return ownKeys(o);
|
26
|
+
};
|
27
|
+
return function (mod) {
|
28
|
+
if (mod && mod.__esModule) return mod;
|
29
|
+
var result = {};
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
31
|
+
__setModuleDefault(result, mod);
|
32
|
+
return result;
|
33
|
+
};
|
34
|
+
})();
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
37
|
+
};
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
39
|
+
exports.setEnvironment = setEnvironment;
|
40
|
+
exports.getEnvironment = getEnvironment;
|
41
|
+
exports.setTokenStorage = setTokenStorage;
|
42
|
+
exports.getTokenStorage = getTokenStorage;
|
43
|
+
exports.openExternalUrl = openExternalUrl;
|
44
|
+
exports.login = login;
|
45
|
+
exports.cancelLogin = cancelLogin;
|
46
|
+
exports.logout = logout;
|
47
|
+
exports.isLoggedIn = isLoggedIn;
|
48
|
+
exports.getUserData = getUserData;
|
49
|
+
exports.decodeJwt = decodeJwt;
|
50
|
+
exports.isJwtExpired = isJwtExpired;
|
51
|
+
const http = __importStar(require("http"));
|
52
|
+
const url_1 = require("url");
|
53
|
+
const crypto_1 = __importDefault(require("crypto"));
|
54
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
55
|
+
const util_1 = __importDefault(require("util"));
|
56
|
+
const child_process_1 = require("child_process");
|
57
|
+
// 转换exec为Promise形式,便于async/await使用
|
58
|
+
const execAsync = util_1.default.promisify(child_process_1.exec);
|
59
|
+
let tokenStorage = {
|
60
|
+
get: async () => {
|
61
|
+
return localStorage.getItem('token');
|
62
|
+
},
|
63
|
+
set: async (token) => {
|
64
|
+
localStorage.setItem('token', token);
|
65
|
+
},
|
66
|
+
clear: async () => {
|
67
|
+
localStorage.removeItem('token');
|
68
|
+
}
|
69
|
+
};
|
70
|
+
let environment = {
|
71
|
+
showInfoMessage: async (message) => {
|
72
|
+
console.log(message);
|
73
|
+
},
|
74
|
+
showErrorMessage: async (message) => {
|
75
|
+
console.error(message);
|
76
|
+
}
|
77
|
+
};
|
78
|
+
function setEnvironment(env) {
|
79
|
+
environment = env;
|
80
|
+
}
|
81
|
+
function getEnvironment() {
|
82
|
+
return environment;
|
83
|
+
}
|
84
|
+
function setTokenStorage(storage) {
|
85
|
+
tokenStorage = storage;
|
86
|
+
}
|
87
|
+
function getTokenStorage() {
|
88
|
+
return tokenStorage;
|
89
|
+
}
|
90
|
+
// 生成随机字符串用于CSRF保护
|
91
|
+
function generateState() {
|
92
|
+
return crypto_1.default.randomBytes(16).toString('hex');
|
93
|
+
}
|
94
|
+
let lastLoginServer = null;
|
95
|
+
let lastReject = null;
|
96
|
+
/**
|
97
|
+
* 启动临时HTTP服务器接收登录回调
|
98
|
+
* @param state 用于验证的state值
|
99
|
+
* @returns 包含token的Promise
|
100
|
+
*/
|
101
|
+
function startCallbackServer(state, port) {
|
102
|
+
return new Promise((resolve, reject) => {
|
103
|
+
lastReject = reject;
|
104
|
+
const server = (lastLoginServer = http.createServer((req, res) => {
|
105
|
+
if (!req.url) {
|
106
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
107
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
108
|
+
res.end('Bad Request');
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
const parsedUrl = new url_1.URL(req.url, `http://${req.headers.host}`);
|
112
|
+
const callbackState = parsedUrl.searchParams.get('state');
|
113
|
+
const token = parsedUrl.searchParams.get('token');
|
114
|
+
// 验证state防止CSRF攻击
|
115
|
+
if (callbackState !== state) {
|
116
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
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>
|
166
|
+
`);
|
167
|
+
reject(new Error('登录验证失败:无效的状态参数'));
|
168
|
+
server.close();
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
// 处理token
|
172
|
+
if (token) {
|
173
|
+
// 构建清理后的URL(移除token和state参数)
|
174
|
+
parsedUrl.searchParams.delete('token');
|
175
|
+
parsedUrl.searchParams.delete('state');
|
176
|
+
const cleanUrl = parsedUrl.toString();
|
177
|
+
// 返回包含自动刷新和关闭逻辑的HTML
|
178
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
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>
|
235
|
+
`);
|
236
|
+
resolve(token);
|
237
|
+
}
|
238
|
+
else {
|
239
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
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>
|
295
|
+
`);
|
296
|
+
reject(new Error('登录失败:未收到token'));
|
297
|
+
}
|
298
|
+
server.close();
|
299
|
+
}));
|
300
|
+
// 监听随机可用端口
|
301
|
+
server.listen(port, 'localhost', () => {
|
302
|
+
const address = server.address();
|
303
|
+
if (typeof address === 'object' && address) {
|
304
|
+
console.log(`回调服务器启动在端口 ${address.port}`);
|
305
|
+
}
|
306
|
+
});
|
307
|
+
// 设置超时(5分钟)
|
308
|
+
setTimeout(() => {
|
309
|
+
server.close();
|
310
|
+
reject(new Error('登录超时,请重试'));
|
311
|
+
}, 5 * 60 * 1000);
|
312
|
+
});
|
313
|
+
}
|
314
|
+
/**
|
315
|
+
* 获取可用端口
|
316
|
+
* @returns 可用端口号
|
317
|
+
*/
|
318
|
+
function getAvailablePort() {
|
319
|
+
return new Promise((resolve, reject) => {
|
320
|
+
const tempServer = http.createServer();
|
321
|
+
tempServer.listen(0, 'localhost', () => {
|
322
|
+
const address = tempServer.address();
|
323
|
+
if (typeof address === 'object' && address && address.port) {
|
324
|
+
const port = address.port;
|
325
|
+
tempServer.close(() => resolve(port));
|
326
|
+
}
|
327
|
+
else {
|
328
|
+
tempServer.close();
|
329
|
+
reject(new Error('无法获取可用端口'));
|
330
|
+
}
|
331
|
+
});
|
332
|
+
});
|
333
|
+
}
|
334
|
+
async function openExternalUrl(url) {
|
335
|
+
try {
|
336
|
+
// 根据不同操作系统,使用相应的命令打开浏览器
|
337
|
+
switch (process.platform) {
|
338
|
+
case 'darwin': // macOS
|
339
|
+
await execAsync(`open "${url}"`);
|
340
|
+
break;
|
341
|
+
case 'win32': // Windows
|
342
|
+
await execAsync(`start "" "${url}"`);
|
343
|
+
break;
|
344
|
+
default: // Linux及其他类Unix系统
|
345
|
+
await execAsync(`xdg-open "${url}"`);
|
346
|
+
break;
|
347
|
+
}
|
348
|
+
return true;
|
349
|
+
}
|
350
|
+
catch (error) {
|
351
|
+
console.error('无法打开外部浏览器:', error);
|
352
|
+
return false;
|
353
|
+
}
|
354
|
+
}
|
355
|
+
const AUTH_SERVER_URL = 'https://login.jianguoke.cn/login';
|
356
|
+
/**
|
357
|
+
* 执行登录流程
|
358
|
+
* @param env 环境接口
|
359
|
+
* @param tokenStorage token存储接口
|
360
|
+
* @param authServerUrl 认证服务器URL
|
361
|
+
* @param clientId 客户端ID
|
362
|
+
* @returns 登录成功后的token
|
363
|
+
*/
|
364
|
+
async function login(clientId) {
|
365
|
+
const authServerUrl = AUTH_SERVER_URL;
|
366
|
+
const env = getEnvironment();
|
367
|
+
const tokenStorage = getTokenStorage();
|
368
|
+
const state = generateState();
|
369
|
+
const port = await getAvailablePort();
|
370
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
371
|
+
await cancelLogin();
|
372
|
+
// 启动回调服务器
|
373
|
+
const serverPromise = startCallbackServer(state, port);
|
374
|
+
// 构建认证服务器URL
|
375
|
+
const authUrl = new url_1.URL(authServerUrl);
|
376
|
+
authUrl.searchParams.set('clientId', clientId);
|
377
|
+
authUrl.searchParams.set('redirectUrl', callbackUrl);
|
378
|
+
authUrl.searchParams.set('state', state);
|
379
|
+
authUrl.searchParams.set('responseType', 'token');
|
380
|
+
// 打开外部登录页面
|
381
|
+
const opened = await openExternalUrl(authUrl.toString());
|
382
|
+
if (!opened) {
|
383
|
+
throw new Error('无法打开外部浏览器进行登录');
|
384
|
+
}
|
385
|
+
// 等待回调返回token
|
386
|
+
const token = await serverPromise;
|
387
|
+
// 存储token
|
388
|
+
await tokenStorage.set(token);
|
389
|
+
env.showInfoMessage('登录成功!');
|
390
|
+
return token;
|
391
|
+
}
|
392
|
+
async function cancelLogin() {
|
393
|
+
if (lastLoginServer) {
|
394
|
+
lastLoginServer.close();
|
395
|
+
lastLoginServer = null;
|
396
|
+
}
|
397
|
+
if (lastReject) {
|
398
|
+
lastReject(new Error('登录已取消'));
|
399
|
+
lastReject = null;
|
400
|
+
}
|
401
|
+
}
|
402
|
+
/**
|
403
|
+
* 执行登出操作
|
404
|
+
* @param tokenStorage token存储接口
|
405
|
+
* @param env 环境接口
|
406
|
+
*/
|
407
|
+
async function logout() {
|
408
|
+
const env = getEnvironment();
|
409
|
+
const tokenStorage = getTokenStorage();
|
410
|
+
await tokenStorage.clear();
|
411
|
+
env.showInfoMessage('已成功登出');
|
412
|
+
}
|
413
|
+
/**
|
414
|
+
* 检查用户是否已登录
|
415
|
+
* @param tokenStorage token存储接口
|
416
|
+
* @returns 是否已登录
|
417
|
+
*/
|
418
|
+
function isLoggedIn() {
|
419
|
+
const tokenStorage = getTokenStorage();
|
420
|
+
const token = tokenStorage.get();
|
421
|
+
if (!token) {
|
422
|
+
return false;
|
423
|
+
}
|
424
|
+
if (isJwtExpired(token)) {
|
425
|
+
return false;
|
426
|
+
}
|
427
|
+
return true;
|
428
|
+
}
|
429
|
+
async function getUserData() {
|
430
|
+
const token = await getTokenStorage().get();
|
431
|
+
if (!token) {
|
432
|
+
return null;
|
433
|
+
}
|
434
|
+
try {
|
435
|
+
const decoded = decodeJwt(token);
|
436
|
+
return decoded;
|
437
|
+
}
|
438
|
+
catch (error) {
|
439
|
+
console.error('解析用户数据失败:', error);
|
440
|
+
}
|
441
|
+
return null;
|
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
|
+
}
|
454
|
+
/**
|
455
|
+
* 解析JWT获取其中的信息
|
456
|
+
* @param token JWT字符串
|
457
|
+
* @returns 解析后的payload对象
|
458
|
+
*/
|
459
|
+
function decodeJwt(token) {
|
460
|
+
try {
|
461
|
+
// 不验证签名,仅解码获取信息
|
462
|
+
const decoded = jsonwebtoken_1.default.decode(token);
|
463
|
+
if (isTokenExpired(decoded)) {
|
464
|
+
return null;
|
465
|
+
}
|
466
|
+
return decoded;
|
467
|
+
}
|
468
|
+
catch (error) {
|
469
|
+
console.error('JWT解码失败:', error);
|
470
|
+
return null;
|
471
|
+
}
|
472
|
+
}
|
473
|
+
/**
|
474
|
+
* 验证JWT是否过期
|
475
|
+
* @param token JWT字符串 或 已解析的JWT payload
|
476
|
+
* @returns 如果过期返回true,否则返回false
|
477
|
+
*/
|
478
|
+
function isJwtExpired(token) {
|
479
|
+
try {
|
480
|
+
let payload;
|
481
|
+
// 如果传入的是字符串,则先解析
|
482
|
+
if (typeof token === 'string') {
|
483
|
+
payload = decodeJwt(token);
|
484
|
+
if (!payload) {
|
485
|
+
return true;
|
486
|
+
} // 解析失败视为过期
|
487
|
+
}
|
488
|
+
else {
|
489
|
+
payload = token;
|
490
|
+
}
|
491
|
+
// 检查是否包含过期时间
|
492
|
+
if (!payload.exp) {
|
493
|
+
return false;
|
494
|
+
} // 没有过期时间,视为不过期
|
495
|
+
// 计算当前时间(秒级时间戳)
|
496
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
497
|
+
// 比较当前时间和过期时间
|
498
|
+
return currentTime > payload.exp;
|
499
|
+
}
|
500
|
+
catch (error) {
|
501
|
+
console.error('JWT过期检查失败:', error);
|
502
|
+
return true; // 发生错误时视为过期
|
503
|
+
}
|
504
|
+
}
|
505
|
+
//# sourceMappingURL=user.js.map
|