aws-runtime-bridge 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +77 -77
  2. package/dist/adapter/ClaudeSdkAdapter.d.ts +1 -0
  3. package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -1
  4. package/dist/adapter/ClaudeSdkAdapter.js +7 -3
  5. package/dist/adapter/ClaudeSdkAdapter.test.js +2 -2
  6. package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
  7. package/dist/adapter/CodexSdkAdapter.js +7 -4
  8. package/dist/adapter/CodexSdkAdapter.test.js +4 -2
  9. package/dist/adapter/OpencodeSdkAdapter.d.ts +2 -0
  10. package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
  11. package/dist/adapter/OpencodeSdkAdapter.js +15 -1
  12. package/dist/adapter/OpencodeSdkAdapter.test.js +5 -0
  13. package/dist/index.js +0 -0
  14. package/dist/routes/properties.test.js +4 -4
  15. package/dist/routes/runtime-binding.d.ts.map +1 -1
  16. package/dist/routes/runtime-binding.js +8 -13
  17. package/dist/routes/runtime-mcp-proxy.d.ts +3 -0
  18. package/dist/routes/runtime-mcp-proxy.d.ts.map +1 -0
  19. package/dist/routes/runtime-mcp-proxy.js +102 -0
  20. package/dist/routes/runtime-mcp-proxy.test.d.ts +2 -0
  21. package/dist/routes/runtime-mcp-proxy.test.d.ts.map +1 -0
  22. package/dist/routes/runtime-mcp-proxy.test.js +111 -0
  23. package/dist/routes/terminal.js +2 -5
  24. package/dist/routes/terminal.test.js +3 -4
  25. package/dist/services/auto-register.d.ts +6 -0
  26. package/dist/services/auto-register.d.ts.map +1 -1
  27. package/dist/services/auto-register.js +63 -1
  28. package/dist/services/aws-client-agent-mcp.d.ts.map +1 -1
  29. package/dist/services/aws-client-agent-mcp.js +4 -4
  30. package/dist/services/aws-client-agent-mcp.test.js +14 -0
  31. package/dist/services/mcp-launch-binding-queue.d.ts +0 -2
  32. package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
  33. package/dist/services/mcp-launch-binding-queue.js +44 -16
  34. package/dist/services/mcp-launch-binding-queue.test.js +42 -37
  35. package/dist/services/runtime-binding.d.ts +1 -0
  36. package/dist/services/runtime-binding.d.ts.map +1 -1
  37. package/dist/services/runtime-binding.js +39 -5
  38. package/dist/services/runtime-binding.test.d.ts +2 -0
  39. package/dist/services/runtime-binding.test.d.ts.map +1 -0
  40. package/dist/services/runtime-binding.test.js +11 -0
  41. package/dist/utils/yaml-utils.test.js +129 -129
  42. package/node_modules/@cc-switch/sdk/README.md +540 -540
  43. package/node_modules/@cc-switch/sdk/dist/sdk-import.test.d.ts +2 -0
  44. package/node_modules/@cc-switch/sdk/dist/sdk-import.test.d.ts.map +1 -0
  45. package/node_modules/@cc-switch/sdk/dist/sdk-import.test.js +119 -0
  46. package/node_modules/@cc-switch/sdk/package.json +31 -31
  47. package/package/aws-client-agent-mcp/README.md +288 -288
  48. package/package/aws-client-agent-mcp/dist/config.d.ts.map +1 -1
  49. package/package/aws-client-agent-mcp/dist/config.js +96 -13
  50. package/package/aws-client-agent-mcp/dist/config.js.map +1 -1
  51. package/package/aws-client-agent-mcp/dist/config.test.js +26 -8
  52. package/package/aws-client-agent-mcp/dist/config.test.js.map +1 -1
  53. package/package/aws-client-agent-mcp/dist/constants.d.ts +0 -1
  54. package/package/aws-client-agent-mcp/dist/constants.d.ts.map +1 -1
  55. package/package/aws-client-agent-mcp/dist/constants.js +0 -1
  56. package/package/aws-client-agent-mcp/dist/constants.js.map +1 -1
  57. package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
  58. package/package/aws-client-agent-mcp/dist/http-client.js +49 -13
  59. package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
  60. package/package/aws-client-agent-mcp/dist/http-client.test.js +40 -13
  61. package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
  62. package/package/aws-client-agent-mcp/dist/index.js +11 -6
  63. package/package/aws-client-agent-mcp/dist/index.js.map +1 -1
  64. package/package/aws-client-agent-mcp/dist/logger.d.ts +11 -1
  65. package/package/aws-client-agent-mcp/dist/logger.d.ts.map +1 -1
  66. package/package/aws-client-agent-mcp/dist/logger.js +91 -6
  67. package/package/aws-client-agent-mcp/dist/logger.js.map +1 -1
  68. package/package/aws-client-agent-mcp/dist/logger.test.d.ts +2 -0
  69. package/package/aws-client-agent-mcp/dist/logger.test.d.ts.map +1 -0
  70. package/package/aws-client-agent-mcp/dist/logger.test.js +27 -0
  71. package/package/aws-client-agent-mcp/dist/logger.test.js.map +1 -0
  72. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
  73. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +18 -14
  74. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
  75. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +51 -21
  76. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +1 -1
  77. package/package/aws-client-agent-mcp/dist/types.d.ts +3 -2
  78. package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
  79. package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
  80. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +1 -0
  81. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
  82. package/package/aws-client-agent-mcp/dist/websocket-client.js +18 -0
  83. package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
  84. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +53 -2
  85. package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
  86. package/package/aws-client-agent-mcp/package.json +52 -52
  87. package/package/cc-switch-sdk/README.md +540 -540
  88. package/package/cc-switch-sdk/dist/sdk-import.test.d.ts +2 -0
  89. package/package/cc-switch-sdk/dist/sdk-import.test.d.ts.map +1 -0
  90. package/package/cc-switch-sdk/dist/sdk-import.test.js +119 -0
  91. package/package/cc-switch-sdk/package.json +31 -31
  92. package/package.json +78 -78
