aws-runtime-bridge 1.0.1 → 1.0.2

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.
@@ -55,6 +55,12 @@ export declare function autoRegister(customConfig?: Partial<AutoRegisterConfig>)
55
55
  /**
56
56
  * 注销实例
57
57
  */
58
+ export declare function requestRuntimeAccessTokenRefresh(): Promise<{
59
+ success: boolean;
60
+ runtimeAccessToken?: string;
61
+ updated?: boolean;
62
+ error?: string;
63
+ }>;
58
64
  export declare function unregister(): Promise<boolean>;
59
65
  /**
60
66
  * 获取注册状态
@@ -1 +1 @@
1
- {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../../src/services/auto-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH;;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;AAwJD;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,kBAAkB,CAuC/C;AA2KD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAyE/F;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;gBA5dtB,OAAO;iBACN,MAAM;kBACL,IAAI;YACV,MAAM;EA2df;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;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2D/G"}
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"}
@@ -7,12 +7,13 @@
7
7
  * 1. 环境变量
8
8
  * 2. 用户目录下的 .aws-bridge/config.json 文件
9
9
  */
10
- import axios from 'axios';
11
- import os from 'node:os';
12
- import path from 'node:path';
13
- import fs from 'node:fs';
14
- import { logger } from '../utils/logger.js';
15
- import { schedulerBaseUrl, runtimeToken } from '../config.js';
10
+ import axios from "axios";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import fs from "node:fs";
14
+ import { logger } from "../utils/logger.js";
15
+ import { schedulerBaseUrl, runtimeToken } from "../config.js";
16
+ import { getRuntimeAccessToken, getRuntimeBindingPublicState, saveRuntimeBinding, } from "./runtime-binding.js";
16
17
  // 默认配置
