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.
- package/README.md +77 -77
- package/dist/adapter/ClaudeSdkAdapter.d.ts +1 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/ClaudeSdkAdapter.js +7 -3
- package/dist/adapter/ClaudeSdkAdapter.test.js +2 -2
- package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/CodexSdkAdapter.js +7 -4
- package/dist/adapter/CodexSdkAdapter.test.js +4 -2
- package/dist/adapter/OpencodeSdkAdapter.d.ts +2 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +15 -1
- package/dist/adapter/OpencodeSdkAdapter.test.js +5 -0
- package/dist/index.js +0 -0
- package/dist/routes/properties.test.js +4 -4
- package/dist/routes/runtime-binding.d.ts.map +1 -1
- package/dist/routes/runtime-binding.js +8 -13
- package/dist/routes/runtime-mcp-proxy.d.ts +3 -0
- package/dist/routes/runtime-mcp-proxy.d.ts.map +1 -0
- package/dist/routes/runtime-mcp-proxy.js +102 -0
- package/dist/routes/runtime-mcp-proxy.test.d.ts +2 -0
- package/dist/routes/runtime-mcp-proxy.test.d.ts.map +1 -0
- package/dist/routes/runtime-mcp-proxy.test.js +111 -0
- package/dist/routes/terminal.js +2 -5
- package/dist/routes/terminal.test.js +3 -4
- package/dist/services/auto-register.d.ts +6 -0
- package/dist/services/auto-register.d.ts.map +1 -1
- package/dist/services/auto-register.js +63 -1
- package/dist/services/aws-client-agent-mcp.d.ts.map +1 -1
- package/dist/services/aws-client-agent-mcp.js +4 -4
- package/dist/services/aws-client-agent-mcp.test.js +14 -0
- package/dist/services/mcp-launch-binding-queue.d.ts +0 -2
- package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
- package/dist/services/mcp-launch-binding-queue.js +44 -16
- package/dist/services/mcp-launch-binding-queue.test.js +42 -37
- package/dist/services/runtime-binding.d.ts +1 -0
- package/dist/services/runtime-binding.d.ts.map +1 -1
- package/dist/services/runtime-binding.js +39 -5
- package/dist/services/runtime-binding.test.d.ts +2 -0
- package/dist/services/runtime-binding.test.d.ts.map +1 -0
- package/dist/services/runtime-binding.test.js +11 -0
- package/dist/utils/yaml-utils.test.js +129 -129
- package/node_modules/@cc-switch/sdk/README.md +540 -540
- package/node_modules/@cc-switch/sdk/dist/sdk-import.test.d.ts +2 -0
- package/node_modules/@cc-switch/sdk/dist/sdk-import.test.d.ts.map +1 -0
- package/node_modules/@cc-switch/sdk/dist/sdk-import.test.js +119 -0
- package/node_modules/@cc-switch/sdk/package.json +31 -31
- package/package/aws-client-agent-mcp/README.md +288 -288
- package/package/aws-client-agent-mcp/dist/config.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/config.js +96 -13
- package/package/aws-client-agent-mcp/dist/config.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/config.test.js +26 -8
- package/package/aws-client-agent-mcp/dist/config.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/constants.js +0 -1
- package/package/aws-client-agent-mcp/dist/constants.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.js +49 -13
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js +40 -13
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/index.js +11 -6
- package/package/aws-client-agent-mcp/dist/index.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/logger.d.ts +11 -1
- package/package/aws-client-agent-mcp/dist/logger.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/logger.js +91 -6
- package/package/aws-client-agent-mcp/dist/logger.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/logger.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/logger.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/logger.test.js +27 -0
- package/package/aws-client-agent-mcp/dist/logger.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +18 -14
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +51 -21
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +3 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +18 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +53 -2
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/package.json +52 -52
- package/package/cc-switch-sdk/README.md +540 -540
- package/package/cc-switch-sdk/dist/sdk-import.test.d.ts +2 -0
- package/package/cc-switch-sdk/dist/sdk-import.test.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/sdk-import.test.js +119 -0
- package/package/cc-switch-sdk/package.json +31 -31
- 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":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAKtC,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAGpB,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AAwHpB;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,YAAa,YAAW,mBAAmB;IAC/E,QAAQ,CAAC,UAAU,iBAAiB;IACpC,QAAQ,CAAC,WAAW,iBAAiB;IAErC,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,YAAY,CAAuD;IAC3E,OAAO,CAAC,gBAAgB,CAA2C;IACnE,OAAO,CAAC,SAAS,CAAiB;IAGlC,OAAO,CAAC,kBAAkB,CAGZ;IAGd,OAAO,CAAC,gBAAgB,CAGV;IAGd,OAAO,CAAC,gBAAgB,CAAkC;IAG1D,OAAO,CAAC,eAAe,CAA0D;IAGjF,OAAO,CAAC,gBAAgB,CAAkC;IAIpD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyF5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwCpE;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAmDrB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrF,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BxD,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;IAIpD,OAAO,IAAI,IAAI;IAoBf,OAAO,CAAC,YAAY,CAAiF;IACrG,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,cAAc,CAAkC;IAExD;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IASzG;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAuCtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,wBAAwB;IAShC,OAAO,CAAC,2BAA2B;
|
|
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
|
|
416
|
+
// 如果空闲命令包含 poll_message/get_message,引导 AI 进入阻塞式消息轮询。
|
|
417
417
|
if (this.isMessagePollingIdleCommand(command)) {
|
|
418
|
-
this.sendIdlePrompt(sessionId,
|
|
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
|
-
|
|
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('
|
|
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(['
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
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(
|
|
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;
|
|
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;
|
|
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
|
|
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 {
|
|
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({
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|
+
});
|