aws-runtime-bridge 1.9.2 → 1.9.6

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.
@@ -6,6 +6,7 @@ export declare function buildConnectionCheckResponse(connectionKeyMd5: string):
6
6
  body: {
7
7
  ok: boolean;
8
8
  runtimeBridge?: "healthy";
9
+ version: string;
9
10
  connectionKeyMatched: boolean;
10
11
  connectionKeyRequired: boolean;
11
12
  error?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqBrD,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;AAsJvC,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACJ,EAAE,EAAE,OAAO,CAAC;QACZ,aAAa,CAAC,EAAE,SAAS,CAAC;QAC1B,oBAAoB,EAAE,OAAO,CAAC;QAC9B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAqCA;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,iCAAiC;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,8BAA8B,CAAC;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,KAAK,EACZ,0BAA0B,EAAE,MAAM,GACjC,4BAA4B,CAiB9B;AAoDD;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,MAAM,EAAE,GACnB,iCAAiC,CAUnC;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAaR;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC,CAY3F"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqBrD,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;AAsJvC,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACJ,EAAE,EAAE,OAAO,CAAC;QACZ,aAAa,CAAC,EAAE,SAAS,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,oBAAoB,EAAE,OAAO,CAAC;QAC9B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAyCA;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,iCAAiC;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,8BAA8B,CAAC;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,KAAK,EACZ,0BAA0B,EAAE,MAAM,GACjC,4BAA4B,CAiB9B;AAoDD;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,MAAM,EAAE,GACnB,iCAAiC,CAUnC;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAaR;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC,CAY3F"}
@@ -2,8 +2,8 @@ import { Router } from "express";
2
2
  import axios from "axios";
3
3
  import { spawn } from "node:child_process";
4
4
  import { createHash, timingSafeEqual } from "node:crypto";
5
- import { resolveSchedulerBaseUrlFrom } from "../config.js";
6
- import { validateToken } from "../middleware/auth.js";
5
+ import { getBridgeVersion, resolveSchedulerBaseUrlFrom } from "../config.js";
6
+ import { validateToken, validateTokenOrPanelSession } from "../middleware/auth.js";
7
7
  import { getConfiguredConnectionKeys, requestRuntimeAccessTokenRefreshForServer } from "../services/auto-register.js";
8
8
  import { discoverCcSwitchConfiguredItems, loadCcSwitchSdk } from "../services/cc-switch-sdk.js";
9
9
  import { initInstance, syncLegacyStateFromSdk } from "../services/instance-init-service.js";
