aws-runtime-bridge 1.0.2 → 1.1.0

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.
Files changed (148) hide show
  1. package/dist/adapter/adapter.test.js +4 -4
  2. package/dist/adapter/types.d.ts.map +1 -1
  3. package/dist/adapter/types.js +0 -7
  4. package/dist/adapter/types.test.js +5 -53
  5. package/dist/config.d.ts +1 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +2 -2
  8. package/dist/middleware/auth.d.ts.map +1 -1
  9. package/dist/middleware/auth.js +4 -0
  10. package/dist/routes/instance.d.ts.map +1 -1
  11. package/dist/routes/instance.js +88 -34
  12. package/dist/routes/runtime-binding.d.ts.map +1 -1
  13. package/dist/routes/runtime-binding.js +59 -2
  14. package/dist/routes/sessions.js +1 -1
  15. package/dist/routes/terminal.d.ts.map +1 -1
  16. package/dist/routes/terminal.js +48 -14
  17. package/dist/routes/terminal.test.js +6 -2
  18. package/dist/services/agent-process-manager.js +4 -4
  19. package/dist/services/auto-register.d.ts +12 -1
  20. package/dist/services/auto-register.d.ts.map +1 -1
  21. package/dist/services/auto-register.js +206 -42
  22. package/dist/services/aws-client-agent-mcp.test.js +3 -0
  23. package/dist/services/mcp-launch-binding-queue.d.ts +36 -0
  24. package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -0
  25. package/dist/services/mcp-launch-binding-queue.js +92 -0
  26. package/dist/services/mcp-launch-binding-queue.test.d.ts +2 -0
  27. package/dist/services/mcp-launch-binding-queue.test.d.ts.map +1 -0
  28. package/dist/services/mcp-launch-binding-queue.test.js +107 -0
  29. package/dist/services/orphan-monitor.js +1 -1
  30. package/dist/services/process-detector.d.ts +1 -1
  31. package/dist/services/process-detector.d.ts.map +1 -1
  32. package/dist/services/process-detector.js +2 -11
  33. package/dist/services/process-registry.d.ts +1 -0
  34. package/dist/services/process-registry.d.ts.map +1 -1
  35. package/dist/services/process-registry.js +129 -108
  36. package/dist/services/runtime-binding.d.ts +11 -1
  37. package/dist/services/runtime-binding.d.ts.map +1 -1
  38. package/dist/services/runtime-binding.js +130 -4
  39. package/dist/services/terminal-persistence.d.ts.map +1 -1
  40. package/dist/services/terminal-persistence.js +47 -37
  41. package/dist/services/terminal-persistence.test.js +47 -1
  42. package/dist/utils/file-utils.d.ts +3 -0
  43. package/dist/utils/file-utils.d.ts.map +1 -1
  44. package/dist/utils/file-utils.js +32 -0
  45. package/package/aws-client-agent-mcp/README.md +288 -288
  46. package/package.json +76 -76
  47. package/dist/routes/aws-mcp.d.ts +0 -10
  48. package/dist/routes/aws-mcp.d.ts.map +0 -1
  49. package/dist/routes/aws-mcp.js +0 -74
  50. package/dist/routes/aws-mcp.test.d.ts +0 -2
  51. package/dist/routes/aws-mcp.test.d.ts.map +0 -1
  52. package/dist/routes/aws-mcp.test.js +0 -42
  53. package/dist/routes/memory.d.ts +0 -13
  54. package/dist/routes/memory.d.ts.map +0 -1
  55. package/dist/routes/memory.js +0 -429
  56. package/dist/services/aws-mcp-http.d.ts +0 -11
  57. package/dist/services/aws-mcp-http.d.ts.map +0 -1
  58. package/dist/services/aws-mcp-http.js +0 -225
  59. package/dist/services/aws-mcp-http.test.d.ts +0 -2
  60. package/dist/services/aws-mcp-http.test.d.ts.map +0 -1
  61. package/dist/services/aws-mcp-http.test.js +0 -27
  62. package/dist/services/easytier-manager.d.ts +0 -106
  63. package/dist/services/easytier-manager.d.ts.map +0 -1
  64. package/dist/services/easytier-manager.js +0 -331
  65. package/dist/services/easytier-manager.test.d.ts +0 -5
  66. package/dist/services/easytier-manager.test.d.ts.map +0 -1
  67. package/dist/services/easytier-manager.test.js +0 -98
  68. package/dist/services/memory-service.d.ts +0 -195
  69. package/dist/services/memory-service.d.ts.map +0 -1
  70. package/dist/services/memory-service.js +0 -650
  71. package/dist/services/session-lookup.d.ts +0 -20
  72. package/dist/services/session-lookup.d.ts.map +0 -1
  73. package/dist/services/session-lookup.js +0 -43
  74. package/dist/services/user-api-key-service.d.ts +0 -28
  75. package/dist/services/user-api-key-service.d.ts.map +0 -1
  76. package/dist/services/user-api-key-service.js +0 -75
  77. package/node_modules/@cc-switch/sdk/dist/adapters/common.d.ts +0 -38
  78. package/node_modules/@cc-switch/sdk/dist/adapters/common.d.ts.map +0 -1
  79. package/node_modules/@cc-switch/sdk/dist/adapters/common.js +0 -47
  80. package/node_modules/@cc-switch/sdk/dist/adapters/index.d.ts +0 -5
  81. package/node_modules/@cc-switch/sdk/dist/adapters/index.d.ts.map +0 -1
  82. package/node_modules/@cc-switch/sdk/dist/adapters/index.js +0 -28
  83. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claude.d.ts +0 -10
  84. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claude.d.ts.map +0 -1
  85. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claude.js +0 -39
  86. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claudecode.d.ts +0 -10
  87. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claudecode.d.ts.map +0 -1
  88. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claudecode.js +0 -40
  89. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.d.ts +0 -18
  90. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.d.ts.map +0 -1
  91. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.js +0 -63
  92. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.test.d.ts +0 -2
  93. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.test.d.ts.map +0 -1
  94. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.test.js +0 -86
  95. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-placeholder.d.ts +0 -9
  96. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-placeholder.d.ts.map +0 -1
  97. package/node_modules/@cc-switch/sdk/dist/adapters/mcp-placeholder.js +0 -14
  98. package/node_modules/@cc-switch/sdk/dist/adapters/skill-claude.d.ts +0 -10
  99. package/node_modules/@cc-switch/sdk/dist/adapters/skill-claude.d.ts.map +0 -1
  100. package/node_modules/@cc-switch/sdk/dist/adapters/skill-claude.js +0 -51
  101. package/node_modules/@cc-switch/sdk/dist/adapters/skill-claudecode.d.ts +0 -10
  102. package/node_modules/@cc-switch/sdk/dist/adapters/skill-claudecode.d.ts.map +0 -1
  103. package/node_modules/@cc-switch/sdk/dist/adapters/skill-claudecode.js +0 -51
  104. package/node_modules/@cc-switch/sdk/dist/adapters/skill-opencode.d.ts +0 -10
  105. package/node_modules/@cc-switch/sdk/dist/adapters/skill-opencode.d.ts.map +0 -1
  106. package/node_modules/@cc-switch/sdk/dist/adapters/skill-opencode.js +0 -51
  107. package/node_modules/@cc-switch/sdk/dist/adapters/skill-placeholder.d.ts +0 -9
  108. package/node_modules/@cc-switch/sdk/dist/adapters/skill-placeholder.d.ts.map +0 -1
  109. package/node_modules/@cc-switch/sdk/dist/adapters/skill-placeholder.js +0 -14
  110. package/node_modules/@cc-switch/sdk/dist/services/instance-service.d.ts +0 -78
  111. package/node_modules/@cc-switch/sdk/dist/services/instance-service.d.ts.map +0 -1
  112. package/node_modules/@cc-switch/sdk/dist/services/instance-service.js +0 -180
  113. package/package/cc-switch-sdk/dist/adapters/common.d.ts +0 -38
  114. package/package/cc-switch-sdk/dist/adapters/common.d.ts.map +0 -1
  115. package/package/cc-switch-sdk/dist/adapters/common.js +0 -47
  116. package/package/cc-switch-sdk/dist/adapters/index.d.ts +0 -5
  117. package/package/cc-switch-sdk/dist/adapters/index.d.ts.map +0 -1
  118. package/package/cc-switch-sdk/dist/adapters/index.js +0 -28
  119. package/package/cc-switch-sdk/dist/adapters/mcp-claude.d.ts +0 -10
  120. package/package/cc-switch-sdk/dist/adapters/mcp-claude.d.ts.map +0 -1
  121. package/package/cc-switch-sdk/dist/adapters/mcp-claude.js +0 -39
  122. package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.d.ts +0 -10
  123. package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.d.ts.map +0 -1
  124. package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.js +0 -40
  125. package/package/cc-switch-sdk/dist/adapters/mcp-opencode.d.ts +0 -18
  126. package/package/cc-switch-sdk/dist/adapters/mcp-opencode.d.ts.map +0 -1
  127. package/package/cc-switch-sdk/dist/adapters/mcp-opencode.js +0 -63
  128. package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.d.ts +0 -2
  129. package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.d.ts.map +0 -1
  130. package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.js +0 -86
  131. package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.d.ts +0 -9
  132. package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.d.ts.map +0 -1
  133. package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.js +0 -14
  134. package/package/cc-switch-sdk/dist/adapters/skill-claude.d.ts +0 -10
  135. package/package/cc-switch-sdk/dist/adapters/skill-claude.d.ts.map +0 -1
  136. package/package/cc-switch-sdk/dist/adapters/skill-claude.js +0 -51
  137. package/package/cc-switch-sdk/dist/adapters/skill-claudecode.d.ts +0 -10
  138. package/package/cc-switch-sdk/dist/adapters/skill-claudecode.d.ts.map +0 -1
  139. package/package/cc-switch-sdk/dist/adapters/skill-claudecode.js +0 -51
  140. package/package/cc-switch-sdk/dist/adapters/skill-opencode.d.ts +0 -10
  141. package/package/cc-switch-sdk/dist/adapters/skill-opencode.d.ts.map +0 -1
  142. package/package/cc-switch-sdk/dist/adapters/skill-opencode.js +0 -51
  143. package/package/cc-switch-sdk/dist/adapters/skill-placeholder.d.ts +0 -9
  144. package/package/cc-switch-sdk/dist/adapters/skill-placeholder.d.ts.map +0 -1
  145. package/package/cc-switch-sdk/dist/adapters/skill-placeholder.js +0 -14
  146. package/package/cc-switch-sdk/dist/services/instance-service.d.ts +0 -78
  147. package/package/cc-switch-sdk/dist/services/instance-service.d.ts.map +0 -1
  148. package/package/cc-switch-sdk/dist/services/instance-service.js +0 -180
