ai-helper-agent 1.14.3 → 1.14.5

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