package/README.md CHANGED
@@ -1,77 +1,77 @@
1
- # aws-runtime-bridge
2
-
3
- AgentsWorkStudio 机器实例运行时桥接服务,用于在实例上管理 Agent 运行时、终端、配置与回调通信。
4
-
5
- ## 全局安装
6
-
7
- 从 npm 包安装时:
8
-
9
- ```bash
10
- npm install -g aws-runtime-bridge
11
- ```
12
-
13
- 安装脚本会在 macOS、Linux 与常见 Unix 平台自动为 CLI 入口补齐可执行权限;Windows 平台无需 chmod,会自动跳过该步骤。
14
-
15
- 从当前仓库安装时:
16
-
17
- ```bash
18
- cd aws-runtime-bridge
19
- npm install
20
- npm run build
21
- npm install -g .
22
- ```
23
-
24
- ## 启动
25
-
26
- 首次运行 `awsb` / `aws-bridge` 时,如果不存在 `~/.aws-bridge/config.json`,CLI 会进入交互式配置引导;也可以在提示中选择跳过。跳过时仍会创建配置文件并自动生成随机 `connectionKey`,终端会输出该密钥,请保存后在 server/面板连接此 Bridge 时使用。非交互环境(如 systemd、CI、Docker 后台启动)不会阻塞等待输入,也会自动生成 `connectionKey` 并跳过引导。
27
-
28
- 引导会生成类似下面的配置:
29
-
30
- ```json
31
- {
32
- "connectionKey": "gkmzjaznX55..",
33
- "autoRegisterTargets": [
34
- {
35
- "serverUrl": "http://127.0.0.1:8080",
36
- "instanceName": "至强主机",
37
- "userKey": "aws_live_crh_g0GGrg5IntODg1sFwnas7qf4yGrcwoisDWPBtd8",
38
- "registerIp": "127.0.0.1"
39
- }
40
- ]
41
- }
42
- ```
43
-
44
- 如需在交互式终端中也强制跳过引导,可设置 `AWS_BRIDGE_SKIP_SETUP=true`;此时仍会生成随机 `connectionKey`。
45
-
46
- ```bash
47
- AWS_RUNTIME_BRIDGE_PORT=18081 \
48
- AWS_RUNTIME_SCHEDULER_BASE_URL=http://your-server-host:7380 \
49
- AWS_RUNTIME_CALLBACK_TOKEN=replace-with-a-long-random-runtime-callback-token \
50
- AWS_RUNTIME_HOME_DIR=/opt/agentswork/runtime-home \
51
- aws-bridge
52
- ```
53
-
54
- `aws-runtime-bridge` 命令仍作为兼容别名保留。安装 `aws-runtime-bridge` 后,包内会随附
55
- `aws-client-agent-mcp` 的编译产物;bridge 启动时只负责准备该 MCP 产物,不再在 Agent 启动时默认动态注入 `aws-mcp`。
56
-
57
- 请在面板中为目标运行时安装/配置 MCP;这样 Claude Code、Codex、OpenCode、PTY 等不同启动模式都走一致的持久化 MCP 配置链路。
58
-
59
- 如需使用自定义 MCP 可执行文件,可设置:
60
-
61
- ```bash
62
- AWS_CLIENT_AGENT_MCP_COMMAND=/absolute/path/to/aws-client-agent-mcp \
63
- AWS_CLIENT_AGENT_MCP_ARGS='[]' \
64
- aws-bridge
65
- ```
66
-
67
- ## 关键环境变量
68
-
69
- | 变量名 | 说明 | 默认值 |
70
- | --- | --- | --- |
71
- | `AWS_RUNTIME_BRIDGE_PORT` | Bridge HTTP 端口 | `18081` |
72
- | `AWS_RUNTIME_SCHEDULER_BASE_URL` | aws-mcp-server 地址 | `http://localhost:8080` |
73
- | `AWS_RUNTIME_CALLBACK_TOKEN` | 后端调用 bridge 的回调 Token | 无,生产必须显式设置 |
74
- | `AWS_RUNTIME_HOME_DIR` | Bridge 管理配置与状态的主目录 | 当前用户 Home |
75
- | `AWS_RUNTIME_CORS_ORIGINS` | 允许访问 bridge 的来源,逗号分隔 | 本地开发地址 |
76
-
77
- 生产环境中,`AWS_RUNTIME_SCHEDULER_BASE_URL` 必须填写机器实例可访问的 `aws-mcp-server` 地址,不能使用容器内的 `localhost`。
1
+ # aws-runtime-bridge
2
+
3
+ AgentsWorkStudio 机器实例运行时桥接服务,用于在实例上管理 Agent 运行时、终端、配置与回调通信。
4
+
5
+ ## 全局安装
6
+
7
+ 从 npm 包安装时:
8
+
9
+ ```bash
10
+ npm install -g aws-runtime-bridge
11
+ ```
12
+
13
+ 安装脚本会在 macOS、Linux 与常见 Unix 平台自动为 CLI 入口补齐可执行权限;Windows 平台无需 chmod,会自动跳过该步骤。
14
+
15
+ 从当前仓库安装时:
16
+
17
+ ```bash
18
+ cd aws-runtime-bridge
19
+ npm install
20
+ npm run build
21
+ npm install -g .
22
+ ```
23
+
24
+ ## 启动
25
+
26
+ 首次运行 `awsb` / `aws-bridge` 时,如果不存在 `~/.aws-bridge/config.json`,CLI 会进入交互式配置引导;也可以在提示中选择跳过。跳过时仍会创建配置文件并自动生成随机 `connectionKey`,终端会输出该密钥,请保存后在 server/面板连接此 Bridge 时使用。非交互环境(如 systemd、CI、Docker 后台启动)不会阻塞等待输入,也会自动生成 `connectionKey` 并跳过引导。
27
+
28
+ 引导会生成类似下面的配置:
29
+
30
+ ```json
31
+ {
32
+ "connectionKey": "gkmzjaznX55..",
33
+ "autoRegisterTargets": [
34
+ {
35
+ "serverUrl": "http://127.0.0.1:8080",
36
+ "instanceName": "至强主机",
37
+ "userKey": "aws_live_crh_g0GGrg5IntODg1sFwnas7qf4yGrcwoisDWPBtd8",
38
+ "registerIp": "127.0.0.1"
39
+ }
40
+ ]
41
+ }
42
+ ```
43
+
44
+ 如需在交互式终端中也强制跳过引导,可设置 `AWS_BRIDGE_SKIP_SETUP=true`;此时仍会生成随机 `connectionKey`。
45
+
46
+ ```bash
47
+ AWS_RUNTIME_BRIDGE_PORT=18081 \
48
+ AWS_RUNTIME_SCHEDULER_BASE_URL=http://your-server-host:7380 \
49
+ AWS_RUNTIME_CALLBACK_TOKEN=replace-with-a-long-random-runtime-callback-token \
50
+ AWS_RUNTIME_HOME_DIR=/opt/agentswork/runtime-home \
51
+ aws-bridge
52
+ ```
53
+
54
+ `aws-runtime-bridge` 命令仍作为兼容别名保留。安装 `aws-runtime-bridge` 后,包内会随附
55
+ `aws-client-agent-mcp` 的编译产物;bridge 启动时只负责准备该 MCP 产物,不再在 Agent 启动时默认动态注入 `aws-mcp`。
56
+
57
+ 请在面板中为目标运行时安装/配置 MCP;这样 Claude Code、Codex、OpenCode、PTY 等不同启动模式都走一致的持久化 MCP 配置链路。
58
+
59
+ 如需使用自定义 MCP 可执行文件,可设置:
60
+
61
+ ```bash
62
+ AWS_CLIENT_AGENT_MCP_COMMAND=/absolute/path/to/aws-client-agent-mcp \
63
+ AWS_CLIENT_AGENT_MCP_ARGS='[]' \
64
+ aws-bridge
65
+ ```
66
+
67
+ ## 关键环境变量
68
+
69
+ | 变量名 | 说明 | 默认值 |
70
+ | --- | --- | --- |
71
+ | `AWS_RUNTIME_BRIDGE_PORT` | Bridge HTTP 端口 | `18081` |
72
+ | `AWS_RUNTIME_SCHEDULER_BASE_URL` | aws-mcp-server 地址 | `http://localhost:8080` |
73
+ | `AWS_RUNTIME_CALLBACK_TOKEN` | 后端调用 bridge 的回调 Token | 无,生产必须显式设置 |
74
+ | `AWS_RUNTIME_HOME_DIR` | Bridge 管理配置与状态的主目录 | 当前用户 Home |
75
+ | `AWS_RUNTIME_CORS_ORIGINS` | 允许访问 bridge 的来源,逗号分隔 | 本地开发地址 |
76
+
77
+ 生产环境中,`AWS_RUNTIME_SCHEDULER_BASE_URL` 必须填写机器实例可访问的 `aws-mcp-server` 地址,不能使用容器内的 `localhost`。
@@ -72,6 +72,7 @@ export declare class ClaudeSdkAdapter extends EventEmitter implements BaseProvid
72
72
  private sendIdlePrompt;