@@ -10,8 +10,9 @@ import { v4 as uuidv4 } from 'uuid';
10
10
  import { adapterRegistry, registerSdkProviders, resolveSdkProviderIdByCommand, } from '../adapter/index.js';
11
11
  import { validateToken } from '../middleware/auth.js';
12
12
  import { getAgentProcessManager } from '../services/agent-process-manager.js';
13
- import { findClaudeCodeProcesses } from '../services/process-detector.js';
13
+ import { findClaudeCodeProcesses, terminateProcessTree, waitForProcessExit } from '../services/process-detector.js';
14
14
  import { flushSessionOutput, scheduleOutputFlush, sendQuestionRequest, sendStatus, sessions } from '../services/session-output.js';
15
+ import { enqueueMcpLaunchBinding } from '../services/mcp-launch-binding-queue.js';
15
16
  import { loadPersistedSessions, removePersistedSession, removePersistedSessionByAgentId, savePersistedSessions, upsertPersistedSession, } from '../services/terminal-persistence.js';
16
17
  export const terminalRouter = Router();
17
18
  // 导出 SDK 会话存储,供其他模块使用(如 sessions.ts)
@@ -69,9 +70,17 @@ terminalRouter.post('/start', validateToken, async (req, res) => {
69
70
  res.status(400).json({ error: 'agentId and workspacePath are required' });
70
71
  return;
71
72
  }
73
+ const queuedBinding = enqueueMcpLaunchBinding({
74
+ agentId: String(agentId),
75
+ workspacePath: String(workspacePath),
76
+ serverUrl: req.body?.serverUrl || req.body?.schedulerBaseUrl,
77
+ runtimeAccessToken: req.body?.runtimeAccessToken,
78
+ userId: req.body?.userId,
79
+ schedulerBaseUrl: req.body?.schedulerBaseUrl,
80
+ });
72
81
  // SDK 模式
73
82
  if (mode === 'sdk') {
74
- await startSdkSession(req, res);
83
+ await startSdkSession(req, res, queuedBinding?.id);
75
84
  return;
76
85
  }
77
86
  // PTY 模式(原有逻辑)
@@ -101,6 +110,8 @@ terminalRouter.post('/start', validateToken, async (req, res) => {
101
110
  env: {
102
111
  ...process.env,
103
112
  AWS_AGENT_ID: String(agentId),
113
+ AWS_MCP_LAUNCH_BINDING_ID: queuedBinding?.id || '',
114
+ AWS_MCP_CLAIM_LAUNCH_BINDING: 'true',
104
115
  },
105
116
  });
106
117
  sessions.set(sessionId, {
@@ -165,13 +176,14 @@ terminalRouter.post('/start', validateToken, async (req, res) => {
165
176
  workspacePath,
166
177
  command: actualCommand,
167
178
  mode: 'pty',
179
+ mcpLaunchBindingId: queuedBinding?.id,
168
180
  });
169
181
  });
170
182
  /**
171
183
  * 启动 SDK 会话
172
184
  * 支持 MCP 配置和空闲命令
173
185
  */
174
- async function startSdkSession(req, res) {
186
+ async function startSdkSession(req, res, mcpLaunchBindingId) {
175
187
  const { agentId, workspacePath, command, autoAccept = true, initialPrompt,
176
188
  // MCP 配置 - 加载 aws-client-agent-mcp
177
189
  mcpConfigPath, extraMcpServers,
@@ -198,6 +210,12 @@ async function startSdkSession(req, res) {
198
210
  // MCP 配置:不再默认注入 aws-mcp;由用户在面板中安装/配置
199
211
  mcpConfigPath,
200
212
  extraMcpServers: extraMcpServers || {},
213
+ envOverrides: {
214
+ AWS_AGENT_ID: String(agentId),
215
+ AWS_WORKSPACE_PATH: String(workspacePath),
216
+ AWS_MCP_LAUNCH_BINDING_ID: mcpLaunchBindingId || '',
217
+ AWS_MCP_CLAIM_LAUNCH_BINDING: 'true',
218
+ },
201
219
  };
202
220
  // 存储会话信息
203
221
  sdkSessions.set(sessionId, {
@@ -533,16 +551,8 @@ terminalRouter.post('/stop', validateToken, async (req, res) => {
533
551
  }
534
552
  catch (error) {
535
553
  const errorMessage = error instanceof Error ? error.message : String(error);
536
- // 即使终止失败,也清理会话记录
537
- sdkSessions.delete(sessionId);
538
- // 同时按 sessionId 和 agentId 删除,确保持久化会话被清除
539
- await removePersistedSession(sessionId);
540
- await removePersistedSessionByAgentId(agentId);
541
- // ★ 也从进程管理器清理
542
- const processManager = getAgentProcessManager();
543
- await processManager.removeProcess(agentId);
544
554
  console.error(`[Runtime] Failed to terminate SDK session ${sessionId}:`, errorMessage);
545
- res.json({ ok: true, status: 'stopped', mode: 'sdk', warning: errorMessage });
555
+ res.status(500).json({ ok: false, status: 'stop_failed', mode: 'sdk', error: errorMessage });
546
556
  return;
547
557
  }
548
558
  }
@@ -568,11 +578,35 @@ terminalRouter.post('/stop', validateToken, async (req, res) => {
568
578
  }
569
579
  // 4. 终止 PTY 进程
570
580
  const agentId = session.agentId;
581
+ const pid = session.ptyProcess.pid;
582
+ let exited = false;
583
+ let terminated = 0;
584
+ let failed = 0;
571
585
  try {
572
586
  session.ptyProcess.kill();
587
+ exited = await waitForProcessExit(pid, 3000);
573
588
  }
574
- catch {
575
- // ignore stop race errors
589
+ catch (error) {
590
+ console.warn(`[Runtime] Failed to kill PTY process gracefully: sessionId=${sessionId}, pid=${pid}`, error);
591
+ }
592
+ if (!exited) {
593
+ const result = terminateProcessTree(pid);
594
+ terminated = result.terminated;
595
+ failed = result.failed;
596
+ exited = await waitForProcessExit(pid, 5000);
597
+ }
598
+ if (!exited) {
599
+ await sendStatus(agentId, sessionId, 'stop_failed');
600
+ res.status(500).json({
601
+ ok: false,
602
+ status: 'stop_failed',
603
+ mode: 'pty',
604
+ pid,
605
+ terminated,
606
+ failed,
607
+ error: `process ${pid} is still running after termination attempts`,
608
+ });
609
+ return;
576
610
  }
577
611
  if (session.flushTimer) {
578
612
  clearTimeout(session.flushTimer);
@@ -56,12 +56,16 @@ describe('terminal configuration', () => {
56
56
  expect(getShellConfig('linux').shell).toBe('bash');
57
57
  });
58
58
  it('builds correct terminal environment', () => {
59
- const buildTerminalEnv = (agentId, baseEnv) => ({
59
+ const buildTerminalEnv = (agentId, bindingId, baseEnv) => ({
60
60
  ...baseEnv,
61
61
  AWS_AGENT_ID: agentId,
62
+ AWS_MCP_LAUNCH_BINDING_ID: bindingId,
63
+ AWS_MCP_CLAIM_LAUNCH_BINDING: 'true',
62
64
  });
63
- const env = buildTerminalEnv('agent-123', { PATH: '/usr/bin' });
65
+ const env = buildTerminalEnv('agent-123', 'binding-456', { PATH: '/usr/bin' });
64
66
  expect(env.AWS_AGENT_ID).toBe('agent-123');
67
+ expect(env.AWS_MCP_LAUNCH_BINDING_ID).toBe('binding-456');
68
+ expect(env.AWS_MCP_CLAIM_LAUNCH_BINDING).toBe('true');
65
69
  });
66
70
  });
67
71
  describe('resolveSdkProviderId', () => {
@@ -260,7 +260,7 @@ export class AgentProcessManager {
260
260
  log.debug(`[stopProcess] Level 1: Sending SIGTERM to pid=${pid}`);
261
261
  const sigtermResult = this.sendSigterm(pid);
262
262
  if (sigtermResult) {
263
- const exited = waitForProcessExit(pid, 3000);
263
+ const exited = await waitForProcessExit(pid, 3000);
264
264
  if (exited) {
265
265
  await this.registry.markTerminated(agentId);
266
266
  log.info(`[stopProcess] Process stopped at Level 1: agentId=${agentId}, pid=${pid}`);
@@ -279,7 +279,7 @@ export class AgentProcessManager {
279
279
  log.debug(`[stopProcess] Level 2: Force killing pid=${pid}`);
280
280
  const forceKillResult = this.sendForceKill(pid);
281
281
  if (forceKillResult) {
282
- const exited = waitForProcessExit(pid, 3000);
282
+ const exited = await waitForProcessExit(pid, 3000);
283
283
  if (exited) {
284
284
  await this.registry.markTerminated(agentId);
285
285
  log.info(`[stopProcess] Process stopped at Level 2: agentId=${agentId}, pid=${pid}`);
@@ -298,7 +298,7 @@ export class AgentProcessManager {
298
298
  log.debug(`[stopProcess] Level 3: Terminating process tree for pid=${pid}`);
299
299
  const treeResult = terminateProcessTree(pid);
300
300
  // Wait for process tree to terminate
301
- const treeExited = waitForProcessExit(pid, 5000);
301
+ const treeExited = await waitForProcessExit(pid, 5000);
302
302
  if (treeExited) {
303
303
  await this.registry.markTerminated(agentId);
304
304
  log.info(`[stopProcess] Process stopped at Level 3: agentId=${agentId}, pid=${pid}, terminated=${treeResult.terminated}, failed=${treeResult.failed}`);
@@ -398,7 +398,7 @@ export class AgentProcessManager {
398
398
  // Direct process tree termination
399
399
  const result = terminateProcessTree(pid);
400
400
  // Wait briefly for termination
401
- const exited = waitForProcessExit(pid, 2000);
401
+ const exited = await waitForProcessExit(pid, 2000);
402
402
  if (exited) {
403
403
  await this.registry.markTerminated(agentId);
404
404
  log.info(`[forceKill] Process terminated: agentId=${agentId}, pid=${pid}`);
@@ -13,10 +13,14 @@
13
13
  interface AutoRegisterConfig {
14
14
  /** 是否启用自动注册 */
15
15
  enabled: boolean;
16
+ /** 要主动注册到的调度中心地址 */
17
+ serverUrl?: string;
16
18
  /** 租户 ID(可选,如果提供 userKey 则可自动推导) */
17
19
  tenantId?: string;
18
20
  /** 用户 API Key(必填,用于验证身份和自动推导租户) */
19
21
  userKey: string;
22
+ /** 连接 Bridge 机器实例需要的密钥 */
23
+ connectionKey?: string;
20
24
  /** 实例名称(默认使用主机名) */
21
25
  instanceName?: string;
22
26
  /** 项目名称 */
@@ -25,13 +29,18 @@ interface AutoRegisterConfig {
25
29
  roleName?: string;
26
30
  /** 工作空间路径 */
27
31
  workspacePath?: string;
28
- /** 虚拟 IP(EasyTier) */
32
+ /** 注册 IP(优先用于生成 runtimeBridgeBaseUrl) */
33
+ registerIp?: string;
34
+ /** 虚拟 IP(EasyTier,兼容旧配置) */
29
35
  virtualIp?: string;
36
+ /** 是否优先选择与 serverUrl 同局域网的本机 IP */
37
+ preferSameLanIp?: boolean;
30
38
  /** 注册重试次数 */
31
39
  maxRetries?: number;
32
40
  /** 注册重试间隔(毫秒) */
33
41
  retryInterval?: number;
34
42
  }
43
+ export declare function getConfiguredConnectionKeys(): string[];
35
44
  /**
36
45
  * 加载自动注册配置
37
46
  *
@@ -40,6 +49,7 @@ interface AutoRegisterConfig {
40
49
  * 如果没有配置文件且没有环境变量,则不启用自动注册
41
50
  */
42
51
  export declare function loadConfig(): AutoRegisterConfig;
52
+ export declare function loadConfigs(): AutoRegisterConfig[];
43
53
  /**
44
54
  * 自动注册主函数
45
55
  *
@@ -68,6 +78,7 @@ export declare function unregister(): Promise<boolean>;
68
78
  export declare function getRegistrationState(): {
69
79
  registered: boolean;
70
80
  instanceId?: string;
81
+ registrations: Record<string, string>;
71
82
  lastAttempt?: Date;
72
83
  error?: string;
73
84
  };
@@ -1 +1 @@
1
- {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../../src/services/auto-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4CH;;GAEG;AACH,UAAU,kBAAkB;IAC1B,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiKD;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,kBAAkB,CAuC/C;AAuLD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GACzC,OAAO,CAAC,OAAO,CAAC,CAmHlB;AAED;;GAEG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAoED;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;gBAxmBtB,OAAO;iBACN,MAAM;kBACL,IAAI;YACV,MAAM;EAumBf;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CA0ED"}
1
+ {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../../src/services/auto-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA8CH;;GAEG;AACH,UAAU,kBAAkB;IAC1B,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAwBD,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAEtD;AAsQD;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,kBAAkB,CA2C/C;AAED,wBAAgB,WAAW,IAAI,kBAAkB,EAAE,CA0ClD;AA8LD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GACzC,OAAO,CAAC,OAAO,CAAC,CAalB;AAyHD;;GAEG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAoED;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;gBA7yBtB,OAAO;iBACN,MAAM;mBACJ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;kBACvB,IAAI;YACV,MAAM;EA2yBf;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CA0ED"}
@@ -17,12 +17,70 @@ import { getRuntimeAccessToken, getRuntimeBindingPublicState, saveRuntimeBinding
17
17
  // 默认配置
18
18
  const DEFAULT_CONFIG = {
19
19
  enabled: false,
20
+ serverUrl: schedulerBaseUrl,
21
+ preferSameLanIp: true,
20
22
  maxRetries: 5,
21
23
  retryInterval: 5000,
22
24
  };
23
25
  let registrationState = {
24
26
  registered: false,
27
+ registrations: {},
25
28
  };
29
+ let cachedConfiguredConnectionKeys = [];
30
+ export function getConfiguredConnectionKeys() {
31
+ return [...cachedConfiguredConnectionKeys];
32
+ }
33
+ function normalizeOptionalString(value) {
34
+ if (value == null) {
35
+ return undefined;
36
+ }
37
+ const normalized = String(value).trim();
38
+ return normalized || undefined;
39
+ }
40
+ function normalizeOptionalBoolean(value) {
41
+ if (typeof value === "boolean") {
42
+ return value;
43
+ }
44
+ if (typeof value === "string") {
45
+ const normalized = value.trim().toLowerCase();
46
+ if (["true", "1", "yes", "y"].includes(normalized)) {
47
+ return true;
48
+ }
49
+ if (["false", "0", "no", "n"].includes(normalized)) {
50
+ return false;
51
+ }
52
+ }
53
+ return undefined;
54
+ }
55
+ function normalizeOptionalNumber(value) {
56
+ if (value == null || value === "") {
57
+ return undefined;
58
+ }
59
+ const parsed = Number(value);
60
+ return Number.isFinite(parsed) ? parsed : undefined;
61
+ }
62
+ function normalizeTargetConfig(source) {
63
+ const serverUrl = normalizeOptionalString(source.serverUrl) ||
64
+ normalizeOptionalString(source.schedulerBaseUrl);
65
+ const connectionKey = normalizeOptionalString(source.connectionKey) ||
66
+ normalizeOptionalString(source.password);
67
+ return {
68
+ enabled: normalizeOptionalBoolean(source.enabled),
69
+ serverUrl,
70
+ tenantId: normalizeOptionalString(source.tenantId),
71
+ userKey: normalizeOptionalString(source.userKey),
72
+ connectionKey,
73
+ instanceName: normalizeOptionalString(source.instanceName),
74
+ projectName: normalizeOptionalString(source.projectName),
75
+ roleName: normalizeOptionalString(source.roleName),
76
+ workspacePath: normalizeOptionalString(source.workspacePath),
77
+ registerIp: normalizeOptionalString(source.registerIp),
78
+ virtualIp: normalizeOptionalString(source.virtualIp),
79
+ preferSameLanIp: normalizeOptionalBoolean(source.preferSameLanIp),
80
+ maxRetries: normalizeOptionalNumber(source.maxRetries),
81
+ retryInterval: normalizeOptionalNumber(source.retryInterval),
82
+ };
83
+ }
26
84
  /**
27
85
  * 配置文件路径
28
86
  */
@@ -65,13 +123,26 @@ function ensureConfigFile() {
65
123
  *
66
124
  * 配置文件格式:
67
125
  * {
126
+ * "connectionKey": "bridge-password", // 可选,server/面板连接该 bridge 的密钥
127
+ * "autoRegisterTargets": [
128
+ * {
129
+ * "serverUrl": "http://scheduler-host:7380", // 必填,要主动注册的 server 地址
130
+ * "userKey": "aws_live_xxx", // 必填,用户 API Key
131
+ * "instanceName": "my-bridge", // 必填/可选,默认主机名
132
+ * "registerIp": "192.168.1.25", // 可选,优先作为注册 IP
133
+ * "connectionKey": "bridge-password" // 可选,不填则使用顶层 connectionKey
134
+ * }
135
+ * ],
136
+ *
137
+ * // 兼容旧版单目标配置:
68
138
  * "userKey": "aws_live_xxx", // 必填,用户 API Key
69
139
  * "tenantId": "xxx", // 可选,租户 ID(不填则自动推导)
70
140
  * "instanceName": "my-bridge", // 可选,默认使用主机名
71
141
  * "projectName": "default", // 可选
72
142
  * "roleName": "Bridge", // 可选
73
143
  * "workspacePath": "/path/to/workspace", // 可选
74
- * "virtualIp": "10.144.144.1", // 可选
144
+ * "registerIp": "192.168.1.25", // 可选,优先作为注册 IP
145
+ * "virtualIp": "10.144.144.1", // 可选,兼容 EasyTier 虚拟 IP
75
146
  * "maxRetries": 5, // 可选
76
147
  * "retryInterval": 5000 // 可选
77
148
  * }
@@ -88,17 +159,7 @@ function loadConfigFromFile() {
88
159
  const content = fs.readFileSync(configPath, "utf-8");
89
160
  const config = JSON.parse(content);
90
161
  logger.info(`[AutoRegister] 从配置文件加载配置: ${configPath}`);
91
- return {
92
- tenantId: config.tenantId,
93
- userKey: config.userKey,
94
- instanceName: config.instanceName,
95
- projectName: config.projectName,
96
- roleName: config.roleName,
97
- workspacePath: config.workspacePath,
98
- virtualIp: config.virtualIp,
99
- maxRetries: config.maxRetries,
100
- retryInterval: config.retryInterval,
101
- };
162
+ return normalizeTargetConfig(config);
102
163
  }
103
164
  catch (error) {
104
165
  const err = error;
@@ -106,6 +167,39 @@ function loadConfigFromFile() {
106
167
  return null;
107
168
  }
108
169
  }
170
+ function loadTargetsFromFile() {
171
+ ensureConfigFile();
172
+ const configPath = getConfigFilePath();
173
+ try {
174
+ if (!fs.existsSync(configPath)) {
175
+ return [];
176
+ }
177
+ const content = fs.readFileSync(configPath, "utf-8");
178
+ const config = JSON.parse(content);
179
+ const topLevel = normalizeTargetConfig(config);
180
+ const rawTargets = Array.isArray(config.autoRegisterTargets)
181
+ ? config.autoRegisterTargets
182
+ : [];
183
+ if (rawTargets.length === 0) {
184
+ return topLevel.userKey ? [topLevel] : [];
185
+ }
186
+ return rawTargets
187
+ .filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item)))
188
+ .map((item) => {
189
+ const target = normalizeTargetConfig(item);
190
+ return {
191
+ ...topLevel,
192
+ ...target,
193
+ connectionKey: target.connectionKey || topLevel.connectionKey,
194
+ };
195
+ });
196
+ }
197
+ catch (error) {
198
+ const err = error;
199
+ logger.warn(`[AutoRegister] 读取自动注册目标失败: ${configPath}, error: ${err.message}`);
200
+ return [];
201
+ }
202
+ }
109
203
  /**
110
204
  * 从环境变量加载配置
111
205
  */
@@ -114,12 +208,18 @@ function loadConfigFromEnv() {
114
208
  if (process.env.AWS_AUTO_REGISTER === "true") {
115
209
  envConfig.enabled = true;
116
210
  }
211
+ if (process.env.AWS_RUNTIME_SCHEDULER_BASE_URL) {
212
+ envConfig.serverUrl = process.env.AWS_RUNTIME_SCHEDULER_BASE_URL;
213
+ }
117
214
  if (process.env.AWS_TENANT_ID) {
118
215
  envConfig.tenantId = process.env.AWS_TENANT_ID;
119
216
  }
120
217
  if (process.env.AWS_USER_KEY) {
121
218
  envConfig.userKey = process.env.AWS_USER_KEY;
122
219
  }
220
+ if (process.env.AWS_BRIDGE_CONNECTION_KEY) {
221
+ envConfig.connectionKey = process.env.AWS_BRIDGE_CONNECTION_KEY;
222
+ }
123
223
  if (process.env.AWS_INSTANCE_NAME) {
124
224
  envConfig.instanceName = process.env.AWS_INSTANCE_NAME;
125
225
  }
@@ -132,9 +232,16 @@ function loadConfigFromEnv() {
132
232
  if (process.env.AWS_WORKSPACE_PATH) {
133
233
  envConfig.workspacePath = process.env.AWS_WORKSPACE_PATH;
134
234
  }
235
+ if (process.env.AWS_REGISTER_IP) {
236
+ envConfig.registerIp = process.env.AWS_REGISTER_IP;
237
+ }
135
238
  if (process.env.AWS_VIRTUAL_IP) {
136
239
  envConfig.virtualIp = process.env.AWS_VIRTUAL_IP;
137
240
  }
241
+ if (process.env.AWS_PREFER_SAME_LAN_IP) {
242
+ envConfig.preferSameLanIp =
243
+ normalizeOptionalBoolean(process.env.AWS_PREFER_SAME_LAN_IP) ?? true;
244
+ }
138
245
  if (process.env.AWS_REGISTER_MAX_RETRIES) {
139
246
  envConfig.maxRetries = parseInt(process.env.AWS_REGISTER_MAX_RETRIES, 10);
140
247
  }
@@ -175,17 +282,60 @@ export function loadConfig() {
175
282
  }
176
283
  return {
177
284
  enabled,
285
+ serverUrl: merged.serverUrl || schedulerBaseUrl,
178
286
  tenantId: merged.tenantId || "", // 保持为空字符串,后续会通过userKey自动推导
179
287
  userKey: merged.userKey || "",
288
+ connectionKey: merged.connectionKey,
180
289
  instanceName,
181
290
  projectName: merged.projectName,
182
291
  roleName: merged.roleName,
183
292
  workspacePath: merged.workspacePath,
293
+ registerIp: merged.registerIp,
184
294
  virtualIp: merged.virtualIp,
295
+ preferSameLanIp: merged.preferSameLanIp !== false,
185
296
  maxRetries: merged.maxRetries,
186
297
  retryInterval: merged.retryInterval,
187
298
  };
188
299
  }
300
+ export function loadConfigs() {
301
+ const fileTargets = loadTargetsFromFile();
302
+ const envConfig = loadConfigFromEnv();
303
+ const targets = fileTargets.length > 0 ? fileTargets : [loadConfigFromFile() || {}];
304
+ const configs = targets.map((target) => {
305
+ const merged = {
306
+ ...DEFAULT_CONFIG,
307
+ ...target,
308
+ ...envConfig,
309
+ };
310
+ let enabled = false;
311
+ if (process.env.AWS_AUTO_REGISTER !== undefined) {
312
+ enabled = process.env.AWS_AUTO_REGISTER === "true";
313
+ }
314
+ else {
315
+ enabled = fileTargets.length > 0 || Boolean(merged.userKey);
316
+ }
317
+ return {
318
+ enabled,
319
+ serverUrl: merged.serverUrl || schedulerBaseUrl,
320
+ tenantId: merged.tenantId || "",
321
+ userKey: merged.userKey || "",
322
+ connectionKey: merged.connectionKey,
323
+ instanceName: merged.instanceName || os.hostname(),
324
+ projectName: merged.projectName,
325
+ roleName: merged.roleName,
326
+ workspacePath: merged.workspacePath,
327
+ registerIp: merged.registerIp,
328
+ virtualIp: merged.virtualIp,
329
+ preferSameLanIp: merged.preferSameLanIp !== false,
330
+ maxRetries: merged.maxRetries,
331
+ retryInterval: merged.retryInterval,
332
+ };
333
+ });
334
+ cachedConfiguredConnectionKeys = configs
335
+ .map((config) => config.connectionKey)
336
+ .filter((key) => Boolean(key && key.trim()));
337
+ return configs;
338
+ }
189
339
  /**
190
340
  * 获取本机所有非内部 IPv4 地址
191
341
  */
@@ -253,8 +403,7 @@ function extractIpFromUrl(url) {
253
403
  * 策略:
254
404
  * 1. 解析调度中心 URL 获取其 IP
255
405
  * 2. 遍历本机网卡,找到与调度中心同子网的 IP
256
- * 3. 如果找不到同子网 IP,返回第一个非内部 IP
257
- * 4. 如果没有任何非内部 IP,返回 127.0.0.1
406
+ * 3. 如果找不到同子网 IP,返回 127.0.0.1
258
407
  */
259
408
  function getPreferredIpAddress(schedulerUrl) {
260
409
  const allIps = getAllLocalIpAddresses();
@@ -272,43 +421,49 @@ function getPreferredIpAddress(schedulerUrl) {
272
421
  return ipInfo.address;
273
422
  }
274
423
  }
275
- logger.warn(`[AutoRegister] 未找到与调度中心 (${schedulerIp}) 同子网的 IP,使用第一个可用 IP`);
424
+ logger.warn(`[AutoRegister] 未找到与调度中心 (${schedulerIp}) 同子网的本机 IP,使用 127.0.0.1`);
276
425
  }
277
426
  else {
278
- logger.info("[AutoRegister] 无法解析调度中心 IP,使用第一个可用 IP");
427
+ logger.warn("[AutoRegister] 无法解析调度中心 IP,使用 127.0.0.1");
279
428
  }
280
- // 回退:返回第一个非内部 IP
281
- return allIps[0].address;
429
+ return "127.0.0.1";
282
430
  }
283
431
  /**
284
432
  * 获取本机 IP 地址(兼容旧接口)
285
433
  */
286
- function getLocalIpAddress() {
287
- return getPreferredIpAddress(schedulerBaseUrl);
434
+ function getLocalIpAddress(targetServerUrl = schedulerBaseUrl) {
435
+ return getPreferredIpAddress(targetServerUrl);
436
+ }
437
+ function resolveRuntimeBridgeBaseUrl(config) {
438
+ const port = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
439
+ const localIp = config.preferSameLanIp === false
440
+ ? "127.0.0.1"
441
+ : getLocalIpAddress(config.serverUrl || schedulerBaseUrl);
442
+ return `http://${config.registerIp || config.virtualIp || localIp}:${port}`;
288
443
  }
289
444
  /**
290
445
  * 执行注册请求
291
446
  */
292
447
  async function doRegister(config) {
293
448
  const instanceName = config.instanceName || os.hostname();
294
- const localIp = getLocalIpAddress();
295
- const port = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
449
+ const targetServerUrl = config.serverUrl || schedulerBaseUrl;
296
450
  const request = {
297
451
  tenantId: config.tenantId,
298
452
  userKey: config.userKey,
299
453
  instanceName,
300
454
  hostname: os.hostname(),
301
- virtualIp: config.virtualIp,
302
- runtimeBridgeBaseUrl: `http://${config.virtualIp || localIp}:${port}`,
455
+ virtualIp: config.registerIp || config.virtualIp,
456
+ runtimeBridgeBaseUrl: resolveRuntimeBridgeBaseUrl(config),
457
+ connectionKey: config.connectionKey,
303
458
  workspacePath: config.workspacePath,
304
459
  projectName: config.projectName,
305
460
  roleName: config.roleName,
306
461
  };
307
462
  logger.info(`[AutoRegister] 正在注册实例: ${instanceName}`);
308
- logger.info(`[AutoRegister] 调度中心: ${schedulerBaseUrl}`);
463
+ logger.info(`[AutoRegister] 调度中心: ${targetServerUrl}`);
309
464
  logger.info(`[AutoRegister] 用户 Key: ${config.userKey ? "****" + config.userKey.slice(-4) : "(未设置)"}`);
310
465
  logger.info(`[AutoRegister] 租户 ID: ${config.tenantId || "(自动推导)"}`);
311
- const response = await axios.post(`${schedulerBaseUrl}/api/instances/register`, request, {
466
+ const response = await axios.post(`${targetServerUrl}/api/instances/register`, request, {
312
467
  headers: {
313
468
  "Content-Type": "application/json",
314
469
  "X-Runtime-Token": runtimeToken,
@@ -342,11 +497,16 @@ async function doUnregister(instanceId) {
342
497
  * 4. 默认值
343
498
  */
344
499
  export async function autoRegister(customConfig) {
345
- // 加载并合并配置
346
- const config = {
347
- ...loadConfig(),
348
- ...customConfig,
349
- };
500
+ const configs = customConfig
501
+ ? [{ ...loadConfig(), ...customConfig }]
502
+ : loadConfigs();
503
+ if (configs.length > 1) {
504
+ const results = await Promise.all(configs.map((config) => autoRegisterSingle(config)));
505
+ return results.every(Boolean);
506
+ }
507
+ return autoRegisterSingle(configs[0] || loadConfig());
508
+ }
509
+ async function autoRegisterSingle(config) {
350
510
  // 检查是否启用
351
511
  if (!config.enabled) {
352
512
  logger.info("[AutoRegister] 自动注册未启用");
@@ -369,6 +529,10 @@ export async function autoRegister(customConfig) {
369
529
  if (response.success) {
370
530
  registrationState.registered = true;
371
531
  registrationState.instanceId = response.instanceId;
532
+ if (response.instanceId) {
533
+ registrationState.registrations[config.serverUrl || schedulerBaseUrl] =
534
+ response.instanceId;
535
+ }
372
536
  registrationState.error = undefined;
373
537
  logger.info(`[AutoRegister] ✓ 注册成功!`);
374
538
  logger.info(`[AutoRegister] 实例 ID: ${response.instanceId}`);
@@ -379,7 +543,8 @@ export async function autoRegister(customConfig) {
379
543
  saveRuntimeBinding({
380
544
  accessToken: response.runtimeAccessToken,
381
545
  instanceId: response.instanceId,
382
- schedulerBaseUrl: response.serverUrl || schedulerBaseUrl,
546
+ userId: response.userId,
547
+ schedulerBaseUrl: response.serverUrl || config.serverUrl || schedulerBaseUrl,
383
548
  });
384
549
  logger.info("[AutoRegister] 已保存调度中心下发的 runtime access token");
385
550
  }
@@ -421,7 +586,7 @@ export async function autoRegister(customConfig) {
421
586
  logger.error(`[AutoRegister] - 用户 Key (userKey): ${config.userKey ? "****" + config.userKey.slice(-4) : "(未设置)"}`);
422
587
  logger.error(`[AutoRegister] - 租户 ID (tenantId): ${config.tenantId || "(可选,将自动推导)"}`);
423
588
  logger.error(`[AutoRegister] - 实例名 (instanceName): ${config.instanceName || "(使用主机名)"}`);
424
- logger.error(`[AutoRegister] - 调度中心地址: ${schedulerBaseUrl}`);
589
+ logger.error(`[AutoRegister] - 调度中心地址: ${config.serverUrl || schedulerBaseUrl}`);
425
590
  logger.error(`[AutoRegister] - 最后错误: ${registrationState.error || "未知"}`);
426
591
  logger.error(`[AutoRegister] ========================================`);
427
592
  logger.error(`[AutoRegister] 配置来源:~/.aws-bridge/config.json 或环境变量`);
@@ -441,7 +606,7 @@ export async function requestRuntimeAccessTokenRefresh() {
441
606
  const state = getRuntimeBindingPublicState();
442
607
  const localIp = getLocalIpAddress();
443
608
  const runtimeBridgePort = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
444
- const runtimeBridgeBaseUrl = `http://${config.virtualIp || localIp}:${runtimeBridgePort}`;
609
+ const runtimeBridgeBaseUrl = `http://${config.registerIp || config.virtualIp || localIp}:${runtimeBridgePort}`;
445
610
  try {
446
611
  const response = await axios.post(`${schedulerBaseUrl}/api/instances/runtime-tokens/refresh`, {
447
612
  tenantId: config.tenantId,
@@ -465,13 +630,12 @@ export async function requestRuntimeAccessTokenRefresh() {
465
630
  }
466
631
  const previousToken = getRuntimeAccessToken();
467
632
  const updated = previousToken !== response.data.runtimeAccessToken;
468
- if (updated) {
469
- saveRuntimeBinding({
470
- accessToken: response.data.runtimeAccessToken,
471
- instanceId: state.instanceId,
472
- schedulerBaseUrl,
473
- });
474
- }
633
+ saveRuntimeBinding({
634
+ accessToken: response.data.runtimeAccessToken,
635
+ instanceId: state.instanceId,
636
+ userId: response.data.userId || config.userKey,
637
+ schedulerBaseUrl,
638
+ });
475
639
  return {
476
640
  success: true,
477
641
  runtimeAccessToken: response.data.runtimeAccessToken,
@@ -552,7 +716,7 @@ export async function bridgeRestartCleanup() {
552
716
  if (config.enabled) {
553
717
  const localIp = getLocalIpAddress();
554
718
  const port = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
555
- requestBody.runtimeBridgeBaseUrl = `http://${config.virtualIp || localIp}:${port}`;
719
+ requestBody.runtimeBridgeBaseUrl = `http://${config.registerIp || config.virtualIp || localIp}:${port}`;
556
720
  }
557
721
  if (!requestBody.instanceId && !requestBody.runtimeBridgeBaseUrl) {
558
722
  logger.info("[AutoRegister] Bridge 重启清理:没有 instanceId 或 runtimeBridgeBaseUrl,跳过清理");
@@ -1,3 +1,4 @@
1
+ import { mkdtempSync } from 'node:fs';
1
2
  import os from 'node:os';
2
3
  import path from 'node:path';
3
4
  import { afterEach, describe, expect, it, vi } from 'vitest';
@@ -61,6 +62,8 @@ describe('aws-client-agent-mcp service', () => {
61
62
  process.env.AWS_CLIENT_AGENT_MCP_COMMAND = 'aws-client-agent-mcp';
62
63
  process.env.AWS_RUNTIME_CALLBACK_TOKEN = 'secret-runtime-token';
63
64
  process.env.CUSTOM_SECRET = 'should-not-leak';
65
+ process.env.AWS_RUNTIME_HOME_DIR = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-config-'));
66
+ process.env.AWS_RUNTIME_SCHEDULER_BASE_URL = '';
64
67
  process.env.AWS_SERVER_URL = '';
65
68
  process.env.AWS_MCP_HTTP_URL = '';
66
69
  const mod = await import('./aws-client-agent-mcp.js');