@@ -143,12 +143,14 @@ function hasMatchingConnectionKeyDigest(connectionKeyMd5) {
143
143
  export function buildConnectionCheckResponse(connectionKeyMd5) {
144
144
  const configuredConnectionKeys = getConfiguredConnectionKeys();
145
145
  const connectionKeyRequired = configuredConnectionKeys.length > 0;
146
+ const version = getBridgeVersion();
146
147
  if (!connectionKeyRequired) {
147
148
  return {
148
149
  status: 200,
149
150
  body: {
150
151
  ok: true,
151
152
  runtimeBridge: "healthy",
153
+ version,
152
154
  connectionKeyMatched: true,
153
155
  connectionKeyRequired: false,
154
156
  },
@@ -160,6 +162,7 @@ export function buildConnectionCheckResponse(connectionKeyMd5) {
160
162
  body: {
161
163
  ok: true,
162
164
  runtimeBridge: "healthy",
165
+ version,
163
166
  connectionKeyMatched: true,
164
167
  connectionKeyRequired: true,
165
168
  },
@@ -170,6 +173,7 @@ export function buildConnectionCheckResponse(connectionKeyMd5) {
170
173
  body: {
171
174
  ok: false,
172
175
  error: "connection_key_mismatch",
176
+ version,
173
177
  connectionKeyMatched: false,
174
178
  connectionKeyRequired: true,
175
179
  },
@@ -279,6 +283,50 @@ export function summarizeToolStatus(toolStatus) {
279
283
  },
280
284
  ]));
281
285
  }
286
+ /**
287
+ * GET /runtime/check-update
288
+ *
289
+ * 查询 npm registry 获取 aws-runtime-bridge 的最新版本并和当前版本比较。
290
+ * 使用 5 分钟内存缓存减少对 npm registry 的请求。
291
+ */
292
+ let updateCheckCache = null;
293
+ const UPDATE_CHECK_CACHE_TTL = 300_000; // 5 分钟
294
+ async function fetchLatestNpmVersion() {
295
+ const response = await axios.get("https://registry.npmjs.org/aws-runtime-bridge/latest", { timeout: 10_000, responseType: "json" });
296
+ const data = response.data;
297
+ return data?.version || "unknown";
298
+ }
299
+ instanceRouter.get("/check-update", async (_req, res) => {
300
+ try {
301
+ let latestVersion;
302
+ if (updateCheckCache && Date.now() - updateCheckCache.ts < UPDATE_CHECK_CACHE_TTL) {
303
+ latestVersion = updateCheckCache.latestVersion;
304
+ }
305
+ else {
306
+ latestVersion = await fetchLatestNpmVersion();
307
+ updateCheckCache = { latestVersion, ts: Date.now() };
308
+ }
309
+ const currentVersion = getBridgeVersion();
310
+ const hasUpdate = latestVersion !== "unknown" && currentVersion !== "unknown" && latestVersion !== currentVersion;
311
+ res.json({
312
+ ok: true,
313
+ hasUpdate,
314
+ currentVersion,
315
+ latestVersion,
316
+ });
317
+ }
318
+ catch (error) {
319
+ const err = error;
320
+ log.warn("[check-update] Failed to query npm registry:", err.message);
321
+ res.json({
322
+ ok: false,
323
+ hasUpdate: false,
324
+ currentVersion: getBridgeVersion(),
325
+ latestVersion: null,
326
+ error: err.message || "failed to check for updates",
327
+ });
328
+ }
329
+ });
282
330
  instanceRouter.get("/healthz", (_req, res) => {
283
331
  res.json({
284
332
  ok: true,
@@ -309,6 +357,7 @@ instanceRouter.get("/ping", validateToken, async (req, res) => {
309
357
  res.json({
310
358
  ok: true,
311
359
  runtimeBridge: "healthy",
360
+ version: getBridgeVersion(),
312
361
  schedulerReachable: schedulerPing.schedulerReachable,
313
362
  tokenRefreshed: schedulerPing.tokenRefreshed,
314
363
  schedulerBaseUrl,
@@ -612,7 +661,7 @@ instanceRouter.post("/prepare-restart", validateToken, async (req, res) => {
612
661
  * 通过当前 Bridge 进程执行全局 npm 包更新,等价于 CLI 的
613
662
  * `awsb update`,用于面板上的“更新实例 Bridge”。
614
663
  */
615
- instanceRouter.post("/update-bridge", validateToken, async (req, res) => {
664
+ instanceRouter.post("/update-bridge", validateTokenOrPanelSession, async (req, res) => {
616
665
  const command = resolveNpmExecutable();
617
666
  const args = ["install", "-g", "aws-runtime-bridge@latest"];
618
667
  const requestedByUserId = normalizeOptionalText(req.body?.requestedByUserId);
@@ -649,7 +698,7 @@ instanceRouter.post("/update-bridge", validateToken, async (req, res) => {
649
698
  * 写入保留会话标记后优雅关闭当前 Bridge 进程。真正的重新拉起由
650
699
  * systemd、PM2、Docker 外部守护或用户手动启动完成。
651
700
  */
652
- instanceRouter.post("/restart-bridge", validateToken, async (req, res) => {
701
+ instanceRouter.post("/restart-bridge", validateTokenOrPanelSession, async (req, res) => {
653
702
  const { preserveSessions = true } = req.body || {};
654
703
  const requestedByUserId = normalizeOptionalText(req.body?.requestedByUserId);
655
704
  try {
@@ -216,6 +216,7 @@ describe('instance route validation', () => {
216
216
  body: {
217
217
  ok: true,
218
218
  runtimeBridge: 'healthy',
219
+ version: expect.any(String),
219
220
  connectionKeyMatched: true,
220
221
  connectionKeyRequired: false,
221
222
  },
@@ -233,6 +234,7 @@ describe('instance route validation', () => {
233
234
  body: {
234
235
  ok: false,
235
236
  error: 'connection_key_mismatch',
237
+ version: expect.any(String),
236
238
  connectionKeyMatched: false,
237
239
  connectionKeyRequired: true,
238
240
  },
@@ -287,6 +289,7 @@ describe('instance route validation', () => {
287
289
  expect(json).toHaveBeenCalledWith({
288
290
  ok: true,
289
291
  runtimeBridge: 'healthy',
292
+ version: expect.any(String),
290
293
  schedulerReachable: true,
291
294
  tokenRefreshed: true,
292
295
  schedulerBaseUrl: 'http://localhost:8080',
@@ -314,6 +317,7 @@ describe('instance route validation', () => {
314
317
  expect(json).toHaveBeenCalledWith({
315
318
  ok: true,
316
319
  runtimeBridge: 'healthy',
320
+ version: expect.any(String),
317
321
  schedulerReachable: true,
318
322
  tokenRefreshed: false,
319
323
  schedulerBaseUrl: 'http://server.local:7380',
@@ -28,7 +28,7 @@ processesRouter.get('/processes', validateToken, async (_req, res) => {
28
28
  .filter(r => r.pid > 0)
29
29
  .map(r => ({ agentId: r.agentId, pid: r.pid }));
30
30
  // 全局发现:扫描全机进程 + 对比注册表
31
- const discovered = discoverAllProcessesFull(registryExport);
31
+ const discovered = await discoverAllProcessesFull(registryExport);
32
32
  // 补充 sessionId 信息
33
33
  const enriched = discovered.map(d => {
34
34
  const record = registryRecords.find(r => r.pid === d.pid || r.agentId === d.agentId);
@@ -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;AAE7C,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,wBAAwB,GAC9B,yBAAyB,CAM3B;AAgPD,wBAAgB,6BAA6B,IAAI,IAAI,CAapD"}
1
+ {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-binding.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,oBAAoB,4CAAW,CAAC;AAE7C,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,wBAAwB,GAC9B,yBAAyB,CAM3B;AAqPD,wBAAgB,6BAA6B,IAAI,IAAI,CAapD"}
@@ -1,4 +1,6 @@
1
+ import axios from "axios";
1
2
  import { Router } from "express";
3
+ import { getConfiguredConnectionKeys } from "../services/auto-register.js";
2
4
  import { validateToken } from "../middleware/auth.js";
3
5
  import { claimMcpLaunchBinding, getLatestAgentToken, getMcpLaunchQueueSize } from "../services/mcp-launch-binding-queue.js";
4
6
  import { clearRuntimeBinding, getRuntimeAccessToken, getRuntimeBindingPublicState, getRuntimePairingCode, hasRuntimeBinding, saveRuntimeBinding, validateRuntimeBindingToken, validateRuntimePairingCode, } from "../services/runtime-binding.js";
@@ -120,6 +122,10 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
120
122
  });
121
123
  return;
122
124
  }
125
+ // ★ 通知调度中心:McpNode 已领取启动绑定(fire-and-forget,不阻塞响应)
126
+ notifySchedulerBindingClaimed(claimed.agentId, claimed.schedulerBaseUrl).catch(() => {
127
+ // 通知失败不影响 MCP node 的 claim 响应
128
+ });
123
129
  res.json({
124
130
  ok: true,
125
131
  agentId: claimed.agentId,
@@ -221,3 +227,34 @@ export function logRuntimeBindingStartupState() {
221
227
  log.info("[runtime-bridge] bridge is unpaired; sensitive APIs will reject requests until paired");
222
228
  log.info(`[runtime-bridge] pairing code: ${getRuntimePairingCode()}`);
223
229
  }
230
+ /**
231
+ * Fire-and-forget: 通知调度中心 McpNode 已领取启动绑定。
232
+ * 主流程:claimMcpLaunchBinding 成功后异步调用此函数,不阻塞 MCP node 的 claim 响应。
233
+ */
234
+ async function notifySchedulerBindingClaimed(agentId, schedulerBaseUrl) {
235
+ if (!schedulerBaseUrl) {
236
+ log.debug(`[runtime-binding] notifySchedulerBindingClaimed skipped: no schedulerBaseUrl for agentId=${agentId}`);
237
+ return;
238
+ }
239
+ const connectionKeys = getConfiguredConnectionKeys();
240
+ const runtimeToken = connectionKeys[0];
241
+ if (!runtimeToken) {
242
+ log.warn(`[runtime-binding] notifySchedulerBindingClaimed skipped: no connection key for agentId=${agentId}`);
243
+ return;
244
+ }
245
+ const url = `${schedulerBaseUrl.replace(/\/+$/, "")}/api/runtime/bridge/binding-claimed`;
246
+ try {
247
+ await axios.post(url, { agentId }, {
248
+ headers: {
249
+ "X-Runtime-Token": runtimeToken,
250
+ "Content-Type": "application/json",
251
+ },
252
+ timeout: 5000,
253
+ });
254
+ log.debug(`[runtime-binding] Notified scheduler: agentId=${agentId}, url=${url}`);
255
+ }
256
+ catch (err) {
257
+ const message = err instanceof Error ? err.message : String(err);
258
+ log.warn(`[runtime-binding] Failed to notify scheduler binding claimed: agentId=${agentId}, error=${message}`);
259
+ }
260
+ }
@@ -120,7 +120,7 @@ sessionsRouter.post('/sessions/recover', validateToken, async (req, res) => {
120
120
  }
121
121
  else if (persistedSession?.mode === 'sdk' || !persistedSession?.pid) {
122
122
  // SDK 模式或无 PID,尝试通过 agentId 查找孤儿进程
123
- const orphanProcess = findOrphanProcessByAgentId(agentId);
123
+ const orphanProcess = await findOrphanProcessByAgentId(agentId);
124
124
  if (orphanProcess) {
125
125
  orphanDetected = true;
126
126
  orphanPid = orphanProcess.pid;
@@ -200,8 +200,8 @@ sessionsRouter.post('/search-orphan', validateToken, async (req, res) => {
200
200
  // 1. 首先检查 runtime-bridge 是否已经有活跃 SDK 会话
201
201
  const activeSdkSession = Array.from(sdkSessions.entries()).find(([, s]) => s.agentId === agentId);
202
202
  const hasActiveSession = activeSdkSession;
203
- // 2. 搜索标记了该 agentId 的进程(命令�?+ 环境变量 + 进程树匹配)
204
- const orphanProcess = findOrphanProcessByAgentId(agentId);
203
+ // 2. 搜索标记了该 agentId 的进程(命令 + 环境变量 + 进程树匹配)
204
+ const orphanProcess = await findOrphanProcessByAgentId(agentId);
205
205
  // 3. 如果找到残留进程,返回进程信�?
206
206
  if (orphanProcess) {
207
207
  log.info(`[search-orphan] Found orphan process for agentId=${agentId}, pid=${orphanProcess.pid}`);
@@ -242,7 +242,7 @@ sessionsRouter.post('/search-orphan', validateToken, async (req, res) => {
242
242
  // 前端传递的 workspacePath,或者从持久化会话中获取
243
243
  const searchWorkspace = workspacePath || persistedSession?.workspacePath;
244
244
  if (searchWorkspace) {
245
- const workspaceOrphan = findOrphanProcessByWorkspace(searchWorkspace);
245
+ const workspaceOrphan = await findOrphanProcessByWorkspace(searchWorkspace);
246
246
  if (workspaceOrphan) {
247
247
  log.info(`[search-orphan] Found orphan by workspace: agentId=${agentId}, pid=${workspaceOrphan.pid}, workspace=${searchWorkspace}`);
248
248
  res.json({
@@ -467,7 +467,7 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
467
467
  let isProcessAlive = false;
468
468
  if (persistedSession.mode === 'sdk') {
469
469
  // 对于SDK模式,尝试通过agentId查找进程
470
- const orphanProcess = findOrphanProcessByAgentId(agentId);
470
+ const orphanProcess = await findOrphanProcessByAgentId(agentId);
471
471
  isProcessAlive = !!orphanProcess;
472
472
  if (!isProcessAlive && persistedSession.pid) {
473
473
  // 如果通过agentId没找到,尝试通过PID检�?
@@ -490,7 +490,7 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
490
490
  // 如果是SDK模式,检查是否已经有活跃的SDK会话(bridge重启后可能丢失)
491
491
  if (persistedSession.mode === 'sdk') {
492
492
  // 尝试通过进程检测找到实际的SDK进程
493
- const orphanProcess = findOrphanProcessByAgentId(agentId);
493
+ const orphanProcess = await findOrphanProcessByAgentId(agentId);
494
494
  if (orphanProcess) {
495
495
  log.info(`[takeover-session] Found SDK process for agentId=${agentId}, pid=${orphanProcess.pid}`);
496
496
  // �?重要:SDK模式下,进程仍在运行,但bridge的内存中可能没有sdkSessions记录
@@ -582,7 +582,7 @@ sessionsRouter.post('/terminate-tree', validateToken, async (req, res) => {
582
582
  res.status(404).json({ error: 'PID not found' });
583
583
  return;
584
584
  }
585
- const result = terminateProcessTree(pid);
585
+ const result = await terminateProcessTree(pid);
586
586
  // 4. 等待进程完全终止(最多 5 秒)
587
587
  const exited = await waitForProcessExit(pid, 5000);
588
588
  // 5. 清理持久化状�?
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ import path from "node:path";
11
11
  import { TextDecoder } from "node:util";
12
12
  import { Router } from "express";
13
13
  import { v4 as uuidv4 } from "uuid";
14
+ import axios from "axios";
14
15
  import { adapterRegistry, getToolActionInfo, isTransientClipboardPasteFilePath, redactTransientClipboardPastePaths, registerSdkProviders, resolveSdkProviderIdByCommand, } from "../adapter/index.js";
15
16
  import { validateToken } from "../middleware/auth.js";
16
17
  import { getAgentProcessManager } from "../services/agent-process-manager.js";
@@ -19,6 +20,7 @@ import { enqueueMcpLaunchBinding } from "../services/mcp-launch-binding-queue.js
19
20
  import { findClaudeCodeProcesses, isProcessRunning, } from "../services/process-detector.js";
20
21
  import { sendOutput, sendQuestionRequest, sendStatus, } from "../services/session-output.js";
21
22
  import { findPersistedSessionByAgentId, removePersistedSession, savePersistedSessions, updatePersistedSessionAutoCommands, updatePersistedSessionRuntimeState, upsertPersistedSession, } from "../services/terminal-persistence.js";
23
+ import { logger as log } from "../utils/logger.js";
22
24
  export const terminalRouter = Router();
23
25
  // 导出 SDK 会话存储,供其他模块使用(如 sessions.ts)
24
26
  export const sdkSessions = new Map();
@@ -1033,8 +1035,14 @@ terminalRouter.post("/start", validateToken, async (req, res) => {
1033
1035
  await startSdkSession(req, res);
1034
1036
  });
1035
1037
  /**
1036
- * 启动 SDK 会话
1037
- * 支持 MCP 配置和空闲命令
1038
+ * 启动 SDK 会话(两阶段架构)
1039
+ *
1040
+ * 主流程:
1041
+ * 1. Phase 1(同步,快):校验 → 创建会话记录 → 立即响应(status=starting)
1042
+ * 2. Phase 2(后台异步,慢):SDK 进程启动 → PID 探测 → 持久化 → 通知 server 会话就绪
1043
+ *
1044
+ * 此架构确保 bridge HTTP 响应不等待 SDK 子进程初始化,
1045
+ * 前端可在数百毫秒内收到响应,实时展示启动进度。
1038
1046
  */
1039
1047
  async function startSdkSession(req, res) {
1040
1048
  const { agentId, workspacePath, command, autoAccept = true, initialPrompt,
@@ -1090,7 +1098,6 @@ async function startSdkSession(req, res) {
1090
1098
  // 根据命令选择对应的 adapter
1091
1099
  const normalizedCommand = (command || "acode").toLowerCase();
1092
1100
  const providerId = resolveSdkProviderId(command);
1093
- // ★ 特征日志:明确输出选择的 provider
1094
1101
  console.log(`[Runtime] ★★★ SDK启动 ★★★ command="${command}", normalizedCommand="${normalizedCommand}", providerId="${providerId}"`);
1095
1102
  const adapter = adapterRegistry.get(providerId);
1096
1103
  sessionId = uuidv4();
@@ -1123,9 +1130,58 @@ async function startSdkSession(req, res) {
1123
1130
  runtimeAccessToken,
1124
1131
  schedulerBaseUrl: requestedSchedulerBaseUrl,
1125
1132
  });
1126
- // 启动 SDK 会话
1133
+ // Phase 1:立即响应(不等待 SDK 进程启动)
1134
+ res.json({
1135
+ sessionId,
1136
+ status: "starting",
1137
+ runtimeStatus: "starting",
1138
+ agentId: normalizedAgentId,
1139
+ workspacePath,
1140
+ command: config.command,
1141
+ mode: "sdk",
1142
+ });
1143
+ // ★ Phase 2:后台异步启动 SDK 进程 + 后处理
1144
+ startSdkSessionBg({
1145
+ sessionId,
1146
+ agentId: normalizedAgentId,
1147
+ config,
1148
+ adapter,
1149
+ providerId,
1150
+ idleInputAutoCommand,
1151
+ nonInputAutoCommand,
1152
+ runtimeAccessToken,
1153
+ requestedSchedulerBaseUrl,
1154
+ });
1155
+ }
1156
+ catch (error) {
1157
+ // 同步错误处理(校验失败等)
1158
+ if (sessionId) {
1159
+ sdkSessions.delete(sessionId);
1160
+ }
1161
+ if (normalizedAgentId) {
1162
+ startingAgents.delete(normalizedAgentId);
1163
+ }
1164
+ if (!res.headersSent) {
1165
+ const errorMessage = error instanceof Error ? error.message : String(error);
1166
+ res
1167
+ .status(500)
1168
+ .json({ error: `Failed to start SDK session: ${errorMessage}` });
1169
+ }
1170
+ }
1171
+ }
1172
+ /**
1173
+ * Phase 2:后台异步启动 SDK 进程,完成后通知 server。
1174
+ *
1175
+ * 主流程:adapter.startSession(SDK 进程启动)→ 空闲命令注入 → PID 探测
1176
+ * → 持久化会话 → 注册进程管理 → 通知 server 状态为 running
1177
+ * → finally 清理 startingAgents 锁
1178
+ */
1179
+ async function startSdkSessionBg(params) {
1180
+ const { sessionId, agentId, config, adapter, providerId, idleInputAutoCommand, nonInputAutoCommand, runtimeAccessToken, requestedSchedulerBaseUrl, } = params;
1181
+ try {
1182
+ // 启动 SDK 会话(真正的耗时操作)
1127
1183
  await adapter.startSession(sessionId, config);
1128
- // 尽早注入空闲命令,避免后续 PID 探测/进程注册延迟 poll_message 空闲轮询。
1184
+ // 尽早注入空闲命令
1129
1185
  if (idleInputAutoCommand || nonInputAutoCommand) {
1130
1186
  adapter.setIdleCommands?.(sessionId, {
1131
1187
  idleInputCommand: idleInputAutoCommand || "",
@@ -1133,21 +1189,16 @@ async function startSdkSession(req, res) {
1133
1189
  });
1134
1190
  }
1135
1191
  // ★ 尝试获取 SDK 进程的 PID(用于孤儿进程检测)
1136
- // SDK 不直接暴露 PID,通过进程检测服务查找匹配的进程
1137
1192
  let sdkPid;
1138
1193
  if (shouldProbeSdkProcessPid(providerId)) {
1139
1194
  try {
1140
- // 等待一小段时间让进程启动
1141
1195
  await new Promise((resolve) => setTimeout(resolve, 500));
1142
- // 尝试通过 adapter 获取 PID(如果支持)
1143
1196
  sdkPid = adapter.getSessionPid?.(sessionId);
1144
- // 如果 adapter 不支持,尝试通过进程检测服务查找
1145
1197
  if (!sdkPid) {
1146
- const processes = findClaudeCodeProcesses();
1147
- // 查找匹配 agentId 或工作目录的进程
1198
+ const processes = await findClaudeCodeProcesses();
1148
1199
  const matched = processes.find((p) => p.agentId === agentId ||
1149
- p.cwd === workspacePath ||
1150
- p.command.includes(workspacePath));
1200
+ p.cwd === config.workingDirectory ||
1201
+ p.command.includes(config.workingDirectory));
1151
1202
  if (matched) {
1152
1203
  sdkPid = matched.pid;
1153
1204
  console.log(`[Runtime] Found SDK process PID: ${sdkPid} for agentId=${agentId}`);
@@ -1163,7 +1214,7 @@ async function startSdkSession(req, res) {
1163
1214
  await upsertPersistedSession({
1164
1215
  sessionId,
1165
1216
  agentId,
1166
- workspacePath,
1217
+ workspacePath: config.workingDirectory,
1167
1218
  command: config.command,
1168
1219
  startedAt: new Date().toISOString(),
1169
1220
  status: "running",
@@ -1176,39 +1227,92 @@ async function startSdkSession(req, res) {
1176
1227
  if (sdkPid) {
1177
1228
  const processManager = getAgentProcessManager();
1178
1229
  await processManager.startProcess({
1179
- agentId: normalizedAgentId,
1230
+ agentId,
1180
1231
  sessionId,
1181
1232
  pid: sdkPid,
1182
1233
  mode: "sdk",
1183
- workspacePath,
1234
+ workspacePath: config.workingDirectory,
1184
1235
  command: config.command,
1185
1236
  });
1186
1237
  }
1187
- res.json({
1238
+ // ★ 通知 server 会话已就绪
1239
+ await notifyServerSessionReady({
1240
+ agentId,
1188
1241
  sessionId,
1242
+ sdkPid,
1189
1243
  status: startedStatus,
1190
- runtimeStatus: startedStatus,
1191
- agentId,
1192
- workspacePath,
1193
- command: config.command,
1194
- mode: "sdk",
1195
- pid: sdkPid, // ★ 返回 PID 信息
1244
+ schedulerBaseUrl: requestedSchedulerBaseUrl,
1245
+ runtimeAccessToken,
1196
1246
  });
1197
1247
  }
1198
1248
  catch (error) {
1199
- if (sessionId) {
1200
- sdkSessions.delete(sessionId);
1201
- await removePersistedSession(sessionId);
1202
- }
1249
+ // 后台启动失败:清理本地状态 + 通知 server
1250
+ sdkSessions.delete(sessionId);
1251
+ await removePersistedSession(sessionId);
1203
1252
  const errorMessage = error instanceof Error ? error.message : String(error);
1204
- res
1205
- .status(500)
1206
- .json({ error: `Failed to start SDK session: ${errorMessage}` });
1253
+ console.error(`[Runtime] Background SDK session startup failed for agentId=${agentId}: ${errorMessage}`);
1254
+ await notifyServerSessionFailed({
1255
+ agentId,
1256
+ sessionId,
1257
+ schedulerBaseUrl: requestedSchedulerBaseUrl,
1258
+ runtimeAccessToken,
1259
+ errorMessage,
1260
+ });
1207
1261
  }
1208
1262
  finally {
1209
- if (normalizedAgentId) {
1210
- startingAgents.delete(normalizedAgentId);
1211
- }
1263
+ startingAgents.delete(agentId);
1264
+ }
1265
+ }
1266
+ /**
1267
+ * 通知调度中心 SDK 会话已完全就绪。
1268
+ * 主流程:Phase 2 完成后调用,复用 /callback/status 端点更新状态为 running。
1269
+ * Fire-and-forget 风格,失败不影响 bridge 自身状态。
1270
+ */
1271
+ async function notifyServerSessionReady(params) {
1272
+ const { agentId, sessionId, sdkPid, status, schedulerBaseUrl, runtimeAccessToken } = params;
1273
+ if (!schedulerBaseUrl || !runtimeAccessToken) {
1274
+ log.debug(`[Runtime] notifyServerSessionReady skipped: missing schedulerBaseUrl or runtimeAccessToken for agentId=${agentId}`);
1275
+ return;
1276
+ }
1277
+ const url = `${schedulerBaseUrl.replace(/\/+$/, "")}/api/runtime/callback/status`;
1278
+ try {
1279
+ await axios.post(url, { agentId, sessionId, status, pid: sdkPid }, {
1280
+ headers: {
1281
+ "X-Runtime-Token": runtimeAccessToken,
1282
+ "Content-Type": "application/json",
1283
+ },
1284
+ timeout: 5000,
1285
+ });
1286
+ log.debug(`[Runtime] Notified server session ready: agentId=${agentId}, sessionId=${sessionId}, status=${status}`);
1287
+ }
1288
+ catch (err) {
1289
+ const message = err instanceof Error ? err.message : String(err);
1290
+ log.warn(`[Runtime] Failed to notify server session ready: agentId=${agentId}, error=${message}`);
1291
+ }
1292
+ }
1293
+ /**
1294
+ * 通知调度中心 SDK 会话启动失败。
1295
+ * 主流程:Phase 2 catch 块调用,复用 /callback/status 端点更新状态为 stopped。
1296
+ */
1297
+ async function notifyServerSessionFailed(params) {
1298
+ const { agentId, sessionId, schedulerBaseUrl, runtimeAccessToken } = params;
1299
+ if (!schedulerBaseUrl || !runtimeAccessToken) {
1300
+ return;
1301
+ }
1302
+ const url = `${schedulerBaseUrl.replace(/\/+$/, "")}/api/runtime/callback/status`;
1303
+ try {
1304
+ await axios.post(url, { agentId, sessionId, status: "stopped" }, {
1305
+ headers: {
1306
+ "X-Runtime-Token": runtimeAccessToken,
1307
+ "Content-Type": "application/json",
1308
+ },
1309
+ timeout: 5000,
1310
+ });
1311
+ log.debug(`[Runtime] Notified server session failed: agentId=${agentId}, sessionId=${sessionId}`);
1312
+ }
1313
+ catch (err) {
1314
+ const message = err instanceof Error ? err.message : String(err);
1315
+ log.warn(`[Runtime] Failed to notify server session failed: agentId=${agentId}, error=${message}`);
1212
1316
  }
1213
1317
  }
1214
1318
  // ============ SDK 专用路由 ============
@@ -209,6 +209,18 @@ export declare class AgentProcessManager {
209
209
  * @returns Statistics for all managed processes
210
210
  */
211
211
  getStats(): Record<ProcessState | 'total', number>;
212
+ /**
213
+ * Clean up stale registry entries
214
+ *
215
+ * Removes records where:
216
+ * - state is 'terminated' or 'unknown'
217
+ * - the process is no longer running
218
+ *
219
+ * This prevents dead entries from accumulating and inflating counts.
220
+ *
221
+ * @returns Number of entries removed
222
+ */
223
+ cleanupStaleRegistry(): Promise<number>;
212
224
  /**
213
225
  * Remove a process from registry
214
226
  *
@@ -1 +1 @@
1
- {"version":3,"file":"agent-process-manager.d.ts","sourceRoot":"","sources":["../../src/services/agent-process-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-process-manager.d.ts","sourceRoot":"","sources":["../../src/services/agent-process-manager.ts"],"names":[],"mappings":"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"}