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.
- package/dist/adapter/adapter.test.js +4 -4
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +0 -7
- package/dist/adapter/types.test.js +5 -53
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +4 -0
- package/dist/routes/instance.d.ts.map +1 -1
- package/dist/routes/instance.js +88 -34
- package/dist/routes/runtime-binding.d.ts.map +1 -1
- package/dist/routes/runtime-binding.js +59 -2
- package/dist/routes/sessions.js +1 -1
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +48 -14
- package/dist/routes/terminal.test.js +6 -2
- package/dist/services/agent-process-manager.js +4 -4
- package/dist/services/auto-register.d.ts +12 -1
- package/dist/services/auto-register.d.ts.map +1 -1
- package/dist/services/auto-register.js +206 -42
- package/dist/services/aws-client-agent-mcp.test.js +3 -0
- package/dist/services/mcp-launch-binding-queue.d.ts +36 -0
- package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -0
- package/dist/services/mcp-launch-binding-queue.js +92 -0
- package/dist/services/mcp-launch-binding-queue.test.d.ts +2 -0
- package/dist/services/mcp-launch-binding-queue.test.d.ts.map +1 -0
- package/dist/services/mcp-launch-binding-queue.test.js +107 -0
- package/dist/services/orphan-monitor.js +1 -1
- package/dist/services/process-detector.d.ts +1 -1
- package/dist/services/process-detector.d.ts.map +1 -1
- package/dist/services/process-detector.js +2 -11
- package/dist/services/process-registry.d.ts +1 -0
- package/dist/services/process-registry.d.ts.map +1 -1
- package/dist/services/process-registry.js +129 -108
- package/dist/services/runtime-binding.d.ts +11 -1
- package/dist/services/runtime-binding.d.ts.map +1 -1
- package/dist/services/runtime-binding.js +130 -4
- package/dist/services/terminal-persistence.d.ts.map +1 -1
- package/dist/services/terminal-persistence.js +47 -37
- package/dist/services/terminal-persistence.test.js +47 -1
- package/dist/utils/file-utils.d.ts +3 -0
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +32 -0
- package/package/aws-client-agent-mcp/README.md +288 -288
- package/package.json +76 -76
- package/dist/routes/aws-mcp.d.ts +0 -10
- package/dist/routes/aws-mcp.d.ts.map +0 -1
- package/dist/routes/aws-mcp.js +0 -74
- package/dist/routes/aws-mcp.test.d.ts +0 -2
- package/dist/routes/aws-mcp.test.d.ts.map +0 -1
- package/dist/routes/aws-mcp.test.js +0 -42
- package/dist/routes/memory.d.ts +0 -13
- package/dist/routes/memory.d.ts.map +0 -1
- package/dist/routes/memory.js +0 -429
- package/dist/services/aws-mcp-http.d.ts +0 -11
- package/dist/services/aws-mcp-http.d.ts.map +0 -1
- package/dist/services/aws-mcp-http.js +0 -225
- package/dist/services/aws-mcp-http.test.d.ts +0 -2
- package/dist/services/aws-mcp-http.test.d.ts.map +0 -1
- package/dist/services/aws-mcp-http.test.js +0 -27
- package/dist/services/easytier-manager.d.ts +0 -106
- package/dist/services/easytier-manager.d.ts.map +0 -1
- package/dist/services/easytier-manager.js +0 -331
- package/dist/services/easytier-manager.test.d.ts +0 -5
- package/dist/services/easytier-manager.test.d.ts.map +0 -1
- package/dist/services/easytier-manager.test.js +0 -98
- package/dist/services/memory-service.d.ts +0 -195
- package/dist/services/memory-service.d.ts.map +0 -1
- package/dist/services/memory-service.js +0 -650
- package/dist/services/session-lookup.d.ts +0 -20
- package/dist/services/session-lookup.d.ts.map +0 -1
- package/dist/services/session-lookup.js +0 -43
- package/dist/services/user-api-key-service.d.ts +0 -28
- package/dist/services/user-api-key-service.d.ts.map +0 -1
- package/dist/services/user-api-key-service.js +0 -75
- package/node_modules/@cc-switch/sdk/dist/adapters/common.d.ts +0 -38
- package/node_modules/@cc-switch/sdk/dist/adapters/common.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/common.js +0 -47
- package/node_modules/@cc-switch/sdk/dist/adapters/index.d.ts +0 -5
- package/node_modules/@cc-switch/sdk/dist/adapters/index.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/index.js +0 -28
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claude.d.ts +0 -10
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claude.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claude.js +0 -39
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claudecode.d.ts +0 -10
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claudecode.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-claudecode.js +0 -40
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.d.ts +0 -18
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.js +0 -63
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.test.d.ts +0 -2
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.test.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-opencode.test.js +0 -86
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-placeholder.d.ts +0 -9
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-placeholder.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/mcp-placeholder.js +0 -14
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-claude.d.ts +0 -10
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-claude.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-claude.js +0 -51
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-claudecode.d.ts +0 -10
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-claudecode.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-claudecode.js +0 -51
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-opencode.d.ts +0 -10
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-opencode.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-opencode.js +0 -51
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-placeholder.d.ts +0 -9
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-placeholder.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/adapters/skill-placeholder.js +0 -14
- package/node_modules/@cc-switch/sdk/dist/services/instance-service.d.ts +0 -78
- package/node_modules/@cc-switch/sdk/dist/services/instance-service.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/instance-service.js +0 -180
- package/package/cc-switch-sdk/dist/adapters/common.d.ts +0 -38
- package/package/cc-switch-sdk/dist/adapters/common.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/common.js +0 -47
- package/package/cc-switch-sdk/dist/adapters/index.d.ts +0 -5
- package/package/cc-switch-sdk/dist/adapters/index.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/index.js +0 -28
- package/package/cc-switch-sdk/dist/adapters/mcp-claude.d.ts +0 -10
- package/package/cc-switch-sdk/dist/adapters/mcp-claude.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/mcp-claude.js +0 -39
- package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.d.ts +0 -10
- package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.js +0 -40
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.d.ts +0 -18
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.js +0 -63
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.d.ts +0 -2
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.js +0 -86
- package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.d.ts +0 -9
- package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.js +0 -14
- package/package/cc-switch-sdk/dist/adapters/skill-claude.d.ts +0 -10
- package/package/cc-switch-sdk/dist/adapters/skill-claude.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/skill-claude.js +0 -51
- package/package/cc-switch-sdk/dist/adapters/skill-claudecode.d.ts +0 -10
- package/package/cc-switch-sdk/dist/adapters/skill-claudecode.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/skill-claudecode.js +0 -51
- package/package/cc-switch-sdk/dist/adapters/skill-opencode.d.ts +0 -10
- package/package/cc-switch-sdk/dist/adapters/skill-opencode.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/skill-opencode.js +0 -51
- package/package/cc-switch-sdk/dist/adapters/skill-placeholder.d.ts +0 -9
- package/package/cc-switch-sdk/dist/adapters/skill-placeholder.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/adapters/skill-placeholder.js +0 -14
- package/package/cc-switch-sdk/dist/services/instance-service.d.ts +0 -78
- package/package/cc-switch-sdk/dist/services/instance-service.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/services/instance-service.js +0 -180
package/dist/routes/terminal.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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
|
-
/**
|
|
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;
|
|
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
|
-
* "
|
|
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
|
|
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})
|
|
424
|
+
logger.warn(`[AutoRegister] 未找到与调度中心 (${schedulerIp}) 同子网的本机 IP,使用 127.0.0.1`);
|
|
276
425
|
}
|
|
277
426
|
else {
|
|
278
|
-
logger.
|
|
427
|
+
logger.warn("[AutoRegister] 无法解析调度中心 IP,使用 127.0.0.1");
|
|
279
428
|
}
|
|
280
|
-
|
|
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(
|
|
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
|
|
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:
|
|
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] 调度中心: ${
|
|
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(`${
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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');
|