aws-runtime-bridge 1.1.1 → 1.1.5
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/index.js +5 -0
- package/dist/services/auto-register.d.ts.map +1 -1
- package/dist/services/auto-register.js +40 -9
- package/dist/services/auto-register.test.d.ts +2 -0
- package/dist/services/auto-register.test.d.ts.map +1 -0
- package/dist/services/auto-register.test.js +154 -0
- package/dist/services/aws-client-agent-mcp.d.ts +6 -1
- package/dist/services/aws-client-agent-mcp.d.ts.map +1 -1
- package/dist/services/aws-client-agent-mcp.js +96 -20
- package/dist/services/aws-client-agent-mcp.test.js +74 -4
- package/dist/services/cli-commands.d.ts +20 -0
- package/dist/services/cli-commands.d.ts.map +1 -0
- package/dist/services/cli-commands.js +55 -0
- package/dist/services/cli-commands.test.d.ts +2 -0
- package/dist/services/cli-commands.test.d.ts.map +1 -0
- package/dist/services/cli-commands.test.js +92 -0
- package/dist/services/startup-config-wizard.d.ts +2 -1
- package/dist/services/startup-config-wizard.d.ts.map +1 -1
- package/dist/services/startup-config-wizard.js +69 -14
- package/dist/services/startup-config-wizard.test.js +83 -2
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +166 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.js +602 -0
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +534 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.d.ts +22 -0
- package/package/aws-client-agent-mcp/dist/config.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.js +106 -0
- package/package/aws-client-agent-mcp/dist/config.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/config.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.test.js +139 -0
- package/package/aws-client-agent-mcp/dist/config.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts +15 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/constants.js +19 -0
- package/package/aws-client-agent-mcp/dist/constants.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +28 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +96 -0
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.js +228 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/index.d.ts +14 -0
- package/package/aws-client-agent-mcp/dist/index.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/index.js +30 -0
- package/package/aws-client-agent-mcp/dist/index.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts +7 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/logger.js +19 -0
- package/package/aws-client-agent-mcp/dist/logger.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +77 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +438 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +624 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +78 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +420 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +61 -0
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.js +268 -0
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +164 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +87 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.js +185 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js +44 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts +53 -0
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/messageContent.js +125 -0
- package/package/aws-client-agent-mcp/dist/messageContent.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +3 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +50 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +90 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +66 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js +237 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js +19 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/types.d.ts +309 -0
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/types.js +9 -0
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +94 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js +316 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +191 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32,6 +32,11 @@ import { logger } from "./utils/logger.js";
|
|
|
32
32
|
import { autoRegister, unregister, bridgeRestartCleanup, } from "./services/auto-register.js";
|
|
33
33
|
import { ensureAwsClientAgentMcpReleased } from "./services/aws-client-agent-mcp.js";
|
|
34
34
|
import { ensureStartupConfig } from "./services/startup-config-wizard.js";
|
|
35
|
+
import { handleCliCommand } from "./services/cli-commands.js";
|
|
36
|
+
const cliCommandResult = await handleCliCommand();
|
|
37
|
+
if (cliCommandResult.handled) {
|
|
38
|
+
process.exit(cliCommandResult.exitCode);
|
|
39
|
+
}
|
|
35
40
|
// 验证生产环境配置
|
|
36
41
|
await ensureStartupConfig();
|
|
37
42
|
validateProductionToken();
|
|
@@ -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;AA+CH;;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;AA6DD;;GAEG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,CAGtD;AAyJD;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,kBAAkB,CA2C/C;AAED,wBAAgB,WAAW,IAAI,kBAAkB,EAAE,CA0ClD;AA4OD;;;;;;;;;;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,CAqED;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;gBAlzBtB,OAAO;iBACN,MAAM;mBACJ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;kBACvB,IAAI;YACV,MAAM;EAgzBf;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,CA2ED"}
|
|
@@ -13,7 +13,7 @@ import path from "node:path";
|
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import { logger } from "../utils/logger.js";
|
|
15
15
|
import { schedulerBaseUrl, runtimeToken } from "../config.js";
|
|
16
|
-
import { getRuntimeAccessToken, getRuntimeBindingPublicState, saveRuntimeBinding, } from "./runtime-binding.js";
|
|
16
|
+
import { getRuntimeAccessToken, getRuntimeBindingPublicState, loadRuntimeBinding, saveRuntimeBinding, } from "./runtime-binding.js";
|
|
17
17
|
// 默认配置
|
|
18
18
|
const DEFAULT_CONFIG = {
|
|
19
19
|
enabled: false,
|
|
@@ -410,6 +410,34 @@ function resolveRuntimeBridgeBaseUrl(config) {
|
|
|
410
410
|
: getLocalIpAddress(config.serverUrl || schedulerBaseUrl);
|
|
411
411
|
return `http://${config.registerIp || config.virtualIp || localIp}:${port}`;
|
|
412
412
|
}
|
|
413
|
+
function loadPrimaryLifecycleConfig() {
|
|
414
|
+
return loadConfigs()[0] || loadConfig();
|
|
415
|
+
}
|
|
416
|
+
function resolveRegisteredSchedulerBaseUrl(instanceId) {
|
|
417
|
+
if (!instanceId) {
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
const registration = Object.entries(registrationState.registrations).find(([, registeredInstanceId]) => registeredInstanceId === instanceId);
|
|
421
|
+
return registration?.[0];
|
|
422
|
+
}
|
|
423
|
+
function resolveLifecycleSchedulerBaseUrl(config, instanceId) {
|
|
424
|
+
const registeredServerUrl = resolveRegisteredSchedulerBaseUrl(instanceId);
|
|
425
|
+
if (registeredServerUrl) {
|
|
426
|
+
return registeredServerUrl;
|
|
427
|
+
}
|
|
428
|
+
const configuredServerUrl = normalizeOptionalString(config.serverUrl);
|
|
429
|
+
if (configuredServerUrl &&
|
|
430
|
+
(configuredServerUrl !== schedulerBaseUrl ||
|
|
431
|
+
Boolean(process.env.AWS_RUNTIME_SCHEDULER_BASE_URL))) {
|
|
432
|
+
return configuredServerUrl;
|
|
433
|
+
}
|
|
434
|
+
const binding = loadRuntimeBinding();
|
|
435
|
+
const pairedSchedulerBaseUrl = normalizeOptionalString(binding.schedulerBaseUrl);
|
|
436
|
+
if (binding.status === "paired" && pairedSchedulerBaseUrl) {
|
|
437
|
+
return pairedSchedulerBaseUrl;
|
|
438
|
+
}
|
|
439
|
+
return configuredServerUrl || schedulerBaseUrl;
|
|
440
|
+
}
|
|
413
441
|
/**
|
|
414
442
|
* 执行注册请求
|
|
415
443
|
*/
|
|
@@ -445,7 +473,8 @@ async function doRegister(config) {
|
|
|
445
473
|
* 执行注销请求
|
|
446
474
|
*/
|
|
447
475
|
async function doUnregister(instanceId) {
|
|
448
|
-
const
|
|
476
|
+
const targetServerUrl = resolveLifecycleSchedulerBaseUrl(loadPrimaryLifecycleConfig(), instanceId);
|
|
477
|
+
const response = await axios.post(`${targetServerUrl}/api/instances/unregister`, { instanceId }, {
|
|
449
478
|
headers: {
|
|
450
479
|
"Content-Type": "application/json",
|
|
451
480
|
"X-Runtime-Token": runtimeToken,
|
|
@@ -565,7 +594,7 @@ async function autoRegisterSingle(config) {
|
|
|
565
594
|
* 注销实例
|
|
566
595
|
*/
|
|
567
596
|
export async function requestRuntimeAccessTokenRefresh() {
|
|
568
|
-
const config =
|
|
597
|
+
const config = loadPrimaryLifecycleConfig();
|
|
569
598
|
if (!config.userKey) {
|
|
570
599
|
return {
|
|
571
600
|
success: false,
|
|
@@ -573,11 +602,12 @@ export async function requestRuntimeAccessTokenRefresh() {
|
|
|
573
602
|
};
|
|
574
603
|
}
|
|
575
604
|
const state = getRuntimeBindingPublicState();
|
|
576
|
-
const
|
|
605
|
+
const targetServerUrl = resolveLifecycleSchedulerBaseUrl(config);
|
|
606
|
+
const localIp = getLocalIpAddress(targetServerUrl);
|
|
577
607
|
const runtimeBridgePort = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
|
|
578
608
|
const runtimeBridgeBaseUrl = `http://${config.registerIp || config.virtualIp || localIp}:${runtimeBridgePort}`;
|
|
579
609
|
try {
|
|
580
|
-
const response = await axios.post(`${
|
|
610
|
+
const response = await axios.post(`${targetServerUrl}/api/instances/runtime-tokens/refresh`, {
|
|
581
611
|
tenantId: config.tenantId,
|
|
582
612
|
userKey: config.userKey,
|
|
583
613
|
instanceId: state.instanceId,
|
|
@@ -603,7 +633,7 @@ export async function requestRuntimeAccessTokenRefresh() {
|
|
|
603
633
|
accessToken: response.data.runtimeAccessToken,
|
|
604
634
|
instanceId: state.instanceId,
|
|
605
635
|
userId: response.data.userId || config.userKey,
|
|
606
|
-
schedulerBaseUrl,
|
|
636
|
+
schedulerBaseUrl: targetServerUrl,
|
|
607
637
|
});
|
|
608
638
|
return {
|
|
609
639
|
success: true,
|
|
@@ -674,8 +704,9 @@ export function getInstanceId() {
|
|
|
674
704
|
* @returns 清理结果
|
|
675
705
|
*/
|
|
676
706
|
export async function bridgeRestartCleanup() {
|
|
677
|
-
const config =
|
|
707
|
+
const config = loadPrimaryLifecycleConfig();
|
|
678
708
|
const instanceId = registrationState.instanceId;
|
|
709
|
+
const targetServerUrl = resolveLifecycleSchedulerBaseUrl(config, instanceId);
|
|
679
710
|
// 构建清理请求
|
|
680
711
|
const requestBody = {};
|
|
681
712
|
if (instanceId) {
|
|
@@ -683,7 +714,7 @@ export async function bridgeRestartCleanup() {
|
|
|
683
714
|
}
|
|
684
715
|
// 如果有配置,也传入 runtimeBridgeBaseUrl
|
|
685
716
|
if (config.enabled) {
|
|
686
|
-
const localIp = getLocalIpAddress();
|
|
717
|
+
const localIp = getLocalIpAddress(targetServerUrl);
|
|
687
718
|
const port = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
|
|
688
719
|
requestBody.runtimeBridgeBaseUrl = `http://${config.registerIp || config.virtualIp || localIp}:${port}`;
|
|
689
720
|
}
|
|
@@ -695,7 +726,7 @@ export async function bridgeRestartCleanup() {
|
|
|
695
726
|
logger.info(`[AutoRegister] Bridge 重启清理:正在通知调度中心...`);
|
|
696
727
|
logger.info(`[AutoRegister] instanceId: ${requestBody.instanceId || "(无)"}`);
|
|
697
728
|
logger.info(`[AutoRegister] runtimeBridgeBaseUrl: ${requestBody.runtimeBridgeBaseUrl || "(无)"}`);
|
|
698
|
-
const response = await axios.post(`${
|
|
729
|
+
const response = await axios.post(`${targetServerUrl}/api/instances/bridge-restart-cleanup`, requestBody, {
|
|
699
730
|
headers: {
|
|
700
731
|
"Content-Type": "application/json",
|
|
701
732
|
"X-Runtime-Token": runtimeToken,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-register.test.d.ts","sourceRoot":"","sources":["../../src/services/auto-register.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
vi.mock("node:os", async () => {
|
|
6
|
+
const actual = await vi.importActual("node:os");
|
|
7
|
+
const mocked = {
|
|
8
|
+
...actual,
|
|
9
|
+
homedir: () => process.env.AWS_TEST_HOME || actual.homedir(),
|
|
10
|
+
};
|
|
11
|
+
return {
|
|
12
|
+
...mocked,
|
|
13
|
+
default: mocked,
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
vi.mock("axios", () => ({
|
|
17
|
+
default: {
|
|
18
|
+
post: vi.fn(),
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
const originalEnv = { ...process.env };
|
|
22
|
+
const tempRoots = [];
|
|
23
|
+
function createRuntimeHome() {
|
|
24
|
+
const root = mkdtempSync(path.join(os.tmpdir(), "aws-auto-register-"));
|
|
25
|
+
tempRoots.push(root);
|
|
26
|
+
return root;
|
|
27
|
+
}
|
|
28
|
+
function writeAutoRegisterConfig(homeDir, targets) {
|
|
29
|
+
const configPath = path.join(homeDir, ".aws-bridge", "config.json");
|
|
30
|
+
const autoRegisterTargets = (typeof targets === "string"
|
|
31
|
+
? [{ serverUrl: targets }]
|
|
32
|
+
: targets).map((target, index) => ({
|
|
33
|
+
serverUrl: target.serverUrl,
|
|
34
|
+
instanceName: target.instanceName || `test-bridge-${index + 1}`,
|
|
35
|
+
userKey: target.userKey || `aws_live_test_key_${index + 1}`,
|
|
36
|
+
registerIp: target.registerIp || "127.0.0.1",
|
|
37
|
+
}));
|
|
38
|
+
mkdirSync(path.dirname(configPath), { recursive: true });
|
|
39
|
+
writeFileSync(configPath, `${JSON.stringify({
|
|
40
|
+
connectionKey: "bridge-key",
|
|
41
|
+
autoRegisterTargets,
|
|
42
|
+
}, null, 2)}\n`, "utf-8");
|
|
43
|
+
}
|
|
44
|
+
function useRuntimeHome(runtimeHome) {
|
|
45
|
+
process.env.AWS_TEST_HOME = runtimeHome;
|
|
46
|
+
process.env.HOME = runtimeHome;
|
|
47
|
+
process.env.USERPROFILE = runtimeHome;
|
|
48
|
+
process.env.AWS_RUNTIME_HOME_DIR = runtimeHome;
|
|
49
|
+
process.env.AWS_RUNTIME_SCHEDULER_BASE_URL = "";
|
|
50
|
+
}
|
|
51
|
+
async function mockSuccessfulPost(response) {
|
|
52
|
+
const { default: axios } = await import("axios");
|
|
53
|
+
vi.mocked(axios.post).mockResolvedValue({ data: response });
|
|
54
|
+
return vi.mocked(axios.post);
|
|
55
|
+
}
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
process.env = { ...originalEnv };
|
|
58
|
+
vi.resetModules();
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
vi.restoreAllMocks();
|
|
61
|
+
for (const root of tempRoots.splice(0)) {
|
|
62
|
+
rmSync(root, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
describe("auto-register scheduler URL selection", () => {
|
|
66
|
+
it("uses configured serverUrl for lifecycle callbacks when env scheduler URL is absent", async () => {
|
|
67
|
+
const runtimeHome = createRuntimeHome();
|
|
68
|
+
useRuntimeHome(runtimeHome);
|
|
69
|
+
writeAutoRegisterConfig(runtimeHome, "http://127.0.0.1:7380");
|
|
70
|
+
const post = await mockSuccessfulPost({
|
|
71
|
+
success: true,
|
|
72
|
+
instanceId: "bridge-instance-1",
|
|
73
|
+
userId: "user-1",
|
|
74
|
+
message: "ok",
|
|
75
|
+
runtimeAccessToken: "runtime-token-123456",
|
|
76
|
+
});
|
|
77
|
+
const { autoRegister, bridgeRestartCleanup, requestRuntimeAccessTokenRefresh, unregister } = await import("./auto-register.js");
|
|
78
|
+
await expect(autoRegister()).resolves.toBe(true);
|
|
79
|
+
await expect(bridgeRestartCleanup()).resolves.toMatchObject({ success: true });
|
|
80
|
+
post.mockResolvedValueOnce({
|
|
81
|
+
data: {
|
|
82
|
+
success: true,
|
|
83
|
+
runtimeAccessToken: "refreshed-token-123456",
|
|
84
|
+
userId: "user-1",
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
await expect(requestRuntimeAccessTokenRefresh()).resolves.toMatchObject({
|
|
88
|
+
success: true,
|
|
89
|
+
runtimeAccessToken: "refreshed-token-123456",
|
|
90
|
+
});
|
|
91
|
+
post.mockResolvedValueOnce({ data: { success: true, message: "unregistered" } });
|
|
92
|
+
await expect(unregister()).resolves.toBe(true);
|
|
93
|
+
expect(post.mock.calls[0]?.[0]).toBe("http://127.0.0.1:7380/api/instances/register");
|
|
94
|
+
expect(post.mock.calls[1]?.[0]).toBe("http://127.0.0.1:7380/api/instances/bridge-restart-cleanup");
|
|
95
|
+
expect(post.mock.calls[2]?.[0]).toBe("http://127.0.0.1:7380/api/instances/runtime-tokens/refresh");
|
|
96
|
+
expect(post.mock.calls[3]?.[0]).toBe("http://127.0.0.1:7380/api/instances/unregister");
|
|
97
|
+
});
|
|
98
|
+
it("uses the registered target URL instead of the first configured target", async () => {
|
|
99
|
+
const runtimeHome = createRuntimeHome();
|
|
100
|
+
useRuntimeHome(runtimeHome);
|
|
101
|
+
writeAutoRegisterConfig(runtimeHome, [
|
|
102
|
+
{ serverUrl: "http://scheduler-a.local:7380", userKey: "aws_live_key_a" },
|
|
103
|
+
{ serverUrl: "http://scheduler-b.local:7380", userKey: "aws_live_key_b" },
|
|
104
|
+
]);
|
|
105
|
+
const post = await mockSuccessfulPost({ success: false, message: "unexpected" });
|
|
106
|
+
post.mockResolvedValueOnce({
|
|
107
|
+
data: {
|
|
108
|
+
success: true,
|
|
109
|
+
instanceId: "bridge-a",
|
|
110
|
+
userId: "user-a",
|
|
111
|
+
runtimeAccessToken: "runtime-token-a-123456",
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
post.mockResolvedValueOnce({
|
|
115
|
+
data: {
|
|
116
|
+
success: true,
|
|
117
|
+
instanceId: "bridge-b",
|
|
118
|
+
userId: "user-b",
|
|
119
|
+
runtimeAccessToken: "runtime-token-b-123456",
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
post.mockResolvedValueOnce({ data: { success: true, message: "cleanup ok" } });
|
|
123
|
+
const { autoRegister, bridgeRestartCleanup } = await import("./auto-register.js");
|
|
124
|
+
await expect(autoRegister()).resolves.toBe(true);
|
|
125
|
+
await expect(bridgeRestartCleanup()).resolves.toMatchObject({ success: true });
|
|
126
|
+
expect(post.mock.calls[0]?.[0]).toBe("http://scheduler-a.local:7380/api/instances/register");
|
|
127
|
+
expect(post.mock.calls[1]?.[0]).toBe("http://scheduler-b.local:7380/api/instances/register");
|
|
128
|
+
expect(post.mock.calls[2]?.[0]).toBe("http://scheduler-b.local:7380/api/instances/bridge-restart-cleanup");
|
|
129
|
+
});
|
|
130
|
+
it("falls back to paired runtime binding scheduler URL when config has no serverUrl", async () => {
|
|
131
|
+
const runtimeHome = createRuntimeHome();
|
|
132
|
+
useRuntimeHome(runtimeHome);
|
|
133
|
+
const post = await mockSuccessfulPost({
|
|
134
|
+
success: true,
|
|
135
|
+
runtimeAccessToken: "binding-refresh-token-123456",
|
|
136
|
+
userId: "user-from-binding",
|
|
137
|
+
});
|
|
138
|
+
const { saveRuntimeBinding } = await import("./runtime-binding.js");
|
|
139
|
+
const { requestRuntimeAccessTokenRefresh } = await import("./auto-register.js");
|
|
140
|
+
saveRuntimeBinding({
|
|
141
|
+
accessToken: "existing-binding-token-123456",
|
|
142
|
+
instanceId: "bridge-from-binding",
|
|
143
|
+
userId: "user-from-binding",
|
|
144
|
+
schedulerBaseUrl: "http://paired-scheduler.local:7380",
|
|
145
|
+
});
|
|
146
|
+
process.env.AWS_AUTO_REGISTER = "true";
|
|
147
|
+
process.env.AWS_USER_KEY = "aws_live_env_key";
|
|
148
|
+
await expect(requestRuntimeAccessTokenRefresh()).resolves.toMatchObject({
|
|
149
|
+
success: true,
|
|
150
|
+
runtimeAccessToken: "binding-refresh-token-123456",
|
|
151
|
+
});
|
|
152
|
+
expect(post.mock.calls[0]?.[0]).toBe("http://paired-scheduler.local:7380/api/instances/runtime-tokens/refresh");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -14,11 +14,16 @@ export interface ResolveAwsClientAgentMcpCommandOptions {
|
|
|
14
14
|
exists?: (filePath: string) => boolean;
|
|
15
15
|
release?: () => string | null;
|
|
16
16
|
}
|
|
17
|
+
export interface ReleaseBundledAwsClientAgentMcpOptions {
|
|
18
|
+
packageRoot?: string;
|
|
19
|
+
releasedRoot?: string;
|
|
20
|
+
now?: () => string;
|
|
21
|
+
}
|
|
17
22
|
export interface AwsMcpPreparedInfo extends AwsMcpCommandSpec {
|
|
18
23
|
source: "override" | "bundled" | "global";
|
|
19
24
|
}
|
|
20
25
|
export declare function getReleasedMcpDistPath(): string;
|
|
21
|
-
export declare function releaseBundledAwsClientAgentMcp(): string | null;
|
|
26
|
+
export declare function releaseBundledAwsClientAgentMcp(options?: ReleaseBundledAwsClientAgentMcpOptions): string | null;
|
|
22
27
|
export declare function resolveAwsClientAgentMcpCommand(options?: ResolveAwsClientAgentMcpCommandOptions): AwsMcpPreparedInfo;
|
|
23
28
|
export declare function getAwsClientAgentMcpPreparedInfo(options?: ResolveAwsClientAgentMcpCommandOptions): AwsMcpPreparedInfo;
|
|
24
29
|
export declare function buildAwsMcpServerConfig(input: AwsMcpServerConfigInput): AwsMcpServerConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-client-agent-mcp.d.ts","sourceRoot":"","sources":["../../src/services/aws-client-agent-mcp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"aws-client-agent-mcp.d.ts","sourceRoot":"","sources":["../../src/services/aws-client-agent-mcp.ts"],"names":[],"mappings":"AAqBA,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,sCAAsC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAaD,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;CAC3C;AAqBD,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AA0FD,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,MAAM,GAAG,IAAI,CAgDf;AA2ED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,kBAAkB,CAEpB;AAED,wBAAgB,gCAAgC,CAC9C,OAAO,GAAE,sCAA2C,GACnD,kBAAkB,CAsBpB;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,uBAAuB,GAC7B,kBAAkB,CAwBpB;AAED,wBAAgB,+BAA+B,IAAI,IAAI,CAgBtD"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { getRuntimeHomeDir, port, schedulerBaseUrl } from "../config.js";
|
|
5
|
-
import { getRuntimeAccessToken, loadRuntimeBinding, } from "./runtime-binding.js";
|
|
6
6
|
import { logger } from "../utils/logger.js";
|
|
7
|
+
import { getRuntimeAccessToken, loadRuntimeBinding, } from "./runtime-binding.js";
|
|
7
8
|
export const AWS_MCP_SERVER_NAME = "aws-mcp";
|
|
8
9
|
const AWS_MCP_ALLOWED_ENV_KEYS = [
|
|
9
10
|
"AWS_INTERNAL_API_KEY",
|
|
@@ -30,8 +31,8 @@ function getPackageRoot() {
|
|
|
30
31
|
const currentFile = fileURLToPath(import.meta.url);
|
|
31
32
|
return path.resolve(path.dirname(currentFile), "..", "..");
|
|
32
33
|
}
|
|
33
|
-
function getBundledEntryPath() {
|
|
34
|
-
return path.join(
|
|
34
|
+
function getBundledEntryPath(packageRoot = getPackageRoot()) {
|
|
35
|
+
return path.join(packageRoot, "package", "aws-client-agent-mcp", "dist", "index.js");
|
|
35
36
|
}
|
|
36
37
|
function getReleasedMcpRoot() {
|
|
37
38
|
return path.join(getRuntimeHomeDir(), ".aws-bridge", "mcp");
|
|
@@ -39,24 +40,107 @@ function getReleasedMcpRoot() {
|
|
|
39
40
|
export function getReleasedMcpDistPath() {
|
|
40
41
|
return path.join(getReleasedMcpRoot(), "dist");
|
|
41
42
|
}
|
|
42
|
-
function
|
|
43
|
-
return path.join(
|
|
43
|
+
function getReleaseManifestPath(releasedRoot = getReleasedMcpRoot()) {
|
|
44
|
+
return path.join(releasedRoot, ".release.json");
|
|
45
|
+
}
|
|
46
|
+
function readPackageVersion(packageJsonPath) {
|
|
47
|
+
try {
|
|
48
|
+
const metadata = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
49
|
+
return metadata.version || "unknown";
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return "unknown";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function getBridgeVersion(packageRoot) {
|
|
56
|
+
return readPackageVersion(path.join(packageRoot, "package.json"));
|
|
57
|
+
}
|
|
58
|
+
function getBundledMcpVersion(packageRoot) {
|
|
59
|
+
return readPackageVersion(path.join(packageRoot, "package", "aws-client-agent-mcp", "package.json"));
|
|
60
|
+
}
|
|
61
|
+
function listDistFiles(dirPath, rootPath = dirPath) {
|
|
62
|
+
return readdirSync(dirPath, { withFileTypes: true }).flatMap((entry) => {
|
|
63
|
+
const entryPath = path.join(dirPath, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
return listDistFiles(entryPath, rootPath);
|
|
66
|
+
}
|
|
67
|
+
if (!entry.isFile()) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return [path.relative(rootPath, entryPath).split(path.sep).join("/")];
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function hashDistDirectory(distPath) {
|
|
74
|
+
const hash = createHash("sha256");
|
|
75
|
+
for (const relativePath of listDistFiles(distPath).sort()) {
|
|
76
|
+
const filePath = path.join(distPath, ...relativePath.split("/"));
|
|
77
|
+
const stat = statSync(filePath);
|
|
78
|
+
hash.update(relativePath);
|
|
79
|
+
hash.update("\0");
|
|
80
|
+
hash.update(String(stat.size));
|
|
81
|
+
hash.update("\0");
|
|
82
|
+
hash.update(readFileSync(filePath));
|
|
83
|
+
hash.update("\0");
|
|
84
|
+
}
|
|
85
|
+
return `sha256:${hash.digest("hex")}`;
|
|
44
86
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
87
|
+
function readReleaseManifest(releasedRoot) {
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(readFileSync(getReleaseManifestPath(releasedRoot), "utf-8"));
|
|
90
|
+
if (typeof parsed.bridgeVersion === "string" &&
|
|
91
|
+
typeof parsed.mcpVersion === "string" &&
|
|
92
|
+
typeof parsed.distHash === "string") {
|
|
93
|
+
return {
|
|
94
|
+
bridgeVersion: parsed.bridgeVersion,
|
|
95
|
+
mcpVersion: parsed.mcpVersion,
|
|
96
|
+
distHash: parsed.distHash,
|
|
97
|
+
releasedAt: typeof parsed.releasedAt === "string" ? parsed.releasedAt : "",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function shouldRefreshReleasedMcp(releasedEntry, manifest, nextManifest) {
|
|
107
|
+
return (!existsSync(releasedEntry) ||
|
|
108
|
+
!manifest ||
|
|
109
|
+
manifest.bridgeVersion !== nextManifest.bridgeVersion ||
|
|
110
|
+
manifest.mcpVersion !== nextManifest.mcpVersion ||
|
|
111
|
+
manifest.distHash !== nextManifest.distHash);
|
|
112
|
+
}
|
|
113
|
+
export function releaseBundledAwsClientAgentMcp(options = {}) {
|
|
114
|
+
const packageRoot = options.packageRoot || getPackageRoot();
|
|
115
|
+
const releasedRoot = options.releasedRoot || getReleasedMcpRoot();
|
|
116
|
+
const bundledDist = path.dirname(getBundledEntryPath(packageRoot));
|
|
117
|
+
const releasedDist = path.join(releasedRoot, "dist");
|
|
48
118
|
const releasedEntry = path.join(releasedDist, "index.js");
|
|
49
119
|
if (!existsSync(path.join(bundledDist, "index.js"))) {
|
|
50
120
|
return null;
|
|
51
121
|
}
|
|
52
|
-
|
|
53
|
-
|
|
122
|
+
const nextManifest = {
|
|
123
|
+
bridgeVersion: getBridgeVersion(packageRoot),
|
|
124
|
+
mcpVersion: getBundledMcpVersion(packageRoot),
|
|
125
|
+
distHash: hashDistDirectory(bundledDist),
|
|
126
|
+
};
|
|
127
|
+
const currentManifest = readReleaseManifest(releasedRoot);
|
|
128
|
+
if (shouldRefreshReleasedMcp(releasedEntry, currentManifest, nextManifest)) {
|
|
129
|
+
rmSync(releasedDist, { recursive: true, force: true });
|
|
130
|
+
mkdirSync(releasedRoot, { recursive: true });
|
|
54
131
|
cpSync(bundledDist, releasedDist, {
|
|
55
132
|
errorOnExist: false,
|
|
56
133
|
force: true,
|
|
57
134
|
recursive: true,
|
|
58
135
|
});
|
|
59
|
-
|
|
136
|
+
writeFileSync(getReleaseManifestPath(releasedRoot), `${JSON.stringify({
|
|
137
|
+
...nextManifest,
|
|
138
|
+
releasedAt: options.now ? options.now() : new Date().toISOString(),
|
|
139
|
+
}, null, 2)}\n`, "utf-8");
|
|
140
|
+
logger.info(`[runtime-bridge] ${AWS_MCP_SERVER_NAME} MCP 已更新到: ${releasedDist}`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
logger.info(`[runtime-bridge] ${AWS_MCP_SERVER_NAME} MCP 已是最新: ${releasedDist}`);
|
|
60
144
|
}
|
|
61
145
|
return releasedEntry;
|
|
62
146
|
}
|
|
@@ -125,14 +209,6 @@ export function getAwsClientAgentMcpPreparedInfo(options = {}) {
|
|
|
125
209
|
};
|
|
126
210
|
}
|
|
127
211
|
const exists = options.exists || existsSync;
|
|
128
|
-
const releasedEntry = getReleasedEntryPath();
|
|
129
|
-
if (exists(releasedEntry)) {
|
|
130
|
-
return {
|
|
131
|
-
source: "bundled",
|
|
132
|
-
command: process.execPath,
|
|
133
|
-
args: [releasedEntry],
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
212
|
const release = options.release || releaseBundledAwsClientAgentMcp;
|
|
137
213
|
const released = release();
|
|
138
214
|
if (released && exists(released)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdtempSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
@@ -8,6 +8,15 @@ afterEach(() => {
|
|
|
8
8
|
vi.resetModules();
|
|
9
9
|
vi.restoreAllMocks();
|
|
10
10
|
});
|
|
11
|
+
function createBundledMcpPackage(version = '1.0.0') {
|
|
12
|
+
const packageRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-package-'));
|
|
13
|
+
const bundledDist = path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'dist');
|
|
14
|
+
mkdirSync(bundledDist, { recursive: true });
|
|
15
|
+
writeFileSync(path.join(packageRoot, 'package.json'), JSON.stringify({ name: 'aws-runtime-bridge', version: '9.9.9' }), 'utf-8');
|
|
16
|
+
writeFileSync(path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'package.json'), JSON.stringify({ name: 'aws-client-agent-mcp', version }), 'utf-8');
|
|
17
|
+
writeFileSync(path.join(bundledDist, 'index.js'), 'console.log("mcp v1");\n', 'utf-8');
|
|
18
|
+
return packageRoot;
|
|
19
|
+
}
|
|
11
20
|
describe('aws-client-agent-mcp service', () => {
|
|
12
21
|
it('prefers explicit command override from env', async () => {
|
|
13
22
|
process.env.AWS_CLIENT_AGENT_MCP_COMMAND = 'custom-aws-client-agent-mcp';
|
|
@@ -19,18 +28,79 @@ describe('aws-client-agent-mcp service', () => {
|
|
|
19
28
|
args: ['--stdio', '--trace'],
|
|
20
29
|
});
|
|
21
30
|
});
|
|
22
|
-
it('uses bundled dist entry
|
|
31
|
+
it('uses bundled dist entry after release check', async () => {
|
|
23
32
|
process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
|
|
24
33
|
const mod = await import('./aws-client-agent-mcp.js');
|
|
25
|
-
const exists = vi.fn().mockReturnValue(true);
|
|
26
|
-
const resolved = mod.resolveAwsClientAgentMcpCommand({ exists });
|
|
27
34
|
const releasedEntry = path.join(os.homedir(), '.aws-bridge', 'mcp', 'dist', 'index.js');
|
|
35
|
+
const exists = vi.fn((filePath) => filePath === releasedEntry);
|
|
36
|
+
const release = vi.fn(() => releasedEntry);
|
|
37
|
+
const resolved = mod.resolveAwsClientAgentMcpCommand({ exists, release });
|
|
28
38
|
expect(resolved).toEqual({
|
|
29
39
|
source: 'bundled',
|
|
30
40
|
command: process.execPath,
|
|
31
41
|
args: [releasedEntry],
|
|
32
42
|
});
|
|
33
43
|
expect(exists).toHaveBeenCalled();
|
|
44
|
+
expect(release).toHaveBeenCalledOnce();
|
|
45
|
+
});
|
|
46
|
+
it('writes release manifest and skips copy when bundled mcp is unchanged', async () => {
|
|
47
|
+
process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
|
|
48
|
+
const packageRoot = createBundledMcpPackage('1.0.0');
|
|
49
|
+
const releasedRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-release-'));
|
|
50
|
+
try {
|
|
51
|
+
const mod = await import('./aws-client-agent-mcp.js');
|
|
52
|
+
const firstRelease = mod.releaseBundledAwsClientAgentMcp({
|
|
53
|
+
packageRoot,
|
|
54
|
+
releasedRoot,
|
|
55
|
+
now: () => '2026-05-04T00:00:00.000Z',
|
|
56
|
+
});
|
|
57
|
+
const releasedIndex = path.join(releasedRoot, 'dist', 'index.js');
|
|
58
|
+
const manifestPath = path.join(releasedRoot, '.release.json');
|
|
59
|
+
expect(firstRelease).toBe(releasedIndex);
|
|
60
|
+
expect(existsSync(releasedIndex)).toBe(true);
|
|
61
|
+
const firstManifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
62
|
+
expect(firstManifest).toMatchObject({
|
|
63
|
+
bridgeVersion: '9.9.9',
|
|
64
|
+
mcpVersion: '1.0.0',
|
|
65
|
+
releasedAt: '2026-05-04T00:00:00.000Z',
|
|
66
|
+
});
|
|
67
|
+
expect(firstManifest.distHash).toMatch(/^sha256:/);
|
|
68
|
+
writeFileSync(releasedIndex, 'console.log("local marker");\n', 'utf-8');
|
|
69
|
+
mod.releaseBundledAwsClientAgentMcp({
|
|
70
|
+
packageRoot,
|
|
71
|
+
releasedRoot,
|
|
72
|
+
now: () => '2026-05-05T00:00:00.000Z',
|
|
73
|
+
});
|
|
74
|
+
expect(readFileSync(releasedIndex, 'utf-8')).toBe('console.log("local marker");\n');
|
|
75
|
+
expect(JSON.parse(readFileSync(manifestPath, 'utf-8')).releasedAt).toBe('2026-05-04T00:00:00.000Z');
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
rmSync(packageRoot, { recursive: true, force: true });
|
|
79
|
+
rmSync(releasedRoot, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
it('refreshes released mcp when bundled dist changes', async () => {
|
|
83
|
+
process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
|
|
84
|
+
const packageRoot = createBundledMcpPackage('1.0.0');
|
|
85
|
+
const releasedRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-release-'));
|
|
86
|
+
try {
|
|
87
|
+
const mod = await import('./aws-client-agent-mcp.js');
|
|
88
|
+
const bundledIndex = path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'dist', 'index.js');
|
|
89
|
+
const releasedIndex = path.join(releasedRoot, 'dist', 'index.js');
|
|
90
|
+
mod.releaseBundledAwsClientAgentMcp({ packageRoot, releasedRoot });
|
|
91
|
+
writeFileSync(bundledIndex, 'console.log("mcp v2");\n', 'utf-8');
|
|
92
|
+
mod.releaseBundledAwsClientAgentMcp({
|
|
93
|
+
packageRoot,
|
|
94
|
+
releasedRoot,
|
|
95
|
+
now: () => '2026-05-05T00:00:00.000Z',
|
|
96
|
+
});
|
|
97
|
+
expect(readFileSync(releasedIndex, 'utf-8')).toBe('console.log("mcp v2");\n');
|
|
98
|
+
expect(JSON.parse(readFileSync(path.join(releasedRoot, '.release.json'), 'utf-8')).releasedAt).toBe('2026-05-05T00:00:00.000Z');
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
rmSync(packageRoot, { recursive: true, force: true });
|
|
102
|
+
rmSync(releasedRoot, { recursive: true, force: true });
|
|
103
|
+
}
|
|
34
104
|
});
|
|
35
105
|
it('releases bundled mcp into home directory when runtime dist is missing', async () => {
|
|
36
106
|
process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StartupConfigWizardResult } from "./startup-config-wizard.js";
|
|
2
|
+
export interface CliCommandResult {
|
|
3
|
+
handled: boolean;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
}
|
|
6
|
+
export interface CommandRunResult {
|
|
7
|
+
status: number | null;
|
|
8
|
+
error?: Error;
|
|
9
|
+
}
|
|
10
|
+
export interface CliCommandOptions {
|
|
11
|
+
argv?: string[];
|
|
12
|
+
packageRoot?: string;
|
|
13
|
+
stdout?: (message: string) => void;
|
|
14
|
+
stderr?: (message: string) => void;
|
|
15
|
+
runCommand?: (command: string, args: string[]) => CommandRunResult;
|
|
16
|
+
configure?: () => Promise<StartupConfigWizardResult>;
|
|
17
|
+
}
|
|
18
|
+
export declare function readPackageVersion(packageRoot?: string): string;
|
|
19
|
+
export declare function handleCliCommand(options?: CliCommandOptions): Promise<CliCommandResult>;
|
|
20
|
+
//# sourceMappingURL=cli-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-commands.d.ts","sourceRoot":"","sources":["../../src/services/cli-commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAG5E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,gBAAgB,CAAC;IACnE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACtD;AAYD,wBAAgB,kBAAkB,CAAC,WAAW,SAA0B,GAAG,MAAM,CAMhF;AAcD,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAqC3B"}
|