73
73
  private isTaskPollingIdleCommand;
74
74
  private isMessagePollingIdleCommand;
75
+ private buildMessagePollingIdlePrompt;
75
76
  /**
76
77
  * 执行空闲命令(支持 [enter], [wait:n] 等 token)
77
78
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ClaudeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/ClaudeSdkAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClaudeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/ClaudeSdkAdapter.ts"],"names":[],"mappings":"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"}
@@ -413,9 +413,9 @@ export class ClaudeSdkAdapter extends EventEmitter {
413
413
  if (commands.idleInputCommand) {
414
414
  // 解析空闲命令,提取实际要执行的指令
415
415
  const command = commands.idleInputCommand;
416
- // 如果空闲命令包含 poll_message,发送一个提示让 AI 执行
416
+ // 如果空闲命令包含 poll_message/get_message,引导 AI 进入阻塞式消息轮询。
417
417
  if (this.isMessagePollingIdleCommand(command)) {
418
- this.sendIdlePrompt(sessionId, '请使用 poll_message 工具检查是否有新消息,如果有的话处理它们。');
418
+ this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt());
419
419
  }
420
420
  else {
421
421
  // 其他命令,直接作为消息发送给 AI
@@ -457,7 +457,11 @@ export class ClaudeSdkAdapter extends EventEmitter {
457
457
  if (!normalized) {
458
458
  return false;
459
459
  }
460
- return normalized.toLowerCase().includes('poll_message') || normalized.includes('取消息');
460
+ const lower = normalized.toLowerCase();
461
+ return lower.includes('poll_message') || lower.includes('get_message') || normalized.includes('取消息');
462
+ }
463
+ buildMessagePollingIdlePrompt() {
464
+ return '请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。';
461
465
  }
462
466
  /**
463
467
  * 执行空闲命令(支持 [enter], [wait:n] 等 token)
@@ -34,7 +34,7 @@ describe('ClaudeSdkAdapter', () => {
34
34
  expect(sentPrompts).toHaveLength(0);
35
35
  expect(sendIdlePrompt).not.toHaveBeenCalled();
36
36
  });
37
- it('still auto-wakes Claude for poll_message-style idle commands', async () => {
37
+ it('starts a blocking poll turn for poll_message-style idle commands', async () => {
38
38
  vi.useFakeTimers();
39
39
  const adapter = new ClaudeSdkAdapter();
40
40
  const sentPrompts = [];
@@ -61,7 +61,7 @@ describe('ClaudeSdkAdapter', () => {
61
61
  await vi.advanceTimersByTimeAsync(1000);
62
62
  expect(sentPrompts).toHaveLength(0);
63
63
  await vi.advanceTimersByTimeAsync(600);
64
- expect(sentPrompts).toEqual(['请使用 poll_message 工具检查是否有新消息,如果有的话处理它们。']);
64
+ expect(sentPrompts).toEqual(['请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。']);
65
65
  });
66
66
  it('still auto-wakes Claude for regular idle commands', async () => {
67
67
  vi.useFakeTimers();
@@ -1 +1 @@
1
- {"version":3,"file":"CodexSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/CodexSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EAEd,MAAM,YAAY,CAAC;AAgRpB,qBAAa,eAAgB,SAAQ,YAAa,YAAW,mBAAmB;IAC9E,QAAQ,CAAC,UAAU,WAAW;IAC9B,QAAQ,CAAC,WAAW,WAAW;IAE/B,OAAO,CAAC,QAAQ,CAAwC;IAElD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkE5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC9D,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAanG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA2BhB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;YAqCZ,OAAO;IAiCrB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,gBAAgB;IA+ExB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAuD1B,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;YAcR,cAAc;IAuB5B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,2BAA2B;CAMpC"}
1
+ {"version":3,"file":"CodexSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/CodexSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EAEd,MAAM,YAAY,CAAC;AAgRpB,qBAAa,eAAgB,SAAQ,YAAa,YAAW,mBAAmB;IAC9E,QAAQ,CAAC,UAAU,WAAW;IAC9B,QAAQ,CAAC,WAAW,WAAW;IAE/B,OAAO,CAAC,QAAQ,CAAwC;IAElD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkE5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC9D,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAanG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA2BhB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;YAqCZ,OAAO;IAiCrB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,gBAAgB;IA+ExB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAuD1B,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;YAcR,cAAc;IAuB5B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
@@ -687,8 +687,6 @@ export class CodexSdkAdapter extends EventEmitter {
687
687
  const session = this.sessions.get(sessionId);
688
688
  if (!session?.idleCommands?.idleInputCommand)
689
689
  return;
690
- if (this.isMessagePollingIdleCommand(session.idleCommands.idleInputCommand))
691
- return;
692
690
  session.lastActivityAt = Date.now();
693
691
  const timer = setInterval(() => {
694
692
  this.checkIdleState(sessionId);
@@ -739,12 +737,17 @@ export class CodexSdkAdapter extends EventEmitter {
739
737
  }
740
738
  }
741
739
  toIdlePrompt(command) {
742
- return command.trim();
740
+ const trimmed = command.trim();
741
+ if (this.isMessagePollingIdleCommand(trimmed)) {
742
+ return '请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。';
743
+ }
744
+ return trimmed;
743
745
  }
744
746
  isMessagePollingIdleCommand(command) {
745
747
  const normalized = command.trim();
746
748
  if (!normalized)
747
749
  return false;
748
- return normalized.toLowerCase().includes('poll_message') || normalized.includes('取消息');
750
+ const lower = normalized.toLowerCase();
751
+ return lower.includes('poll_message') || lower.includes('get_message') || normalized.includes('取消息');
749
752
  }
750
753
  }
@@ -177,7 +177,7 @@ describe('CodexSdkAdapter', () => {
177
177
  expect(runMessages).toContain('continue');
178
178
  expect(adapter.getConversation('session-1').some((message) => message.content === 'continue')).toBe(false);
179
179
  });
180
- it('does not auto-wake Codex for poll_message-style idle commands', async () => {
180
+ it('starts a blocking poll turn for poll_message-style idle commands', async () => {
181
181
  vi.useFakeTimers();
182
182
  const adapter = new CodexSdkAdapter();
183
183
  await adapter.startSession('session-1', {
@@ -202,8 +202,10 @@ describe('CodexSdkAdapter', () => {
202
202
  idleInputCommand: 'system: 醒来了吗?使用get_profile获取自己的信息,继续未完成的任务或使用poll_message阻塞获取消息',
203
203
  nonInputCommand: '',
204
204
  });
205
- await vi.advanceTimersByTimeAsync(3000);
205
+ await vi.advanceTimersByTimeAsync(1000);
206
206
  expect(runMessages).toHaveLength(0);
207
+ await vi.advanceTimersByTimeAsync(600);
208
+ expect(runMessages).toEqual(['请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。']);
207
209
  const userMessages = adapter.getConversation('session-1').filter((message) => message.role === 'user');
208
210
  expect(userMessages).toHaveLength(0);
209
211
  });
@@ -41,5 +41,7 @@ export declare class OpencodeSdkAdapter extends EventEmitter implements BaseProv
41
41
  private startIdleDetection;
42
42
  private stopIdleDetection;
43
43
  private checkIdleState;
44
+ private toIdlePrompt;
45
+ private isMessagePollingIdleCommand;
44
46
  }
45
47
  //# sourceMappingURL=OpencodeSdkAdapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAGpB,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AA4FpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAErD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+H5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAOnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;YAaD,aAAa;IAmB3B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;CAQvB"}
1
+ {"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAGpB,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AA4FpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAErD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+H5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAOnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;YAaD,aAAa;IAmB3B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
@@ -616,7 +616,21 @@ export class OpencodeSdkAdapter extends EventEmitter {
616
616
  return;
617
617
  if (!session.idleCommands?.idleInputCommand)
618
618
  return;
619
- void this.sendMessage(sessionId, session.idleCommands.idleInputCommand);
619
+ void this.sendMessage(sessionId, this.toIdlePrompt(session.idleCommands.idleInputCommand));
620
620
  this.stopIdleDetection(sessionId);
621
621
  }
622
+ toIdlePrompt(command) {
623
+ const trimmed = command.trim();
624
+ if (this.isMessagePollingIdleCommand(trimmed)) {
625
+ return '请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。';
626
+ }
627
+ return trimmed;
628
+ }
629
+ isMessagePollingIdleCommand(command) {
630
+ const normalized = command.trim();
631
+ if (!normalized)
632
+ return false;
633
+ const lower = normalized.toLowerCase();
634
+ return lower.includes('poll_message') || lower.includes('get_message') || normalized.includes('取消息');
635
+ }
622
636
  }
@@ -11,4 +11,9 @@ describe('OpencodeSdkAdapter', () => {
11
11
  expect(adapter.hasSession('missing-session')).toBe(false);
12
12
  expect(adapter.getSessionStatus('missing-session')).toBeUndefined();
13
13
  });
14
+ it('normalizes poll_message-style idle commands into a blocking poll prompt', () => {
15
+ const adapter = new OpencodeSdkAdapter();
16
+ const prompt = Reflect.get(adapter, 'toIdlePrompt').call(adapter, 'system: 醒来了吗?使用get_profile获取自己的信息,继续未完成的任务或使用poll_message阻塞获取消息');
17
+ expect(prompt).toBe('请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。');
18
+ });
14
19
  });
package/dist/index.js CHANGED
File without changes
@@ -41,10 +41,10 @@ describe('properties file operations', () => {
41
41
  }
42
42
  return result;
43
43
  };
44
- const props = parseProperties(`
45
- # Comment
46
- agent.role.name=Backend Developer
47
- project.workspace.path=/home/project
44
+ const props = parseProperties(`
45
+ # Comment
46
+ agent.role.name=Backend Developer
47
+ project.workspace.path=/home/project
48
48
  `);
49
49
  expect(props['agent.role.name']).toBe('Backend Developer');
50
50
  expect(props['project.workspace.path']).toBe('/home/project');
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-binding.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,oBAAoB,4CAAW,CAAC;AAqO7C,wBAAgB,6BAA6B,IAAI,IAAI,CAapD"}
1
+ {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-binding.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,oBAAoB,4CAAW,CAAC;AAgO7C,wBAAgB,6BAA6B,IAAI,IAAI,CAapD"}
@@ -2,7 +2,7 @@ import { Router } from "express";
2
2
  import { clearRuntimeBinding, getRuntimeAccessToken, getRuntimeBindingPublicState, getRuntimePairingCode, hasRuntimeBinding, saveRuntimeBinding, validateRuntimeBindingToken, validateRuntimePairingCode, } from "../services/runtime-binding.js";
3
3
  import { validateToken } from "../middleware/auth.js";
4
4
  import { runtimeToken } from "../config.js";
5
- import { requestRuntimeAccessTokenRefresh } from "../services/auto-register.js";
5
+ import { requestRuntimeAccessTokenRefresh, requestRuntimeAccessTokenRefreshForServer } from "../services/auto-register.js";
6
6
  import { claimMcpLaunchBinding, getMcpLaunchQueueSize } from "../services/mcp-launch-binding-queue.js";
7
7
  import { createLogger } from "../utils/logger.js";
8
8
  const log = createLogger("runtime-binding-route");
@@ -84,7 +84,10 @@ runtimeBindingRouter.post("/binding/request-token", async (req, res) => {
84
84
  });
85
85
  return;
86
86
  }
87
- const result = await requestRuntimeAccessTokenRefresh();
87
+ const requestedServerUrl = serverUrl || schedulerBaseUrl;
88
+ const result = requestedServerUrl
89
+ ? await requestRuntimeAccessTokenRefreshForServer(String(requestedServerUrl))
90
+ : await requestRuntimeAccessTokenRefresh();
88
91
  if (!result.success || !result.runtimeAccessToken) {
89
92
  res.status(400).json({
90
93
  ok: false,
@@ -103,15 +106,7 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
103
106
  res.status(403).json({ ok: false, error: "loopback request required" });
104
107
  return;
105
108
  }
106
- const { agentId, bindingId, workspacePath, serverUrl } = req.body || {};
107
- if (!agentId || !String(agentId).trim()) {
108
- res.status(400).json({ ok: false, error: "agentId is required" });
109
- return;
110
- }
111
- if (!bindingId || !String(bindingId).trim()) {
112
- res.status(400).json({ ok: false, error: "bindingId is required" });
113
- return;
114
- }
109
+ const { workspacePath, serverUrl } = req.body || {};
115
110
  if (!workspacePath || !String(workspacePath).trim()) {
116
111
  res.status(400).json({ ok: false, error: "workspacePath is required" });
117
112
  return;
@@ -120,11 +115,11 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
120
115
  res.status(400).json({ ok: false, error: "serverUrl is required" });
121
116
  return;
122
117
  }
123
- const claimed = claimMcpLaunchBinding({ agentId, bindingId, workspacePath, serverUrl });
118
+ const claimed = claimMcpLaunchBinding({ workspacePath, serverUrl });
124
119
  if (!claimed) {
125
120
  res.status(404).json({
126
121
  ok: false,
127
- error: "launch binding not found",
122
+ error: "launch binding not found for workspacePath and serverUrl",
128
123
  queueSize: getMcpLaunchQueueSize(workspacePath),
129
124
  });
130
125
  return;
@@ -0,0 +1,3 @@
1
+ export declare const runtimeMcpProxyRouter: import("express-serve-static-core").Router;
2
+ export declare function toRuntimeMcpProxyUrl(bridgeBaseUrl?: string): string;
3
+ //# sourceMappingURL=runtime-mcp-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-mcp-proxy.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-mcp-proxy.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,qBAAqB,4CAAW,CAAC;AAwI9C,wBAAgB,oBAAoB,CAClC,aAAa,SAA6B,GACzC,MAAM,CAER"}
@@ -0,0 +1,102 @@
1
+ import axios from "axios";
2
+ import { Router } from "express";
3
+ import { port, schedulerBaseUrl } from "../config.js";
4
+ import { requestRuntimeAccessTokenRefresh } from "../services/auto-register.js";
5
+ import { getRuntimeAccessToken, loadRuntimeBinding, } from "../services/runtime-binding.js";
6
+ import { createLogger } from "../utils/logger.js";
7
+ const log = createLogger("runtime-mcp-proxy");
8
+ export const runtimeMcpProxyRouter = Router();
9
+ function isLoopbackRequest(req) {
10
+ const ip = req.ip || req.socket.remoteAddress || "";
11
+ return ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1";
12
+ }
13
+ function normalizeSchedulerBaseUrl(value) {
14
+ const candidate = String(value || "").trim() || schedulerBaseUrl;
15
+ return candidate.replace(/\/+$/, "");
16
+ }
17
+ function normalizeMcpCallBody(rawBody) {
18
+ const tool = String(rawBody?.tool || "").trim();
19
+ if (!/^[a-zA-Z0-9_.:-]{1,128}$/.test(tool)) {
20
+ return null;
21
+ }
22
+ const args = rawBody?.arguments;
23
+ if (args == null) {
24
+ return { tool, arguments: {} };
25
+ }
26
+ if (typeof args !== "object" || Array.isArray(args)) {
27
+ return null;
28
+ }
29
+ return { tool, arguments: args };
30
+ }
31
+ async function forwardMcpCall(options) {
32
+ const url = new URL("/mcp/call", options.schedulerBaseUrl).toString();
33
+ const response = await axios.post(url, options.body, {
34
+ headers: {
35
+ "Content-Type": "application/json",
36
+ "X-Runtime-Token": options.runtimeAccessToken,
37
+ Authorization: `Bearer ${options.runtimeAccessToken}`,
38
+ },
39
+ timeout: 15000,
40
+ validateStatus: () => true,
41
+ });
42
+ return { status: response.status, data: response.data };
43
+ }
44
+ function resolveRuntimeCredential(req) {
45
+ const binding = loadRuntimeBinding();
46
+ const requestedSchedulerBaseUrl = normalizeSchedulerBaseUrl(req.body?.schedulerBaseUrl || req.body?.serverUrl || binding.schedulerBaseUrl);
47
+ const runtimeAccessToken = getRuntimeAccessToken(req.body?.userId || binding.userId, requestedSchedulerBaseUrl);
48
+ return { runtimeAccessToken, schedulerBaseUrl: requestedSchedulerBaseUrl };
49
+ }
50
+ runtimeMcpProxyRouter.post("/mcp/call", async (req, res) => {
51
+ if (!isLoopbackRequest(req)) {
52
+ res.status(403).json({ ok: false, error: "loopback request required" });
53
+ return;
54
+ }
55
+ const body = normalizeMcpCallBody(req.body || {});
56
+ if (!body) {
57
+ res.status(400).json({ ok: false, error: "invalid MCP call payload" });
58
+ return;
59
+ }
60
+ let credential = resolveRuntimeCredential(req);
61
+ if (!credential.runtimeAccessToken) {
62
+ const refresh = await requestRuntimeAccessTokenRefresh();
63
+ if (!refresh.success || !refresh.runtimeAccessToken) {
64
+ res.status(401).json({
65
+ ok: false,
66
+ error: refresh.error || "runtime access token is not available",
67
+ });
68
+ return;
69
+ }
70
+ credential = {
71
+ runtimeAccessToken: refresh.runtimeAccessToken,
72
+ schedulerBaseUrl: credential.schedulerBaseUrl,
73
+ };
74
+ }
75
+ const runtimeAccessToken = String(credential.runtimeAccessToken || "");
76
+ try {
77
+ let forwarded = await forwardMcpCall({
78
+ runtimeAccessToken,
79
+ schedulerBaseUrl: credential.schedulerBaseUrl,
80
+ body,
81
+ });
82
+ if (forwarded.status === 401) {
83
+ const refresh = await requestRuntimeAccessTokenRefresh();
84
+ if (refresh.success && refresh.runtimeAccessToken) {
85
+ forwarded = await forwardMcpCall({
86
+ runtimeAccessToken: refresh.runtimeAccessToken,
87
+ schedulerBaseUrl: credential.schedulerBaseUrl,
88
+ body,
89
+ });
90
+ }
91
+ }
92
+ res.status(forwarded.status).json(forwarded.data);
93
+ }
94
+ catch (error) {
95
+ const err = error;
96
+ log.warn(`[runtime-bridge] MCP proxy call failed: ${err.message}`);
97
+ res.status(502).json({ ok: false, error: "scheduler MCP call failed" });
98
+ }
99
+ });
100
+ export function toRuntimeMcpProxyUrl(bridgeBaseUrl = `http://127.0.0.1:${port}`) {
101
+ return new URL("/runtime/mcp/call", bridgeBaseUrl).toString();
102
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime-mcp-proxy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-mcp-proxy.test.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-mcp-proxy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,111 @@
1
+ import express from "express";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ const postMock = vi.hoisted(() => vi.fn());
4
+ const getRuntimeAccessTokenMock = vi.hoisted(() => vi.fn());
5
+ const loadRuntimeBindingMock = vi.hoisted(() => vi.fn());
6
+ const refreshMock = vi.hoisted(() => vi.fn());
7
+ vi.mock("axios", () => ({
8
+ default: {
9
+ post: postMock,
10
+ },
11
+ }));
12
+ vi.mock("../services/runtime-binding.js", () => ({
13
+ getRuntimeAccessToken: getRuntimeAccessTokenMock,
14
+ loadRuntimeBinding: loadRuntimeBindingMock,
15
+ }));
16
+ vi.mock("../services/auto-register.js", () => ({
17
+ requestRuntimeAccessTokenRefresh: refreshMock,
18
+ }));
19
+ async function createServer() {
20
+ const { runtimeMcpProxyRouter } = await import("./runtime-mcp-proxy.js");
21
+ const app = express();
22
+ app.use(express.json());
23
+ app.use("/runtime", runtimeMcpProxyRouter);
24
+ return new Promise((resolve) => {
25
+ const server = app.listen(0, "127.0.0.1", () => {
26
+ const address = server.address();
27
+ if (!address || typeof address === "string") {
28
+ throw new Error("expected TCP server address");
29
+ }
30
+ resolve({ server, baseUrl: `http://127.0.0.1:${address.port}` });
31
+ });
32
+ });
33
+ }
34
+ async function postJson(baseUrl, body) {
35
+ const response = await fetch(`${baseUrl}/runtime/mcp/call`, {
36
+ method: "POST",
37
+ headers: { "Content-Type": "application/json" },
38
+ body: JSON.stringify(body),
39
+ });
40
+ return { status: response.status, body: await response.json() };
41
+ }
42
+ describe("runtime MCP proxy", () => {
43
+ afterEach(() => {
44
+ vi.resetModules();
45
+ vi.clearAllMocks();
46
+ });
47
+ it("forwards loopback MCP calls with the Bridge-held runtime token", async () => {
48
+ loadRuntimeBindingMock.mockReturnValue({
49
+ status: "paired",
50
+ userId: "user-1",
51
+ schedulerBaseUrl: "http://scheduler:7380",
52
+ });
53
+ getRuntimeAccessTokenMock.mockReturnValue("aws_rt_token_1234567890");
54
+ postMock.mockResolvedValue({ status: 200, data: { ok: true } });
55
+ const { server, baseUrl } = await createServer();
56
+ try {
57
+ const result = await postJson(baseUrl, {
58
+ tool: "discover_colleague",
59
+ arguments: { agentId: "agent-1" },
60
+ });
61
+ expect(result).toEqual({ status: 200, body: { ok: true } });
62
+ expect(postMock).toHaveBeenCalledWith("http://scheduler:7380/mcp/call", { tool: "discover_colleague", arguments: { agentId: "agent-1" } }, expect.objectContaining({
63
+ headers: expect.objectContaining({
64
+ "X-Runtime-Token": "aws_rt_token_1234567890",
65
+ }),
66
+ }));
67
+ }
68
+ finally {
69
+ server.close();
70
+ }
71
+ });
72
+ it("rejects malformed MCP payloads before forwarding", async () => {
73
+ const { server, baseUrl } = await createServer();
74
+ try {
75
+ const result = await postJson(baseUrl, {
76
+ tool: "bad tool name",
77
+ arguments: [],
78
+ });
79
+ expect(result.status).toBe(400);
80
+ expect(postMock).not.toHaveBeenCalled();
81
+ }
82
+ finally {
83
+ server.close();
84
+ }
85
+ });
86
+ it("refreshes runtime token once after scheduler 401", async () => {
87
+ loadRuntimeBindingMock.mockReturnValue({
88
+ status: "paired",
89
+ userId: "user-1",
90
+ schedulerBaseUrl: "http://scheduler:7380",
91
+ });
92
+ getRuntimeAccessTokenMock.mockReturnValue("expired-token-1234567890");
93
+ refreshMock.mockResolvedValue({
94
+ success: true,
95
+ runtimeAccessToken: "fresh-token-1234567890",
96
+ });
97
+ postMock
98
+ .mockResolvedValueOnce({ status: 401, data: { error: "unauthorized" } })
99
+ .mockResolvedValueOnce({ status: 200, data: { ok: true } });
100
+ const { server, baseUrl } = await createServer();
101
+ try {
102
+ const result = await postJson(baseUrl, { tool: "discover_colleague" });
103
+ expect(result).toEqual({ status: 200, body: { ok: true } });
104
+ expect(postMock).toHaveBeenCalledTimes(2);
105
+ expect(postMock.mock.calls[1][2].headers["X-Runtime-Token"]).toBe("fresh-token-1234567890");
106
+ }
107
+ finally {
108
+ server.close();
109
+ }
110
+ });
111
+ });