17
18
  const DEFAULT_CONFIG = {
18
19
  enabled: false,
@@ -27,7 +28,7 @@ let registrationState = {
27
28
  */
28
29
  function getConfigFilePath() {
29
30
  const homeDir = os.homedir();
30
- return path.join(homeDir, '.aws-bridge', 'config.json');
31
+ return path.join(homeDir, ".aws-bridge", "config.json");
31
32
  }
32
33
  /**
33
34
  * 确保配置文件存在,如果不存在则创建空配置文件
@@ -46,7 +47,7 @@ function ensureConfigFile() {
46
47
  }
47
48
  // 创建空配置文件
48
49
  const emptyConfig = {};
49
- fs.writeFileSync(configPath, JSON.stringify(emptyConfig, null, 2), 'utf-8');
50
+ fs.writeFileSync(configPath, JSON.stringify(emptyConfig, null, 2), "utf-8");
50
51
  logger.info(`[AutoRegister] 已创建配置文件: ${configPath}`);
51
52
  logger.info(`[AutoRegister] 要启用自动注册,请编辑配置文件设置 tenantId 和 userKey`);
52
53
  return true;
@@ -84,7 +85,7 @@ function loadConfigFromFile() {
84
85
  logger.debug(`[AutoRegister] 配置文件不存在: ${configPath}`);
85
86
  return null;
86
87
  }
87
- const content = fs.readFileSync(configPath, 'utf-8');
88
+ const content = fs.readFileSync(configPath, "utf-8");
88
89
  const config = JSON.parse(content);
89
90
  logger.info(`[AutoRegister] 从配置文件加载配置: ${configPath}`);
90
91
  return {
@@ -110,7 +111,7 @@ function loadConfigFromFile() {
110
111
  */
111
112
  function loadConfigFromEnv() {
112
113
  const envConfig = {};
113
- if (process.env.AWS_AUTO_REGISTER === 'true') {
114
+ if (process.env.AWS_AUTO_REGISTER === "true") {
114
115
  envConfig.enabled = true;
115
116
  }
116
117
  if (process.env.AWS_TENANT_ID) {
@@ -167,15 +168,15 @@ export function loadConfig() {
167
168
  // 否则,如果配置文件存在(无论是否有内容),则启用自动注册
168
169
  let enabled = false;
169
170
  if (process.env.AWS_AUTO_REGISTER !== undefined) {
170
- enabled = process.env.AWS_AUTO_REGISTER === 'true';
171
+ enabled = process.env.AWS_AUTO_REGISTER === "true";
171
172
  }
172
173
  else if (fileConfig) {
173
174
  enabled = true;
174
175
  }
175
176
  return {
176
177
  enabled,
177
- tenantId: merged.tenantId || '', // 保持为空字符串,后续会通过userKey自动推导
178
- userKey: merged.userKey || '',
178
+ tenantId: merged.tenantId || "", // 保持为空字符串,后续会通过userKey自动推导
179
+ userKey: merged.userKey || "",
179
180
  instanceName,
180
181
  projectName: merged.projectName,
181
182
  roleName: merged.roleName,
@@ -197,7 +198,7 @@ function getAllLocalIpAddresses() {
197
198
  continue;
198
199
  for (const net of nets) {
199
200
  // 跳过内部和非 IPv4 地址
200
- if (net.family === 'IPv4' && !net.internal) {
201
+ if (net.family === "IPv4" && !net.internal) {
201
202
  addresses.push({
202
203
  address: net.address,
203
204
  netmask: net.netmask,
@@ -212,7 +213,7 @@ function getAllLocalIpAddresses() {
212
213
  * 将 IP 地址转换为整数
213
214
  */
214
215
  function ipToInt(ip) {
215
- const parts = ip.split('.').map(Number);
216
+ const parts = ip.split(".").map(Number);
216
217
  return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3];
217
218
  }
218
219
  /**
@@ -231,8 +232,8 @@ function extractIpFromUrl(url) {
231
232
  try {
232
233
  const urlObj = new URL(url);
233
234
  // 如果是主机名,尝试解析
234
- if (urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1') {
235
- return '127.0.0.1';
235
+ if (urlObj.hostname === "localhost" || urlObj.hostname === "127.0.0.1") {
236
+ return "127.0.0.1";
236
237
  }
237
238
  // 检查是否是有效的 IP 地址
238
239
  const ipPattern = /^(\d{1,3}\.){3}\d{1,3}$/;
@@ -258,8 +259,8 @@ function extractIpFromUrl(url) {
258
259
  function getPreferredIpAddress(schedulerUrl) {
259
260
  const allIps = getAllLocalIpAddresses();
260
261
  if (allIps.length === 0) {
261
- logger.warn('[AutoRegister] 未找到任何非内部 IP 地址,使用 127.0.0.1');
262
- return '127.0.0.1';
262
+ logger.warn("[AutoRegister] 未找到任何非内部 IP 地址,使用 127.0.0.1");
263
+ return "127.0.0.1";
263
264
  }
264
265
  // 尝试从调度中心 URL 提取 IP
265
266
  const schedulerIp = extractIpFromUrl(schedulerUrl);
@@ -274,7 +275,7 @@ function getPreferredIpAddress(schedulerUrl) {
274
275
  logger.warn(`[AutoRegister] 未找到与调度中心 (${schedulerIp}) 同子网的 IP,使用第一个可用 IP`);
275
276
  }
276
277
  else {
277
- logger.info('[AutoRegister] 无法解析调度中心 IP,使用第一个可用 IP');
278
+ logger.info("[AutoRegister] 无法解析调度中心 IP,使用第一个可用 IP");
278
279
  }
279
280
  // 回退:返回第一个非内部 IP
280
281
  return allIps[0].address;
@@ -305,12 +306,12 @@ async function doRegister(config) {
305
306
  };
306
307
  logger.info(`[AutoRegister] 正在注册实例: ${instanceName}`);
307
308
  logger.info(`[AutoRegister] 调度中心: ${schedulerBaseUrl}`);
308
- logger.info(`[AutoRegister] 用户 Key: ${config.userKey ? '****' + config.userKey.slice(-4) : '(未设置)'}`);
309
- logger.info(`[AutoRegister] 租户 ID: ${config.tenantId || '(自动推导)'}`);
309
+ logger.info(`[AutoRegister] 用户 Key: ${config.userKey ? "****" + config.userKey.slice(-4) : "(未设置)"}`);
310
+ logger.info(`[AutoRegister] 租户 ID: ${config.tenantId || "(自动推导)"}`);
310
311
  const response = await axios.post(`${schedulerBaseUrl}/api/instances/register`, request, {
311
312
  headers: {
312
- 'Content-Type': 'application/json',
313
- 'X-Runtime-Token': runtimeToken,
313
+ "Content-Type": "application/json",
314
+ "X-Runtime-Token": runtimeToken,
314
315
  },
315
316
  timeout: 10000,
316
317
  });
@@ -322,8 +323,8 @@ async function doRegister(config) {
322
323
  async function doUnregister(instanceId) {
323
324
  const response = await axios.post(`${schedulerBaseUrl}/api/instances/unregister`, { instanceId }, {
324
325
  headers: {
325
- 'Content-Type': 'application/json',
326
- 'X-Runtime-Token': runtimeToken,
326
+ "Content-Type": "application/json",
327
+ "X-Runtime-Token": runtimeToken,
327
328
  },
328
329
  timeout: 10000,
329
330
  });
@@ -348,13 +349,13 @@ export async function autoRegister(customConfig) {
348
349
  };
349
350
  // 检查是否启用
350
351
  if (!config.enabled) {
351
- logger.info('[AutoRegister] 自动注册未启用');
352
- logger.info('[AutoRegister] 要启用自动注册,请创建配置文件 ~/.aws-bridge/config.json 或设置环境变量 AWS_AUTO_REGISTER=true');
352
+ logger.info("[AutoRegister] 自动注册未启用");
353
+ logger.info("[AutoRegister] 要启用自动注册,请创建配置文件 ~/.aws-bridge/config.json 或设置环境变量 AWS_AUTO_REGISTER=true");
353
354
  return false;
354
355
  }
355
356
  // 验证必填字段(userKey 为必填,tenantId 现在可选)
356
357
  if (!config.userKey) {
357
- logger.error('[AutoRegister] 缺少用户 Key(在配置文件中设置 userKey 或设置环境变量 AWS_USER_KEY)');
358
+ logger.error("[AutoRegister] 缺少用户 Key(在配置文件中设置 userKey 或设置环境变量 AWS_USER_KEY)");
358
359
  return false;
359
360
  }
360
361
  // tenantId 现在是可选的,如果没有提供,后端会根据 userKey 自动推导
@@ -372,7 +373,26 @@ export async function autoRegister(customConfig) {
372
373
  logger.info(`[AutoRegister] ✓ 注册成功!`);
373
374
  logger.info(`[AutoRegister] 实例 ID: ${response.instanceId}`);
374
375
  logger.info(`[AutoRegister] 消息: ${response.message}`);
375
- logger.info(`[AutoRegister] 更新: ${response.isUpdate ? '' : ''}`);
376
+ logger.info(`[AutoRegister] 更新: ${response.isUpdate ? "" : ""}`);
377
+ if (response.runtimeAccessToken) {
378
+ try {
379
+ saveRuntimeBinding({
380
+ accessToken: response.runtimeAccessToken,
381
+ instanceId: response.instanceId,
382
+ schedulerBaseUrl: response.serverUrl || schedulerBaseUrl,
383
+ });
384
+ logger.info("[AutoRegister] 已保存调度中心下发的 runtime access token");
385
+ }
386
+ catch (error) {
387
+ const err = error;
388
+ logger.error(`[AutoRegister] 保存 runtime access token 失败: ${err.message}`);
389
+ registrationState.error = err.message;
390
+ return false;
391
+ }
392
+ }
393
+ else {
394
+ logger.warn("[AutoRegister] 注册响应未包含 runtime access token,MCP 将只能使用兼容的静态内部密钥访问调度中心");
395
+ }
376
396
  return true;
377
397
  }
378
398
  else {
@@ -382,25 +402,27 @@ export async function autoRegister(customConfig) {
382
402
  }
383
403
  catch (error) {
384
404
  const err = error;
385
- const message = err.response?.data ? JSON.stringify(err.response.data) : err.message;
405
+ const message = err.response?.data
406
+ ? JSON.stringify(err.response.data)
407
+ : err.message;
386
408
  logger.error(`[AutoRegister] 注册请求失败 (尝试 ${attempt}/${maxRetries}): ${message}`);
387
409
  registrationState.error = message;
388
410
  }
389
411
  // 如果不是最后一次尝试,等待后重试
390
412
  if (attempt < maxRetries) {
391
413
  logger.info(`[AutoRegister] ${retryInterval / 1000} 秒后重试...`);
392
- await new Promise(resolve => setTimeout(resolve, retryInterval));
414
+ await new Promise((resolve) => setTimeout(resolve, retryInterval));
393
415
  }
394
416
  }
395
417
  logger.error(`[AutoRegister] ✗ 注册失败,已达最大重试次数`);
396
418
  logger.error(`[AutoRegister] ========================================`);
397
419
  logger.error(`[AutoRegister] 自动注册已启用但注册失败,实例无法正常工作`);
398
420
  logger.error(`[AutoRegister] 请检查以下配置:`);
399
- logger.error(`[AutoRegister] - 用户 Key (userKey): ${config.userKey ? '****' + config.userKey.slice(-4) : '(未设置)'}`);
400
- logger.error(`[AutoRegister] - 租户 ID (tenantId): ${config.tenantId || '(可选,将自动推导)'}`);
401
- logger.error(`[AutoRegister] - 实例名 (instanceName): ${config.instanceName || '(使用主机名)'}`);
421
+ logger.error(`[AutoRegister] - 用户 Key (userKey): ${config.userKey ? "****" + config.userKey.slice(-4) : "(未设置)"}`);
422
+ logger.error(`[AutoRegister] - 租户 ID (tenantId): ${config.tenantId || "(可选,将自动推导)"}`);
423
+ logger.error(`[AutoRegister] - 实例名 (instanceName): ${config.instanceName || "(使用主机名)"}`);
402
424
  logger.error(`[AutoRegister] - 调度中心地址: ${schedulerBaseUrl}`);
403
- logger.error(`[AutoRegister] - 最后错误: ${registrationState.error || '未知'}`);
425
+ logger.error(`[AutoRegister] - 最后错误: ${registrationState.error || "未知"}`);
404
426
  logger.error(`[AutoRegister] ========================================`);
405
427
  logger.error(`[AutoRegister] 配置来源:~/.aws-bridge/config.json 或环境变量`);
406
428
  return false;
@@ -408,9 +430,65 @@ export async function autoRegister(customConfig) {
408
430
  /**
409
431
  * 注销实例
410
432
  */
433
+ export async function requestRuntimeAccessTokenRefresh() {
434
+ const config = loadConfig();
435
+ if (!config.userKey) {
436
+ return {
437
+ success: false,
438
+ error: "userKey is required for runtime token refresh",
439
+ };
440
+ }
441
+ const state = getRuntimeBindingPublicState();
442
+ const localIp = getLocalIpAddress();
443
+ const runtimeBridgePort = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
444
+ const runtimeBridgeBaseUrl = `http://${config.virtualIp || localIp}:${runtimeBridgePort}`;
445
+ try {
446
+ const response = await axios.post(`${schedulerBaseUrl}/api/instances/runtime-tokens/refresh`, {
447
+ tenantId: config.tenantId,
448
+ userKey: config.userKey,
449
+ instanceId: state.instanceId,
450
+ instanceName: config.instanceName,
451
+ runtimeBridgeBaseUrl,
452
+ }, {
453
+ headers: {
454
+ "Content-Type": "application/json",
455
+ "X-Runtime-Token": runtimeToken,
456
+ },
457
+ timeout: 10000,
458
+ });
459
+ if (!response.data.success || !response.data.runtimeAccessToken) {
460
+ return {
461
+ success: false,
462
+ error: response.data.message ||
463
+ "scheduler did not return runtimeAccessToken",
464
+ };
465
+ }
466
+ const previousToken = getRuntimeAccessToken();
467
+ const updated = previousToken !== response.data.runtimeAccessToken;
468
+ if (updated) {
469
+ saveRuntimeBinding({
470
+ accessToken: response.data.runtimeAccessToken,
471
+ instanceId: state.instanceId,
472
+ schedulerBaseUrl,
473
+ });
474
+ }
475
+ return {
476
+ success: true,
477
+ runtimeAccessToken: response.data.runtimeAccessToken,
478
+ updated,
479
+ };
480
+ }
481
+ catch (error) {
482
+ const err = error;
483
+ const message = err.response?.data
484
+ ? JSON.stringify(err.response.data)
485
+ : err.message;
486
+ return { success: false, error: message };
487
+ }
488
+ }
411
489
  export async function unregister() {
412
490
  if (!registrationState.registered || !registrationState.instanceId) {
413
- logger.info('[AutoRegister] 实例未注册,无需注销');
491
+ logger.info("[AutoRegister] 实例未注册,无需注销");
414
492
  return true;
415
493
  }
416
494
  try {
@@ -477,17 +555,17 @@ export async function bridgeRestartCleanup() {
477
555
  requestBody.runtimeBridgeBaseUrl = `http://${config.virtualIp || localIp}:${port}`;
478
556
  }
479
557
  if (!requestBody.instanceId && !requestBody.runtimeBridgeBaseUrl) {
480
- logger.info('[AutoRegister] Bridge 重启清理:没有 instanceId 或 runtimeBridgeBaseUrl,跳过清理');
558
+ logger.info("[AutoRegister] Bridge 重启清理:没有 instanceId 或 runtimeBridgeBaseUrl,跳过清理");
481
559
  return { success: true, agentCount: 0 };
482
560
  }
483
561
  try {
484
562
  logger.info(`[AutoRegister] Bridge 重启清理:正在通知调度中心...`);
485
- logger.info(`[AutoRegister] instanceId: ${requestBody.instanceId || '(无)'}`);
486
- logger.info(`[AutoRegister] runtimeBridgeBaseUrl: ${requestBody.runtimeBridgeBaseUrl || '(无)'}`);
563
+ logger.info(`[AutoRegister] instanceId: ${requestBody.instanceId || "(无)"}`);
564
+ logger.info(`[AutoRegister] runtimeBridgeBaseUrl: ${requestBody.runtimeBridgeBaseUrl || "(无)"}`);
487
565
  const response = await axios.post(`${schedulerBaseUrl}/api/instances/bridge-restart-cleanup`, requestBody, {
488
566
  headers: {
489
- 'Content-Type': 'application/json',
490
- 'X-Runtime-Token': runtimeToken,
567
+ "Content-Type": "application/json",
568
+ "X-Runtime-Token": runtimeToken,
491
569
  },
492
570
  timeout: 10000,
493
571
  });
@@ -503,7 +581,9 @@ export async function bridgeRestartCleanup() {
503
581
  }
504
582
  catch (error) {
505
583
  const err = error;
506
- const message = err.response?.data ? JSON.stringify(err.response.data) : err.message;
584
+ const message = err.response?.data
585
+ ? JSON.stringify(err.response.data)
586
+ : err.message;
507
587
  logger.warn(`[AutoRegister] Bridge 重启清理请求失败:${message}`);
508
588
  return { success: false, error: message };
509
589
  }
@@ -15,7 +15,7 @@ export interface ResolveAwsClientAgentMcpCommandOptions {
15
15
  release?: () => string | null;
16
16
  }
17
17
  export interface AwsMcpPreparedInfo extends AwsMcpCommandSpec {
18
- source: 'override' | 'bundled' | 'global';
18
+ source: "override" | "bundled" | "global";
19
19
  }
20
20
  export declare function getReleasedMcpDistPath(): string;
21
21
  export declare function releaseBundledAwsClientAgentMcp(): string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"aws-client-agent-mcp.d.ts","sourceRoot":"","sources":["../../src/services/aws-client-agent-mcp.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAqB7C,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,sCAAsC;IACrD,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;CAC3C;AAeD,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAMD,wBAAgB,+BAA+B,IAAI,MAAM,GAAG,IAAI,CAoB/D;AA6CD,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,sCAA2C,GAAG,kBAAkB,CAExH;AAED,wBAAgB,gCAAgC,CAAC,OAAO,GAAE,sCAA2C,GAAG,kBAAkB,CAuBzH;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAe1F;AAED,wBAAgB,+BAA+B,IAAI,IAAI,CAUtD"}
1
+ {"version":3,"file":"aws-client-agent-mcp.d.ts","sourceRoot":"","sources":["../../src/services/aws-client-agent-mcp.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAuB7C,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,sCAAsC;IACrD,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;CAC3C;AAqBD,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAMD,wBAAgB,+BAA+B,IAAI,MAAM,GAAG,IAAI,CAsB/D;AA2ED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,kBAAkB,CAEpB;AAED,wBAAgB,gCAAgC,CAC9C,OAAO,GAAE,sCAA2C,GACnD,kBAAkB,CA+BpB;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,uBAAuB,GAC7B,kBAAkB,CAwBpB;AAED,wBAAgB,+BAA+B,IAAI,IAAI,CAgBtD"}
@@ -1,49 +1,52 @@
1
- import { cpSync, existsSync, mkdirSync } from 'node:fs';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { getRuntimeHomeDir, schedulerBaseUrl } from '../config.js';
5
- import { logger } from '../utils/logger.js';
6
- export const AWS_MCP_SERVER_NAME = 'aws-mcp';
1
+ import { cpSync, existsSync, mkdirSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { getRuntimeHomeDir, port, schedulerBaseUrl } from "../config.js";
5
+ import { getRuntimeAccessToken, loadRuntimeBinding, } from "./runtime-binding.js";
6
+ import { logger } from "../utils/logger.js";
7
+ export const AWS_MCP_SERVER_NAME = "aws-mcp";
7
8
  const AWS_MCP_ALLOWED_ENV_KEYS = [
8
- 'AWS_INTERNAL_API_KEY',
9
- 'AWS_PROJECT_NAME',
10
- 'AWS_PROMPT',
11
- 'AWS_ROLE_NAME',
12
- 'AWS_SERVER_URL',
13
- 'AWS_MCP_HTTP_URL',
14
- 'AWS_HEARTBEAT_INTERVAL',
15
- 'AWS_HEARTBEAT_TIMEOUT',
16
- 'PATH',
17
- 'HOME',
18
- 'USERPROFILE',
19
- 'TMP',
20
- 'TEMP',
21
- 'SystemRoot',
22
- 'COMSPEC',
23
- 'PATHEXT',
24
- 'WINDIR',
9
+ "AWS_INTERNAL_API_KEY",
10
+ "AWS_PROJECT_NAME",
11
+ "AWS_PROMPT",
12
+ "AWS_ROLE_NAME",
13
+ "AWS_SERVER_URL",
14
+ "AWS_MCP_HTTP_URL",
15
+ "AWS_RUNTIME_ACCESS_TOKEN",
16
+ "AWS_RUNTIME_BRIDGE_BASE_URL",
17
+ "AWS_HEARTBEAT_INTERVAL",
18
+ "AWS_HEARTBEAT_TIMEOUT",
19
+ "PATH",
20
+ "HOME",
21
+ "USERPROFILE",
22
+ "TMP",
23
+ "TEMP",
24
+ "SystemRoot",
25
+ "COMSPEC",
26
+ "PATHEXT",
27
+ "WINDIR",
25
28
  ];
26
29
  function getPackageRoot() {
27
30
  const currentFile = fileURLToPath(import.meta.url);
28
- return path.resolve(path.dirname(currentFile), '..', '..');
31
+ return path.resolve(path.dirname(currentFile), "..", "..");
29
32
  }
30
33
  function getBundledEntryPath() {
31
- return path.join(getPackageRoot(), 'package', 'aws-client-agent-mcp', 'dist', 'index.js');
34
+ return path.join(getPackageRoot(), "package", "aws-client-agent-mcp", "dist", "index.js");
32
35
  }
33
36
  function getReleasedMcpRoot() {
34
- return path.join(getRuntimeHomeDir(), '.aws-bridge', 'mcp');
37
+ return path.join(getRuntimeHomeDir(), ".aws-bridge", "mcp");
35
38
  }
36
39
  export function getReleasedMcpDistPath() {
37
- return path.join(getReleasedMcpRoot(), 'dist');
40
+ return path.join(getReleasedMcpRoot(), "dist");
38
41
  }
39
42
  function getReleasedEntryPath() {
40
- return path.join(getReleasedMcpDistPath(), 'index.js');
43
+ return path.join(getReleasedMcpDistPath(), "index.js");
41
44
  }
42
45
  export function releaseBundledAwsClientAgentMcp() {
43
46
  const bundledDist = path.dirname(getBundledEntryPath());
44
47
  const releasedDist = getReleasedMcpDistPath();
45
- const releasedEntry = path.join(releasedDist, 'index.js');
46
- if (!existsSync(path.join(bundledDist, 'index.js'))) {
48
+ const releasedEntry = path.join(releasedDist, "index.js");
49
+ if (!existsSync(path.join(bundledDist, "index.js"))) {
47
50
  return null;
48
51
  }
49
52
  if (!existsSync(releasedEntry)) {
@@ -58,67 +61,90 @@ export function releaseBundledAwsClientAgentMcp() {
58
61
  return releasedEntry;
59
62
  }
60
63
  function getStringEnv() {
61
- return Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === 'string'));
64
+ return Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string"));
62
65
  }
63
66
  function getAllowedAwsMcpEnv(baseEnv) {
64
67
  return Object.fromEntries(AWS_MCP_ALLOWED_ENV_KEYS.flatMap((key) => {
65
68
  const value = baseEnv[key];
66
- return typeof value === 'string' && value.length > 0 ? [[key, value]] : [];
69
+ return typeof value === "string" && value.length > 0
70
+ ? [[key, value]]
71
+ : [];
67
72
  }));
68
73
  }
69
74
  function parseAwsClientAgentMcpArgs(raw) {
70
75
  try {
71
76
  const parsed = JSON.parse(raw);
72
- if (Array.isArray(parsed) && parsed.every((value) => typeof value === 'string')) {
77
+ if (Array.isArray(parsed) &&
78
+ parsed.every((value) => typeof value === "string")) {
73
79
  return parsed;
74
80
  }
75
81
  }
76
82
  catch (error) {
77
83
  logger.warn(`[runtime-bridge] AWS_CLIENT_AGENT_MCP_ARGS 不是合法 JSON,已忽略: ${error instanceof Error ? error.message : String(error)}`);
78
84
  }
79
- logger.warn('[runtime-bridge] AWS_CLIENT_AGENT_MCP_ARGS 必须是 string[] JSON,已忽略');
85
+ logger.warn("[runtime-bridge] AWS_CLIENT_AGENT_MCP_ARGS 必须是 string[] JSON,已忽略");
80
86
  return [];
81
87
  }
82
88
  function toWebSocketUrl(baseUrl) {
83
- const url = new URL('/ws/agent', baseUrl);
84
- if (url.protocol === 'https:') {
85
- url.protocol = 'wss:';
89
+ const url = new URL("/ws/agent", baseUrl);
90
+ if (url.protocol === "https:") {
91
+ url.protocol = "wss:";
86
92
  }
87
93
  else {
88
- url.protocol = 'ws:';
94
+ url.protocol = "ws:";
89
95
  }
90
96
  return url.toString();
91
97
  }
92
98
  function toMcpHttpUrl(baseUrl) {
93
- return new URL('/mcp/call', baseUrl).toString();
99
+ return new URL("/mcp/call", baseUrl).toString();
100
+ }
101
+ function resolveSchedulerBaseUrlForMcp() {
102
+ const envSchedulerBaseUrl = String(process.env.AWS_RUNTIME_SCHEDULER_BASE_URL || "").trim();
103
+ if (envSchedulerBaseUrl) {
104
+ return envSchedulerBaseUrl;
105
+ }
106
+ const binding = loadRuntimeBinding();
107
+ const pairedSchedulerBaseUrl = String(binding.schedulerBaseUrl || "").trim();
108
+ if (binding.status === "paired" && pairedSchedulerBaseUrl) {
109
+ return pairedSchedulerBaseUrl;
110
+ }
111
+ return schedulerBaseUrl;
94
112
  }
95
113
  export function resolveAwsClientAgentMcpCommand(options = {}) {
96
114
  return getAwsClientAgentMcpPreparedInfo(options);
97
115
  }
98
116
  export function getAwsClientAgentMcpPreparedInfo(options = {}) {
99
- const overrideCommand = String(process.env.AWS_CLIENT_AGENT_MCP_COMMAND || '').trim();
117
+ const overrideCommand = String(process.env.AWS_CLIENT_AGENT_MCP_COMMAND || "").trim();
100
118
  if (overrideCommand) {
101
119
  return {
102
- source: 'override',
120
+ source: "override",
103
121
  command: overrideCommand,
104
- args: process.env.AWS_CLIENT_AGENT_MCP_ARGS ? parseAwsClientAgentMcpArgs(process.env.AWS_CLIENT_AGENT_MCP_ARGS) : [],
122
+ args: process.env.AWS_CLIENT_AGENT_MCP_ARGS
123
+ ? parseAwsClientAgentMcpArgs(process.env.AWS_CLIENT_AGENT_MCP_ARGS)
124
+ : [],
105
125
  };
106
126
  }
107
127
  const exists = options.exists || existsSync;
108
128
  const releasedEntry = getReleasedEntryPath();
109
129
  if (exists(releasedEntry)) {
110
- return { source: 'bundled', command: process.execPath, args: [releasedEntry] };
130
+ return {
131
+ source: "bundled",
132
+ command: process.execPath,
133
+ args: [releasedEntry],
134
+ };
111
135
  }
112
136
  const release = options.release || releaseBundledAwsClientAgentMcp;
113
137
  const released = release();
114
138
  if (released && exists(released)) {
115
- return { source: 'bundled', command: process.execPath, args: [released] };
139
+ return { source: "bundled", command: process.execPath, args: [released] };
116
140
  }
117
- return { source: 'global', command: 'aws-client-agent-mcp', args: [] };
141
+ return { source: "global", command: "aws-client-agent-mcp", args: [] };
118
142
  }
119
143
  export function buildAwsMcpServerConfig(input) {
120
144
  const env = getStringEnv();
121
145
  const command = getAwsClientAgentMcpPreparedInfo();
146
+ const effectiveSchedulerBaseUrl = resolveSchedulerBaseUrlForMcp();
147
+ const issuedRuntimeAccessToken = getRuntimeAccessToken();
122
148
  return {
123
149
  command: command.command,
124
150
  args: command.args,
@@ -126,15 +152,21 @@ export function buildAwsMcpServerConfig(input) {
126
152
  ...getAllowedAwsMcpEnv(env),
127
153
  AWS_AGENT_ID: input.agentId,
128
154
  AWS_WORKSPACE_PATH: input.workspacePath,
129
- AWS_SERVER_URL: env.AWS_SERVER_URL || toWebSocketUrl(schedulerBaseUrl),
130
- AWS_MCP_HTTP_URL: env.AWS_MCP_HTTP_URL || toMcpHttpUrl(schedulerBaseUrl),
155
+ AWS_SERVER_URL: env.AWS_SERVER_URL || toWebSocketUrl(effectiveSchedulerBaseUrl),
156
+ AWS_MCP_HTTP_URL: env.AWS_MCP_HTTP_URL || toMcpHttpUrl(effectiveSchedulerBaseUrl),
157
+ AWS_RUNTIME_BRIDGE_BASE_URL: env.AWS_RUNTIME_BRIDGE_BASE_URL || `http://127.0.0.1:${port}`,
158
+ ...(issuedRuntimeAccessToken
159
+ ? { AWS_RUNTIME_ACCESS_TOKEN: issuedRuntimeAccessToken }
160
+ : {}),
131
161
  },
132
162
  };
133
163
  }
134
164
  export function ensureAwsClientAgentMcpReleased() {
135
165
  const prepared = getAwsClientAgentMcpPreparedInfo();
136
- const commandLine = prepared.args[0] ? `${prepared.command} ${prepared.args.join(' ')}` : prepared.command;
137
- if (prepared.source === 'global') {
166
+ const commandLine = prepared.args[0]
167
+ ? `${prepared.command} ${prepared.args.join(" ")}`
168
+ : prepared.command;
169
+ if (prepared.source === "global") {
138
170
  logger.warn(`[runtime-bridge] ${AWS_MCP_SERVER_NAME} MCP 未找到 bundled 产物,启动时将回退到 PATH 命令: ${commandLine}`);
139
171
  return;
140
172
  }
@@ -0,0 +1,32 @@
1
+ export type RuntimeBindingState = {
2
+ status: "unpaired" | "paired";
3
+ instanceId?: string;
4
+ schedulerBaseUrl?: string;
5
+ /**
6
+ * Plain runtime access token issued by the scheduler.
7
+ * Stored locally with 0600 permissions so child MCP processes can authenticate
8
+ * scheduler calls without exposing the token in public status APIs.
9
+ */
10
+ accessToken?: string;
11
+ tokenHash?: string;
12
+ createdAt?: string;
13
+ updatedAt?: string;
14
+ revokedAt?: string;
15
+ };
16
+ export declare function getRuntimePairingCode(): string;
17
+ export declare function getRuntimeBindingFilePath(): string;
18
+ export declare function loadRuntimeBinding(): RuntimeBindingState;
19
+ export declare function getRuntimeBindingPublicState(): Omit<RuntimeBindingState, "tokenHash" | "accessToken"> & {
20
+ paired: boolean;
21
+ };
22
+ export declare function hasRuntimeBinding(): boolean;
23
+ export declare function validateRuntimeBindingToken(token: unknown): boolean;
24
+ export declare function getRuntimeAccessToken(): string | undefined;
25
+ export declare function validateRuntimePairingCode(code: unknown): boolean;
26
+ export declare function saveRuntimeBinding(input: {
27
+ accessToken: string;
28
+ instanceId?: string;
29
+ schedulerBaseUrl?: string;
30
+ }): RuntimeBindingState;
31
+ export declare function clearRuntimeBinding(): void;
32
+ //# sourceMappingURL=runtime-binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/services/runtime-binding.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAuCF,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,kBAAkB,IAAI,mBAAmB,CAWxD;AAED,wBAAgB,4BAA4B,IAAI,IAAI,CAClD,mBAAmB,EACnB,WAAW,GAAG,aAAa,CAC5B,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAWtB;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAG3C;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAYnE;AAED,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAM1D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,mBAAmB,CA6BtB;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAkB1C"}