aws-runtime-bridge 1.9.74 → 1.9.75

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.
@@ -17,6 +17,14 @@ export interface CliCommandOptions {
17
17
  platform?: NodeJS.Platform;
18
18
  serviceUnitPath?: string;
19
19
  executablePath?: string;
20
+ /** Windows 服务日志文件路径(默认 ProgramData 下的 awsb-service.log),供 install 重定向与 log 读取。 */
21
+ windowsServiceLogPath?: string;
22
+ /** 检测正在运行的 bridge 进程 PID 列表(默认按平台用 pgrep / powershell)。 */
23
+ findRunningBridgeProcesses?: (platform: NodeJS.Platform) => number[];
24
+ /** 关闭指定 PID 的进程(默认按平台用 kill -9 / taskkill)。 */
25
+ killBridgeProcess?: (pid: number, platform: NodeJS.Platform) => void;
26
+ /** 交互式询问,返回用户输入(默认从 stdin 读取一行)。 */
27
+ prompt?: (question: string) => Promise<string>;
20
28
  }
21
29
  export declare function readPackageVersion(packageRoot?: string): string;
22
30
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"cli-commands.d.ts","sourceRoot":"","sources":["../../src/services/cli-commands.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAM5E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,gBAAgB,CAAC;IACnE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAYD,wBAAgB,kBAAkB,CAAC,WAAW,SAA0B,GAAG,MAAM,CAMhF;AAgCD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAsBvE;AAwOD,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAgD3B"}
1
+ {"version":3,"file":"cli-commands.d.ts","sourceRoot":"","sources":["../../src/services/cli-commands.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAsB5E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,gBAAgB,CAAC;IACnE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,0BAA0B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;IACrE,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC;IACrE,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD;AAYD,wBAAgB,kBAAkB,CAAC,WAAW,SAA0B,GAAG,MAAM,CAMhF;AAgCD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAsBvE;AA+rBD,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CA8B3B"}
@@ -1,10 +1,17 @@
1
1
  import { spawnSync } from "node:child_process";
2
+ import { createInterface } from "node:readline/promises";
2
3
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
3
4
  import path from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { configureStartupConfig } from "./startup-config-wizard.js";
6
7
  const SYSTEMD_SERVICE_NAME = "awsb.service";
7
8
  const DEFAULT_SYSTEMD_UNIT_PATH = `/etc/systemd/system/${SYSTEMD_SERVICE_NAME}`;
9
+ const WINDOWS_SERVICE_NAME = "awsb";
10
+ // Windows 服务日志:install 时用 cmd 包装脚本把 node 输出重定向到此文件,
11
+ // `awsb log` 再读取它。路径放在 ProgramData 下,保证安装用户与服务运行账户(LocalSystem)都能访问。
12
+ const WINDOWS_SERVICE_LOG_DIR = path.join(process.env.ProgramData || "C:\\ProgramData", "aws-runtime-bridge", "logs");
13
+ const WINDOWS_SERVICE_LOG_PATH = path.join(WINDOWS_SERVICE_LOG_DIR, "awsb-service.log");
14
+ const WINDOWS_SERVICE_WRAPPER_PATH = path.join(WINDOWS_SERVICE_LOG_DIR, "awsb-service.cmd");
8
15
  function getDefaultPackageRoot() {
9
16
  const currentFile = fileURLToPath(import.meta.url);
10
17
  return path.resolve(path.dirname(currentFile), "..", "..");
@@ -83,7 +90,7 @@ function ensureLinuxSystemd(platform, stderr) {
83
90
  return false;
84
91
  }
85
92
  function printServiceUsage(stdout) {
86
- stdout("Usage: awsb service <install|uninstall>");
93
+ stdout("Usage: awsb service <install|uninstall|start|stop|restart>");
87
94
  }
88
95
  /**
89
96
  * 安装 systemd 服务,主干流程是写入 unit、重载 systemd、启用开机自启,并在未运行时启动服务。
@@ -198,6 +205,173 @@ function uninstallSystemdService(options) {
198
205
  options.stdout("[runtime-bridge] awsb systemd 服务已卸载。");
199
206
  return { handled: true, exitCode: 0 };
200
207
  }
208
+ /**
209
+ * 重启 systemd 服务,即执行 systemctl restart awsb.service。
210
+ * 若 unit 尚未安装,提示先执行 install。
211
+ */
212
+ function restartSystemdService(options) {
213
+ if (!ensureLinuxSystemd(options.platform, options.stderr)) {
214
+ return { handled: true, exitCode: 1 };
215
+ }
216
+ if (!existsSync(options.serviceUnitPath)) {
217
+ options.stderr("[runtime-bridge] awsb systemd 服务尚未安装,请先执行 `awsb service install`。");
218
+ return { handled: true, exitCode: 1 };
219
+ }
220
+ const restartResult = runSystemctl(options.runCommand, [
221
+ "restart",
222
+ SYSTEMD_SERVICE_NAME,
223
+ ]);
224
+ if (!commandSucceeded(restartResult)) {
225
+ options.stderr(systemctlFailureMessage("重启 awsb 服务", restartResult));
226
+ return { handled: true, exitCode: restartResult.status ?? 1 };
227
+ }
228
+ options.stdout("[runtime-bridge] awsb systemd 服务已重启。");
229
+ return { handled: true, exitCode: 0 };
230
+ }
231
+ /**
232
+ * 启动 systemd 服务,即执行 systemctl start awsb.service。
233
+ * 若 unit 尚未安装,提示先执行 install。
234
+ */
235
+ function startSystemdService(options) {
236
+ if (!ensureLinuxSystemd(options.platform, options.stderr)) {
237
+ return { handled: true, exitCode: 1 };
238
+ }
239
+ if (!existsSync(options.serviceUnitPath)) {
240
+ options.stderr("[runtime-bridge] awsb systemd 服务尚未安装,请先执行 `awsb service install`。");
241
+ return { handled: true, exitCode: 1 };
242
+ }
243
+ const startResult = runSystemctl(options.runCommand, [
244
+ "start",
245
+ SYSTEMD_SERVICE_NAME,
246
+ ]);
247
+ if (!commandSucceeded(startResult)) {
248
+ options.stderr(systemctlFailureMessage("启动 awsb 服务", startResult));
249
+ return { handled: true, exitCode: startResult.status ?? 1 };
250
+ }
251
+ options.stdout("[runtime-bridge] awsb systemd 服务已启动。");
252
+ return { handled: true, exitCode: 0 };
253
+ }
254
+ /**
255
+ * 停止 systemd 服务,即执行 systemctl stop awsb.service。
256
+ * 若 unit 尚未安装,提示先执行 install。
257
+ */
258
+ function stopSystemdService(options) {
259
+ if (!ensureLinuxSystemd(options.platform, options.stderr)) {
260
+ return { handled: true, exitCode: 1 };
261
+ }
262
+ if (!existsSync(options.serviceUnitPath)) {
263
+ options.stderr("[runtime-bridge] awsb systemd 服务尚未安装,请先执行 `awsb service install`。");
264
+ return { handled: true, exitCode: 1 };
265
+ }
266
+ const stopResult = runSystemctl(options.runCommand, [
267
+ "stop",
268
+ SYSTEMD_SERVICE_NAME,
269
+ ]);
270
+ if (!commandSucceeded(stopResult)) {
271
+ options.stderr(systemctlFailureMessage("停止 awsb 服务", stopResult));
272
+ return { handled: true, exitCode: stopResult.status ?? 1 };
273
+ }
274
+ options.stdout("[runtime-bridge] awsb systemd 服务已停止。");
275
+ return { handled: true, exitCode: 0 };
276
+ }
277
+ /**
278
+ * 安装并启动 Windows 服务(通过 PowerShell 的 New-Service / Start-Service)。
279
+ * 服务以 `cmd /c wrapper.cmd` 运行,wrapper 把 node 的 stdout/stderr 重定向到日志文件,
280
+ * 等价于 systemd 的 ExecStart + 日志采集,供 `awsb log` 读取。
281
+ * 日志重定向为尽力而为:若日志目录/包装脚本创建失败,则回退为直接运行 node,
282
+ * 此时 `awsb log` 会提示无日志文件,但服务仍可正常安装。
283
+ */
284
+ function installWindowsService(options) {
285
+ const logPath = options.windowsServiceLogPath || WINDOWS_SERVICE_LOG_PATH;
286
+ let binPath = `"${process.execPath}" "${options.executablePath}"`;
287
+ try {
288
+ mkdirSync(WINDOWS_SERVICE_LOG_DIR, { recursive: true });
289
+ const wrapperContent = `@echo off\r\n` +
290
+ `"${process.execPath}" "${options.executablePath}" >> "${logPath}" 2>&1\r\n`;
291
+ writeFileSync(WINDOWS_SERVICE_WRAPPER_PATH, wrapperContent, {
292
+ encoding: "utf-8",
293
+ });
294
+ const cmdExe = path.join(process.env.SystemRoot || "C:\\Windows", "System32", "cmd.exe");
295
+ binPath = `"${cmdExe}" /c "${WINDOWS_SERVICE_WRAPPER_PATH}"`;
296
+ }
297
+ catch (error) {
298
+ const err = error instanceof Error ? error : new Error(String(error));
299
+ options.stderr(`[runtime-bridge] 无法配置日志重定向(将直接运行,awsb log 可能无文件可读): ${err.message}`);
300
+ }
301
+ const script = `if (Get-Service -Name ${WINDOWS_SERVICE_NAME} -ErrorAction SilentlyContinue) { ` +
302
+ `Set-Service -Name ${WINDOWS_SERVICE_NAME} -StartupType Automatic -BinaryPathName '${binPath}' } ` +
303
+ `else { New-Service -Name ${WINDOWS_SERVICE_NAME} -BinaryPathName '${binPath}' -StartupType Automatic }; ` +
304
+ `Start-Service -Name ${WINDOWS_SERVICE_NAME}`;
305
+ const result = options.runCommand("powershell", ["-NoProfile", "-Command", script]);
306
+ if (!commandSucceeded(result)) {
307
+ options.stderr(`[runtime-bridge] 安装/启动 Windows 服务失败: ${result.error?.message ?? `退出码 ${result.status ?? "unknown"}`}`);
308
+ return { handled: true, exitCode: result.status ?? 1 };
309
+ }
310
+ options.stdout(`[runtime-bridge] awsb Windows 服务已安装并已启动(日志: ${logPath})。`);
311
+ return { handled: true, exitCode: 0 };
312
+ }
313
+ /**
314
+ * 卸载 Windows 服务(Stop-Service + Remove-Service)。
315
+ */
316
+ function uninstallWindowsService(options) {
317
+ const script = `Stop-Service -Name ${WINDOWS_SERVICE_NAME} -Force -ErrorAction SilentlyContinue; ` +
318
+ `Remove-Service -Name ${WINDOWS_SERVICE_NAME} -ErrorAction SilentlyContinue`;
319
+ const result = options.runCommand("powershell", ["-NoProfile", "-Command", script]);
320
+ if (!commandSucceeded(result)) {
321
+ options.stderr(`[runtime-bridge] 卸载 Windows 服务失败: ${result.error?.message ?? `退出码 ${result.status ?? "unknown"}`}`);
322
+ return { handled: true, exitCode: result.status ?? 1 };
323
+ }
324
+ options.stdout("[runtime-bridge] awsb Windows 服务已卸载。");
325
+ return { handled: true, exitCode: 0 };
326
+ }
327
+ /**
328
+ * 重启 Windows 服务(Restart-Service,未运行时会被启动)。
329
+ */
330
+ function restartWindowsService(options) {
331
+ const result = options.runCommand("powershell", [
332
+ "-NoProfile",
333
+ "-Command",
334
+ `Restart-Service -Name ${WINDOWS_SERVICE_NAME}`,
335
+ ]);
336
+ if (!commandSucceeded(result)) {
337
+ options.stderr(`[runtime-bridge] 重启 Windows 服务失败: ${result.error?.message ?? `退出码 ${result.status ?? "unknown"}`}`);
338
+ return { handled: true, exitCode: result.status ?? 1 };
339
+ }
340
+ options.stdout("[runtime-bridge] awsb Windows 服务已重启。");
341
+ return { handled: true, exitCode: 0 };
342
+ }
343
+ /**
344
+ * 启动 Windows 服务(Start-Service)。
345
+ */
346
+ function startWindowsService(options) {
347
+ const result = options.runCommand("powershell", [
348
+ "-NoProfile",
349
+ "-Command",
350
+ `Start-Service -Name ${WINDOWS_SERVICE_NAME}`,
351
+ ]);
352
+ if (!commandSucceeded(result)) {
353
+ options.stderr(`[runtime-bridge] 启动 Windows 服务失败: ${result.error?.message ?? `退出码 ${result.status ?? "unknown"}`}`);
354
+ return { handled: true, exitCode: result.status ?? 1 };
355
+ }
356
+ options.stdout("[runtime-bridge] awsb Windows 服务已启动。");
357
+ return { handled: true, exitCode: 0 };
358
+ }
359
+ /**
360
+ * 停止 Windows 服务(Stop-Service -Force)。
361
+ */
362
+ function stopWindowsService(options) {
363
+ const result = options.runCommand("powershell", [
364
+ "-NoProfile",
365
+ "-Command",
366
+ `Stop-Service -Name ${WINDOWS_SERVICE_NAME} -Force`,
367
+ ]);
368
+ if (!commandSucceeded(result)) {
369
+ options.stderr(`[runtime-bridge] 停止 Windows 服务失败: ${result.error?.message ?? `退出码 ${result.status ?? "unknown"}`}`);
370
+ return { handled: true, exitCode: result.status ?? 1 };
371
+ }
372
+ options.stdout("[runtime-bridge] awsb Windows 服务已停止。");
373
+ return { handled: true, exitCode: 0 };
374
+ }
201
375
  function handleServiceCommand(argv, options, stdout, stderr) {
202
376
  const subcommand = argv[1];
203
377
  if (!subcommand ||
@@ -215,16 +389,205 @@ function handleServiceCommand(argv, options, stdout, stderr) {
215
389
  serviceUnitPath: options.serviceUnitPath || DEFAULT_SYSTEMD_UNIT_PATH,
216
390
  executablePath: options.executablePath || getDefaultExecutablePath(),
217
391
  };
392
+ // 仅 Linux systemd 与 Windows 服务支持 install/uninstall/restart
393
+ if (serviceOptions.platform !== "linux" &&
394
+ serviceOptions.platform !== "win32") {
395
+ stderr("[runtime-bridge] service 命令仅支持 Linux systemd 与 Windows 服务环境。");
396
+ return { handled: true, exitCode: 1 };
397
+ }
218
398
  if (subcommand === "install") {
219
- return installSystemdService(serviceOptions);
399
+ return serviceOptions.platform === "win32"
400
+ ? installWindowsService(serviceOptions)
401
+ : installSystemdService(serviceOptions);
220
402
  }
221
403
  if (subcommand === "uninstall") {
222
- return uninstallSystemdService(serviceOptions);
404
+ return serviceOptions.platform === "win32"
405
+ ? uninstallWindowsService(serviceOptions)
406
+ : uninstallSystemdService(serviceOptions);
407
+ }
408
+ if (subcommand === "restart") {
409
+ return serviceOptions.platform === "win32"
410
+ ? restartWindowsService(serviceOptions)
411
+ : restartSystemdService(serviceOptions);
412
+ }
413
+ if (subcommand === "start") {
414
+ return serviceOptions.platform === "win32"
415
+ ? startWindowsService(serviceOptions)
416
+ : startSystemdService(serviceOptions);
417
+ }
418
+ if (subcommand === "stop") {
419
+ return serviceOptions.platform === "win32"
420
+ ? stopWindowsService(serviceOptions)
421
+ : stopSystemdService(serviceOptions);
223
422
  }
224
423
  stderr(`[runtime-bridge] 未知 service 子命令: ${subcommand}`);
225
424
  printServiceUsage(stdout);
226
425
  return { handled: true, exitCode: 1 };
227
426
  }
427
+ function parsePids(raw) {
428
+ if (!raw) {
429
+ return [];
430
+ }
431
+ return raw
432
+ .split(/\r?\n/)
433
+ .map((line) => line.trim())
434
+ .filter((line) => /^\d+$/.test(line))
435
+ .map(Number);
436
+ }
437
+ /**
438
+ * 默认进程检测:列出正在运行且与当前进程不同的 bridge 实例 PID。
439
+ * - Windows: 通过 PowerShell 查询命令行包含 aws-runtime-bridge 的 node.exe。
440
+ * - Linux/macOS: 通过 pgrep -f aws-runtime-bridge。
441
+ * 任何异常都返回空数组,避免更新流程被检测逻辑阻断。
442
+ */
443
+ function defaultFindRunningBridgeProcesses(platform) {
444
+ try {
445
+ if (platform === "win32") {
446
+ const result = spawnSync("powershell", [
447
+ "-NoProfile",
448
+ "-Command",
449
+ `Get-CimInstance Win32_Process -Filter "Name='node.exe'" | ` +
450
+ `Where-Object { $_.CommandLine -like '*aws-runtime-bridge*' -and $_.ProcessId -ne ${process.pid} } | ` +
451
+ `ForEach-Object { $_.ProcessId }`,
452
+ ], { encoding: "utf-8" });
453
+ return parsePids(result.stdout);
454
+ }
455
+ const result = spawnSync("pgrep", ["-f", "aws-runtime-bridge"], {
456
+ encoding: "utf-8",
457
+ });
458
+ return parsePids(result.stdout).filter((pid) => pid !== process.pid);
459
+ }
460
+ catch {
461
+ return [];
462
+ }
463
+ }
464
+ function defaultKillBridgeProcess(pid, platform) {
465
+ if (platform === "win32") {
466
+ // /T 结束进程树,/F 强制
467
+ spawnSync("taskkill", ["/PID", String(pid), "/F", "/T"], {
468
+ stdio: "ignore",
469
+ });
470
+ return;
471
+ }
472
+ // Linux/macOS:直接 SIGKILL。注意若进程由 systemd 托管,kill 后可能被自动拉起。
473
+ spawnSync("kill", ["-9", String(pid)], { stdio: "ignore" });
474
+ }
475
+ function defaultPrompt(question) {
476
+ if (!process.stdin.isTTY) {
477
+ // 非交互环境(管道 / CI)不要挂起,默认不关闭进程。
478
+ return Promise.resolve("n");
479
+ }
480
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
481
+ return rl.question(question).finally(() => rl.close());
482
+ }
483
+ /**
484
+ * 更新 aws-runtime-bridge:
485
+ * 1. 检测是否有正在运行的实例;
486
+ * 2. 若有,询问用户是否关闭(y 关闭 / 其他 不关闭);
487
+ * 3. 关闭后再安装;若不关闭,则以 --force 强制安装(文件被占用可能仍有警告)。
488
+ */
489
+ async function handleUpdateCommand(options, stdout, stderr) {
490
+ const runCommand = options.runCommand || defaultRunCommand;
491
+ const platform = options.platform || process.platform;
492
+ const findProcesses = options.findRunningBridgeProcesses || defaultFindRunningBridgeProcesses;
493
+ const prompt = options.prompt || defaultPrompt;
494
+ const killProcess = options.killBridgeProcess || defaultKillBridgeProcess;
495
+ stdout("[runtime-bridge] 正在更新 aws-runtime-bridge 到最新版本...");
496
+ const running = findProcesses(platform).filter((pid) => pid !== process.pid);
497
+ let forceInstall = false;
498
+ if (running.length > 0) {
499
+ stdout(`[runtime-bridge] 检测到 aws-runtime-bridge 正在运行 (PID: ${running.join(", ")}),` +
500
+ "更新时这些进程可能占用文件导致覆盖失败。");
501
+ const answer = (await prompt("是否关闭上述进程以继续更新? (y/N) ")).trim().toLowerCase();
502
+ if (answer === "y" || answer === "yes") {
503
+ for (const pid of running) {
504
+ try {
505
+ killProcess(pid, platform);
506
+ stdout(`[runtime-bridge] 已关闭进程 PID: ${pid}`);
507
+ }
508
+ catch (error) {
509
+ const err = error instanceof Error ? error : new Error(String(error));
510
+ stderr(`[runtime-bridge] 关闭进程 PID ${pid} 失败: ${err.message}`);
511
+ }
512
+ }
513
+ }
514
+ else {
515
+ forceInstall = true;
516
+ stdout("[runtime-bridge] 已选择不关闭进程,将以 --force 方式强制安装(若文件仍被占用可能会有警告)。");
517
+ }
518
+ }
519
+ const args = ["install", "-g", "aws-runtime-bridge@latest", "--omit=optional"];
520
+ if (forceInstall) {
521
+ args.push("--force");
522
+ }
523
+ const result = runCommand("npm", args);
524
+ if (result.error) {
525
+ stderr(`[runtime-bridge] 更新失败: ${result.error.message}`);
526
+ return { handled: true, exitCode: 1 };
527
+ }
528
+ if (result.status !== 0) {
529
+ stderr(`[runtime-bridge] 更新失败,npm 退出码: ${result.status ?? "unknown"}`);
530
+ return { handled: true, exitCode: result.status ?? 1 };
531
+ }
532
+ stdout("[runtime-bridge] 更新完成。请重新运行 awsb -v 查看当前版本。");
533
+ return { handled: true, exitCode: 0 };
534
+ }
535
+ /**
536
+ * 查看 awsb 服务日志。
537
+ * - Linux systemd:journalctl -u awsb.service(支持 --follow 实时跟踪)。
538
+ * - Windows:读取 install 时重定向到磁盘的日志文件(支持 --follow 实时跟踪)。
539
+ * 非服务环境(未安装 / 不支持的平台)给出明确提示。
540
+ */
541
+ function handleLogCommand(options, stdout, stderr) {
542
+ const argv = options.argv || process.argv.slice(2);
543
+ const follow = argv
544
+ .slice(1)
545
+ .some((arg) => arg === "--follow" || arg === "-f");
546
+ const platform = options.platform || process.platform;
547
+ const runCommand = options.runCommand || defaultRunCommand;
548
+ const unitPath = options.serviceUnitPath || DEFAULT_SYSTEMD_UNIT_PATH;
549
+ if (platform === "linux") {
550
+ if (!existsSync(unitPath)) {
551
+ stderr("[runtime-bridge] awsb 服务尚未安装(awsb service install),无日志可查看。");
552
+ return { handled: true, exitCode: 1 };
553
+ }
554
+ const args = ["-u", SYSTEMD_SERVICE_NAME];
555
+ if (follow) {
556
+ args.push("-f");
557
+ }
558
+ else {
559
+ args.push("-n", "200", "--no-pager");
560
+ }
561
+ const result = runCommand("journalctl", args);
562
+ if (result.error) {
563
+ stderr(`[runtime-bridge] 读取日志失败: ${result.error.message}`);
564
+ return { handled: true, exitCode: 1 };
565
+ }
566
+ return { handled: true, exitCode: result.status ?? 0 };
567
+ }
568
+ if (platform === "win32") {
569
+ const logPath = options.windowsServiceLogPath || WINDOWS_SERVICE_LOG_PATH;
570
+ if (!existsSync(logPath)) {
571
+ stderr(`[runtime-bridge] 尚未生成日志文件: ${logPath}。请先执行 ` +
572
+ "`awsb service install` 并运行服务。");
573
+ return { handled: true, exitCode: 1 };
574
+ }
575
+ const psCommand = `Get-Content -Encoding UTF8 -Tail 200 "${logPath}"` +
576
+ (follow ? " -Wait" : "");
577
+ const result = runCommand("powershell", [
578
+ "-NoProfile",
579
+ "-Command",
580
+ psCommand,
581
+ ]);
582
+ if (result.error) {
583
+ stderr(`[runtime-bridge] 读取日志失败: ${result.error.message}`);
584
+ return { handled: true, exitCode: 1 };
585
+ }
586
+ return { handled: true, exitCode: result.status ?? 0 };
587
+ }
588
+ stderr("[runtime-bridge] awsb log 仅支持 Linux systemd 与 Windows 服务环境。");
589
+ return { handled: true, exitCode: 1 };
590
+ }
228
591
  export async function handleCliCommand(options = {}) {
229
592
  const argv = options.argv || process.argv.slice(2);
230
593
  const command = argv[0];
@@ -235,24 +598,7 @@ export async function handleCliCommand(options = {}) {
235
598
  return { handled: true, exitCode: 0 };
236
599
  }
237
600
  if (command === "update") {
238
- stdout("[runtime-bridge] 正在更新 aws-runtime-bridge 到最新版本...");
239
- const runCommand = options.runCommand || defaultRunCommand;
240
- const result = runCommand("npm", [
241
- "install",
242
- "-g",
243
- "aws-runtime-bridge@latest",
244
- "--omit=optional",
245
- ]);
246
- if (result.error) {
247
- stderr(`[runtime-bridge] 更新失败: ${result.error.message}`);
248
- return { handled: true, exitCode: 1 };
249
- }
250
- if (result.status !== 0) {
251
- stderr(`[runtime-bridge] 更新失败,npm 退出码: ${result.status ?? "unknown"}`);
252
- return { handled: true, exitCode: result.status ?? 1 };
253
- }
254
- stdout("[runtime-bridge] 更新完成。请重新运行 awsb -v 查看当前版本。");
255
- return { handled: true, exitCode: 0 };
601
+ return await handleUpdateCommand(options, stdout, stderr);
256
602
  }
257
603
  if (command === "config") {
258
604
  const configure = options.configure || configureStartupConfig;
@@ -262,5 +608,8 @@ export async function handleCliCommand(options = {}) {
262
608
  if (command === "service") {
263
609
  return handleServiceCommand(argv, options, stdout, stderr);
264
610
  }
611
+ if (command === "log") {
612
+ return handleLogCommand(options, stdout, stderr);
613
+ }
265
614
  return { handled: false, exitCode: 0 };
266
615
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.9.74",
3
+ "version": "1.9.75",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",