aws-runtime-bridge 1.0.1 → 1.0.3

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 CHANGED
@@ -30,10 +30,9 @@ aws-bridge
30
30
  ```
31
31
 
32
32
  `aws-runtime-bridge` 命令仍作为兼容别名保留。安装 `aws-runtime-bridge` 后,包内会随附
33
- `aws-client-agent-mcp` 的编译产物;bridge 启动时会自动准备该 MCP,并在 **Claude Code SDK 模式**
34
- 启动 Agent 时默认注入名为 `aws-mcp` 的 MCP Server。
33
+ `aws-client-agent-mcp` 的编译产物;bridge 启动时只负责准备该 MCP 产物,不再在 Agent 启动时默认动态注入 `aws-mcp`。
35
34
 
36
- > 当前 `Codex` SDK 不支持和 Claude Code 相同的动态 `extraMcpServers` 注入;如果使用 Codex,需走其原生 MCP 配置持久化链路。
35
+ 请在面板中为目标运行时安装/配置 MCP;这样 Claude Code、Codex、OpenCode、PTY 等不同启动模式都走一致的持久化 MCP 配置链路。
37
36
 
38
37
  如需使用自定义 MCP 可执行文件,可设置:
39
38
 
package/dist/config.d.ts CHANGED
@@ -67,7 +67,7 @@ export declare const AUTO_REGISTER_PROJECT_NAME: string;
67
67
  export declare const AUTO_REGISTER_ROLE_NAME: string;
68
68
  /** 工作空间路径 */
69
69
  export declare const AUTO_REGISTER_WORKSPACE_PATH: string;
70
- /** 虚拟 IPEasyTier */
70
+ /** 注册 IP(优先),兼容 EasyTier 虚拟 IP */
71
71
  export declare const AUTO_REGISTER_VIRTUAL_IP: string;
72
72
  /** 注册重试次数 */
73
73
  export declare const AUTO_REGISTER_MAX_RETRIES: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,WAAW;AACX,eAAO,MAAM,IAAI,EAAE,MAA6D,CAAC;AAEjF,gBAAgB;AAChB,eAAO,MAAM,gBAAgB,EAAE,MAA8E,CAAC;AAE9G,oBAAoB;AACpB,eAAO,MAAM,8BAA8B,gCAAgC,CAAC;AAE5E,cAAc;AACd,eAAO,MAAM,OAAO,EAAE,MAA8C,CAAC;AAErE,kBAAkB;AAClB,eAAO,MAAM,YAAY,EAAE,MAAiF,CAAC;AAE7G,2BAA2B;AAC3B,eAAO,MAAM,wBAAwB,EAAE,OAAgE,CAAC;AAExG,oCAAoC;AACpC,eAAO,MAAM,oBAAoB,EAAE,OAA4D,CAAC;AAEhG,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB,EAAE,MAAM,EAKC,CAAC;AAEzC,uBAAuB;AACvB,wBAAgB,uBAAuB,IAAI,IAAI,CAU9C;AAED,eAAe;AACf,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED,2BAA2B;AAC3B,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED,0BAA0B;AAC1B,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,yBAAyB;AACzB,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,sBAAsB;AACtB,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,4BAA4B;AAC5B,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED;;GAEG;AAEH,yBAAyB;AACzB,eAAO,MAAM,oBAAoB,QAAwD,CAAC;AAE1F,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,EAA0D,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEzH,eAAe;AACf,eAAO,MAAM,mBAAmB,SAAkD,CAAC;AAEnF,iCAAiC;AACjC,eAAO,MAAM,2BAA2B,QAAgE,CAAC;AAEzG,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,QAAsD,CAAC;AAExF,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,QAA0D,CAAC;AAE/F;;GAEG;AAEH,eAAe;AACf,eAAO,MAAM,qBAAqB,SAA2C,CAAC;AAE9E,YAAY;AACZ,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,iBAAiB;AACjB,eAAO,MAAM,sBAAsB,QAAiC,CAAC;AAErE,oBAAoB;AACpB,eAAO,MAAM,2BAA2B,QAAsC,CAAC;AAE/E,WAAW;AACX,eAAO,MAAM,0BAA0B,QAAqC,CAAC;AAE7E,WAAW;AACX,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,aAAa;AACb,eAAO,MAAM,4BAA4B,QAAuC,CAAC;AAEjF,sBAAsB;AACtB,eAAO,MAAM,wBAAwB,QAAmC,CAAC;AAEzE,aAAa;AACb,eAAO,MAAM,yBAAyB,QAAoD,CAAC;AAE3F,iBAAiB;AACjB,eAAO,MAAM,4BAA4B,QAA0D,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,WAAW;AACX,eAAO,MAAM,IAAI,EAAE,MAElB,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,gBAAgB,EAAE,MACwC,CAAC;AAExE,oBAAoB;AACpB,eAAO,MAAM,8BAA8B,gCAAgC,CAAC;AAE5E,cAAc;AACd,eAAO,MAAM,OAAO,EAAE,MAA8C,CAAC;AAErE,kBAAkB;AAClB,eAAO,MAAM,YAAY,EAAE,MAC+C,CAAC;AAE3E,2BAA2B;AAC3B,eAAO,MAAM,wBAAwB,EAAE,OACiB,CAAC;AAEzD,oCAAoC;AACpC,eAAO,MAAM,oBAAoB,EAAE,OACiB,CAAC;AAErD,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB,EAAE,MAAM,EAMC,CAAC;AAEzC,uBAAuB;AACvB,wBAAgB,uBAAuB,IAAI,IAAI,CAc9C;AAED,eAAe;AACf,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,2BAA2B;AAC3B,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlE;AAED,0BAA0B;AAC1B,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,yBAAyB;AACzB,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,sBAAsB;AACtB,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,4BAA4B;AAC5B,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED;;GAEG;AAEH,yBAAyB;AACzB,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,EACrB,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C,eAAe;AACf,eAAO,MAAM,mBAAmB,SACiB,CAAC;AAElD,iCAAiC;AACjC,eAAO,MAAM,2BAA2B,QAEvC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AAEF;;GAEG;AAEH,eAAe;AACf,eAAO,MAAM,qBAAqB,SAA2C,CAAC;AAE9E,YAAY;AACZ,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,iBAAiB;AACjB,eAAO,MAAM,sBAAsB,QAAiC,CAAC;AAErE,oBAAoB;AACpB,eAAO,MAAM,2BAA2B,QAAsC,CAAC;AAE/E,WAAW;AACX,eAAO,MAAM,0BAA0B,QAAqC,CAAC;AAE7E,WAAW;AACX,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,aAAa;AACb,eAAO,MAAM,4BAA4B,QACH,CAAC;AAEvC,kCAAkC;AAClC,eAAO,MAAM,wBAAwB,QAC4B,CAAC;AAElE,aAAa;AACb,eAAO,MAAM,yBAAyB,QAErC,CAAC;AAEF,iBAAiB;AACjB,eAAO,MAAM,4BAA4B,QAExC,CAAC"}
package/dist/config.js CHANGED
@@ -3,71 +3,73 @@
3
3
  *
4
4
  * 集中管理所有环境变量和配置常量
5
5
  */
6
- import os from 'node:os';
7
- import path from 'node:path';
8
- import { logger } from './utils/logger.js';
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import { logger } from "./utils/logger.js";
9
9
  /** 服务端口 */
10
10
  export const port = Number(process.env.AWS_RUNTIME_BRIDGE_PORT || 18081);
11
11
  /** 调度器基础 URL */
12
- export const schedulerBaseUrl = process.env.AWS_RUNTIME_SCHEDULER_BASE_URL || 'http://localhost:8080';
12
+ export const schedulerBaseUrl = process.env.AWS_RUNTIME_SCHEDULER_BASE_URL || "http://localhost:8080";
13
13
  /** 默认运行时回调 Token */
14
- export const DEFAULT_RUNTIME_CALLBACK_TOKEN = 'agentswork-runtime-callback';
14
+ export const DEFAULT_RUNTIME_CALLBACK_TOKEN = "agentswork-runtime-callback";
15
15
  /** Node 环境 */
16
- export const nodeEnv = process.env.NODE_ENV || 'development';
16
+ export const nodeEnv = process.env.NODE_ENV || "development";
17
17
  /** 运行时回调 Token */
18
18
  export const runtimeToken = process.env.AWS_RUNTIME_CALLBACK_TOKEN || DEFAULT_RUNTIME_CALLBACK_TOKEN;
19
19
  /** 是否显式允许本地开发使用默认 Token */
20
- export const allowDefaultRuntimeToken = process.env.AWS_ALLOW_DEFAULT_RUNTIME_TOKEN === 'true';
20
+ export const allowDefaultRuntimeToken = process.env.AWS_ALLOW_DEFAULT_RUNTIME_TOKEN === "true";
21
21
  /** 是否允许浏览宿主机任意目录(默认关闭,仅建议本地排障启用) */
22
- export const allowHostFileBrowser = process.env.AWS_ALLOW_HOST_FILE_BROWSER === 'true';
22
+ export const allowHostFileBrowser = process.env.AWS_ALLOW_HOST_FILE_BROWSER === "true";
23
23
  /** CORS 允许来源列表,默认仅允许本机开发前端 */
24
- export const allowedCorsOrigins = String(process.env.AWS_RUNTIME_CORS_ORIGINS || 'http://localhost:3000,http://127.0.0.1:3000,http://localhost:5173,http://127.0.0.1:5173')
25
- .split(',')
24
+ export const allowedCorsOrigins = String(process.env.AWS_RUNTIME_CORS_ORIGINS ||
25
+ "http://localhost:3000,http://127.0.0.1:3000,http://localhost:5173,http://127.0.0.1:5173")
26
+ .split(",")
26
27
  .map((origin) => origin.trim())
27
28
  .filter((origin) => origin.length > 0);
28
29
  /** 验证生产环境必须设置 Token */
29
30
  export function validateProductionToken() {
30
31
  if (!process.env.AWS_RUNTIME_CALLBACK_TOKEN) {
31
- if (nodeEnv === 'production' || !allowDefaultRuntimeToken) {
32
- throw new Error('[runtime-bridge] AWS_RUNTIME_CALLBACK_TOKEN must be set. ' +
33
- 'For isolated local development only, set AWS_ALLOW_DEFAULT_RUNTIME_TOKEN=true.');
32
+ if (allowDefaultRuntimeToken) {
33
+ logger.warn("[runtime-bridge] AWS_RUNTIME_CALLBACK_TOKEN not set, using default development token");
34
+ return;
34
35
  }
35
- logger.warn('[runtime-bridge] AWS_RUNTIME_CALLBACK_TOKEN not set, using default development token');
36
+ logger.info("[runtime-bridge] AWS_RUNTIME_CALLBACK_TOKEN not set; starting in unpaired mode. " +
37
+ "Sensitive APIs require a panel/server issued runtime access token after pairing.");
36
38
  }
37
39
  }
38
40
  /** 获取运行时主目录 */
39
41
  export function getRuntimeHomeDir() {
40
- return String(process.env.AWS_RUNTIME_HOME_DIR || '').trim() || os.homedir();
42
+ return String(process.env.AWS_RUNTIME_HOME_DIR || "").trim() || os.homedir();
41
43
  }
42
44
  /** 获取 Claude 配置文件路径(AI 配置写入 settings.json) */
43
45
  export function getClaudeConfigFile(runtimeHome) {
44
- return String(process.env.AWS_RUNTIME_CLAUDE_CONFIG_FILE || '').trim()
45
- || path.join(runtimeHome, '.claude', 'settings.json');
46
+ return (String(process.env.AWS_RUNTIME_CLAUDE_CONFIG_FILE || "").trim() ||
47
+ path.join(runtimeHome, ".claude", "settings.json"));
46
48
  }
47
49
  /** 获取 Claude MCP 配置文件路径 */
48
50
  export function getClaudeMcpConfigFile(runtimeHome) {
49
- return String(process.env.AWS_RUNTIME_CLAUDE_MCP_CONFIG_FILE || '').trim()
50
- || path.join(runtimeHome, '.claude.json');
51
+ return (String(process.env.AWS_RUNTIME_CLAUDE_MCP_CONFIG_FILE || "").trim() ||
52
+ path.join(runtimeHome, ".claude.json"));
51
53
  }
52
54
  /** 获取 Claude Skills 目录 */
53
55
  export function getClaudeSkillsDir(runtimeHome) {
54
- return String(process.env.AWS_RUNTIME_CLAUDE_SKILLS_DIR || '').trim()
55
- || path.join(runtimeHome, '.claude', 'skills');
56
+ return (String(process.env.AWS_RUNTIME_CLAUDE_SKILLS_DIR || "").trim() ||
57
+ path.join(runtimeHome, ".claude", "skills"));
56
58
  }
57
59
  /** 获取 OpenCode 配置文件路径 */
58
60
  export function getOpencodeConfigFile(runtimeHome) {
59
- return String(process.env.AWS_RUNTIME_OPENCODE_CONFIG_FILE || '').trim()
60
- || path.join(runtimeHome, '.config', 'opencode', 'opencode.json');
61
+ return (String(process.env.AWS_RUNTIME_OPENCODE_CONFIG_FILE || "").trim() ||
62
+ path.join(runtimeHome, ".config", "opencode", "opencode.json"));
61
63
  }
62
64
  /** 获取 Codex 配置文件路径 */
63
65
  export function getCodexConfigFile(runtimeHome) {
64
- return String(process.env.AWS_RUNTIME_CODEX_CONFIG_FILE || '').trim()
65
- || path.join(runtimeHome, '.codex', 'config.toml');
66
+ return (String(process.env.AWS_RUNTIME_CODEX_CONFIG_FILE || "").trim() ||
67
+ path.join(runtimeHome, ".codex", "config.toml"));
66
68
  }
67
69
  /** 获取 OpenCode Skills 目录 */
68
70
  export function getOpencodeSkillsDir(runtimeHome) {
69
- return String(process.env.AWS_RUNTIME_OPENCODE_SKILLS_DIR || '').trim()
70
- || path.join(runtimeHome, '.opencode', 'skills');
71
+ return (String(process.env.AWS_RUNTIME_OPENCODE_SKILLS_DIR || "").trim() ||
72
+ path.join(runtimeHome, ".opencode", "skills"));
71
73
  }
72
74
  /**
73
75
  * 孤儿进程监控配置
@@ -75,9 +77,10 @@ export function getOpencodeSkillsDir(runtimeHome) {
75
77
  /** 定期扫描间隔(毫秒),默认 30 秒 */
76
78
  export const ORPHAN_SCAN_INTERVAL = Number(process.env.AWS_ORPHAN_SCAN_INTERVAL || 30000);
77
79
  /** 自动清理模式: 'auto' | 'alert' | 'manual' */
78
- export const ORPHAN_AUTO_CLEAN_MODE = (process.env.AWS_ORPHAN_AUTO_CLEAN_MODE || 'alert');
80
+ export const ORPHAN_AUTO_CLEAN_MODE = (process.env.AWS_ORPHAN_AUTO_CLEAN_MODE ||
81
+ "alert");
79
82
  /** 是否启用定期扫描 */
80
- export const ORPHAN_SCAN_ENABLED = process.env.AWS_ORPHAN_SCAN_ENABLED !== 'false';
83
+ export const ORPHAN_SCAN_ENABLED = process.env.AWS_ORPHAN_SCAN_ENABLED !== "false";
81
84
  /** 健康检测:进程最大无响应时间(毫秒),默认 5 分钟 */
82
85
  export const ORPHAN_UNRESPONSIVE_TIMEOUT = Number(process.env.AWS_ORPHAN_UNRESPONSIVE_TIMEOUT || 300000);
83
86
  /** 健康检测:CPU 使用率阈值(百分比),超过则认为卡死,默认 100% */
@@ -88,21 +91,21 @@ export const ORPHAN_MEMORY_THRESHOLD = Number(process.env.AWS_ORPHAN_MEMORY_THRE
88
91
  * 自动注册配置
89
92
  */
90
93
  /** 是否启用自动注册 */
91
- export const AUTO_REGISTER_ENABLED = process.env.AWS_AUTO_REGISTER === 'true';
94
+ export const AUTO_REGISTER_ENABLED = process.env.AWS_AUTO_REGISTER === "true";
92
95
  /** 租户 ID */
93
- export const AUTO_REGISTER_TENANT_ID = process.env.AWS_TENANT_ID || '';
96
+ export const AUTO_REGISTER_TENANT_ID = process.env.AWS_TENANT_ID || "";
94
97
  /** 用户 API Key */
95
- export const AUTO_REGISTER_USER_KEY = process.env.AWS_USER_KEY || '';
98
+ export const AUTO_REGISTER_USER_KEY = process.env.AWS_USER_KEY || "";
96
99
  /** 实例名称(默认使用主机名) */
97
- export const AUTO_REGISTER_INSTANCE_NAME = process.env.AWS_INSTANCE_NAME || '';
100
+ export const AUTO_REGISTER_INSTANCE_NAME = process.env.AWS_INSTANCE_NAME || "";
98
101
  /** 项目名称 */
99
- export const AUTO_REGISTER_PROJECT_NAME = process.env.AWS_PROJECT_NAME || '';
102
+ export const AUTO_REGISTER_PROJECT_NAME = process.env.AWS_PROJECT_NAME || "";
100
103
  /** 角色名称 */
101
- export const AUTO_REGISTER_ROLE_NAME = process.env.AWS_ROLE_NAME || '';
104
+ export const AUTO_REGISTER_ROLE_NAME = process.env.AWS_ROLE_NAME || "";
102
105
  /** 工作空间路径 */
103
- export const AUTO_REGISTER_WORKSPACE_PATH = process.env.AWS_WORKSPACE_PATH || '';
104
- /** 虚拟 IPEasyTier */
105
- export const AUTO_REGISTER_VIRTUAL_IP = process.env.AWS_VIRTUAL_IP || '';
106
+ export const AUTO_REGISTER_WORKSPACE_PATH = process.env.AWS_WORKSPACE_PATH || "";
107
+ /** 注册 IP(优先),兼容 EasyTier 虚拟 IP */
108
+ export const AUTO_REGISTER_VIRTUAL_IP = process.env.AWS_REGISTER_IP || process.env.AWS_VIRTUAL_IP || "";
106
109
  /** 注册重试次数 */
107
110
  export const AUTO_REGISTER_MAX_RETRIES = Number(process.env.AWS_REGISTER_MAX_RETRIES || 5);
108
111
  /** 注册重试间隔(毫秒) */
package/dist/index.js CHANGED
@@ -21,18 +21,20 @@ import { gitRouter } from "./routes/git.js";
21
21
  import { fileBrowserRouter } from "./routes/file-browser.js";
22
22
  import { aiSourcesRouter } from "./routes/ai-sources.js";
23
23
  import { processesRouter } from "./routes/processes.js";
24
+ import { runtimeBindingRouter, logRuntimeBindingStartupState, } from "./routes/runtime-binding.js";
24
25
  import { sessions, flushSessionOutput } from "./services/session-output.js";
25
- import { loadPersistedSessions, savePersistedSessions } from "./services/terminal-persistence.js";
26
+ import { loadPersistedSessions, savePersistedSessions, } from "./services/terminal-persistence.js";
26
27
  import { detectOrphanProcesses } from "./services/process-detector.js";
27
- import { startOrphanMonitor, stopOrphanMonitor } from "./services/orphan-monitor.js";
28
+ import { startOrphanMonitor, stopOrphanMonitor, } from "./services/orphan-monitor.js";
28
29
  import { getAgentProcessManager } from "./services/agent-process-manager.js";
29
30
  import { adapterRegistry } from "./adapter/index.js";
30
31
  import { logger } from "./utils/logger.js";
31
- import { autoRegister, unregister, bridgeRestartCleanup } from "./services/auto-register.js";
32
+ import { autoRegister, unregister, bridgeRestartCleanup, } from "./services/auto-register.js";
32
33
  import { ensureAwsClientAgentMcpReleased } from "./services/aws-client-agent-mcp.js";
33
34
  // 验证生产环境配置
34
35
  validateProductionToken();
35
36
  ensureAwsClientAgentMcpReleased();
37
+ logRuntimeBindingStartupState();
36
38
  // ★ 启动时自动注册
37
39
  async function performAutoRegister() {
38
40
  try {
@@ -71,7 +73,7 @@ performAutoRegister()
71
73
  // 这样可以确保 bridge 重启后,之前启动的 Agent 显示为"离线+停止"状态
72
74
  return performBridgeRestartCleanup();
73
75
  })
74
- .catch(err => {
76
+ .catch((err) => {
75
77
  logger.warn("[runtime-bridge] 自动注册或清理出错:", err);
76
78
  });
77
79
  // ★★★ Bridge 重启清理
@@ -109,13 +111,13 @@ async function rebuildProcessRegistry() {
109
111
  return;
110
112
  }
111
113
  const manager = getAgentProcessManager();
112
- await manager.rebuildRegistry(persistedSessions.map(s => ({
114
+ await manager.rebuildRegistry(persistedSessions.map((s) => ({
113
115
  agentId: s.agentId,
114
116
  sessionId: s.sessionId,
115
117
  pid: s.pid,
116
118
  workspacePath: s.workspacePath,
117
119
  command: s.command,
118
- mode: s.mode || 'pty',
120
+ mode: s.mode || "pty",
119
121
  })));
120
122
  logger.info(`[runtime-bridge] 进程注册表重建完成: ${persistedSessions.length} 条记录`);
121
123
  }
@@ -124,7 +126,7 @@ async function rebuildProcessRegistry() {
124
126
  logger.warn("[runtime-bridge] 进程注册表重建失败:", err.message);
125
127
  }
126
128
  }
127
- rebuildProcessRegistry().catch(err => {
129
+ rebuildProcessRegistry().catch((err) => {
128
130
  logger.warn("[runtime-bridge] 进程注册表重建出错:", err);
129
131
  });
130
132
  // ★ 启动时检测孤儿进程并自动清理
@@ -134,13 +136,13 @@ rebuildProcessRegistry().catch(err => {
134
136
  async function detectOrphansOnStartup() {
135
137
  try {
136
138
  // ★★★ 检查是否存在保留会话标记文件
137
- const fs = await import('fs/promises');
138
- const path = await import('path');
139
- const os = await import('os');
140
- const restartFlagFile = path.join(os.tmpdir(), 'agentswork-runtime-bridge', '.preserve-sessions');
139
+ const fs = await import("fs/promises");
140
+ const path = await import("path");
141
+ const os = await import("os");
142
+ const restartFlagFile = path.join(os.tmpdir(), "agentswork-runtime-bridge", ".preserve-sessions");
141
143
  let preserveSessions = false;
142
144
  try {
143
- const flagContent = await fs.readFile(restartFlagFile, 'utf-8');
145
+ const flagContent = await fs.readFile(restartFlagFile, "utf-8");
144
146
  const flag = JSON.parse(flagContent);
145
147
  preserveSessions = flag.preserveSessions === true;
146
148
  // 读取后删除标记文件
@@ -160,20 +162,20 @@ async function detectOrphansOnStartup() {
160
162
  logger.info(`[runtime-bridge] ★ aws-mcp-server 重启恢复场景,保留 ${persistedSessions.length} 个会话`);
161
163
  // 重建进程注册表
162
164
  const manager = getAgentProcessManager();
163
- await manager.rebuildRegistry(persistedSessions.map(s => ({
165
+ await manager.rebuildRegistry(persistedSessions.map((s) => ({
164
166
  agentId: s.agentId,
165
167
  sessionId: s.sessionId,
166
168
  pid: s.pid,
167
169
  workspacePath: s.workspacePath,
168
170
  command: s.command,
169
- mode: s.mode || 'pty',
171
+ mode: s.mode || "pty",
170
172
  })));
171
173
  return;
172
174
  }
173
175
  logger.info(`[runtime-bridge] ★ Bridge 重启,检测 ${persistedSessions.length} 个持久化会话的孤儿进程...`);
174
176
  // ★★★ 修复:不再区分 SDK 和 PTY 模式,全部清理
175
177
  // bridge 重启 = 整个运行环境重启,所有 agent 都应该被终止
176
- const orphans = detectOrphanProcesses(persistedSessions.map(s => ({
178
+ const orphans = detectOrphanProcesses(persistedSessions.map((s) => ({
177
179
  agentId: s.agentId,
178
180
  pid: s.pid,
179
181
  workspacePath: s.workspacePath,
@@ -190,13 +192,16 @@ async function detectOrphansOnStartup() {
190
192
  try {
191
193
  if (orphan.process.pid) {
192
194
  // 使用跨平台方式终止进程
193
- if (process.platform === 'win32') {
194
- const { execSync } = await import('child_process');
195
- execSync(`taskkill /PID ${orphan.process.pid} /T /F`, { encoding: 'utf8', timeout: 3000 });
195
+ if (process.platform === "win32") {
196
+ const { execSync } = await import("child_process");
197
+ execSync(`taskkill /PID ${orphan.process.pid} /T /F`, {
198
+ encoding: "utf8",
199
+ timeout: 3000,
200
+ });
196
201
  logger.info(`[runtime-bridge] 已自动终止孤儿进程: agentId=${orphan.agentId}, PID=${orphan.process.pid}`);
197
202
  }
198
203
  else {
199
- process.kill(orphan.process.pid, 'SIGKILL');
204
+ process.kill(orphan.process.pid, "SIGKILL");
200
205
  logger.info(`[runtime-bridge] 已自动终止孤儿进程: agentId=${orphan.agentId}, PID=${orphan.process.pid}`);
201
206
  }
202
207
  }
@@ -207,7 +212,7 @@ async function detectOrphansOnStartup() {
207
212
  }
208
213
  }
209
214
  // 等待进程完全终止
210
- await new Promise(resolve => setTimeout(resolve, 1000));
215
+ await new Promise((resolve) => setTimeout(resolve, 1000));
211
216
  }
212
217
  // ★★★ 修复:bridge 重启后清空所有持久化会话
213
218
  // 不再保留任何会话,因为所有 agent 都应该被清理
@@ -220,11 +225,11 @@ async function detectOrphansOnStartup() {
220
225
  }
221
226
  }
222
227
  // 执行孤儿进程检测
223
- detectOrphansOnStartup().catch(err => {
228
+ detectOrphansOnStartup().catch((err) => {
224
229
  logger.warn("[runtime-bridge] 孤儿进程检测出错:", err);
225
230
  });
226
231
  // ★ 启动孤儿进程定期监控
227
- startOrphanMonitor().catch(err => {
232
+ startOrphanMonitor().catch((err) => {
228
233
  logger.warn("[runtime-bridge] 启动孤儿进程监控失败:", err);
229
234
  });
230
235
  // 创建 Express 应用
@@ -241,6 +246,7 @@ app.use(cors({
241
246
  }));
242
247
  app.use(express.json({ limit: "1mb" }));
243
248
  // 注册路由
249
+ app.use("/runtime", runtimeBindingRouter);
244
250
  app.use("/runtime", propertiesRouter);
245
251
  app.use("/runtime", ymlRouter);
246
252
  app.use("/runtime", instanceRouter);
@@ -345,7 +351,7 @@ async function gracefulShutdown(signal, server, preserveSessions = false) {
345
351
  const providerId = entry.providerId || "claude-code";
346
352
  const adapter = adapterRegistry.get(providerId);
347
353
  // 尝试获取 PID 用于日志
348
- let pidInfo = '';
354
+ let pidInfo = "";
349
355
  try {
350
356
  const pid = adapter.getSessionPid?.(sessionId);
351
357
  if (pid) {
@@ -371,14 +377,14 @@ async function gracefulShutdown(signal, server, preserveSessions = false) {
371
377
  stopOrphanMonitor();
372
378
  // 4. ★ 验证所有进程已终止(等待一小段时间让进程完全退出)
373
379
  logger.info("[runtime-bridge] 等待进程完全终止...");
374
- await new Promise(resolve => setTimeout(resolve, 1000));
380
+ await new Promise((resolve) => setTimeout(resolve, 1000));
375
381
  // 5. 检查是否还有残留进程,如果有则强制终止
376
382
  try {
377
383
  const { detectOrphanProcesses, terminateProcessTree } = await import("./services/process-detector.js");
378
384
  const { loadPersistedSessions } = await import("./services/terminal-persistence.js");
379
385
  const persistedSessions = await loadPersistedSessions();
380
386
  if (persistedSessions.length > 0) {
381
- const orphans = detectOrphanProcesses(persistedSessions.map(s => ({
387
+ const orphans = detectOrphanProcesses(persistedSessions.map((s) => ({
382
388
  agentId: s.agentId,
383
389
  pid: s.pid,
384
390
  workspacePath: s.workspacePath,
@@ -455,11 +461,11 @@ const server = startServer();
455
461
  *
456
462
  * 信号处理器必须使用同步 I/O,因为异步操作在信号上下文中不可靠。
457
463
  */
458
- const RESTART_FLAG_FILE = path.join(os.tmpdir(), 'agentswork-runtime-bridge', '.preserve-sessions');
464
+ const RESTART_FLAG_FILE = path.join(os.tmpdir(), "agentswork-runtime-bridge", ".preserve-sessions");
459
465
  function checkPreserveFlagSync() {
460
466
  try {
461
467
  if (existsSync(RESTART_FLAG_FILE)) {
462
- const content = readFileSync(RESTART_FLAG_FILE, 'utf-8');
468
+ const content = readFileSync(RESTART_FLAG_FILE, "utf-8");
463
469
  const flag = JSON.parse(content);
464
470
  return flag.preserveSessions === true;
465
471
  }
@@ -3,10 +3,12 @@
3
3
  *
4
4
  * 验证请求头中的 X-Runtime-Token 是否与配置的 runtimeToken 匹配
5
5
  */
6
- import type { Request, Response, NextFunction } from 'express';
6
+ import type { Request, Response, NextFunction } from "express";
7
7
  /**
8
8
  * Token 验证中间件
9
- * 检查请求头中的 X-Runtime-Token 是否有效
9
+ * 检查请求头中的 X-Runtime-Token 或 Authorization: Bearer 是否有效。
10
+ *
11
+ * 优先支持面板/服务器绑定时写入的动态令牌;仍兼容环境变量配置的静态令牌。
10
12
  */
11
13
  export declare function validateToken(req: Request, res: Response, next: NextFunction): void;
12
14
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG/D;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAOnF"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA8B/D;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,IAAI,CAqBN"}
@@ -3,16 +3,46 @@
3
3
  *
4
4
  * 验证请求头中的 X-Runtime-Token 是否与配置的 runtimeToken 匹配
5
5
  */
6
- import { runtimeToken } from '../config.js';
6
+ import { allowDefaultRuntimeToken, runtimeToken } from "../config.js";
7
+ import { hasRuntimeBinding, validateRuntimeBindingToken, } from "../services/runtime-binding.js";
8
+ function extractRuntimeToken(req) {
9
+ const headerToken = req.header("X-Runtime-Token");
10
+ if (headerToken) {
11
+ return headerToken.trim();
12
+ }
13
+ const authorization = req.header("Authorization") || "";
14
+ const bearerMatch = authorization.match(/^Bearer\s+(.+)$/i);
15
+ return bearerMatch?.[1]?.trim() || "";
16
+ }
17
+ function validateConfiguredToken(token) {
18
+ if (!token) {
19
+ return false;
20
+ }
21
+ if (process.env.AWS_RUNTIME_CALLBACK_TOKEN) {
22
+ return token === runtimeToken;
23
+ }
24
+ return allowDefaultRuntimeToken && token === runtimeToken;
25
+ }
7
26
  /**
8
27
  * Token 验证中间件
9
- * 检查请求头中的 X-Runtime-Token 是否有效
28
+ * 检查请求头中的 X-Runtime-Token 或 Authorization: Bearer 是否有效。
29
+ *
30
+ * 优先支持面板/服务器绑定时写入的动态令牌;仍兼容环境变量配置的静态令牌。
10
31
  */
11
32
  export function validateToken(req, res, next) {
12
- const token = req.header('X-Runtime-Token');
13
- if (token !== runtimeToken) {
14
- res.status(401).json({ error: 'unauthorized' });
33
+ const token = extractRuntimeToken(req);
34
+ if (validateRuntimeBindingToken(token) || validateConfiguredToken(token)) {
35
+ next();
36
+ return;
37
+ }
38
+ if (!hasRuntimeBinding() &&
39
+ !process.env.AWS_RUNTIME_CALLBACK_TOKEN &&
40
+ !allowDefaultRuntimeToken) {
41
+ res.status(401).json({
42
+ error: "runtime_bridge_unpaired",
43
+ message: "Runtime bridge is not paired. Add this instance from the panel first.",
44
+ });
15
45
  return;
16
46
  }
17
- next();
47
+ res.status(401).json({ error: "unauthorized" });
18
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AAeA,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAE5G;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AAwBA,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,IAAI,CAEN;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC"}
@@ -1,12 +1,12 @@
1
- import { Router } from 'express';
2
- import axios from 'axios';
3
- import { validateToken } from '../middleware/auth.js';
4
- import { schedulerBaseUrl, runtimeToken } from '../config.js';
5
- import { loadInstanceState, saveInstanceState } from '../services/instance-state.js';
6
- import { initInstance } from '../services/instance-init-service.js';
7
- import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS } from '../services/tool-installer.js';
8
- import { createLogger } from '../utils/logger.js';
9
- const log = createLogger('instance');
1
+ import { Router } from "express";
2
+ import axios from "axios";
3
+ import { validateToken } from "../middleware/auth.js";
4
+ import { schedulerBaseUrl, runtimeToken } from "../config.js";
5
+ import { loadInstanceState, saveInstanceState, } from "../services/instance-state.js";
6
+ import { initInstance } from "../services/instance-init-service.js";
7
+ import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS, } from "../services/tool-installer.js";
8
+ import { createLogger } from "../utils/logger.js";
9
+ const log = createLogger("instance");
10
10
  // ★★★ 导出 gracefulShutdown 的触发函数
11
11
  // 用于 aws-mcp-server 通知 bridge 保留会话状态关闭
12
12
  let gracefulShutdownFn = null;
@@ -14,12 +14,18 @@ export function setGracefulShutdownFn(fn) {
14
14
  gracefulShutdownFn = fn;
15
15
  }
16
16
  export const instanceRouter = Router();
17
- instanceRouter.get('/ping', validateToken, async (_req, res) => {
17
+ instanceRouter.get("/healthz", (_req, res) => {
18
+ res.json({
19
+ ok: true,
20
+ runtimeBridge: "healthy",
21
+ });
22
+ });
23
+ instanceRouter.get("/ping", validateToken, async (_req, res) => {
18
24
  try {
19
- const schedulerResponse = await axios.get(`${schedulerBaseUrl}/api/runtime/ping`, { headers: { 'X-Runtime-Token': runtimeToken } });
25
+ const schedulerResponse = await axios.get(`${schedulerBaseUrl}/api/runtime/ping`, { headers: { "X-Runtime-Token": runtimeToken } });
20
26
  res.json({
21
27
  ok: true,
22
- runtimeBridge: 'healthy',
28
+ runtimeBridge: "healthy",
23
29
  schedulerReachable: schedulerResponse.status >= 200 && schedulerResponse.status < 300,
24
30
  schedulerBaseUrl,
25
31
  });
@@ -28,15 +34,15 @@ instanceRouter.get('/ping', validateToken, async (_req, res) => {
28
34
  const err = error;
29
35
  res.status(502).json({
30
36
  ok: false,
31
- error: err?.message || 'scheduler ping failed',
37
+ error: err?.message || "scheduler ping failed",
32
38
  schedulerBaseUrl,
33
39
  });
34
40
  }
35
41
  });
36
- instanceRouter.post('/init-instance', validateToken, async (req, res) => {
42
+ instanceRouter.post("/init-instance", validateToken, async (req, res) => {
37
43
  const { agentId, workspacePath, skillEnabled, mcpEnabled, ccSwitchEnabledTools, skillPackage, skillPackages, mcpServers, } = req.body || {};
38
44
  if (!agentId || !workspacePath) {
39
- res.status(400).json({ error: 'agentId and workspacePath are required' });
45
+ res.status(400).json({ error: "agentId and workspacePath are required" });
40
46
  return;
41
47
  }
42
48
  try {
@@ -53,39 +59,51 @@ instanceRouter.post('/init-instance', validateToken, async (req, res) => {
53
59
  catch (error) {
54
60
  const err = error;
55
61
  res.status(400).json({
56
- error: err?.message || 'initialize instance failed',
62
+ error: err?.message || "initialize instance failed",
57
63
  });
58
64
  }
59
65
  });
60
- instanceRouter.post('/cc-switch/state', validateToken, async (req, res) => {
66
+ instanceRouter.post("/cc-switch/state", validateToken, async (req, res) => {
61
67
  const { agentId } = req.body || {};
62
68
  if (!agentId) {
63
- res.status(400).json({ error: 'agentId is required' });
69
+ res.status(400).json({ error: "agentId is required" });
64
70
  return;
65
71
  }
66
72
  try {
67
73
  const state = await loadInstanceState(agentId);
68
74
  const toolStatus = await detectToolStatuses(state.enabledTools || []);
69
- res.json({ ok: true, agentId: String(agentId), state: { ...state, toolStatus } });
75
+ res.json({
76
+ ok: true,
77
+ agentId: String(agentId),
78
+ state: { ...state, toolStatus },
79
+ });
70
80
  }
71
81
  catch (error) {
72
82
  const err = error;
73
- res.status(400).json({ error: err?.message || 'load state failed' });
83
+ res.status(400).json({ error: err?.message || "load state failed" });
74
84
  }
75
85
  });
76
- instanceRouter.post('/cc-switch/install-tools', validateToken, async (req, res) => {
86
+ instanceRouter.post("/cc-switch/install-tools", validateToken, async (req, res) => {
77
87
  const { agentId, tools } = req.body || {};
78
88
  if (!agentId) {
79
- res.status(400).json({ error: 'agentId is required' });
89
+ res.status(400).json({ error: "agentId is required" });
80
90
  return;
81
91
  }
82
92
  const requestedTools = Array.isArray(tools)
83
- ? tools.map((tool) => String(tool || '').trim().toLowerCase()).filter(Boolean)
93
+ ? tools
94
+ .map((tool) => String(tool || "")
95
+ .trim()
96
+ .toLowerCase())
97
+ .filter(Boolean)
84
98
  : [];
85
99
  const supportedInstallableTools = new Set(SUPPORTED_INSTALLABLE_TOOLS);
86
100
  const installableTools = requestedTools.filter((tool) => supportedInstallableTools.has(tool));
87
101
  if (installableTools.length === 0) {
88
- res.status(400).json({ error: `tools must include ${SUPPORTED_INSTALLABLE_TOOLS.join(', ')}` });
102
+ res
103
+ .status(400)
104
+ .json({
105
+ error: `tools must include ${SUPPORTED_INSTALLABLE_TOOLS.join(", ")}`,
106
+ });
89
107
  return;
90
108
  }
91
109
  try {
@@ -111,7 +129,7 @@ instanceRouter.post('/cc-switch/install-tools', validateToken, async (req, res)
111
129
  }
112
130
  catch (error) {
113
131
  const err = error;
114
- res.status(400).json({ error: err?.message || 'install tools failed' });
132
+ res.status(400).json({ error: err?.message || "install tools failed" });
115
133
  }
116
134
  });
117
135
  /**
@@ -126,22 +144,22 @@ instanceRouter.post('/cc-switch/install-tools', validateToken, async (req, res)
126
144
  * - preserveSessions: true = 保留会话(aws-mcp-server 重启)
127
145
  * false = 清理会话(bridge 自己关闭)
128
146
  */
129
- instanceRouter.post('/prepare-restart', validateToken, async (req, res) => {
147
+ instanceRouter.post("/prepare-restart", validateToken, async (req, res) => {
130
148
  const { preserveSessions = true } = req.body || {};
131
149
  try {
132
150
  log.info(`[prepare-restart] 收到准备重启通知,preserveSessions=${preserveSessions}`);
133
151
  // 记录重启模式到文件,供下次启动时判断
134
- const fs = await import('fs/promises');
135
- const path = await import('path');
136
- const os = await import('os');
137
- const restartFlagFile = path.join(os.tmpdir(), 'agentswork-runtime-bridge', '.preserve-sessions');
152
+ const fs = await import("fs/promises");
153
+ const path = await import("path");
154
+ const os = await import("os");
155
+ const restartFlagFile = path.join(os.tmpdir(), "agentswork-runtime-bridge", ".preserve-sessions");
138
156
  if (preserveSessions) {
139
157
  // 创建标记文件,表示 aws-mcp-server 重启,需要保留会话
140
158
  await fs.mkdir(path.dirname(restartFlagFile), { recursive: true });
141
159
  await fs.writeFile(restartFlagFile, JSON.stringify({
142
160
  preserveSessions: true,
143
161
  timestamp: new Date().toISOString(),
144
- }), 'utf-8');
162
+ }), "utf-8");
145
163
  log.info(`[prepare-restart] 已创建保留会话标记文件: ${restartFlagFile}`);
146
164
  }
147
165
  else {
@@ -158,13 +176,13 @@ instanceRouter.post('/prepare-restart', validateToken, async (req, res) => {
158
176
  ok: true,
159
177
  preserveSessions,
160
178
  message: preserveSessions
161
- ? 'Bridge will preserve sessions for aws-mcp-server restart'
162
- : 'Bridge will clean up sessions on shutdown',
179
+ ? "Bridge will preserve sessions for aws-mcp-server restart"
180
+ : "Bridge will clean up sessions on shutdown",
163
181
  });
164
182
  }
165
183
  catch (error) {
166
184
  const err = error;
167
- log.error('[prepare-restart] Error:', err);
185
+ log.error("[prepare-restart] Error:", err);
168
186
  res.status(500).json({ error: err.message });
169
187
  }
170
188
  });
@@ -0,0 +1,3 @@
1
+ export declare const runtimeBindingRouter: import("express-serve-static-core").Router;
2
+ export declare function logRuntimeBindingStartupState(): void;
3
+ //# sourceMappingURL=runtime-binding.d.ts.map