aws-runtime-bridge 1.9.82 → 1.9.90

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/README.md CHANGED
@@ -96,7 +96,67 @@ sudo awsb service install
96
96
  sudo awsb service uninstall
97
97
  ```
98
98
 
99
- `service` 命令仅支持 Linux systemd;在其他平台会直接返回清晰错误。生成的 unit 会设置 `AWS_BRIDGE_SKIP_SETUP=true`,避免后台服务启动时阻塞在交互式配置引导。
99
+ `service` 命令还支持 `start` / `stop` / `restart` 子命令,分别对应 `systemctl start/stop/restart awsb.service`。生成的 unit 会设置 `AWS_BRIDGE_SKIP_SETUP=true`,避免后台服务启动时阻塞在交互式配置引导。
100
+
101
+ ## macOS launchd 服务管理
102
+
103
+ macOS 环境中可使用同一套 `awsb service` CLI 安装或卸载用户级 LaunchAgent(无需 `sudo`)。安装命令会生成 plist 文件写入 `~/Library/LaunchAgents/com.agentsworkstudio.awsb.plist`,并通过 `launchctl bootstrap` 加载启动:
104
+
105
+ ```bash
106
+ awsb service install
107
+ ```
108
+
109
+ 卸载命令会执行 `launchctl bootout` 停止并卸载服务,然后删除 plist 文件:
110
+
111
+ ```bash
112
+ awsb service uninstall
113
+ ```
114
+
115
+ 其他子命令与 launchctl 的对应关系:
116
+
117
+ | 子命令 | 行为 |
118
+ | --- | --- |
119
+ | `start` | 先尝试 `launchctl kickstart`(已加载未运行时启动),失败再 `bootstrap` 重新加载 |
120
+ | `stop` | `launchctl bootout` 卸载并停止(保留 plist,下次 `start` 重新加载) |
121
+ | `restart` | `launchctl bootout`(忽略错误)→ `launchctl bootstrap` 重新加载 |
122
+
123
+ 生成的 plist 配置要点:
124
+
125
+ - `RunAtLoad=true`:加载时立即启动;
126
+ - `KeepAlive=true`:进程退出后自动拉起;
127
+ - `AWS_BRIDGE_SKIP_SETUP=true`:避免后台服务阻塞在交互式配置引导;
128
+ - `AWS_RUNTIME_HOME_DIR`:注入当前用户主目录,确保服务读到 `~/.aws-bridge/config.json`;
129
+ - `StandardOutPath` / `StandardErrorPath`:日志输出到 `~/.aws-bridge/log/awsb.out.log` 与 `awsb.err.log`,可用 `awsb log` 读取。
130
+
131
+ > 注意:因 `KeepAlive=true`,`stop` 通过 `bootout` 卸载服务才能真正停止;仅发信号会被 launchd 立即拉起。`stop` 后 plist 仍保留,`start` 会重新 `bootstrap` 加载。
132
+
133
+ ## Windows 服务管理
134
+
135
+ Windows 环境中可使用同一套 `awsb service` CLI 通过 [node-windows](https://github.com/coreybutler/node-windows)(随包作为可选依赖安装)注册系统服务。安装命令会用 node-windows 注册服务(SCM 服务名 `awsb.exe`),并调用 `sc.exe start` 启动:
136
+
137
+ ```bash
138
+ awsb service install
139
+ ```
140
+
141
+ 卸载命令会执行 `sc.exe stop` + `sc.exe delete`:
142
+
143
+ ```bash
144
+ awsb service uninstall
145
+ ```
146
+
147
+ 其他子命令均通过 `sc.exe` 操作:`start` / `stop` / `restart`(`stop` + `start`)。服务日志重定向到 `~/.aws-bridge/log/awsb.out.log`,可用 `awsb log` 读取。
148
+
149
+ > Windows 服务默认以 `LocalSystem` 身份运行,其主目录是 `C:\Windows\System32\config\systemprofile`,找不到当前用户的 `~/.aws-bridge/config.json`。安装时会自动注入 `AWS_RUNTIME_HOME_DIR` 环境变量指向当前用户主目录,bridge 才能读到正确配置。
150
+
151
+ ## 查看服务日志
152
+
153
+ `awsb log` 命令按平台读取服务日志,支持 `--follow` / `-f` 实时跟踪:
154
+
155
+ - Linux:`journalctl -u awsb.service`;
156
+ - macOS:`tail -n 200 ~/.aws-bridge/log/awsb.out.log`(`--follow` 时 `tail -f`);
157
+ - Windows:`Get-Content -Tail 200 ~/.aws-bridge/log/awsb.out.log`(`--follow` 时附加 `-Wait`)。
158
+
159
+ 服务未安装或日志文件未生成时会返回明确错误提示。
100
160
 
101
161
  ## 关键环境变量
102
162
 
@@ -24,6 +24,15 @@ export interface CliCommandOptions {
24
24
  windowsServiceLogPath?: string;
25
25
  /** Windows 服务管理器(基于 node-windows),用于 install 操作;测试时可注入 mock。 */
26
26
  windowsServiceManager?: WindowsServiceManager;
27
+ /** macOS launchd plist 文件路径(默认 ~/Library/LaunchAgents/com.agentsworkstudio.awsb.plist)。 */
28
+ launchdPlistPath?: string;
29
+ /** macOS launchd 日志文件路径(默认 ~/.aws-bridge/log/awsb.out.log),供 install 写入 plist 与 log 读取。 */
30
+ launchdLogPath?: string;
31
+ /**
32
+ * macOS launchd 域目标(默认 `gui/${process.getuid()}`)。
33
+ * service-target = `${domainTarget}/${LAUNCHD_LABEL}`,测试时可注入固定值避免依赖 getuid。
34
+ */
35
+ launchdDomainTarget?: string;
27
36
  /** 检测正在运行的 bridge 进程 PID 列表(默认按平台用 pgrep / powershell)。 */
28
37
  findRunningBridgeProcesses?: (platform: NodeJS.Platform) => number[];
29
38
  /** 关闭指定 PID 的进程(默认按平台用 kill -9 / taskkill)。 */
@@ -42,4 +51,21 @@ export declare function readPackageVersion(packageRoot?: string): string;
42
51
  * 生成 systemd unit 内容,主流程是用当前 Node 可执行文件启动当前 CLI 入口。
43
52
  */
44
53
  export declare function createSystemdUnitContent(executablePath: string): string;
54
+ /**
55
+ * 生成 macOS launchd plist 内容。
56
+ *
57
+ * - Label:launchd 注册的服务标识,必须与 plist 文件名匹配;
58
+ * - ProgramArguments:用当前 Node 可执行文件启动 CLI 入口;
59
+ * - RunAtLoad=true:bootstrap 时立即启动;
60
+ * - KeepAlive=true:进程退出后自动拉起(对应 systemd Restart=always 语义;
61
+ * systemd 用 on-failure,launchd 没有细粒度条件时用 true 最稳);
62
+ * - StandardOutPath/StandardErrorPath:与 Windows 服务日志统一放在 ~/.aws-bridge/log/;
63
+ * - AWS_RUNTIME_HOME_DIR:注入当前用户主目录,避免 LaunchAgent 在某些上下文下
64
+ * 解析到错误 home 导致读不到 ~/.aws-bridge/config.json(与 Windows LocalSystem
65
+ * 场景同源,统一注入更稳)。
66
+ */
67
+ export declare function createLaunchdPlistContent(executablePath: string, options?: {
68
+ userHomeDir?: string;
69
+ logPath?: string;
70
+ }): string;
45
71
  export declare function handleCliCommand(options?: CliCommandOptions): Promise<CliCommandResult>;
@@ -1,6 +1,7 @@
1
- import{spawnSync as S}from"node:child_process";import{createInterface as T}from"node:readline/promises";import{existsSync as h,mkdirSync as A,readFileSync as L,rmSync as j,unlinkSync as O,writeFileSync as F}from"node:fs";import f from"node:path";import W from"node:os";import{fileURLToPath as R}from"node:url";import{configureStartupConfig as B,printConfigExampleCommand as G}from"./startup-config-wizard.js";import{createWindowsServiceManager as V,isNodeWindowsAvailable as q}from"./windows-service-manager.js";const o="awsb.service",_=`/etc/systemd/system/${o}`,I="awsb",C=`${I}.exe`,k=f.join(W.homedir(),".aws-bridge","log"),D=f.join(k,"awsb.out.log");function H(){const e=R(import.meta.url);return f.resolve(f.dirname(e),"..","..")}function K(e=H()){const r=f.join(e,"package.json");return JSON.parse(L(r,"utf-8")).version||"unknown"}function E(e,r){const t=S(e,r,{shell:process.platform==="win32",stdio:"inherit"});return{status:t.status,error:t.error}}function Y(){try{const e=S("npm",["root","-g"],{shell:process.platform==="win32",encoding:"utf-8",stdio:["ignore","pipe","ignore"]});if(e.status===0&&e.stdout){const r=e.stdout.trim();if(r)return r}}catch{}return null}function J(e){process.stdout.write(`${e}
2
- `)}function z(e){process.stderr.write(`${e}
3
- `)}function Q(e){return e==="-v"||e==="--version"||e==="version"}function X(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Z(){return process.argv[1]?f.resolve(process.argv[1]):R(import.meta.url)}function ee(e){return["[Unit]","Description=AgentsWorkStudio Runtime Bridge","After=network-online.target","Wants=network-online.target","","[Service]","Type=simple","Environment=AWS_BRIDGE_SKIP_SETUP=true",`ExecStart=${[process.execPath,e].map(t=>X(t)).join(" ")}`,"Restart=on-failure","RestartSec=5s","","[Install]","WantedBy=multi-user.target",""].join(`
4
- `)}function c(e,r){return e("systemctl",r)}function l(e,r){return r.error?`[runtime-bridge] ${e}\u5931\u8D25: ${r.error.message}`:`[runtime-bridge] ${e}\u5931\u8D25\uFF0Csystemctl \u9000\u51FA\u7801: ${r.status??"unknown"}`}function d(e){return!e.error&&e.status===0}function P(e,r){return e==="linux"?!0:(r("[runtime-bridge] service \u547D\u4EE4\u4EC5\u652F\u6301 Linux systemd \u73AF\u5883\u3002"),!1)}function U(e){e("Usage: awsb service <install|uninstall|start|stop|restart>")}function re(e){if(!P(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=ee(e.executablePath);try{A(f.dirname(e.serviceUnitPath),{recursive:!0}),F(e.serviceUnitPath,r,{encoding:"utf-8",mode:420})}catch(n){const a=n instanceof Error?n:new Error(String(n));return e.stderr(`[runtime-bridge] \u5199\u5165 systemd unit \u5931\u8D25: ${a.message}`),{handled:!0,exitCode:1}}const t=c(e.runCommand,["daemon-reload"]);if(!d(t))return e.stderr(l("\u91CD\u8F7D systemd",t)),{handled:!0,exitCode:t.status??1};const i=c(e.runCommand,["enable",o]);if(!d(i))return e.stderr(l("\u542F\u7528 awsb \u5F00\u673A\u81EA\u542F",i)),{handled:!0,exitCode:i.status??1};const s=c(e.runCommand,["is-active","--quiet",o]);if(s.error)return e.stderr(l("\u68C0\u67E5 awsb \u670D\u52A1\u72B6\u6001",s)),{handled:!0,exitCode:1};if(s.status!==0){const n=c(e.runCommand,["start",o]);return d(n)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u5B89\u88C5\u3001\u5DF2\u542F\u7528\u5F00\u673A\u81EA\u542F\u5E76\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(l("\u542F\u52A8 awsb \u670D\u52A1",n)),{handled:!0,exitCode:n.status??1})}return e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u5DF2\u542F\u7528\u5F00\u673A\u81EA\u542F\uFF1B\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u3002"),{handled:!0,exitCode:0}}function te(e){if(!P(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=h(e.serviceUnitPath),t=c(e.runCommand,["is-active","--quiet",o]);if(t.error)return e.stderr(l("\u68C0\u67E5 awsb \u670D\u52A1\u72B6\u6001",t)),{handled:!0,exitCode:1};if(t.status===0){const n=c(e.runCommand,["stop",o]);if(!d(n))return e.stderr(l("\u505C\u6B62 awsb \u670D\u52A1",n)),{handled:!0,exitCode:n.status??1}}const i=c(e.runCommand,["disable",o]);if(i.error||i.status!==0&&r)return e.stderr(l("\u7981\u7528 awsb \u5F00\u673A\u81EA\u542F",i)),{handled:!0,exitCode:i.status??1};if(r)try{O(e.serviceUnitPath)}catch(n){const a=n instanceof Error?n:new Error(String(n));return e.stderr(`[runtime-bridge] \u5220\u9664 systemd unit \u5931\u8D25: ${a.message}`),{handled:!0,exitCode:1}}else e.stdout("[runtime-bridge] awsb systemd unit \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5220\u9664\u3002");const s=c(e.runCommand,["daemon-reload"]);return d(s)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u5378\u8F7D\u3002"),{handled:!0,exitCode:0}):(e.stderr(l("\u91CD\u8F7D systemd",s)),{handled:!0,exitCode:s.status??1})}function ne(e){if(!P(e.platform,e.stderr))return{handled:!0,exitCode:1};if(!h(e.serviceUnitPath))return e.stderr("[runtime-bridge] awsb systemd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const r=c(e.runCommand,["restart",o]);return d(r)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u91CD\u542F\u3002"),{handled:!0,exitCode:0}):(e.stderr(l("\u91CD\u542F awsb \u670D\u52A1",r)),{handled:!0,exitCode:r.status??1})}function se(e){if(!P(e.platform,e.stderr))return{handled:!0,exitCode:1};if(!h(e.serviceUnitPath))return e.stderr("[runtime-bridge] awsb systemd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const r=c(e.runCommand,["start",o]);return d(r)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(l("\u542F\u52A8 awsb \u670D\u52A1",r)),{handled:!0,exitCode:r.status??1})}function ie(e){if(!P(e.platform,e.stderr))return{handled:!0,exitCode:1};if(!h(e.serviceUnitPath))return e.stderr("[runtime-bridge] awsb systemd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const r=c(e.runCommand,["stop",o]);return d(r)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u505C\u6B62\u3002"),{handled:!0,exitCode:0}):(e.stderr(l("\u505C\u6B62 awsb \u670D\u52A1",r)),{handled:!0,exitCode:r.status??1})}function ae(e){return e(`[runtime-bridge] \u672A\u68C0\u6D4B\u5230 node-windows \u6A21\u5757\u3002\u8BF7\u91CD\u65B0\u5B89\u88C5 aws-runtime-bridge\uFF1A
5
- npm install -g aws-runtime-bridge`),{handled:!0,exitCode:1}}async function ue(e){const r=e.windowsServiceManager;if(!r&&!q())return ae(e.stderr);const t=e.windowsServiceLogPath||D,i=f.dirname(e.executablePath),s=W.homedir();try{await(r||V()).install({name:I,description:"awsb runtime bridge",script:e.executablePath,workingDirectory:i,env:[{name:"AWS_BRIDGE_SKIP_SETUP",value:"true"},{name:"AWS_RUNTIME_HOME_DIR",value:s}],logpath:k})}catch(a){const g=a instanceof Error?a:new Error(String(a));return e.stderr(`[runtime-bridge] \u5B89\u88C5 Windows \u670D\u52A1\u5931\u8D25: ${g.message}`),{handled:!0,exitCode:1}}const n=e.runCommand("sc.exe",["start",C]);return d(n)?(e.stdout(`[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u5DF2\u542F\u52A8\uFF08\u65E5\u5FD7: ${t}\uFF09\u3002`),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u542F\u52A8 Windows \u670D\u52A1\u5931\u8D25: ${n.error?.message??`\u9000\u51FA\u7801 ${n.status??"unknown"}`}`),{handled:!0,exitCode:n.status??1})}function de(e){const r=i=>e.runCommand("sc.exe",i);r(["stop",C]);const t=r(["delete",C]);return d(t)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u5378\u8F7D\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u5378\u8F7D Windows \u670D\u52A1\u5931\u8D25: ${t.error?.message??`\u9000\u51FA\u7801 ${t.status??"unknown"}`}`),{handled:!0,exitCode:t.status??1})}function oe(e){const r=i=>e.runCommand("sc.exe",i);r(["stop",C]);const t=r(["start",C]);return d(t)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u91CD\u542F\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u91CD\u542F Windows \u670D\u52A1\u5931\u8D25: ${t.error?.message??`\u9000\u51FA\u7801 ${t.status??"unknown"}`}`),{handled:!0,exitCode:t.status??1})}function ce(e){const r=e.runCommand("sc.exe",["start",C]);return d(r)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u542F\u52A8 Windows \u670D\u52A1\u5931\u8D25: ${r.error?.message??`\u9000\u51FA\u7801 ${r.status??"unknown"}`}`),{handled:!0,exitCode:r.status??1})}function le(e){const r=e.runCommand("sc.exe",["stop",C]);return d(r)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u505C\u6B62\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u505C\u6B62 Windows \u670D\u52A1\u5931\u8D25: ${r.error?.message??`\u9000\u51FA\u7801 ${r.status??"unknown"}`}`),{handled:!0,exitCode:r.status??1})}async function me(e,r,t,i){const s=e[1];if(!s||s==="-h"||s==="--help"||s==="help")return U(t),{handled:!0,exitCode:s?0:1};const n={stdout:t,stderr:i,runCommand:r.runCommand||E,platform:r.platform||process.platform,serviceUnitPath:r.serviceUnitPath||_,executablePath:r.executablePath||Z(),windowsServiceManager:r.windowsServiceManager};return n.platform!=="linux"&&n.platform!=="win32"?(i("[runtime-bridge] service \u547D\u4EE4\u4EC5\u652F\u6301 Linux systemd \u4E0E Windows \u670D\u52A1\u73AF\u5883\u3002"),{handled:!0,exitCode:1}):s==="install"?n.platform==="win32"?await ue(n):re(n):s==="uninstall"?n.platform==="win32"?de(n):te(n):s==="restart"?n.platform==="win32"?oe(n):ne(n):s==="start"?n.platform==="win32"?ce(n):se(n):s==="stop"?n.platform==="win32"?le(n):ie(n):(i(`[runtime-bridge] \u672A\u77E5 service \u5B50\u547D\u4EE4: ${s}`),U(t),{handled:!0,exitCode:1})}function M(e){return e?e.split(/\r?\n/).map(r=>r.trim()).filter(r=>/^\d+$/.test(r)).map(Number):[]}function fe(e){try{if(e==="win32"){const t=S("powershell",["-NoProfile","-Command",`Get-CimInstance Win32_Process -Filter "Name='node.exe'" | Where-Object { $_.CommandLine -like '*aws-runtime-bridge*' -and $_.ProcessId -ne ${process.pid} } | ForEach-Object { $_.ProcessId }`],{encoding:"utf-8"});return M(t.stdout)}const r=S("pgrep",["-f","aws-runtime-bridge"],{encoding:"utf-8"});return M(r.stdout).filter(t=>t!==process.pid)}catch{return[]}}function ge(e,r){if(r==="win32"){S("taskkill",["/PID",String(e),"/F","/T"],{stdio:"ignore"});return}S("kill",["-9",String(e)],{stdio:"ignore"})}function we(e){if(!process.stdin.isTTY)return Promise.resolve("n");const r=T({input:process.stdin,output:process.stdout});return r.question(e).finally(()=>r.close())}async function be(e,r,t){const i=e.runCommand||E,s=e.platform||process.platform,n=e.findRunningBridgeProcesses||fe,a=e.prompt||we,g=e.killBridgeProcess||ge,u=e.globalModulePath||Y;r("[runtime-bridge] \u6B63\u5728\u66F4\u65B0 aws-runtime-bridge \u5230\u6700\u65B0\u7248\u672C...");const m=n(s).filter(w=>w!==process.pid);let x=!1;if(m.length>0){r(`[runtime-bridge] \u68C0\u6D4B\u5230 aws-runtime-bridge \u6B63\u5728\u8FD0\u884C (PID: ${m.join(", ")})\uFF0C\u66F4\u65B0\u65F6\u8FD9\u4E9B\u8FDB\u7A0B\u53EF\u80FD\u5360\u7528\u6587\u4EF6\u5BFC\u81F4\u8986\u76D6\u5931\u8D25\u3002`);const w=(await a("\u662F\u5426\u5173\u95ED\u4E0A\u8FF0\u8FDB\u7A0B\u4EE5\u7EE7\u7EED\u66F4\u65B0? (y/N) ")).trim().toLowerCase();if(w==="y"||w==="yes")for(const b of m)try{g(b,s),r(`[runtime-bridge] \u5DF2\u5173\u95ED\u8FDB\u7A0B PID: ${b}`)}catch(y){const N=y instanceof Error?y:new Error(String(y));t(`[runtime-bridge] \u5173\u95ED\u8FDB\u7A0B PID ${b} \u5931\u8D25: ${N.message}`)}else x=!0,r("[runtime-bridge] \u5DF2\u9009\u62E9\u4E0D\u5173\u95ED\u8FDB\u7A0B\uFF0C\u5C06\u4EE5 --force \u65B9\u5F0F\u5F3A\u5236\u5B89\u88C5\uFF08\u82E5\u6587\u4EF6\u4ECD\u88AB\u5360\u7528\u53EF\u80FD\u4F1A\u6709\u8B66\u544A\uFF09\u3002")}const $=u();if($){const w=f.join($,"aws-runtime-bridge");if(h(w)){r("[runtime-bridge] \u6E05\u7406\u65E7\u7248\u672C\u6B8B\u7559\u76EE\u5F55...");try{j(w,{recursive:!0,force:!0})}catch(b){const y=b instanceof Error?b:new Error(String(b));t(`[runtime-bridge] \u6E05\u7406\u6B8B\u7559\u76EE\u5F55\u5931\u8D25\uFF08\u5C06\u5C1D\u8BD5\u7EE7\u7EED\u5B89\u88C5\uFF09: ${y.message}`)}}}const p=["install","-g","aws-runtime-bridge@latest","--omit=optional"];x&&p.push("--force");const v=i("npm",p);return v.error?(t(`[runtime-bridge] \u66F4\u65B0\u5931\u8D25: ${v.error.message}`),{handled:!0,exitCode:1}):v.status!==0?(t(`[runtime-bridge] \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${v.status??"unknown"}`),{handled:!0,exitCode:v.status??1}):(r("[runtime-bridge] \u66F4\u65B0\u5B8C\u6210\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C awsb -v \u67E5\u770B\u5F53\u524D\u7248\u672C\u3002"),{handled:!0,exitCode:0})}function he(e,r,t){const s=(e.argv||process.argv.slice(2)).slice(1).some(u=>u==="--follow"||u==="-f"),n=e.platform||process.platform,a=e.runCommand||E,g=e.serviceUnitPath||_;if(n==="linux"){if(!h(g))return t("[runtime-bridge] awsb \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF08awsb service install\uFF09\uFF0C\u65E0\u65E5\u5FD7\u53EF\u67E5\u770B\u3002"),{handled:!0,exitCode:1};const u=["-u",o];s?u.push("-f"):u.push("-n","200","--no-pager");const m=a("journalctl",u);return m.error?(t(`[runtime-bridge] \u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25: ${m.error.message}`),{handled:!0,exitCode:1}):{handled:!0,exitCode:m.status??0}}if(n==="win32"){const u=e.windowsServiceLogPath||D;if(!h(u))return t(`[runtime-bridge] \u5C1A\u672A\u751F\u6210\u65E5\u5FD7\u6587\u4EF6: ${u}\u3002\u8BF7\u5148\u6267\u884C \`awsb service install\` \u5E76\u8FD0\u884C\u670D\u52A1\u3002`),{handled:!0,exitCode:1};const m=`Get-Content -Encoding UTF8 -Tail 200 "${u}"`+(s?" -Wait":""),x=a("powershell",["-NoProfile","-Command",m]);return x.error?(t(`[runtime-bridge] \u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25: ${x.error.message}`),{handled:!0,exitCode:1}):{handled:!0,exitCode:x.status??0}}return t("[runtime-bridge] awsb log \u4EC5\u652F\u6301 Linux systemd \u4E0E Windows \u670D\u52A1\u73AF\u5883\u3002"),{handled:!0,exitCode:1}}async function pe(e={}){const r=e.argv||process.argv.slice(2),t=r[0],i=e.stdout||J,s=e.stderr||z;if(Q(t))return i(`aws-runtime-bridge ${K(e.packageRoot)}`),{handled:!0,exitCode:0};if(t==="update")return await be(e,i,s);if(t==="config"){const n=r[1];if(n==="example"||n==="minimal"||n==="--minimal"||n==="-m")return{handled:!0,exitCode:await(e.configureExample||G)()==="created"?0:1};const g=await(e.configure||B)();return{handled:!0,exitCode:g==="configured"||g==="created"?0:1}}return t==="service"?await me(r,e,i,s):t==="log"?he(e,i,s):{handled:!1,exitCode:0}}export{ee as createSystemdUnitContent,pe as handleCliCommand,K as readPackageVersion};
1
+ import{spawnSync as P}from"node:child_process";import{createInterface as q}from"node:readline/promises";import{existsSync as l,mkdirSync as p,readFileSync as Y,rmSync as J,unlinkSync as M,writeFileSync as N}from"node:fs";import o from"node:path";import S from"node:os";import{fileURLToPath as O}from"node:url";import{configureStartupConfig as z,printConfigExampleCommand as Q}from"./startup-config-wizard.js";import{createWindowsServiceManager as X,isNodeWindowsAvailable as Z}from"./windows-service-manager.js";const m="awsb.service",j=`/etc/systemd/system/${m}`,H="awsb",x=`${H}.exe`,F=o.join(S.homedir(),".aws-bridge","log"),B=o.join(F,"awsb.out.log"),T="com.agentsworkstudio.awsb",_=o.join(S.homedir(),"Library","LaunchAgents",`${T}.plist`),ee=o.join(S.homedir(),".aws-bridge","log"),W=o.join(ee,"awsb.out.log");function re(){const e=O(import.meta.url);return o.resolve(o.dirname(e),"..","..")}function te(e=re()){const r=o.join(e,"package.json");return JSON.parse(Y(r,"utf-8")).version||"unknown"}function I(e,r){const n=P(e,r,{shell:process.platform==="win32",stdio:"inherit"});return{status:n.status,error:n.error}}function ne(){try{const e=P("npm",["root","-g"],{shell:process.platform==="win32",encoding:"utf-8",stdio:["ignore","pipe","ignore"]});if(e.status===0&&e.stdout){const r=e.stdout.trim();if(r)return r}}catch{}return null}function ae(e){process.stdout.write(`${e}
2
+ `)}function se(e){process.stderr.write(`${e}
3
+ `)}function ie(e){return e==="-v"||e==="--version"||e==="version"}function ue(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function de(){return process.argv[1]?o.resolve(process.argv[1]):O(import.meta.url)}function oe(e){return["[Unit]","Description=AgentsWorkStudio Runtime Bridge","After=network-online.target","Wants=network-online.target","","[Service]","Type=simple","Environment=AWS_BRIDGE_SKIP_SETUP=true",`ExecStart=${[process.execPath,e].map(n=>ue(n)).join(" ")}`,"Restart=on-failure","RestartSec=5s","","[Install]","WantedBy=multi-user.target",""].join(`
4
+ `)}function v(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ce(e,r={}){const n=r.userHomeDir??S.homedir(),s=r.logPath??W,a=s.replace(/\.out\.log$/,".err.log");return['<?xml version="1.0" encoding="UTF-8"?>','<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">','<plist version="1.0">',"<dict>"," <key>Label</key>",` <string>${v(T)}</string>`," <key>ProgramArguments</key>"," <array>",` <string>${v(process.execPath)}</string>`,` <string>${v(e)}</string>`," </array>"," <key>EnvironmentVariables</key>"," <dict>"," <key>AWS_BRIDGE_SKIP_SETUP</key>"," <string>true</string>"," <key>AWS_RUNTIME_HOME_DIR</key>",` <string>${v(n)}</string>`," </dict>"," <key>RunAtLoad</key>"," <true/>"," <key>KeepAlive</key>"," <true/>"," <key>StandardOutPath</key>",` <string>${v(s)}</string>`," <key>StandardErrorPath</key>",` <string>${v(a)}</string>`,"</dict>","</plist>",""].join(`
5
+ `)}function f(e,r){return e("systemctl",r)}function g(e,r){return r.error?`[runtime-bridge] ${e}\u5931\u8D25: ${r.error.message}`:`[runtime-bridge] ${e}\u5931\u8D25\uFF0Csystemctl \u9000\u51FA\u7801: ${r.status??"unknown"}`}function d(e){return!e.error&&e.status===0}function E(e,r){return e==="linux"?!0:(r("[runtime-bridge] service \u547D\u4EE4\u4EC5\u652F\u6301 Linux systemd \u73AF\u5883\u3002"),!1)}function k(e,r){return e==="darwin"?!0:(r("[runtime-bridge] service \u547D\u4EE4\u4EC5\u652F\u6301 macOS launchd \u73AF\u5883\u3002"),!1)}function G(e){e("Usage: awsb service <install|uninstall|start|stop|restart>")}function le(e){if(!E(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=oe(e.executablePath);try{p(o.dirname(e.serviceUnitPath),{recursive:!0}),N(e.serviceUnitPath,r,{encoding:"utf-8",mode:420})}catch(t){const i=t instanceof Error?t:new Error(String(t));return e.stderr(`[runtime-bridge] \u5199\u5165 systemd unit \u5931\u8D25: ${i.message}`),{handled:!0,exitCode:1}}const n=f(e.runCommand,["daemon-reload"]);if(!d(n))return e.stderr(g("\u91CD\u8F7D systemd",n)),{handled:!0,exitCode:n.status??1};const s=f(e.runCommand,["enable",m]);if(!d(s))return e.stderr(g("\u542F\u7528 awsb \u5F00\u673A\u81EA\u542F",s)),{handled:!0,exitCode:s.status??1};const a=f(e.runCommand,["is-active","--quiet",m]);if(a.error)return e.stderr(g("\u68C0\u67E5 awsb \u670D\u52A1\u72B6\u6001",a)),{handled:!0,exitCode:1};if(a.status!==0){const t=f(e.runCommand,["start",m]);return d(t)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u5B89\u88C5\u3001\u5DF2\u542F\u7528\u5F00\u673A\u81EA\u542F\u5E76\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(g("\u542F\u52A8 awsb \u670D\u52A1",t)),{handled:!0,exitCode:t.status??1})}return e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u5DF2\u542F\u7528\u5F00\u673A\u81EA\u542F\uFF1B\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u3002"),{handled:!0,exitCode:0}}function me(e){if(!E(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=l(e.serviceUnitPath),n=f(e.runCommand,["is-active","--quiet",m]);if(n.error)return e.stderr(g("\u68C0\u67E5 awsb \u670D\u52A1\u72B6\u6001",n)),{handled:!0,exitCode:1};if(n.status===0){const t=f(e.runCommand,["stop",m]);if(!d(t))return e.stderr(g("\u505C\u6B62 awsb \u670D\u52A1",t)),{handled:!0,exitCode:t.status??1}}const s=f(e.runCommand,["disable",m]);if(s.error||s.status!==0&&r)return e.stderr(g("\u7981\u7528 awsb \u5F00\u673A\u81EA\u542F",s)),{handled:!0,exitCode:s.status??1};if(r)try{M(e.serviceUnitPath)}catch(t){const i=t instanceof Error?t:new Error(String(t));return e.stderr(`[runtime-bridge] \u5220\u9664 systemd unit \u5931\u8D25: ${i.message}`),{handled:!0,exitCode:1}}else e.stdout("[runtime-bridge] awsb systemd unit \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5220\u9664\u3002");const a=f(e.runCommand,["daemon-reload"]);return d(a)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u5378\u8F7D\u3002"),{handled:!0,exitCode:0}):(e.stderr(g("\u91CD\u8F7D systemd",a)),{handled:!0,exitCode:a.status??1})}function fe(e){if(!E(e.platform,e.stderr))return{handled:!0,exitCode:1};if(!l(e.serviceUnitPath))return e.stderr("[runtime-bridge] awsb systemd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const r=f(e.runCommand,["restart",m]);return d(r)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u91CD\u542F\u3002"),{handled:!0,exitCode:0}):(e.stderr(g("\u91CD\u542F awsb \u670D\u52A1",r)),{handled:!0,exitCode:r.status??1})}function ge(e){if(!E(e.platform,e.stderr))return{handled:!0,exitCode:1};if(!l(e.serviceUnitPath))return e.stderr("[runtime-bridge] awsb systemd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const r=f(e.runCommand,["start",m]);return d(r)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(g("\u542F\u52A8 awsb \u670D\u52A1",r)),{handled:!0,exitCode:r.status??1})}function he(e){if(!E(e.platform,e.stderr))return{handled:!0,exitCode:1};if(!l(e.serviceUnitPath))return e.stderr("[runtime-bridge] awsb systemd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const r=f(e.runCommand,["stop",m]);return d(r)?(e.stdout("[runtime-bridge] awsb systemd \u670D\u52A1\u5DF2\u505C\u6B62\u3002"),{handled:!0,exitCode:0}):(e.stderr(g("\u505C\u6B62 awsb \u670D\u52A1",r)),{handled:!0,exitCode:r.status??1})}function be(e){return e(`[runtime-bridge] \u672A\u68C0\u6D4B\u5230 node-windows \u6A21\u5757\u3002\u8BF7\u91CD\u65B0\u5B89\u88C5 aws-runtime-bridge\uFF1A
6
+ npm install -g aws-runtime-bridge`),{handled:!0,exitCode:1}}async function we(e){const r=e.windowsServiceManager;if(!r&&!Z())return be(e.stderr);const n=e.windowsServiceLogPath||B,s=o.dirname(e.executablePath),a=S.homedir();try{await(r||X()).install({name:H,description:"awsb runtime bridge",script:e.executablePath,workingDirectory:s,env:[{name:"AWS_BRIDGE_SKIP_SETUP",value:"true"},{name:"AWS_RUNTIME_HOME_DIR",value:a}],logpath:F})}catch(i){const h=i instanceof Error?i:new Error(String(i));return e.stderr(`[runtime-bridge] \u5B89\u88C5 Windows \u670D\u52A1\u5931\u8D25: ${h.message}`),{handled:!0,exitCode:1}}const t=e.runCommand("sc.exe",["start",x]);return d(t)?(e.stdout(`[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u5DF2\u542F\u52A8\uFF08\u65E5\u5FD7: ${n}\uFF09\u3002`),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u542F\u52A8 Windows \u670D\u52A1\u5931\u8D25: ${t.error?.message??`\u9000\u51FA\u7801 ${t.status??"unknown"}`}`),{handled:!0,exitCode:t.status??1})}function Ce(e){const r=s=>e.runCommand("sc.exe",s);r(["stop",x]);const n=r(["delete",x]);return d(n)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u5378\u8F7D\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u5378\u8F7D Windows \u670D\u52A1\u5931\u8D25: ${n.error?.message??`\u9000\u51FA\u7801 ${n.status??"unknown"}`}`),{handled:!0,exitCode:n.status??1})}function xe(e){const r=s=>e.runCommand("sc.exe",s);r(["stop",x]);const n=r(["start",x]);return d(n)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u91CD\u542F\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u91CD\u542F Windows \u670D\u52A1\u5931\u8D25: ${n.error?.message??`\u9000\u51FA\u7801 ${n.status??"unknown"}`}`),{handled:!0,exitCode:n.status??1})}function Pe(e){const r=e.runCommand("sc.exe",["start",x]);return d(r)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u542F\u52A8 Windows \u670D\u52A1\u5931\u8D25: ${r.error?.message??`\u9000\u51FA\u7801 ${r.status??"unknown"}`}`),{handled:!0,exitCode:r.status??1})}function Se(e){const r=e.runCommand("sc.exe",["stop",x]);return d(r)?(e.stdout("[runtime-bridge] awsb Windows \u670D\u52A1\u5DF2\u505C\u6B62\u3002"),{handled:!0,exitCode:0}):(e.stderr(`[runtime-bridge] \u505C\u6B62 Windows \u670D\u52A1\u5931\u8D25: ${r.error?.message??`\u9000\u51FA\u7801 ${r.status??"unknown"}`}`),{handled:!0,exitCode:r.status??1})}function ve(){return`gui/${process.getuid?.()??0}`}function R(e){return e.launchdDomainTarget||ve()}function L(e){return`${e}/${T}`}function D(e,r){return r.error?`[runtime-bridge] ${e}\u5931\u8D25: ${r.error.message}`:`[runtime-bridge] ${e}\u5931\u8D25\uFF0Claunchctl \u9000\u51FA\u7801: ${r.status??"unknown"}`}function ye(e){if(!k(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=e.launchdPlistPath||_,n=e.launchdLogPath||W,s=ce(e.executablePath,{userHomeDir:S.homedir(),logPath:n});try{p(o.dirname(r),{recursive:!0}),p(o.dirname(n),{recursive:!0}),N(r,s,{encoding:"utf-8",mode:420})}catch(i){const h=i instanceof Error?i:new Error(String(i));return e.stderr(`[runtime-bridge] \u5199\u5165 launchd plist \u5931\u8D25: ${h.message}`),{handled:!0,exitCode:1}}const a=R(e);e.runCommand("launchctl",["bootout",L(a)]);const t=e.runCommand("launchctl",["bootstrap",a,r]);return d(t)?(e.stdout(`[runtime-bridge] awsb launchd \u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u5DF2\u542F\u52A8\uFF08\u65E5\u5FD7: ${n}\uFF09\u3002`),{handled:!0,exitCode:0}):(e.stderr(D("\u5B89\u88C5 awsb \u670D\u52A1",t)),{handled:!0,exitCode:t.status??1})}function $e(e){if(!k(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=e.launchdPlistPath||_,n=R(e),s=L(n),a=e.runCommand("launchctl",["bootout",s]);if(a.error)return e.stderr(D("\u5378\u8F7D awsb \u670D\u52A1",a)),{handled:!0,exitCode:1};if(l(r))try{M(r)}catch(t){const i=t instanceof Error?t:new Error(String(t));return e.stderr(`[runtime-bridge] \u5220\u9664 launchd plist \u5931\u8D25: ${i.message}`),{handled:!0,exitCode:1}}else e.stdout("[runtime-bridge] awsb launchd plist \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5220\u9664\u3002");return e.stdout("[runtime-bridge] awsb launchd \u670D\u52A1\u5DF2\u5378\u8F7D\u3002"),{handled:!0,exitCode:0}}function Ee(e){if(!k(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=e.launchdPlistPath||_;if(!l(r))return e.stderr("[runtime-bridge] awsb launchd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const n=R(e),s=L(n),a=e.runCommand("launchctl",["kickstart",s]);if(d(a))return e.stdout("[runtime-bridge] awsb launchd \u670D\u52A1\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0};const t=e.runCommand("launchctl",["bootstrap",n,r]);return d(t)?(e.stdout("[runtime-bridge] awsb launchd \u670D\u52A1\u5DF2\u542F\u52A8\u3002"),{handled:!0,exitCode:0}):(e.stderr(D("\u542F\u52A8 awsb \u670D\u52A1",t)),{handled:!0,exitCode:t.status??1})}function ke(e){if(!k(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=R(e),n=e.runCommand("launchctl",["bootout",L(r)]);return d(n)?(e.stdout("[runtime-bridge] awsb launchd \u670D\u52A1\u5DF2\u505C\u6B62\u3002"),{handled:!0,exitCode:0}):(e.stderr(D("\u505C\u6B62 awsb \u670D\u52A1",n)),{handled:!0,exitCode:n.status??1})}function Re(e){if(!k(e.platform,e.stderr))return{handled:!0,exitCode:1};const r=e.launchdPlistPath||_;if(!l(r))return e.stderr("[runtime-bridge] awsb launchd \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C `awsb service install`\u3002"),{handled:!0,exitCode:1};const n=R(e);e.runCommand("launchctl",["bootout",L(n)]);const s=e.runCommand("launchctl",["bootstrap",n,r]);return d(s)?(e.stdout("[runtime-bridge] awsb launchd \u670D\u52A1\u5DF2\u91CD\u542F\u3002"),{handled:!0,exitCode:0}):(e.stderr(D("\u91CD\u542F awsb \u670D\u52A1",s)),{handled:!0,exitCode:s.status??1})}async function Le(e,r,n,s){const a=e[1];if(!a||a==="-h"||a==="--help"||a==="help")return G(n),{handled:!0,exitCode:a?0:1};const t={stdout:n,stderr:s,runCommand:r.runCommand||I,platform:r.platform||process.platform,serviceUnitPath:r.serviceUnitPath||j,executablePath:r.executablePath||de(),windowsServiceManager:r.windowsServiceManager,launchdPlistPath:r.launchdPlistPath,launchdLogPath:r.launchdLogPath,launchdDomainTarget:r.launchdDomainTarget};return t.platform!=="linux"&&t.platform!=="win32"&&t.platform!=="darwin"?(s("[runtime-bridge] service \u547D\u4EE4\u4EC5\u652F\u6301 Linux systemd\u3001Windows \u670D\u52A1\u4E0E macOS launchd \u73AF\u5883\u3002"),{handled:!0,exitCode:1}):a==="install"?t.platform==="win32"?await we(t):t.platform==="darwin"?ye(t):le(t):a==="uninstall"?t.platform==="win32"?Ce(t):t.platform==="darwin"?$e(t):me(t):a==="restart"?t.platform==="win32"?xe(t):t.platform==="darwin"?Re(t):fe(t):a==="start"?t.platform==="win32"?Pe(t):t.platform==="darwin"?Ee(t):ge(t):a==="stop"?t.platform==="win32"?Se(t):t.platform==="darwin"?ke(t):he(t):(s(`[runtime-bridge] \u672A\u77E5 service \u5B50\u547D\u4EE4: ${a}`),G(n),{handled:!0,exitCode:1})}function V(e){return e?e.split(/\r?\n/).map(r=>r.trim()).filter(r=>/^\d+$/.test(r)).map(Number):[]}function De(e){try{if(e==="win32"){const n=P("powershell",["-NoProfile","-Command",`Get-CimInstance Win32_Process -Filter "Name='node.exe'" | Where-Object { $_.CommandLine -like '*aws-runtime-bridge*' -and $_.ProcessId -ne ${process.pid} } | ForEach-Object { $_.ProcessId }`],{encoding:"utf-8"});return V(n.stdout)}const r=P("pgrep",["-f","aws-runtime-bridge"],{encoding:"utf-8"});return V(r.stdout).filter(n=>n!==process.pid)}catch{return[]}}function _e(e,r){if(r==="win32"){P("taskkill",["/PID",String(e),"/F","/T"],{stdio:"ignore"});return}P("kill",["-9",String(e)],{stdio:"ignore"})}function pe(e){if(!process.stdin.isTTY)return Promise.resolve("n");const r=q({input:process.stdin,output:process.stdout});return r.question(e).finally(()=>r.close())}async function Te(e,r,n){const s=e.runCommand||I,a=e.platform||process.platform,t=e.findRunningBridgeProcesses||De,i=e.prompt||pe,h=e.killBridgeProcess||_e,u=e.globalModulePath||ne;r("[runtime-bridge] \u6B63\u5728\u66F4\u65B0 aws-runtime-bridge \u5230\u6700\u65B0\u7248\u672C...");const c=t(a).filter(w=>w!==process.pid);let b=!1;if(c.length>0){r(`[runtime-bridge] \u68C0\u6D4B\u5230 aws-runtime-bridge \u6B63\u5728\u8FD0\u884C (PID: ${c.join(", ")})\uFF0C\u66F4\u65B0\u65F6\u8FD9\u4E9B\u8FDB\u7A0B\u53EF\u80FD\u5360\u7528\u6587\u4EF6\u5BFC\u81F4\u8986\u76D6\u5931\u8D25\u3002`);const w=(await i("\u662F\u5426\u5173\u95ED\u4E0A\u8FF0\u8FDB\u7A0B\u4EE5\u7EE7\u7EED\u66F4\u65B0? (y/N) ")).trim().toLowerCase();if(w==="y"||w==="yes")for(const C of c)try{h(C,a),r(`[runtime-bridge] \u5DF2\u5173\u95ED\u8FDB\u7A0B PID: ${C}`)}catch($){const K=$ instanceof Error?$:new Error(String($));n(`[runtime-bridge] \u5173\u95ED\u8FDB\u7A0B PID ${C} \u5931\u8D25: ${K.message}`)}else b=!0,r("[runtime-bridge] \u5DF2\u9009\u62E9\u4E0D\u5173\u95ED\u8FDB\u7A0B\uFF0C\u5C06\u4EE5 --force \u65B9\u5F0F\u5F3A\u5236\u5B89\u88C5\uFF08\u82E5\u6587\u4EF6\u4ECD\u88AB\u5360\u7528\u53EF\u80FD\u4F1A\u6709\u8B66\u544A\uFF09\u3002")}const U=u();if(U){const w=o.join(U,"aws-runtime-bridge");if(l(w)){r("[runtime-bridge] \u6E05\u7406\u65E7\u7248\u672C\u6B8B\u7559\u76EE\u5F55...");try{J(w,{recursive:!0,force:!0})}catch(C){const $=C instanceof Error?C:new Error(String(C));n(`[runtime-bridge] \u6E05\u7406\u6B8B\u7559\u76EE\u5F55\u5931\u8D25\uFF08\u5C06\u5C1D\u8BD5\u7EE7\u7EED\u5B89\u88C5\uFF09: ${$.message}`)}}}const A=["install","-g","aws-runtime-bridge@latest","--omit=optional"];b&&A.push("--force");const y=s("npm",A);return y.error?(n(`[runtime-bridge] \u66F4\u65B0\u5931\u8D25: ${y.error.message}`),{handled:!0,exitCode:1}):y.status!==0?(n(`[runtime-bridge] \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${y.status??"unknown"}`),{handled:!0,exitCode:y.status??1}):(r("[runtime-bridge] \u66F4\u65B0\u5B8C\u6210\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C awsb -v \u67E5\u770B\u5F53\u524D\u7248\u672C\u3002"),{handled:!0,exitCode:0})}function We(e,r,n){const a=(e.argv||process.argv.slice(2)).slice(1).some(u=>u==="--follow"||u==="-f"),t=e.platform||process.platform,i=e.runCommand||I,h=e.serviceUnitPath||j;if(t==="linux"){if(!l(h))return n("[runtime-bridge] awsb \u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF08awsb service install\uFF09\uFF0C\u65E0\u65E5\u5FD7\u53EF\u67E5\u770B\u3002"),{handled:!0,exitCode:1};const u=["-u",m];a?u.push("-f"):u.push("-n","200","--no-pager");const c=i("journalctl",u);return c.error?(n(`[runtime-bridge] \u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25: ${c.error.message}`),{handled:!0,exitCode:1}):{handled:!0,exitCode:c.status??0}}if(t==="win32"){const u=e.windowsServiceLogPath||B;if(!l(u))return n(`[runtime-bridge] \u5C1A\u672A\u751F\u6210\u65E5\u5FD7\u6587\u4EF6: ${u}\u3002\u8BF7\u5148\u6267\u884C \`awsb service install\` \u5E76\u8FD0\u884C\u670D\u52A1\u3002`),{handled:!0,exitCode:1};const c=`Get-Content -Encoding UTF8 -Tail 200 "${u}"`+(a?" -Wait":""),b=i("powershell",["-NoProfile","-Command",c]);return b.error?(n(`[runtime-bridge] \u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25: ${b.error.message}`),{handled:!0,exitCode:1}):{handled:!0,exitCode:b.status??0}}if(t==="darwin"){const u=e.launchdLogPath||W;if(!l(u))return n(`[runtime-bridge] \u5C1A\u672A\u751F\u6210\u65E5\u5FD7\u6587\u4EF6: ${u}\u3002\u8BF7\u5148\u6267\u884C \`awsb service install\` \u5E76\u8FD0\u884C\u670D\u52A1\u3002`),{handled:!0,exitCode:1};const b=i("tail",a?["-f",u]:["-n","200",u]);return b.error?(n(`[runtime-bridge] \u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25: ${b.error.message}`),{handled:!0,exitCode:1}):{handled:!0,exitCode:b.status??0}}return n("[runtime-bridge] awsb log \u4EC5\u652F\u6301 Linux systemd\u3001Windows \u670D\u52A1\u4E0E macOS launchd \u73AF\u5883\u3002"),{handled:!0,exitCode:1}}async function Fe(e={}){const r=e.argv||process.argv.slice(2),n=r[0],s=e.stdout||ae,a=e.stderr||se;if(ie(n))return s(`aws-runtime-bridge ${te(e.packageRoot)}`),{handled:!0,exitCode:0};if(n==="update")return await Te(e,s,a);if(n==="config"){const t=r[1];if(t==="example"||t==="minimal"||t==="--minimal"||t==="-m")return{handled:!0,exitCode:await(e.configureExample||Q)()==="created"?0:1};const h=await(e.configure||z)();return{handled:!0,exitCode:h==="configured"||h==="created"?0:1}}return n==="service"?await Le(r,e,s,a):n==="log"?We(e,s,a):{handled:!1,exitCode:0}}export{ce as createLaunchdPlistContent,oe as createSystemdUnitContent,Fe as handleCliCommand,te as readPackageVersion};
6
7
 
@@ -1,2 +1,2 @@
1
- import{configManager as g}from"./config.js";import{MESSAGE_POLL_INTERVAL_MS as p}from"./constants.js";import{HttpClient as f}from"./http-client.js";import{logger as s,setMcpNodeLogSink as C,updateStartupLogIdentity as m}from"./logger.js";import{claimRuntimeLaunchBinding as I}from"./runtime-launch-binding.js";import{StatusReporter as y}from"./status-reporter.js";import{WebSocketClient as u}from"./websocket-client.js";class _{wsClient;httpClient;agentConfig;serverConfig;agentId=null;displayName=null;projectName=null;isRunning=!1;isDegradedMode=!1;messageHandler=null;profileHandler=null;pollTimer=null;statusReporter=null;newCharacterHandler=null;runtimeTokenRefreshTimer=null;cachedProfile=null;constructor(){g.loadConfig(),this.agentConfig=g.getAgentConfig(),this.serverConfig=g.getServerConfig(),this.wsClient=new u(this.serverConfig),this.httpClient=new f(this.serverConfig),this.setupWebSocketListeners()}async initialize(){const e=await I(this.agentConfig,this.serverConfig);if(!e){this.serverConfig.runtimeBridgeBaseUrl=void 0;return}g.applyRuntimeLaunchBinding(e),this.agentConfig=g.getAgentConfig(),this.serverConfig=g.getServerConfig(),this.wsClient=new u(this.serverConfig),this.httpClient=new f(this.serverConfig),this.setupWebSocketListeners()}setupWebSocketListeners(){this.wsClient.on("directMessage",e=>{this.handleIncomingDm(e)}),this.wsClient.on("groupMessage",e=>{this.handleIncomingGroup(e)}),this.wsClient.on("profileInfo",e=>{s.info(`[AgentClient] WebSocket profileInfo \u4E8B\u4EF6\u89E6\u53D1\uFF0CdisplayName: ${e.displayName}`),this.handleProfileInfo(e)}),this.wsClient.on("agentStatus",e=>{s.info(`[AgentClient] WebSocket agentStatus \u4E8B\u4EF6\u89E6\u53D1: ${e.status}`)}),this.wsClient.on("disconnected",(e,t)=>{const n=t?.toString()||"";s.warn("[AgentClient] WebSocket \u65AD\u5F00\u8FDE\u63A5: code=%s, reason=%s, agentId=%s, \u6B63\u5728\u81EA\u52A8\u91CD\u8FDE",e,n,this.agentId)}),this.wsClient.on("error",e=>{s.error("[AgentClient] WebSocket \u9519\u8BEF: %s, stack=%s",e.message,e.stack)}),this.wsClient.on("reconnectFailed",()=>{s.error("[AgentClient] \u91CD\u8FDE\u5931\u8D25\uFF08\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF09\uFF0C\u505C\u6B62\u6D88\u606F\u5FAA\u73AF, agentId=%s",this.agentId),this.stop()})}async register(){return s.info("[AgentClient] \u6B63\u5728\u6CE8\u518C Agent..."),this.hasPresetAgentId()?await this.registerWithExistingAgentId():await this.registerAsNewAgent()}hasPresetAgentId(){return!!(this.agentConfig.agentId&&this.agentConfig.agentId.trim().length>0)}async registerWithExistingAgentId(){const e=this.agentConfig.agentId;if(!e)throw new Error("Preset agentId is missing");this.agentId=e.trim();let t;try{t=await this.getProfileByAgentId(this.agentId)}catch(i){throw s.error("[AgentClient] getProfileByAgentId \u5931\u8D25 (presetAgentId=%s): %s",this.agentId,i instanceof Error?i.message:String(i)),i}this.displayName=t.displayName,this.projectName=t.projectName||null,s.info(`[AgentClient] \u4F7F\u7528\u9884\u8BBE AgentID \u7ED1\u5B9A\u5B9E\u4F8B: ${this.displayName} (${this.agentId})`),s.info(`[AgentClient] \u6240\u5C5E\u9879\u76EE: ${this.projectName||"(\u672A\u8BBE\u7F6E)"}`);try{await this.wsClient.connect(this.agentId)}catch(i){throw s.error("[AgentClient] WebSocket \u8FDE\u63A5\u5931\u8D25 (agentId=%s): %s",this.agentId,i instanceof Error?i.message:String(i)),i}s.info("[AgentClient] WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB\uFF0CAgent \u73B0\u5728\u5DF2\u4E0A\u7EBF"),this.initializeStatusReporter();const n=await this.getProfileByAgentId(this.agentId);return this.displayName=n.displayName,this.projectName=n.projectName||null,m(n.roleName||"unknown-role",n.instanceName||this.agentId),{agentId:this.agentId,displayName:this.displayName,status:n.isOnline?"online":"offline",projectName:n.projectName,workspacePath:n.workspacePath,runtimeStatus:n.runtimeStatus,runtimeSessionId:n.runtimeSessionId}}async registerAsNewAgent(){const e=await this.httpClient.callTool("register",{roleName:this.agentConfig.roleName,projectName:this.agentConfig.projectName,workspacePath:this.agentConfig.workspacePath,prompt:this.agentConfig.prompt,runtimeBridgeBaseUrl:this.serverConfig.runtimeBridgeBaseUrl}),t=e.agentId;if(!t)throw new Error("Registration failed: agentId was not returned");return this.agentId=t,this.displayName=e.displayName,this.projectName=e.projectName||null,m(this.agentConfig.roleName||"unknown-role",e.instanceName||t),s.info(`[AgentClient] \u6CE8\u518C\u6210\u529F: ${this.displayName} (${this.agentId})`),s.info(`[AgentClient] \u72B6\u6001: ${e.status}`),s.info(`[AgentClient] \u6240\u5C5E\u9879\u76EE: ${this.projectName||"(\u672A\u8BBE\u7F6E)"}`),await this.wsClient.connect(this.agentId),s.info("[AgentClient] WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB\uFF0CAgent \u73B0\u5728\u5DF2\u4E0A\u7EBF"),this.initializeStatusReporter(),e.isExisting===!1&&this.newCharacterHandler&&this.newCharacterHandler(),e}async unregister(){s.info("[AgentClient] \u6B63\u5728\u6CE8\u9500 Agent..."),this.agentId&&await this.httpClient.callTool("unregister",{agentId:this.agentId}),this.stopRuntimeTokenRefresh(),this.wsClient.disconnect(),this.agentId=null,this.displayName=null,this.projectName=null,this.isRunning=!1,s.info("[AgentClient] \u5DF2\u6CE8\u9500")}async discoverColleagues(){return await this.httpClient.callTool("discover_colleague",{agentId:this.agentId})}async discoverGroupRooms(){return this.ensureRegistered(),await this.httpClient.callTool("discover_group_rooms",{agentId:this.agentId})}async hireColleague(e){return this.ensureRegistered(),await this.httpClient.callTool("hire_colleague",{agentId:this.agentId,...e})}async hireColleaguesBatch(e){return this.ensureRegistered(),await this.httpClient.callTool("hire_colleagues_batch",{agentId:this.agentId,...e})}async callAgentScopedTool(e,t){return this.ensureRegistered(),await this.httpClient.callTool(e,{agentId:this.agentId,...t})}async reportMemoryStats(e){await this.callAgentScopedTool("report_memory_stats",{memoryStats:e})}async sendGroupMessage(e,t,n){this.ensureRegistered();const i=t||this.projectName||this.agentConfig.projectName;return await this.httpClient.callTool("send_group_message",{agentId:this.agentId,projectName:i,roomId:n,content:e})}async sendDirectMessage(e,t,n=!1,i,r){this.ensureRegistered();const o={agentId:this.agentId,targetId:e,content:t,requireReply:n,waitReply:n,replyToCallId:i};if(typeof r=="number"&&r>0&&(o.timeoutMs=r),n){const h=typeof r=="number"&&r>0?r+3e4:0;return await this.httpClient.callToolOnce("send_dm",o,h)}return await this.httpClient.callTool("send_dm",o)}async sendCallAndWaitReply(e,t,n=6e4){const i=await this.sendDirectMessage(e,t,!0,void 0,n);if(i.reply)return i.reply;throw new Error("No reply received for call-style direct message before timeout")}async replyToCall(e,t,n){return await this.sendDirectMessage(t,n,!1,e)}async getDmHistory(e,t=50,n){this.ensureRegistered();const i={agentId:this.agentId,targetUserId:e,limit:t};return n&&(i.since=n),await this.httpClient.callTool("get_dm_history",i)}async getGroupHistory(e=50,t,n){if(this.ensureRegistered(),t){const a={projectName:this.projectName||this.agentConfig.projectName,roomId:t,limit:e};return n&&(a.since=n),await this.httpClient.callTool("get_group_history",a)}const i=await this.discoverGroupRooms(),r=this.projectName||this.agentConfig.projectName,o=this.collectRoomIds(i,r);if(o.length===0)return{messages:[],count:0};const c=(await Promise.all(o.map(async a=>{const l={projectName:r,roomId:a,limit:e};return n&&(l.since=n),await this.httpClient.callTool("get_group_history",l)}))).flatMap(a=>a.messages).sort((a,l)=>new Date(a.timestamp).getTime()-new Date(l.timestamp).getTime());return{messages:c,count:c.length}}async fetchUnreadMessages(e={}){this.ensureRegistered();const t=this.projectName||this.agentConfig.projectName,n={agentId:this.agentId,blockIfEmpty:e.blockIfEmpty??!1};e.since&&(n.since=e.since),e.blockTimeoutMs&&e.blockTimeoutMs>0&&(n.blockTimeoutMs=e.blockTimeoutMs);const i=e.includePipelineTasks?"poll_message":"get_dm_messages",r=await this.fetchDirectMessagesWithRecovery(i,n),o=await this.discoverGroupRooms(),h=this.collectRoomIds(o,t),c=await Promise.all(h.map(async a=>{const l={agentId:this.agentId,projectName:t,roomId:a};e.since&&(l.since=e.since);try{return await this.httpClient.callTool("get_group_messages",l)}catch(d){return s.warn("[AgentClient] \u8DF3\u8FC7\u7FA4\u623F\u95F4\u672A\u8BFB\u8865\u62C9\u5931\u8D25: roomId=%s, error=%s",a,d instanceof Error?d.message:String(d)),{messages:[],currentReadPos:0,hasMore:!1}}}));return{directMessages:Array.isArray(r.messages)?r.messages:[],groupMessages:c.flatMap(a=>this.normalizeGroupMessages(a)).sort((a,l)=>new Date(a.timestamp).getTime()-new Date(l.timestamp).getTime())}}async fetchDirectMessagesWithRecovery(e,t){if(e==="poll_message")try{return await this.httpClient.callToolOnce(e,t)}catch(n){if(this.isRecoverableLongPollError(n))return s.warn("[AgentClient] poll_message \u5355\u6B21\u957F\u8F6E\u8BE2\u8D85\u65F6\uFF0C\u6309\u7A7A\u7ED3\u679C\u9000\u907F\u540E\u7EE7\u7EED\u7B49\u5F85:",n),{messages:[]};throw n}return await this.httpClient.callTool(e,t)}isRecoverableLongPollError(e){return e instanceof Error?/MCP tool poll_message call failed: 503(?:\s|$)/.test(e.message)||/MCP tool poll_message timed out after \d+ms/.test(e.message):!1}normalizeGroupMessages(e){return Array.isArray(e.messages)?e.messages.filter(t=>!!(t&&typeof t=="object"&&typeof t.msgId=="number"&&typeof t.senderId=="string"&&typeof t.content=="string"&&typeof t.timestamp=="string")):[]}collectRoomIds(e,t){const n=e.groups.filter(i=>i.projectName===t).map(i=>i.id);return Array.from(new Set(n))}async getProfile(){this.ensureRegistered();const e=this.agentId;if(!e)throw new Error("Agent is not registered");return{...await this.getProfileByAgentId(e),describe:this.agentConfig.describe}}async getProfileByAgentId(e){return await this.httpClient.callTool("get_profile",{agentId:e})}async getMyTasks(){return this.ensureRegistered(),await this.httpClient.callTool("my_task",{agentId:this.agentId})}async createTaskInstanceComment(e){this.ensureRegistered();const t={agentId:this.agentId,...e};return await this.httpClient.callTool("create_task_instance_comment",t)}async submitTaskResult(e,t,n){this.ensureRegistered();const i={agentId:this.agentId,nodeId:e,outputPayload:t};return n&&Array.isArray(n)&&n.length>0&&(i.changeLog=n),await this.httpClient.callTool("submit_task_result",i)}async startTaskRollback(e){return this.ensureRegistered(),await this.httpClient.callTool("start_task_rollback",{agentId:this.agentId,...e})}async finishTaskRollback(e){return this.ensureRegistered(),await this.httpClient.callTool("finish_task_rollback",{agentId:this.agentId,...e})}async rejectTask(e,t){return this.ensureRegistered(),await this.httpClient.callTool("reject_task",{agentId:this.agentId,nodeId:e,reason:t})}async terminateTask(e,t){return this.ensureRegistered(),await this.httpClient.callTool("terminate_task",{agentId:this.agentId,taskInstanceId:e,reason:t})}start(e,t){if(this.isRunning){s.warn("[AgentClient] \u6D88\u606F\u5FAA\u73AF\u5DF2\u5728\u8FD0\u884C\u4E2D");return}this.isDegradedMode||this.ensureRegistered(),this.messageHandler=e,this.profileHandler=t?.profileHandler||null,this.isRunning=!0,s.info(`[AgentClient] start() \u88AB\u8C03\u7528\uFF0CcachedProfile: ${this.cachedProfile?"\u5B58\u5728":"\u4E0D\u5B58\u5728"}, profileHandler: ${this.profileHandler?"\u5DF2\u8BBE\u7F6E":"\u672A\u8BBE\u7F6E"}, \u964D\u7EA7\u6A21\u5F0F: ${this.isDegradedMode?"\u662F":"\u5426"}`),this.cachedProfile&&this.profileHandler&&(s.info(`[AgentClient] \u5904\u7406\u7F13\u5B58\u7684 Profile \u4FE1\u606F: ${this.cachedProfile.displayName}`),this.profileHandler(this.cachedProfile),this.cachedProfile=null),t?.pollMode??!1?(s.warn("[AgentClient] \u542F\u52A8\u6D88\u606F\u4E3B\u5FAA\u73AF\uFF08HTTP\u8F6E\u8BE2\u6A21\u5F0F - \u4E0D\u63A8\u8350\uFF09"),this.messageLoop()):s.info("[AgentClient] \u542F\u52A8\u6D88\u606F\u4E3B\u5FAA\u73AF\uFF08WebSocket\u63A8\u9001\u6A21\u5F0F - \u63A8\u8350\uFF09")}stop(){this.isRunning=!1,this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=null),this.stopRuntimeTokenRefresh(),s.info("[AgentClient] \u6D88\u606F\u4E3B\u5FAA\u73AF\u5DF2\u505C\u6B62")}async messageLoop(){for(;this.isRunning;)try{await this.delay(p)}catch(e){s.error("[AgentClient] \u6D88\u606F\u5FAA\u73AF\u9519\u8BEF:",e),await this.delay(5e3)}}handleIncomingDm(e){e.senderId!==this.agentId&&this.processMessage(e)}handleIncomingGroup(e){e.senderId!==this.agentId&&this.processMessage(e)}handleProfileInfo(e){s.info(`[AgentClient] \u6536\u5230 Profile \u4FE1\u606F: ${e.displayName} (${e.roleName})`),e.projectName&&(this.projectName=e.projectName,s.info(`[AgentClient] \u66F4\u65B0\u6240\u5C5E\u9879\u76EE: ${this.projectName}`)),e.roleName&&e.roleName!==this.agentConfig.roleName&&(s.info(`[AgentClient] \u540C\u6B65\u89D2\u8272\u540D: "${this.agentConfig.roleName||"(\u7A7A)"}" -> "${e.roleName}"`),this.agentConfig.roleName=e.roleName),this.profileHandler?this.profileHandler(e):(s.info("[AgentClient] profileHandler \u672A\u8BBE\u7F6E\uFF0C\u7F13\u5B58 Profile \u4FE1\u606F"),this.cachedProfile=e)}async processMessage(e){if(!this.messageHandler)return;const t=typeof e.msgId=="string";s.info(`[AgentClient] \u6536\u5230${t?"\u79C1\u4FE1":"\u7FA4\u6D88\u606F"}\u6765\u81EA ${e.senderName}: ${e.content.substring(0,50)}...`);try{const n=await this.messageHandler(e);if(t){const i=e;i.requireReply&&i.callId&&await this.replyToCall(i.callId,i.senderId,n||"\u5DF2\u6536\u5230\u6D88\u606F\uFF0C\u6B63\u5728\u5904\u7406\u4E2D...")}}catch(n){const i=typeof e.msgId=="string";if(s.error("[AgentClient] \u5904\u7406\u6D88\u606F\u5931\u8D25: type=%s, senderId=%s, senderName=%s, msgId=%s, error=%s",i?"\u79C1\u4FE1":"\u7FA4\u6D88\u606F",e.senderId,e.senderName,e.msgId,n instanceof Error?n.message:String(n)),i){const r=e;r.requireReply&&r.callId&&await this.replyToCall(r.callId,r.senderId,`\u5904\u7406\u5931\u8D25: ${n}`)}}}ensureRegistered(){if(!this.agentId)throw new Error("Agent is not registered")}delay(e){return new Promise(t=>{this.pollTimer=setTimeout(t,e)})}onNewCharacter(e){this.newCharacterHandler=e}getState(){return{agentId:this.agentId,displayName:this.displayName,projectName:this.projectName,isConnected:this.wsClient.isConnected(),isRunning:this.isRunning}}getAgentConfig(){return this.agentConfig}reloadConfig(){g.loadConfig(),this.agentConfig=g.getAgentConfig(),s.info(`[AgentClient] \u914D\u7F6E\u5DF2\u91CD\u65B0\u52A0\u8F7D\uFF0C\u89D2\u8272: ${this.agentConfig.roleName}`)}enterDegradedMode(){this.isDegradedMode=!0,s.warn("[AgentClient] \u8FDB\u5165\u964D\u7EA7\u6A21\u5F0F\uFF0C\u5C06\u5728\u540E\u53F0\u6301\u7EED\u5C1D\u8BD5\u91CD\u8FDE")}exitDegradedMode(){this.isDegradedMode=!1,s.info("[AgentClient] \u9000\u51FA\u964D\u7EA7\u6A21\u5F0F")}isInDegradedMode(){return this.isDegradedMode}async retryRegistrationInBackground(e,t){let n=0;(async()=>{for(;this.isDegradedMode;)try{s.info(`[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u5C1D\u8BD5\u91CD\u65B0\u6CE8\u518C (${n+1}/${t===-1?"\u65E0\u9650":t})`);const r=await this.register();s.info(`[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6CE8\u518C\u6210\u529F - ${r.displayName} (${r.agentId})`),this.exitDegradedMode(),this.messageHandler&&this.start(this.messageHandler);return}catch(r){n++;const o=r instanceof Error?r.message:String(r);if(s.warn(`[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6CE8\u518C\u5931\u8D25 (${n}/${t===-1?"\u65E0\u9650":t}): ${o}`),t>0&&n>=t){s.error("[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u505C\u6B62\u91CD\u8FDE"),this.isDegradedMode=!1;return}await new Promise(h=>setTimeout(h,e))}})().catch(r=>{s.error("[AgentClient] \u964D\u7EA7\u6A21\u5F0F\u540E\u53F0\u91CD\u8BD5\u5FAA\u73AF\u5F02\u5E38\u9000\u51FA: error=%s, stack=%s",r instanceof Error?r.message:String(r),r instanceof Error?r.stack:"(no stack)")})}initializeStatusReporter(){if(!this.agentId){s.warn("[AgentClient] \u65E0\u6CD5\u521D\u59CB\u5316\u72B6\u6001\u62A5\u544A\u5668\uFF1AagentId \u4E3A\u7A7A");return}this.statusReporter=new y({agentId:this.agentId,sendStatusUpdate:e=>{this.wsClient.send({type:"AGENT_STATUS",data:e,timestamp:new Date().toISOString()})}}),C(e=>this.reportMcpNodeLog(e)),this.startRuntimeTokenRefresh(),s.info("[AgentClient] \u72B6\u6001\u62A5\u544A\u5668\u5DF2\u521D\u59CB\u5316")}startRuntimeTokenRefresh(){this.stopRuntimeTokenRefresh();const e=this.serverConfig.runtimeTokenRefreshIntervalMs;if(!e||e<=0){s.warn("[AgentClient] runtimeTokenRefreshIntervalMs \u672A\u914D\u7F6E\u6216\u65E0\u6548\uFF0C\u8DF3\u8FC7\u4E3B\u52A8\u7EED\u671F");return}const t=this.agentId;if(!t){s.warn("[AgentClient] agentId \u4E3A\u7A7A\uFF0C\u8DF3\u8FC7\u4E3B\u52A8\u7EED\u671F");return}this.runtimeTokenRefreshTimer=setInterval(async()=>{try{await this.httpClient.refreshRuntimeAccessToken(t)&&s.info("[AgentClient] runtimeAccessToken \u5DF2\u4E3B\u52A8\u7EED\u671F")}catch(n){s.warn("[AgentClient] \u4E3B\u52A8\u7EED\u671F runtimeAccessToken \u5931\u8D25: %s",n instanceof Error?n.message:String(n)),n instanceof Error&&n.stack&&s.warn("[AgentClient] \u4E3B\u52A8\u7EED\u671F\u5F02\u5E38\u5806\u6808:",n.stack)}},e),s.info(`[AgentClient] runtime token \u4E3B\u52A8\u7EED\u671F\u5DF2\u542F\u52A8\uFF0C\u95F4\u9694 ${e}ms`)}stopRuntimeTokenRefresh(){this.runtimeTokenRefreshTimer&&(clearInterval(this.runtimeTokenRefreshTimer),this.runtimeTokenRefreshTimer=null)}reportMcpNodeLog(e){const t=this.wsClient;!this.agentId||!this.wsClient.isConnected()||typeof t.sendMcpNodeLog!="function"||t.sendMcpNodeLog(e)}getStatusReporter(){return this.statusReporter}}export{_ as AgentClient};
1
+ import{configManager as g}from"./config.js";import{MESSAGE_POLL_INTERVAL_MS as p}from"./constants.js";import{HttpClient as m}from"./http-client.js";import{logger as s,setMcpNodeLogSink as C,updateStartupLogIdentity as f}from"./logger.js";import{claimRuntimeLaunchBinding as I}from"./runtime-launch-binding.js";import{StatusReporter as y}from"./status-reporter.js";import{WebSocketClient as u}from"./websocket-client.js";class _{wsClient;httpClient;agentConfig;serverConfig;agentId=null;displayName=null;projectName=null;isRunning=!1;isDegradedMode=!1;messageHandler=null;profileHandler=null;pollTimer=null;statusReporter=null;newCharacterHandler=null;runtimeTokenRefreshTimer=null;cachedProfile=null;constructor(){g.loadConfig(),this.agentConfig=g.getAgentConfig(),this.serverConfig=g.getServerConfig(),this.wsClient=new u(this.serverConfig),this.httpClient=new m(this.serverConfig),this.setupWebSocketListeners()}async initialize(){const e=await I(this.agentConfig,this.serverConfig);if(!e){this.serverConfig.runtimeBridgeBaseUrl=void 0;return}g.applyRuntimeLaunchBinding(e),this.agentConfig=g.getAgentConfig(),this.serverConfig=g.getServerConfig(),this.wsClient=new u(this.serverConfig),this.httpClient=new m(this.serverConfig),this.setupWebSocketListeners()}setupWebSocketListeners(){this.wsClient.on("directMessage",e=>{this.handleIncomingDm(e)}),this.wsClient.on("groupMessage",e=>{this.handleIncomingGroup(e)}),this.wsClient.on("profileInfo",e=>{s.info(`[AgentClient] WebSocket profileInfo \u4E8B\u4EF6\u89E6\u53D1\uFF0CdisplayName: ${e.displayName}`),this.handleProfileInfo(e)}),this.wsClient.on("agentStatus",e=>{s.info(`[AgentClient] WebSocket agentStatus \u4E8B\u4EF6\u89E6\u53D1: ${e.status}`)}),this.wsClient.on("disconnected",(e,t)=>{const n=t?.toString()||"";s.warn("[AgentClient] WebSocket \u65AD\u5F00\u8FDE\u63A5: code=%s, reason=%s, agentId=%s, \u6B63\u5728\u81EA\u52A8\u91CD\u8FDE",e,n,this.agentId)}),this.wsClient.on("error",e=>{s.error("[AgentClient] WebSocket \u9519\u8BEF: %s, stack=%s",e.message,e.stack)}),this.wsClient.on("reconnectFailed",()=>{s.error("[AgentClient] \u91CD\u8FDE\u5931\u8D25\uFF08\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF09\uFF0C\u505C\u6B62\u6D88\u606F\u5FAA\u73AF, agentId=%s",this.agentId),this.stop()})}async register(){return s.info("[AgentClient] \u6B63\u5728\u6CE8\u518C Agent..."),this.hasPresetAgentId()?await this.registerWithExistingAgentId():await this.registerAsNewAgent()}hasPresetAgentId(){return!!(this.agentConfig.agentId&&this.agentConfig.agentId.trim().length>0)}async registerWithExistingAgentId(){const e=this.agentConfig.agentId;if(!e)throw new Error("Preset agentId is missing");this.agentId=e.trim();let t;try{t=await this.getProfileByAgentId(this.agentId)}catch(i){throw s.error("[AgentClient] getProfileByAgentId \u5931\u8D25 (presetAgentId=%s): %s",this.agentId,i instanceof Error?i.message:String(i)),i}this.displayName=t.displayName,this.projectName=t.projectName||null,s.info(`[AgentClient] \u4F7F\u7528\u9884\u8BBE AgentID \u7ED1\u5B9A\u5B9E\u4F8B: ${this.displayName} (${this.agentId})`),s.info(`[AgentClient] \u6240\u5C5E\u9879\u76EE: ${this.projectName||"(\u672A\u8BBE\u7F6E)"}`);try{await this.wsClient.connect(this.agentId)}catch(i){throw s.error("[AgentClient] WebSocket \u8FDE\u63A5\u5931\u8D25 (agentId=%s): %s",this.agentId,i instanceof Error?i.message:String(i)),i}s.info("[AgentClient] WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB\uFF0CAgent \u73B0\u5728\u5DF2\u4E0A\u7EBF"),this.initializeStatusReporter();const n=await this.getProfileByAgentId(this.agentId);return this.displayName=n.displayName,this.projectName=n.projectName||null,f(n.roleName||"unknown-role",n.instanceName||this.agentId),{agentId:this.agentId,displayName:this.displayName,status:n.isOnline?"online":"offline",projectName:n.projectName,workspacePath:n.workspacePath,runtimeStatus:n.runtimeStatus,runtimeSessionId:n.runtimeSessionId}}async registerAsNewAgent(){const e=await this.httpClient.callTool("register",{roleName:this.agentConfig.roleName,projectName:this.agentConfig.projectName,workspacePath:this.agentConfig.workspacePath,prompt:this.agentConfig.prompt,runtimeBridgeBaseUrl:this.serverConfig.runtimeBridgeBaseUrl}),t=e.agentId;if(!t)throw new Error("Registration failed: agentId was not returned");return this.agentId=t,this.displayName=e.displayName,this.projectName=e.projectName||null,f(this.agentConfig.roleName||"unknown-role",e.instanceName||t),s.info(`[AgentClient] \u6CE8\u518C\u6210\u529F: ${this.displayName} (${this.agentId})`),s.info(`[AgentClient] \u72B6\u6001: ${e.status}`),s.info(`[AgentClient] \u6240\u5C5E\u9879\u76EE: ${this.projectName||"(\u672A\u8BBE\u7F6E)"}`),await this.wsClient.connect(this.agentId),s.info("[AgentClient] WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB\uFF0CAgent \u73B0\u5728\u5DF2\u4E0A\u7EBF"),this.initializeStatusReporter(),e.isExisting===!1&&this.newCharacterHandler&&this.newCharacterHandler(),e}async unregister(){s.info("[AgentClient] \u6B63\u5728\u6CE8\u9500 Agent..."),this.agentId&&await this.httpClient.callTool("unregister",{agentId:this.agentId}),this.stopRuntimeTokenRefresh(),this.wsClient.disconnect(),this.agentId=null,this.displayName=null,this.projectName=null,this.isRunning=!1,s.info("[AgentClient] \u5DF2\u6CE8\u9500")}async discoverColleagues(){return await this.httpClient.callTool("discover_colleague",{agentId:this.agentId})}async discoverGroupRooms(){return this.ensureRegistered(),await this.httpClient.callTool("discover_group_rooms",{agentId:this.agentId})}async hireColleague(e){return this.ensureRegistered(),await this.httpClient.callTool("hire_colleague",{agentId:this.agentId,...e})}async hireColleaguesBatch(e){return this.ensureRegistered(),await this.httpClient.callTool("hire_colleagues_batch",{agentId:this.agentId,...e})}async callAgentScopedTool(e,t){return this.ensureRegistered(),await this.httpClient.callTool(e,{agentId:this.agentId,...t})}async reportMemoryStats(e){await this.callAgentScopedTool("report_memory_stats",{memoryStats:e})}async sendGroupMessage(e,t,n){this.ensureRegistered();const i=t||this.projectName||this.agentConfig.projectName;return await this.httpClient.callTool("send_group_message",{agentId:this.agentId,projectName:i,roomId:n,content:e})}async sendDirectMessage(e,t,n=!1,i,r){this.ensureRegistered();const o={agentId:this.agentId,targetId:e,content:t,requireReply:n,waitReply:n,replyToCallId:i};if(typeof r=="number"&&r>0&&(o.timeoutMs=r),n){const h=typeof r=="number"&&r>0?r+3e4:0;return await this.httpClient.callToolOnce("send_dm",o,h)}return await this.httpClient.callTool("send_dm",o)}async sendCallAndWaitReply(e,t,n=6e4){const i=await this.sendDirectMessage(e,t,!0,void 0,n);if(i.reply)return i.reply;throw new Error("No reply received for call-style direct message before timeout")}async replyToCall(e,t,n){return await this.sendDirectMessage(t,n,!1,e)}async getDmHistory(e,t=50,n){this.ensureRegistered();const i={agentId:this.agentId,targetUserId:e,limit:t};return n&&(i.since=n),await this.httpClient.callTool("get_dm_history",i)}async getGroupHistory(e=50,t,n){if(this.ensureRegistered(),t){const a={projectName:this.projectName||this.agentConfig.projectName,roomId:t,limit:e};return n&&(a.since=n),await this.httpClient.callTool("get_group_history",a)}const i=await this.discoverGroupRooms(),r=this.projectName||this.agentConfig.projectName,o=this.collectRoomIds(i,r);if(o.length===0)return{messages:[],count:0};const c=(await Promise.all(o.map(async a=>{const l={projectName:r,roomId:a,limit:e};return n&&(l.since=n),await this.httpClient.callTool("get_group_history",l)}))).flatMap(a=>a.messages).sort((a,l)=>new Date(a.timestamp).getTime()-new Date(l.timestamp).getTime());return{messages:c,count:c.length}}async fetchUnreadMessages(e={}){this.ensureRegistered();const t=this.projectName||this.agentConfig.projectName,n={agentId:this.agentId,blockIfEmpty:e.blockIfEmpty??!1};e.since&&(n.since=e.since),e.blockTimeoutMs&&e.blockTimeoutMs>0&&(n.blockTimeoutMs=e.blockTimeoutMs);const i=e.includePipelineTasks?"poll_message":"get_dm_messages",r=await this.fetchDirectMessagesWithRecovery(i,n),o=await this.discoverGroupRooms(),h=this.collectRoomIds(o,t),c=await Promise.all(h.map(async a=>{const l={agentId:this.agentId,projectName:t,roomId:a};e.since&&(l.since=e.since);try{return await this.httpClient.callTool("get_group_messages",l)}catch(d){return s.warn("[AgentClient] \u8DF3\u8FC7\u7FA4\u623F\u95F4\u672A\u8BFB\u8865\u62C9\u5931\u8D25: roomId=%s, error=%s",a,d instanceof Error?d.message:String(d)),{messages:[],currentReadPos:0,hasMore:!1}}}));return{directMessages:Array.isArray(r.messages)?r.messages:[],groupMessages:c.flatMap(a=>this.normalizeGroupMessages(a)).sort((a,l)=>new Date(a.timestamp).getTime()-new Date(l.timestamp).getTime())}}async fetchDirectMessagesWithRecovery(e,t){if(e==="poll_message")try{return await this.httpClient.callToolOnce(e,t)}catch(n){if(this.isRecoverableLongPollError(n))return s.warn("[AgentClient] poll_message \u5355\u6B21\u957F\u8F6E\u8BE2\u8D85\u65F6\uFF0C\u6309\u7A7A\u7ED3\u679C\u9000\u907F\u540E\u7EE7\u7EED\u7B49\u5F85:",n),{messages:[]};throw n}return await this.httpClient.callTool(e,t)}isRecoverableLongPollError(e){return e instanceof Error?/MCP tool poll_message call failed: 503(?:\s|$)/.test(e.message)||/MCP tool poll_message timed out after \d+ms/.test(e.message):!1}normalizeGroupMessages(e){return Array.isArray(e.messages)?e.messages.filter(t=>!!(t&&typeof t=="object"&&typeof t.msgId=="number"&&typeof t.senderId=="string"&&typeof t.content=="string"&&typeof t.timestamp=="string")):[]}collectRoomIds(e,t){const n=e.groups.filter(i=>i.projectName===t).map(i=>i.id);return Array.from(new Set(n))}async getProfile(){this.ensureRegistered();const e=this.agentId;if(!e)throw new Error("Agent is not registered");return{...await this.getProfileByAgentId(e),describe:this.agentConfig.describe}}async getProfileByAgentId(e){return await this.httpClient.callTool("get_profile",{agentId:e})}async getMyTasks(){return this.ensureRegistered(),await this.httpClient.callTool("my_task",{agentId:this.agentId})}async createTaskInstanceComment(e){this.ensureRegistered();const t={agentId:this.agentId,...e};return await this.httpClient.callTool("create_task_instance_comment",t)}async submitTaskResult(e,t,n){this.ensureRegistered();const i={agentId:this.agentId,nodeId:e,outputPayload:t};return n&&Array.isArray(n)&&n.length>0&&(i.changeLog=n),await this.httpClient.callTool("submit_task_result",i)}async startTaskRollback(e){return this.ensureRegistered(),await this.httpClient.callTool("start_task_rollback",{agentId:this.agentId,...e})}async finishTaskRollback(e){return this.ensureRegistered(),await this.httpClient.callTool("finish_task_rollback",{agentId:this.agentId,...e})}async rejectTask(e,t){return this.ensureRegistered(),await this.httpClient.callTool("reject_task",{agentId:this.agentId,nodeId:e,reason:t})}async terminateTask(e,t){return this.ensureRegistered(),await this.httpClient.callTool("terminate_task",{agentId:this.agentId,taskInstanceId:e,reason:t})}start(e,t){if(this.isRunning){s.warn("[AgentClient] \u6D88\u606F\u5FAA\u73AF\u5DF2\u5728\u8FD0\u884C\u4E2D");return}this.isDegradedMode||this.ensureRegistered(),this.messageHandler=e,this.profileHandler=t?.profileHandler||null,this.isRunning=!0,s.info(`[AgentClient] start() \u88AB\u8C03\u7528\uFF0CcachedProfile: ${this.cachedProfile?"\u5B58\u5728":"\u4E0D\u5B58\u5728"}, profileHandler: ${this.profileHandler?"\u5DF2\u8BBE\u7F6E":"\u672A\u8BBE\u7F6E"}, \u964D\u7EA7\u6A21\u5F0F: ${this.isDegradedMode?"\u662F":"\u5426"}`),this.cachedProfile&&this.profileHandler&&(s.info(`[AgentClient] \u5904\u7406\u7F13\u5B58\u7684 Profile \u4FE1\u606F: ${this.cachedProfile.displayName}`),this.profileHandler(this.cachedProfile),this.cachedProfile=null),t?.pollMode??!1?(s.warn("[AgentClient] \u542F\u52A8\u6D88\u606F\u4E3B\u5FAA\u73AF\uFF08HTTP\u8F6E\u8BE2\u6A21\u5F0F - \u4E0D\u63A8\u8350\uFF09"),this.messageLoop()):s.info("[AgentClient] \u542F\u52A8\u6D88\u606F\u4E3B\u5FAA\u73AF\uFF08WebSocket\u63A8\u9001\u6A21\u5F0F - \u63A8\u8350\uFF09")}stop(){this.isRunning=!1,this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=null),this.stopRuntimeTokenRefresh(),s.info("[AgentClient] \u6D88\u606F\u4E3B\u5FAA\u73AF\u5DF2\u505C\u6B62")}async messageLoop(){for(;this.isRunning;)try{await this.delay(p)}catch(e){s.error("[AgentClient] \u6D88\u606F\u5FAA\u73AF\u9519\u8BEF:",e),await this.delay(5e3)}}handleIncomingDm(e){e.senderId!==this.agentId&&this.processMessage(e)}handleIncomingGroup(e){e.senderId!==this.agentId&&this.processMessage(e)}handleProfileInfo(e){s.info(`[AgentClient] \u6536\u5230 Profile \u4FE1\u606F: ${e.displayName} (${e.roleName})`),e.projectName&&(this.projectName=e.projectName,s.info(`[AgentClient] \u66F4\u65B0\u6240\u5C5E\u9879\u76EE: ${this.projectName}`)),e.roleName&&e.roleName!==this.agentConfig.roleName&&(s.info(`[AgentClient] \u540C\u6B65\u89D2\u8272\u540D: "${this.agentConfig.roleName||"(\u7A7A)"}" -> "${e.roleName}"`),this.agentConfig.roleName=e.roleName),e.roleName&&f(e.roleName,e.instanceName||this.agentId||""),this.profileHandler?this.profileHandler(e):(s.info("[AgentClient] profileHandler \u672A\u8BBE\u7F6E\uFF0C\u7F13\u5B58 Profile \u4FE1\u606F"),this.cachedProfile=e)}async processMessage(e){if(!this.messageHandler)return;const t=typeof e.msgId=="string";s.info(`[AgentClient] \u6536\u5230${t?"\u79C1\u4FE1":"\u7FA4\u6D88\u606F"}\u6765\u81EA ${e.senderName}: ${e.content.substring(0,50)}...`);try{const n=await this.messageHandler(e);if(t){const i=e;i.requireReply&&i.callId&&await this.replyToCall(i.callId,i.senderId,n||"\u5DF2\u6536\u5230\u6D88\u606F\uFF0C\u6B63\u5728\u5904\u7406\u4E2D...")}}catch(n){const i=typeof e.msgId=="string";if(s.error("[AgentClient] \u5904\u7406\u6D88\u606F\u5931\u8D25: type=%s, senderId=%s, senderName=%s, msgId=%s, error=%s",i?"\u79C1\u4FE1":"\u7FA4\u6D88\u606F",e.senderId,e.senderName,e.msgId,n instanceof Error?n.message:String(n)),i){const r=e;r.requireReply&&r.callId&&await this.replyToCall(r.callId,r.senderId,`\u5904\u7406\u5931\u8D25: ${n}`)}}}ensureRegistered(){if(!this.agentId)throw new Error("Agent is not registered")}delay(e){return new Promise(t=>{this.pollTimer=setTimeout(t,e)})}onNewCharacter(e){this.newCharacterHandler=e}getState(){return{agentId:this.agentId,displayName:this.displayName,projectName:this.projectName,isConnected:this.wsClient.isConnected(),isRunning:this.isRunning}}getAgentConfig(){return this.agentConfig}reloadConfig(){g.loadConfig(),this.agentConfig=g.getAgentConfig(),s.info(`[AgentClient] \u914D\u7F6E\u5DF2\u91CD\u65B0\u52A0\u8F7D\uFF0C\u89D2\u8272: ${this.agentConfig.roleName}`)}enterDegradedMode(){this.isDegradedMode=!0,s.warn("[AgentClient] \u8FDB\u5165\u964D\u7EA7\u6A21\u5F0F\uFF0C\u5C06\u5728\u540E\u53F0\u6301\u7EED\u5C1D\u8BD5\u91CD\u8FDE")}exitDegradedMode(){this.isDegradedMode=!1,s.info("[AgentClient] \u9000\u51FA\u964D\u7EA7\u6A21\u5F0F")}isInDegradedMode(){return this.isDegradedMode}async retryRegistrationInBackground(e,t){let n=0;(async()=>{for(;this.isDegradedMode;)try{s.info(`[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u5C1D\u8BD5\u91CD\u65B0\u6CE8\u518C (${n+1}/${t===-1?"\u65E0\u9650":t})`);const r=await this.register();s.info(`[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6CE8\u518C\u6210\u529F - ${r.displayName} (${r.agentId})`),this.exitDegradedMode(),this.messageHandler&&this.start(this.messageHandler);return}catch(r){n++;const o=r instanceof Error?r.message:String(r);if(s.warn(`[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6CE8\u518C\u5931\u8D25 (${n}/${t===-1?"\u65E0\u9650":t}): ${o}`),t>0&&n>=t){s.error("[AgentClient] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u505C\u6B62\u91CD\u8FDE"),this.isDegradedMode=!1;return}await new Promise(h=>setTimeout(h,e))}})().catch(r=>{s.error("[AgentClient] \u964D\u7EA7\u6A21\u5F0F\u540E\u53F0\u91CD\u8BD5\u5FAA\u73AF\u5F02\u5E38\u9000\u51FA: error=%s, stack=%s",r instanceof Error?r.message:String(r),r instanceof Error?r.stack:"(no stack)")})}initializeStatusReporter(){if(!this.agentId){s.warn("[AgentClient] \u65E0\u6CD5\u521D\u59CB\u5316\u72B6\u6001\u62A5\u544A\u5668\uFF1AagentId \u4E3A\u7A7A");return}this.statusReporter=new y({agentId:this.agentId,sendStatusUpdate:e=>{this.wsClient.send({type:"AGENT_STATUS",data:e,timestamp:new Date().toISOString()})}}),C(e=>this.reportMcpNodeLog(e)),this.startRuntimeTokenRefresh(),s.info("[AgentClient] \u72B6\u6001\u62A5\u544A\u5668\u5DF2\u521D\u59CB\u5316")}startRuntimeTokenRefresh(){this.stopRuntimeTokenRefresh();const e=this.serverConfig.runtimeTokenRefreshIntervalMs;if(!e||e<=0){s.warn("[AgentClient] runtimeTokenRefreshIntervalMs \u672A\u914D\u7F6E\u6216\u65E0\u6548\uFF0C\u8DF3\u8FC7\u4E3B\u52A8\u7EED\u671F");return}const t=this.agentId;if(!t){s.warn("[AgentClient] agentId \u4E3A\u7A7A\uFF0C\u8DF3\u8FC7\u4E3B\u52A8\u7EED\u671F");return}this.runtimeTokenRefreshTimer=setInterval(async()=>{try{await this.httpClient.refreshRuntimeAccessToken(t)&&s.info("[AgentClient] runtimeAccessToken \u5DF2\u4E3B\u52A8\u7EED\u671F")}catch(n){s.warn("[AgentClient] \u4E3B\u52A8\u7EED\u671F runtimeAccessToken \u5931\u8D25: %s",n instanceof Error?n.message:String(n)),n instanceof Error&&n.stack&&s.warn("[AgentClient] \u4E3B\u52A8\u7EED\u671F\u5F02\u5E38\u5806\u6808:",n.stack)}},e),s.info(`[AgentClient] runtime token \u4E3B\u52A8\u7EED\u671F\u5DF2\u542F\u52A8\uFF0C\u95F4\u9694 ${e}ms`)}stopRuntimeTokenRefresh(){this.runtimeTokenRefreshTimer&&(clearInterval(this.runtimeTokenRefreshTimer),this.runtimeTokenRefreshTimer=null)}reportMcpNodeLog(e){const t=this.wsClient;!this.agentId||!this.wsClient.isConnected()||typeof t.sendMcpNodeLog!="function"||t.sendMcpNodeLog(e)}getStatusReporter(){return this.statusReporter}}export{_ as AgentClient};
2
2
 
@@ -5,13 +5,20 @@ export interface McpNodeLogEntry {
5
5
  timestamp: string;
6
6
  }
7
7
  export type McpNodeLogSink = (entry: McpNodeLogEntry) => void;
8
- export declare function formatStartupTimestamp(date: Date): string;
8
+ /**
9
+ * 格式化日志归档的年-月目录名(用于 yyyy-MM 子目录)。
10
+ */
11
+ export declare function formatLogYearMonth(date: Date): string;
12
+ /**
13
+ * 格式化日志文件名的时间戳部分:dd-HHmmssSSS(日-时分秒毫秒)。
14
+ */
15
+ export declare function formatLogFileTimestamp(date: Date): string;
9
16
  export declare function sanitizeLogFileNameSegment(segment: string): string;
10
17
  /**
11
18
  * 构建 MCP 启动日志文件路径。
12
- * 路径格式:{logDir}/agent/{roleName}-{instanceName}-{startupTimestamp}.log
19
+ * 路径格式:{logDir}/agent/aws-mcp-node/{roleName}-{instanceName}/{yyyy-MM}/{dd-HHmmssSSS}.log
13
20
  * roleName/instanceName 优先使用传入值,未传入时使用环境变量或 "unknown-*" 兜底。
14
- * 所有 agent 日志统一放在 agent/ 子目录下,通过文件名中的 role-instance 区分。
21
+ * 通过 "进程类型 / 角色-实例 / 年-月" 三级子目录归档,便于按角色与时间检索。
15
22
  */
16
23
  export declare function buildStartupLogFilePath(options?: {
17
24
  homeDir?: string;
@@ -23,8 +30,9 @@ export declare function buildStartupLogFilePath(options?: {
23
30
  export declare function initializeStartupLogFile(): string | null;
24
31
  /**
25
32
  * 在 Agent 注册后更新日志文件路径的角色-实例标识。
26
- * 主流程:使用真实的 roleName/instanceName 重新计算文件名,若与原路径不同则重命名文件。
27
- * 所有 agent 日志统一存放在 agent/ 子目录下,仅文件名中的 role-instance 部分发生变化。
33
+ * 主流程:使用真实的 roleName/instanceName 重新计算路径,若与原路径不同则重命名文件。
34
+ * 重命名后日志落入 agent/aws-mcp-node/{roleName}-{instanceName}/{yyyy-MM}/ 下,仅角色-实例目录变化。
35
+ * 重命名后旧的角色-实例目录及其 yyyy-MM 子目录若已空,则向上清理,避免残留 unknown-* 空目录。
28
36
  */
29
37
  export declare function updateStartupLogIdentity(roleName: string, instanceName: string): void;
30
38
  /**
@@ -1,3 +1,3 @@
1
- import*as u from"node:fs";import*as p from"node:os";import*as i from"node:path";import{format as m}from"node:util";const S=new Date,w=new Set(["0","false","off","no"]),d=200;let r,c=null,g=null;const l=[];function a(e,n=2){return String(e).padStart(n,"0")}function D(e){return[e.getFullYear(),a(e.getMonth()+1),a(e.getDate()),"-",a(e.getHours()),a(e.getMinutes()),a(e.getSeconds()),"-",a(e.getMilliseconds(),3)].join("")}function L(e){const n=new RegExp("[\0-]","g");return e.replace(/[<>:"/\\|?*]/g,"_").replace(n,"_").replace(/\s+/g," ").trim().replace(/[. ]+$/g,"")||"workspace"}function _(e={}){const n=e.logDir||i.join(e.homeDir||p.homedir(),".aws-bridge","log"),t=L(e.roleName||process.env.AWS_ROLE_NAME||"unknown-role"),o=L(e.instanceName||process.env.AWS_AGENT_ID||"unknown-instance"),s=i.join(n,"agent"),h=D(e.startupTime||S);return i.join(s,`${t}-${o}-${h}.log`)}function M(){if(r!==void 0)return r;const e=String(process.env.AWS_MCP_FILE_LOG||"").trim().toLowerCase();if(w.has(e))return r=null,null;try{return c=process.env.AWS_MCP_LOG_DIR||void 0||i.join(p.homedir(),".aws-bridge","log"),r=_({logDir:c,roleName:process.env.AWS_ROLE_NAME||void 0,instanceName:process.env.AWS_AGENT_ID||void 0}),u.mkdirSync(i.dirname(r),{recursive:!0}),r}catch(n){return r=null,c=null,console.warn("[Logger] \u65E0\u6CD5\u521B\u5EFA MCP \u542F\u52A8\u65E5\u5FD7\u6587\u4EF6:",n),null}}function C(e,n){if(r==null||!c)return;const t=r,o=_({logDir:c,roleName:e,instanceName:n,startupTime:S});if(t!==o)try{u.mkdirSync(i.dirname(o),{recursive:!0}),u.renameSync(t,o),r=o,console.log(`[Logger] MCP \u542F\u52A8\u65E5\u5FD7\u8DEF\u5F84\u5DF2\u66F4\u65B0: ${t} -> ${o}`)}catch(s){console.warn("[Logger] \u65E0\u6CD5\u66F4\u65B0 MCP \u542F\u52A8\u65E5\u5FD7\u8EAB\u4EFD\u6807\u8BC6:",s)}}function E(e,n){if(r===void 0)return;const t=r;if(!t)return;const o=`${new Date().toISOString()} [${e.toUpperCase()}] ${m(...n)}
2
- `;try{u.appendFileSync(t,o,"utf8")}catch(s){r=null,console.warn("[Logger] \u65E0\u6CD5\u5199\u5165 MCP \u542F\u52A8\u65E5\u5FD7\u6587\u4EF6:",s)}}function f(e,n){const t={level:e,message:m(...n),timestamp:new Date().toISOString()};switch(e){case"info":console.error(...n);break;case"warn":console.warn(...n);break;case"error":console.error(...n);break}E(e,n),N(t)}function P(e){if(g=e,!!e)for(const n of l)e(n)}function N(e){l.push(e),l.length>d&&l.splice(0,l.length-d),g&&g(e)}const F={info:(...e)=>f("info",e),warn:(...e)=>f("warn",e),error:(...e)=>f("error",e)};export{_ as buildStartupLogFilePath,D as formatStartupTimestamp,M as initializeStartupLogFile,F as logger,L as sanitizeLogFileNameSegment,P as setMcpNodeLogSink,C as updateStartupLogIdentity};
1
+ import*as l from"node:fs";import*as m from"node:os";import*as i from"node:path";import{format as p}from"node:util";const d=new Date,E=new Set(["0","false","off","no"]),S=200,L="aws-mcp-node";let t,c=null,g=null;const u=[];function s(e,n=2){return String(e).padStart(n,"0")}function M(e){return`${e.getFullYear()}-${s(e.getMonth()+1)}`}function N(e){return[s(e.getDate()),"-",s(e.getHours()),s(e.getMinutes()),s(e.getSeconds()),s(e.getMilliseconds(),3)].join("")}function _(e){const n=new RegExp("[\0-]","g");return e.replace(/[<>:"/\\|?*]/g,"_").replace(n,"_").replace(/\s+/g," ").trim().replace(/[. ]+$/g,"")||"workspace"}function h(e={}){const n=e.logDir||i.join(e.homeDir||m.homedir(),".aws-bridge","log"),r=_(e.roleName||process.env.AWS_ROLE_NAME||"unknown-role"),o=_(e.instanceName||process.env.AWS_AGENT_ID||"unknown-instance"),a=e.startupTime||d,w=M(a),D=`${N(a)}.log`;return i.join(n,"agent",L,`${r}-${o}`,w,D)}function F(){if(t!==void 0)return t;const e=String(process.env.AWS_MCP_FILE_LOG||"").trim().toLowerCase();if(E.has(e))return t=null,null;try{return c=process.env.AWS_MCP_LOG_DIR||void 0||i.join(m.homedir(),".aws-bridge","log"),t=h({logDir:c,roleName:process.env.AWS_ROLE_NAME||void 0,instanceName:process.env.AWS_AGENT_ID||void 0}),l.mkdirSync(i.dirname(t),{recursive:!0}),t}catch(n){return t=null,c=null,console.warn("[Logger] \u65E0\u6CD5\u521B\u5EFA MCP \u542F\u52A8\u65E5\u5FD7\u6587\u4EF6:",n),null}}function $(e,n){if(t==null||!c)return;const r=t,o=h({logDir:c,roleName:e,instanceName:n,startupTime:d});if(r!==o)try{l.mkdirSync(i.dirname(o),{recursive:!0}),l.renameSync(r,o),t=o,console.log(`[Logger] MCP \u542F\u52A8\u65E5\u5FD7\u8DEF\u5F84\u5DF2\u66F4\u65B0: ${r} -> ${o}`);const a=i.join(c,"agent",L);A(i.dirname(r),a)}catch(a){console.warn("[Logger] \u65E0\u6CD5\u66F4\u65B0 MCP \u542F\u52A8\u65E5\u5FD7\u8EAB\u4EFD\u6807\u8BC6:",a)}}function A(e,n){let r=i.normalize(e);const o=i.normalize(n),a=i.parse(r).root;for(;r!==o&&r!==a;){try{l.rmdirSync(r)}catch{break}r=i.dirname(r)}}function P(e,n){if(t===void 0)return;const r=t;if(!r)return;const o=`${new Date().toISOString()} [${e.toUpperCase()}] ${p(...n)}
2
+ `;try{l.appendFileSync(r,o,"utf8")}catch(a){t=null,console.warn("[Logger] \u65E0\u6CD5\u5199\u5165 MCP \u542F\u52A8\u65E5\u5FD7\u6587\u4EF6:",a)}}function f(e,n){const r={level:e,message:p(...n),timestamp:new Date().toISOString()};switch(e){case"info":console.error(...n);break;case"warn":console.warn(...n);break;case"error":console.error(...n);break}P(e,n),C(r)}function I(e){if(g=e,!!e)for(const n of u)e(n)}function C(e){u.push(e),u.length>S&&u.splice(0,u.length-S),g&&g(e)}const O={info:(...e)=>f("info",e),warn:(...e)=>f("warn",e),error:(...e)=>f("error",e)};export{h as buildStartupLogFilePath,N as formatLogFileTimestamp,M as formatLogYearMonth,F as initializeStartupLogFile,O as logger,_ as sanitizeLogFileNameSegment,I as setMcpNodeLogSink,$ as updateStartupLogIdentity};
3
3
 
@@ -1,16 +1,20 @@
1
1
  import assert from "node:assert/strict";
2
2
  import * as path from "node:path";
3
3
  import { describe, it } from "node:test";
4
- import { buildStartupLogFilePath, formatStartupTimestamp, sanitizeLogFileNameSegment, } from "./logger.js";
4
+ import { buildStartupLogFilePath, formatLogFileTimestamp, formatLogYearMonth, sanitizeLogFileNameSegment, } from "./logger.js";
5
5
  describe("logger startup log path", () => {
6
- it("formats startup time for log filenames", () => {
6
+ it("formats year-month directory for log archiving", () => {
7
7
  const startupTime = new Date(2026, 4, 5, 9, 8, 7, 6);
8
- assert.equal(formatStartupTimestamp(startupTime), "20260505-090807-006");
8
+ assert.equal(formatLogYearMonth(startupTime), "2026-05");
9
+ });
10
+ it("formats file timestamp as dd-HHmmssSSS", () => {
11
+ const startupTime = new Date(2026, 4, 5, 9, 8, 7, 6);
12
+ assert.equal(formatLogFileTimestamp(startupTime), "05-090807006");
9
13
  });
10
14
  it("sanitizes unsafe filename characters", () => {
11
15
  assert.equal(sanitizeLogFileNameSegment('demo:bad*name?'), "demo_bad_name_");
12
16
  });
13
- it("builds log path under the aws bridge log agent directory with role-instance in filename", () => {
17
+ it("builds log path nested under agent/aws-mcp-node/role-instance/yyyy-MM", () => {
14
18
  const startupTime = new Date(2026, 4, 5, 9, 8, 7, 6);
15
19
  const logFilePath = buildStartupLogFilePath({
16
20
  homeDir: "C:/Users/dev",
@@ -18,6 +22,6 @@ describe("logger startup log path", () => {
18
22
  instanceName: "128M",
19
23
  startupTime,
20
24
  });
21
- assert.equal(logFilePath, path.join("C:/Users/dev", ".aws-bridge", "log", "agent", "测试助手-128M-20260505-090807-006.log"));
25
+ assert.equal(logFilePath, path.join("C:/Users/dev", ".aws-bridge", "log", "agent", "aws-mcp-node", "测试助手-128M", "2026-05", "05-090807006.log"));
22
26
  });
23
27
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.9.82",
3
+ "version": "1.9.90",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",