ai-helper-agent 1.14.10 → 1.14.11
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/bin/agent.js +17 -5
- package/package.json +1 -1
- package/src/server.js +33 -17
package/bin/agent.js
CHANGED
|
@@ -372,20 +372,32 @@ if (command === 'start') {
|
|
|
372
372
|
|
|
373
373
|
const running = await isRunning(config);
|
|
374
374
|
if (running) {
|
|
375
|
+
let apiShutdownOk = false;
|
|
375
376
|
try {
|
|
376
377
|
const resp = await fetch(`http://${config.host}:${config.port}/api/shutdown`, {
|
|
377
378
|
method: 'POST'
|
|
378
379
|
});
|
|
379
380
|
if (resp.ok) {
|
|
380
|
-
|
|
381
|
-
|
|
381
|
+
try {
|
|
382
|
+
const data = await resp.json();
|
|
383
|
+
console.log(`[Agent] ${data.message || t('agentStopped')}`);
|
|
384
|
+
} catch {
|
|
385
|
+
// 服务端在发送响应后关闭连接,json() 可能读不到完整数据
|
|
386
|
+
// 但 shutdown 请求已成功送达,服务正在关闭
|
|
387
|
+
console.log(`[Agent] ${t('agentStopped')}`);
|
|
388
|
+
}
|
|
389
|
+
apiShutdownOk = true;
|
|
382
390
|
removePidFile();
|
|
383
391
|
process.exit(0);
|
|
384
392
|
}
|
|
385
|
-
} catch
|
|
386
|
-
|
|
393
|
+
} catch {
|
|
394
|
+
// isRunning() 刚确认服务在线,fetch 出错说明服务端已关闭连接(shutdown 正在执行)
|
|
395
|
+
// 视为关闭成功,不打印错误
|
|
396
|
+
apiShutdownOk = true;
|
|
397
|
+
}
|
|
398
|
+
if (!apiShutdownOk) {
|
|
399
|
+
console.log(`[Agent] ${t('apiShutdownFallback')}`);
|
|
387
400
|
}
|
|
388
|
-
console.log(`[Agent] ${t('apiShutdownFallback')}`);
|
|
389
401
|
}
|
|
390
402
|
|
|
391
403
|
const pid = getPidFromFile();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-helper-agent",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.11",
|
|
4
4
|
"description": "AI Helper Agent - Local file access and system command execution proxy for Chrome extension / AI Helper 代理服务,为 Chrome 扩展提供本地文件读写和系统命令执行",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server.js",
|
package/src/server.js
CHANGED
|
@@ -485,6 +485,39 @@ export function startServer() {
|
|
|
485
485
|
});
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
// Agent 关闭:本机来源(CLI stop)免认证,远程来源仍需认证(防止 0.0.0.0 监听时遭网络攻击)
|
|
489
|
+
if (req.method === 'POST' && pathname === '/api/shutdown') {
|
|
490
|
+
const remoteIp = req.socket.remoteAddress || '';
|
|
491
|
+
const isLocal = remoteIp === '127.0.0.1' || remoteIp === '::1' || remoteIp === '::ffff:127.0.0.1' || remoteIp === 'localhost';
|
|
492
|
+
if (!isLocal) {
|
|
493
|
+
// 远程来源:走正常认证流程
|
|
494
|
+
const authHeader = req.headers.authorization;
|
|
495
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
496
|
+
logSecurity('shutdown_auth_missing', { path: pathname, remote: remoteIp });
|
|
497
|
+
return jsonResponse(res, 401, { success: false, error: t('error.noToken') });
|
|
498
|
+
}
|
|
499
|
+
const extId = verifyToken(authHeader.slice(7));
|
|
500
|
+
if (!extId) {
|
|
501
|
+
logSecurity('shutdown_auth_invalid', { path: pathname, remote: remoteIp });
|
|
502
|
+
return jsonResponse(res, 403, { success: false, error: t('error.invalidToken') });
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (agentOperationInProgress) {
|
|
506
|
+
return jsonResponse(res, 409, { success: false, error: t('error.operationInProgress') });
|
|
507
|
+
}
|
|
508
|
+
if (Date.now() - lastOperationTime < OPERATION_COOLDOWN_MS) {
|
|
509
|
+
const wait = Math.ceil((OPERATION_COOLDOWN_MS - (Date.now() - lastOperationTime)) / 1000);
|
|
510
|
+
return jsonResponse(res, 429, { success: false, error: t('error.tooFrequent', { wait }) });
|
|
511
|
+
}
|
|
512
|
+
agentOperationInProgress = true;
|
|
513
|
+
lastOperationTime = Date.now();
|
|
514
|
+
logSystem('shutdown', { reason: 'api_request', local: isLocal, remote: remoteIp });
|
|
515
|
+
jsonResponse(res, 200, { success: true, message: t('message.shuttingDown') });
|
|
516
|
+
// 延迟 200ms 再关闭,确保响应数据已送达客户端(res.end() 仅写入缓冲区,不保证客户端已接收)
|
|
517
|
+
setTimeout(() => shutdown(), 200);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
|
|
488
521
|
// ---------- 需要认证的接口 ----------
|
|
489
522
|
const authHeader = req.headers.authorization;
|
|
490
523
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
@@ -503,23 +536,6 @@ export function startServer() {
|
|
|
503
536
|
return jsonResponse(res, 200, { success: true, time: Date.now() });
|
|
504
537
|
}
|
|
505
538
|
|
|
506
|
-
// Agent 关闭(需认证)
|
|
507
|
-
if (req.method === 'POST' && pathname === '/api/shutdown') {
|
|
508
|
-
if (agentOperationInProgress) {
|
|
509
|
-
return jsonResponse(res, 409, { success: false, error: t('error.operationInProgress') });
|
|
510
|
-
}
|
|
511
|
-
if (Date.now() - lastOperationTime < OPERATION_COOLDOWN_MS) {
|
|
512
|
-
const wait = Math.ceil((OPERATION_COOLDOWN_MS - (Date.now() - lastOperationTime)) / 1000);
|
|
513
|
-
return jsonResponse(res, 429, { success: false, error: t('error.tooFrequent', { wait }) });
|
|
514
|
-
}
|
|
515
|
-
agentOperationInProgress = true;
|
|
516
|
-
lastOperationTime = Date.now();
|
|
517
|
-
logSystem('shutdown', { reason: 'api_request', extId });
|
|
518
|
-
jsonResponse(res, 200, { success: true, message: t('message.shuttingDown') });
|
|
519
|
-
shutdown();
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
539
|
// 重启 Agent(需认证)
|
|
524
540
|
if (req.method === 'POST' && pathname === '/api/agent/restart') {
|
|
525
541
|
if (agentOperationInProgress) {
|