ai-helper-agent 1.14.3 → 1.14.4

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 CHANGED
@@ -1,19 +1,84 @@
1
1
  #!/usr/bin/env node
2
- // agent/bin/agent.js - CLI 入口
3
- // 用法: ai-helper-agent <start|stop|restart|status|paircode|config> [options]
4
- import { join } from 'path';
2
+ // agent/bin/agent.js - CLI entry point
3
+ // Usage: ai-helper-agent <start|stop|restart|status|paircode|config> [options]
4
+ import { join, dirname } from 'path';
5
5
  import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from 'fs';
6
6
  import { spawn } from 'child_process';
7
7
  import { homedir } from 'os';
8
+ import { fileURLToPath } from 'url';
8
9
 
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
11
  const AGENT_DIR = join(homedir(), '.ai-helper-agent');
10
12
  const CONFIG_FILE = join(AGENT_DIR, 'config.json');
11
13
  const PID_FILE = join(AGENT_DIR, 'agent.pid');
12
14
  const SKILLS_DIR = join(AGENT_DIR, 'skills');
13
15
  const WORKSPACE_DIR = join(AGENT_DIR, 'workspace');
14
16
 
17
+ // ==================== CLI i18n ====================
18
+ function loadCliTranslations() {
19
+ try {
20
+ const raw = readFileSync(join(__dirname, '..', 'src', 'locales', 'cli.json'), 'utf-8');
21
+ return JSON.parse(raw);
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ function resolveLang(args) {
28
+ // Priority: --lang flag > AI_HELPER_LANG env > default 'en'
29
+ for (let i = 0; i < args.length; i++) {
30
+ if (args[i] === '--lang' && args[i + 1]) {
31
+ const val = args[i + 1].toLowerCase();
32
+ if (val === 'zh' || val === 'en') return val;
33
+ }
34
+ }
35
+ const envLang = process.env.AI_HELPER_LANG;
36
+ if (envLang && (envLang === 'zh' || envLang === 'en')) return envLang;
37
+ return 'en';
38
+ }
39
+
40
+ // Remove --lang <val> from args to avoid passing to server
41
+ function filterLangArgs(args) {
42
+ const filtered = [];
43
+ for (let i = 0; i < args.length; i++) {
44
+ if (args[i] === '--lang' && args[i + 1]) {
45
+ i++; // skip the value too
46
+ } else {
47
+ filtered.push(args[i]);
48
+ }
49
+ }
50
+ return filtered;
51
+ }
52
+
53
+ const lang = resolveLang(process.argv);
54
+ process.env.AI_HELPER_LANG = lang;
55
+ const translations = loadCliTranslations();
56
+ const msgs = translations ? translations[lang] : null;
57
+
58
+ function t(key, vars) {
59
+ if (!msgs || !msgs[key]) return key;
60
+ let msg = msgs[key];
61
+ if (vars) {
62
+ for (const [k, v] of Object.entries(vars)) {
63
+ msg = msg.replace(new RegExp(`\\{${k}\\}`, 'g'), v);
64
+ }
65
+ }
66
+ return msg;
67
+ }
68
+
69
+ function tRestart(key, vars) {
70
+ if (!msgs?.restartHelper || !msgs.restartHelper[key]) return key;
71
+ let msg = msgs.restartHelper[key];
72
+ if (vars) {
73
+ for (const [k, v] of Object.entries(vars)) {
74
+ msg = msg.replace(new RegExp(`\\{${k}\\}`, 'g'), v);
75
+ }
76
+ }
77
+ return msg;
78
+ }
79
+
15
80
  /**
16
- * 读取 Agent 配置文件
81
+ * Read Agent config file
17
82
  */
18
83
  function readAgentConfig() {
19
84
  try {
@@ -22,39 +87,38 @@ function readAgentConfig() {
22
87
  return JSON.parse(raw);
23
88
  }
24
89
  } catch (err) {
25
- console.error(`[Agent] 配置文件读取失败: ${err.message}`);
90
+ console.error(`[Agent] ${t('configReadFailed')}: ${err.message}`);
26
91
  }
27
92
  return { port: 18910, host: '127.0.0.1' };
28
93
  }
29
94
 
30
95
  /**
31
- * 确保 Agent 配置目录存在
96
+ * Ensure Agent config directory exists
32
97
  */
33
98
  function ensureAgentDir() {
34
99
  try {
35
100
  if (!existsSync(AGENT_DIR)) {
36
101
  mkdirSync(AGENT_DIR, { recursive: true });
37
102
  }
38
- // 确保 skills 和 workspace 子目录存在
39
103
  for (const dir of [SKILLS_DIR, WORKSPACE_DIR]) {
40
104
  if (!existsSync(dir)) {
41
105
  mkdirSync(dir, { recursive: true });
42
106
  }
43
107
  }
44
108
  } catch (err) {
45
- console.error(`[Agent] 无法创建配置目录 ${AGENT_DIR}: ${err.message}`);
109
+ console.error(`[Agent] ${t('configDirCreateFailed')} ${AGENT_DIR}: ${err.message}`);
46
110
  }
47
111
  }
48
112
 
49
113
  /**
50
- * 从 PID 文件读取进程 ID(原子化读取,无 TOCTOU)
114
+ * Read PID from PID file (atomic read, no TOCTOU)
51
115
  */
52
116
  function getPidFromFile() {
53
117
  try {
54
118
  const raw = readFileSync(PID_FILE, 'utf-8').trim();
55
119
  const pid = parseInt(raw, 10);
56
120
  if (isNaN(pid) || pid <= 0) {
57
- console.error(`[Agent] PID 文件内容无效: "${raw}"`);
121
+ console.error(`[Agent] ${t('invalidPidContent')}: "${raw}"`);
58
122
  return null;
59
123
  }
60
124
  return pid;
@@ -64,18 +128,18 @@ function getPidFromFile() {
64
128
  }
65
129
 
66
130
  /**
67
- * 删除 PID 文件
131
+ * Delete PID file
68
132
  */
69
133
  function removePidFile() {
70
134
  try {
71
135
  if (existsSync(PID_FILE)) unlinkSync(PID_FILE);
72
136
  } catch (err) {
73
- console.error(`[Agent] 删除 PID 文件失败: ${err.message}`);
137
+ console.error(`[Agent] ${t('deletePidFailed')}: ${err.message}`);
74
138
  }
75
139
  }
76
140
 
77
141
  /**
78
- * 通过 PID 终止进程
142
+ * Kill process by PID
79
143
  */
80
144
  function killByPid(pid) {
81
145
  if (!pid || typeof pid !== 'number' || pid <= 0) return false;
@@ -88,14 +152,14 @@ function killByPid(pid) {
88
152
  }
89
153
 
90
154
  /**
91
- * 校验端口号
155
+ * Validate port number
92
156
  */
93
157
  function isValidPort(port) {
94
158
  return Number.isInteger(port) && port >= 1 && port <= 65535;
95
159
  }
96
160
 
97
161
  /**
98
- * 检查 Agent 是否在运行(轮询直到确认)
162
+ * Check if Agent is running (poll until confirmed)
99
163
  */
100
164
  async function isRunning(config, retries = 2) {
101
165
  for (let i = 0; i < retries; i++) {
@@ -109,7 +173,7 @@ async function isRunning(config, retries = 2) {
109
173
  }
110
174
 
111
175
  /**
112
- * 等待 Agent 退出(轮询端口释放)
176
+ * Wait for Agent to exit (poll port release)
113
177
  */
114
178
  async function waitForExit(config, timeoutMs = 5000) {
115
179
  const start = Date.now();
@@ -122,29 +186,30 @@ async function waitForExit(config, timeoutMs = 5000) {
122
186
  }
123
187
 
124
188
  /**
125
- * 打印帮助信息
189
+ * Print help message
126
190
  */
127
191
  function printHelp() {
128
- console.log('AI Helper Agent - 本地文件读写和命令执行代理');
192
+ console.log(t('title'));
129
193
  console.log('');
130
- console.log('用法: ai-helper-agent <命令> [选项]');
194
+ console.log(t('usage'));
131
195
  console.log('');
132
- console.log('命令:');
133
- console.log(' start 启动代理服务(前台运行)');
134
- console.log(' stop 停止正在运行的代理');
135
- console.log(' restart 重启代理服务');
136
- console.log(' status 查看代理运行状态');
137
- console.log(' paircode 查看当前配对码(需认证状态)');
138
- console.log(' config 查看当前配置');
139
- console.log(' help 显示此帮助信息');
196
+ console.log(t('commands'));
197
+ console.log(t('cmdStart'));
198
+ console.log(t('cmdStop'));
199
+ console.log(t('cmdRestart'));
200
+ console.log(t('cmdStatus'));
201
+ console.log(t('cmdPaircode'));
202
+ console.log(t('cmdConfig'));
203
+ console.log(t('cmdHelp'));
140
204
  console.log('');
141
- console.log('启动选项:');
142
- console.log(' --background, -b 后台运行(守护进程模式)');
143
- console.log(' --port <端口> 设置监听端口 (默认: 18910)');
144
- console.log(' --host <地址> 设置监听地址 (默认: 127.0.0.1)');
145
- console.log(' --workdir <目录> 设置工作目录 (默认: 当前目录)');
205
+ console.log(t('startupOptions'));
206
+ console.log(t('optBackground'));
207
+ console.log(t('optPort'));
208
+ console.log(t('optHost'));
209
+ console.log(t('optWorkdir'));
210
+ console.log(t('optLang'));
146
211
  console.log('');
147
- console.log('示例:');
212
+ console.log(t('examples'));
148
213
  console.log(' ai-helper-agent start');
149
214
  console.log(' ai-helper-agent start --background');
150
215
  console.log(' ai-helper-agent start -b --port 18911');
@@ -154,7 +219,7 @@ function printHelp() {
154
219
  }
155
220
 
156
221
  /**
157
- * 从命令行参数提取端口配置,校验后覆盖 config
222
+ * Apply CLI args to config, with validation
158
223
  */
159
224
  function applyCliArgs(config, args) {
160
225
  for (let i = 0; i < args.length; i++) {
@@ -162,7 +227,7 @@ function applyCliArgs(config, args) {
162
227
  if (arg === '--port' && args[i + 1]) {
163
228
  const port = parseInt(args[i + 1], 10);
164
229
  if (!isValidPort(port)) {
165
- console.error(`[Agent] 无效端口号: ${args[i + 1]},使用默认端口 ${config.port}`);
230
+ console.error(`[Agent] ${t('invalidPort', { port: args[i + 1], defaultPort: config.port })}`);
166
231
  } else {
167
232
  config.port = port;
168
233
  }
@@ -178,7 +243,9 @@ function applyCliArgs(config, args) {
178
243
  return config;
179
244
  }
180
245
 
181
- const command = process.argv[2] || 'help';
246
+ const rawArgs = process.argv.slice(2);
247
+ const command = rawArgs[0] || 'help';
248
+ const passArgs = filterLangArgs(rawArgs.slice(1));
182
249
 
183
250
  // --version / -v
184
251
  if (command === '--version' || command === '-v') {
@@ -186,7 +253,7 @@ if (command === '--version' || command === '-v') {
186
253
  const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
187
254
  console.log(`ai-helper-agent v${pkg.version}`);
188
255
  } catch (err) {
189
- console.error(`[Agent] 无法读取版本信息: ${err.message}`);
256
+ console.error(`[Agent] ${t('readVersionFailed')}: ${err.message}`);
190
257
  }
191
258
  process.exit(0);
192
259
  }
@@ -195,20 +262,15 @@ if (command === '--version' || command === '-v') {
195
262
  if (command === 'start') {
196
263
  const isBg = process.argv.includes('--background') || process.argv.includes('-b');
197
264
 
198
- // 过滤掉 --background/-b,其余参数传给子进程
199
- const passArgs = process.argv.slice(3).filter(a => a !== '--background' && a !== '-b');
200
-
201
265
  if (isBg) {
202
266
  ensureAgentDir();
203
267
 
204
- // 检查是否已有 Agent 在运行
205
268
  const existingConfig = readAgentConfig();
206
269
  if (await isRunning(existingConfig)) {
207
- console.log('[Agent] Agent 已在运行中,无需重复启动');
270
+ console.log(`[Agent] ${t('agentAlreadyRunning')}`);
208
271
  process.exit(0);
209
272
  }
210
273
 
211
- // 以 detached 模式启动子进程
212
274
  const child = spawn(process.execPath, [
213
275
  ...process.execArgv,
214
276
  process.argv[1],
@@ -222,89 +284,85 @@ if (command === 'start') {
222
284
  windowsHide: true
223
285
  });
224
286
 
225
- // 监听 spawn 错误
226
287
  let spawnFailed = false;
227
288
  child.on('error', (err) => {
228
289
  spawnFailed = true;
229
- console.error(`[Agent] 后台启动失败: ${err.message}`);
290
+ console.error(`[Agent] ${t('backgroundStartFailed')}: ${err.message}`);
230
291
  try { unlinkSync(PID_FILE); } catch {}
231
292
  process.exit(1);
232
293
  });
233
294
 
234
- // 等待进程启动确认
235
295
  await new Promise(resolve => setTimeout(resolve, 500));
236
296
 
237
297
  if (!spawnFailed && child.pid) {
238
298
  try {
239
299
  writeFileSync(PID_FILE, String(child.pid));
240
300
  } catch (err) {
241
- console.error(`[Agent] PID 文件写入失败: ${err.message}`);
301
+ console.error(`[Agent] ${t('pidWriteFailed')}: ${err.message}`);
242
302
  process.exit(1);
243
303
  }
244
304
  child.unref();
245
- console.log(`[Agent] Agent 已在后台启动 (PID: ${child.pid})`);
246
- console.log(`[Agent] 使用 'ai-helper-agent stop' 停止服务`);
247
- console.log(`[Agent] 使用 'ai-helper-agent status' 查看状态`);
305
+ console.log(`[Agent] ${t('agentStartedBg', { pid: child.pid })}`);
306
+ console.log(`[Agent] ${t('stopHint')}`);
307
+ console.log(`[Agent] ${t('statusHint')}`);
248
308
  }
249
309
 
250
310
  process.exit(spawnFailed ? 1 : 0);
251
311
  }
252
312
 
253
- // 前台模式:写入 PID 文件以便 stop 能找到
313
+ // Foreground mode
254
314
  ensureAgentDir();
255
315
  try {
256
316
  writeFileSync(PID_FILE, String(process.pid));
257
317
  } catch (err) {
258
- console.error(`[Agent] PID 文件写入失败: ${err.message}`);
318
+ console.error(`[Agent] ${t('pidWriteFailed')}: ${err.message}`);
259
319
  }
260
320
 
261
321
  const { startServer } = await import('../src/server.js');
262
322
  const { loadConfig } = await import('../src/config.js');
263
323
 
264
- console.log('[Agent] AI Helper Agent 启动中...');
324
+ console.log(`[Agent] ${t('agentStarting')}`);
265
325
 
266
326
  const config = applyCliArgs(loadConfig(), passArgs);
267
327
 
268
- console.log(`[Agent] 工作目录: ${config.workdir}`);
269
- console.log(`[Agent] 监听地址: ${config.host}:${config.port}`);
328
+ console.log(`[Agent] ${t('workingDir', { dir: config.workdir })}`);
329
+ console.log(`[Agent] ${t('listeningAddr', { host: config.host, port: config.port })}`);
270
330
 
271
331
  startServer();
272
332
 
273
333
  // ==================== stop ====================
274
334
  } else if (command === 'stop') {
275
335
  const config = readAgentConfig();
276
- console.log('[Agent] 正在停止 Agent...');
336
+ console.log(`[Agent] ${t('stoppingAgent')}`);
277
337
 
278
338
  const running = await isRunning(config);
279
339
  if (running) {
280
- // 优先通过 API 优雅关闭
281
340
  try {
282
341
  const resp = await fetch(`http://${config.host}:${config.port}/api/shutdown`, {
283
342
  method: 'POST'
284
343
  });
285
344
  if (resp.ok) {
286
345
  const data = await resp.json();
287
- console.log(`[Agent] ${data.message || 'Agent 已停止'}`);
346
+ console.log(`[Agent] ${data.message || t('agentStopped')}`);
288
347
  removePidFile();
289
348
  process.exit(0);
290
349
  }
291
350
  } catch (err) {
292
- console.error(`[Agent] API 关闭请求失败: ${err.message}`);
351
+ console.error(`[Agent] ${t('apiShutdownFailed')}: ${err.message}`);
293
352
  }
294
- console.log('[Agent] API 关闭失败,尝试通过 PID 终止...');
353
+ console.log(`[Agent] ${t('apiShutdownFallback')}`);
295
354
  }
296
355
 
297
- // 回退:通过 PID 文件终止
298
356
  const pid = getPidFromFile();
299
357
  if (pid) {
300
358
  if (killByPid(pid)) {
301
- console.log(`[Agent] 已向 PID ${pid} 发送终止信号`);
359
+ console.log(`[Agent] ${t('sigtermSent', { pid })}`);
302
360
  } else {
303
- console.log(`[Agent] 进程 ${pid} 已不存在`);
361
+ console.log(`[Agent] ${t('processGone', { pid })}`);
304
362
  }
305
363
  removePidFile();
306
364
  } else if (!running) {
307
- console.log('[Agent] Agent 未在运行');
365
+ console.log(`[Agent] ${t('agentNotRunning')}`);
308
366
  }
309
367
 
310
368
  process.exit(0);
@@ -313,25 +371,22 @@ if (command === 'start') {
313
371
  } else if (command === 'restart') {
314
372
  const isBg = process.argv.includes('--background') || process.argv.includes('-b');
315
373
 
316
- // 先停止现有实例
317
374
  const config = readAgentConfig();
318
375
  const running = await isRunning(config);
319
376
 
320
377
  if (running) {
321
- console.log('[Agent] 正在停止 Agent...');
378
+ console.log(`[Agent] ${t('stoppingAgent')}`);
322
379
  try {
323
380
  await fetch(`http://${config.host}:${config.port}/api/shutdown`, { method: 'POST' });
324
381
  } catch (err) {
325
- console.error(`[Agent] API 关闭请求失败: ${err.message}`);
382
+ console.error(`[Agent] ${t('apiShutdownFailed')}: ${err.message}`);
326
383
  }
327
- // 等待进程退出(轮询而非硬延迟)
328
384
  const exited = await waitForExit(config, 8000);
329
385
  if (!exited) {
330
- console.log('[Agent] 等待超时,尝试强制终止...');
386
+ console.log(`[Agent] ${t('waitTimeoutForceKill')}`);
331
387
  }
332
388
  }
333
389
 
334
- // 再通过 PID 兜底
335
390
  const pid = getPidFromFile();
336
391
  if (pid) {
337
392
  killByPid(pid);
@@ -339,10 +394,8 @@ if (command === 'start') {
339
394
  await new Promise(resolve => setTimeout(resolve, 500));
340
395
  }
341
396
 
342
- // 重新启动
343
397
  if (isBg) {
344
398
  ensureAgentDir();
345
- const passArgs = process.argv.slice(3).filter(a => a !== '--background' && a !== '-b');
346
399
 
347
400
  const child = spawn(process.execPath, [
348
401
  ...process.execArgv,
@@ -360,7 +413,7 @@ if (command === 'start') {
360
413
  let spawnFailed = false;
361
414
  child.on('error', (err) => {
362
415
  spawnFailed = true;
363
- console.error(`[Agent] 后台重启失败: ${err.message}`);
416
+ console.error(`[Agent] ${t('backgroundRestartFailed')}: ${err.message}`);
364
417
  try { unlinkSync(PID_FILE); } catch {}
365
418
  process.exit(1);
366
419
  });
@@ -371,40 +424,36 @@ if (command === 'start') {
371
424
  try {
372
425
  writeFileSync(PID_FILE, String(child.pid));
373
426
  } catch (err) {
374
- console.error(`[Agent] PID 文件写入失败: ${err.message}`);
427
+ console.error(`[Agent] ${t('pidWriteFailed')}: ${err.message}`);
375
428
  process.exit(1);
376
429
  }
377
430
  child.unref();
378
- console.log(`[Agent] Agent 已在后台重启 (PID: ${child.pid})`);
379
- console.log(`[Agent] 使用 'ai-helper-agent stop' 停止服务`);
431
+ console.log(`[Agent] ${t('agentRestartedBg', { pid: child.pid })}`);
432
+ console.log(`[Agent] ${t('stopHint')}`);
380
433
  }
381
434
 
382
435
  process.exit(spawnFailed ? 1 : 0);
383
436
  }
384
437
 
385
- // 前台模式
386
- console.log('[Agent] 正在启动 Agent...');
438
+ // Foreground mode
439
+ console.log(`[Agent] ${t('agentRestarting')}`);
387
440
  ensureAgentDir();
388
441
  try {
389
442
  writeFileSync(PID_FILE, String(process.pid));
390
443
  } catch (err) {
391
- console.error(`[Agent] PID 文件写入失败: ${err.message}`);
444
+ console.error(`[Agent] ${t('pidWriteFailed')}: ${err.message}`);
392
445
  }
393
446
 
394
447
  const { startServer: restartServer } = await import('../src/server.js');
395
448
  const { loadConfig: restartConfig } = await import('../src/config.js');
396
- const cfg = applyCliArgs(restartConfig(),
397
- process.argv.slice(3).filter(a => a !== '--background' && a !== '-b'));
449
+ const cfg = applyCliArgs(restartConfig(), passArgs);
398
450
 
399
- console.log(`[Agent] 工作目录: ${cfg.workdir}`);
451
+ console.log(`[Agent] ${t('workingDir', { dir: cfg.workdir })}`);
400
452
  restartServer();
401
453
 
402
- // ==================== _restart-helper(内部命令,两阶段重启包装器) ====================
454
+ // ==================== _restart-helper (internal, two-phase restart wrapper) ====================
403
455
  } else if (command === '_restart-helper') {
404
- // 由 /api/agent/restart 和 /api/agent/update 调用,detached 运行
405
- // 职责:等待老进程退出 + 端口释放后,再以 --background 模式启动新进程
406
- // 解决直接 spawn 新进程时 isRunning 检查劝退、端口 EADDRINUSE 的时序问题
407
- const helperConfig = applyCliArgs({ port: 18910, host: '127.0.0.1' }, process.argv.slice(3));
456
+ const helperConfig = applyCliArgs({ port: 18910, host: '127.0.0.1' }, passArgs);
408
457
  const oldPidIdx = process.argv.indexOf('--old-pid');
409
458
  const oldPid = oldPidIdx > -1 ? parseInt(process.argv[oldPidIdx + 1], 10) : 0;
410
459
  const scriptIdx = process.argv.indexOf('--script');
@@ -415,9 +464,8 @@ if (command === 'start') {
415
464
  const workdir = helperConfig.workdir || process.cwd();
416
465
 
417
466
  const log = (msg) => console.log(`[Agent Restart Helper] ${msg}`);
418
- log(`等待老进程 ${oldPid} 退出...`);
467
+ log(tRestart('waitingOldExit', { pid: oldPid }));
419
468
 
420
- // 1. 等待老进程退出(轮询 PID 探活,process.kill(pid,0) 不发信号只检测存在性)
421
469
  const waitForOldExit = (pid, timeoutMs = 15000) => new Promise((resolve) => {
422
470
  if (!pid) return resolve(true);
423
471
  const start = Date.now();
@@ -431,14 +479,13 @@ if (command === 'start') {
431
479
  }
432
480
  if (Date.now() - start > timeoutMs) {
433
481
  clearInterval(timer);
434
- log('等待老进程退出超时,尝试强制终止...');
482
+ log(tRestart('waitOldTimeout'));
435
483
  try { process.kill(pid, 'SIGKILL'); } catch {}
436
484
  resolve(false);
437
485
  }
438
486
  }, 200);
439
487
  });
440
488
 
441
- // 2. 等待端口释放(fetch /api/status 失败即视为释放)
442
489
  const waitForPortRelease = async (host, port, timeoutMs = 10000) => {
443
490
  const start = Date.now();
444
491
  while (Date.now() - start < timeoutMs) {
@@ -458,20 +505,13 @@ if (command === 'start') {
458
505
  (async () => {
459
506
  await waitForOldExit(oldPid);
460
507
  await waitForPortRelease(host, port);
461
- // 额外等待,确保 socket 完全回收
462
508
  await new Promise(r => setTimeout(r, 500));
463
509
 
464
- log('老进程已退出,端口已释放,启动新进程...');
510
+ log(tRestart('oldExited'));
465
511
 
466
- // 清理旧 PID 文件,避免新进程 isRunning 检查误判
467
512
  removePidFile();
468
513
  ensureAgentDir();
469
514
 
470
- // 直接以前台模式启动服务进程(不经过 --background 中间层)
471
- // 原因:--background 会多 spawn 一层中间进程,中间进程退出后其 PID 会被
472
- // 本 helper 覆盖写入 PID 文件,导致 stop/restart 读到已死的中间进程 PID。
473
- // 前台模式下 child.pid 就是服务进程 PID,与服务进程自身写入的 PID 一致。
474
- // 前台 start 不会 isRunning 检查,但本 helper 已确保老进程退出 + 端口释放。
475
515
  const child = spawn(process.execPath, [
476
516
  ...process.execArgv,
477
517
  agentScript,
@@ -490,14 +530,12 @@ if (command === 'start') {
490
530
  let spawnFailed = false;
491
531
  child.on('error', (err) => {
492
532
  spawnFailed = true;
493
- log(`启动失败: ${err.message}`);
533
+ log(`${tRestart('startupFailed')}: ${err.message}`);
494
534
  process.exit(1);
495
535
  });
496
536
 
497
537
  await new Promise(resolve => setTimeout(resolve, 1000));
498
538
 
499
- // 健康检查:确认服务进程已成功监听端口,避免写入已崩溃进程的 PID
500
- // (若端口意外未释放导致 EADDRINUSE,服务进程会崩溃,此时不应写错误 PID)
501
539
  let healthy = false;
502
540
  if (!spawnFailed && child.pid) {
503
541
  try {
@@ -510,12 +548,12 @@ if (command === 'start') {
510
548
  try {
511
549
  writeFileSync(PID_FILE, String(child.pid));
512
550
  } catch (err) {
513
- log(`PID 文件写入失败: ${err.message}`);
551
+ log(`${tRestart('pidWriteFailed')}: ${err.message}`);
514
552
  }
515
553
  child.unref();
516
- log(`新进程已启动 (PID: ${child.pid})`);
554
+ log(tRestart('newProcessStarted', { pid: child.pid }));
517
555
  } else if (!spawnFailed && child.pid) {
518
- log(`警告:新进程健康检查失败,未更新 PID 文件(可能端口仍被占用或进程已崩溃)`);
556
+ log(tRestart('healthCheckFailed'));
519
557
  }
520
558
 
521
559
  process.exit(spawnFailed ? 1 : 0);
@@ -526,18 +564,18 @@ if (command === 'start') {
526
564
  const config = readAgentConfig();
527
565
  const running = await isRunning(config);
528
566
 
529
- console.log('[Agent] ======== Agent 状态 ========');
530
- console.log(`[Agent] 监听地址: ${config.host}:${config.port}`);
531
- console.log(`[Agent] 运行状态: ${running ? '✅ 运行中' : '❌ 未运行'}`);
532
- console.log(`[Agent] 配置文件: ${CONFIG_FILE}`);
567
+ console.log(`[Agent] ${t('agentStatus')}`);
568
+ console.log(`[Agent] ${t('listeningAddr', { host: config.host, port: config.port })}`);
569
+ console.log(`[Agent] ${t('runningStatus')}: ${running ? t('runningYes') : t('runningNo')}`);
570
+ console.log(`[Agent] ${t('configFile')}: ${CONFIG_FILE}`);
533
571
 
534
572
  if (running) {
535
573
  try {
536
574
  const resp = await fetch(`http://${config.host}:${config.port}/api/status`);
537
575
  const data = await resp.json();
538
- console.log(`[Agent] 版本: ${data.version}`);
576
+ console.log(`[Agent] ${t('version')}: ${data.version}`);
539
577
  } catch (err) {
540
- console.error(`[Agent] 获取状态失败: ${err.message}`);
578
+ console.error(`[Agent] ${t('getStatusFailed')}: ${err.message}`);
541
579
  }
542
580
  }
543
581
 
@@ -547,30 +585,30 @@ if (command === 'start') {
547
585
  const running = await isRunning(config);
548
586
 
549
587
  if (!running) {
550
- console.log('[Agent] Agent 未在运行,请先启动');
588
+ console.log(`[Agent] ${t('agentNotRunningStartFirst')}`);
551
589
  process.exit(1);
552
590
  }
553
591
 
554
- console.log('[Agent] 配对码已显示在 Agent 启动终端中');
555
- console.log('[Agent] 请在启动 Agent 的终端窗口中查看配对码');
592
+ console.log(`[Agent] ${t('paircodeInTerminal')}`);
593
+ console.log(`[Agent] ${t('checkPaircodeInTerminal')}`);
556
594
  console.log('');
557
- console.log('[Agent] 或查看状态获取更多信息:');
595
+ console.log(`[Agent] ${t('orCheckStatus')}`);
558
596
  try {
559
597
  const resp = await fetch(`http://${config.host}:${config.port}/api/status`);
560
598
  if (resp.ok) {
561
599
  const data = await resp.json();
562
- console.log(`[Agent] 版本: ${data.version}`);
600
+ console.log(`[Agent] ${t('version')}: ${data.version}`);
563
601
  }
564
602
  } catch (err) {
565
- console.error(`[Agent] 获取状态失败: ${err.message}`);
603
+ console.error(`[Agent] ${t('getStatusFailed')}: ${err.message}`);
566
604
  }
567
605
 
568
606
  // ==================== config ====================
569
607
  } else if (command === 'config') {
570
608
  const config = readAgentConfig();
571
- console.log('[Agent] ======== 当前配置 ========');
609
+ console.log(`[Agent] ${t('currentConfig')}`);
572
610
  console.log(JSON.stringify(config, null, 2));
573
- console.log(`[Agent] 配置文件位置: ${CONFIG_FILE}`);
611
+ console.log(`[Agent] ${t('configFileLocation', { path: CONFIG_FILE })}`);
574
612
 
575
613
  // ==================== help ====================
576
614
  } else {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-helper-agent",
3
- "version": "1.14.3",
4
- "description": "AI Helper 代理服务 - 为 Chrome 扩展提供本地文件读写和系统命令执行能力",
3
+ "version": "1.14.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",
7
7
  "bin": {