aws-runtime-bridge 1.9.1 → 1.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +5 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -4
- package/dist/index.js +20 -4
- package/dist/middleware/auth.d.ts +8 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +44 -0
- package/dist/routes/ai-sources.js +18 -14
- package/dist/routes/dashboard.d.ts +8 -0
- package/dist/routes/dashboard.d.ts.map +1 -0
- package/dist/routes/dashboard.js +1347 -0
- package/dist/routes/instance.d.ts +1 -0
- package/dist/routes/instance.d.ts.map +1 -1
- package/dist/routes/instance.js +53 -4
- package/dist/routes/instance.test.js +4 -0
- package/dist/routes/processes.js +1 -1
- package/dist/routes/runtime-binding.d.ts.map +1 -1
- package/dist/routes/runtime-binding.js +37 -0
- package/dist/routes/sessions.js +7 -7
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +143 -33
- package/dist/services/agent-process-manager.d.ts +12 -0
- package/dist/services/agent-process-manager.d.ts.map +1 -1
- package/dist/services/agent-process-manager.js +47 -13
- package/dist/services/auto-register.d.ts.map +1 -1
- package/dist/services/auto-register.js +4 -5
- package/dist/services/mcp-launch-binding-queue.d.ts +5 -0
- package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
- package/dist/services/mcp-launch-binding-queue.js +17 -0
- package/dist/services/mcp-launch-binding-queue.test.js +16 -1
- package/dist/services/orphan-monitor.js +2 -2
- package/dist/services/panel-auth.d.ts +26 -0
- package/dist/services/panel-auth.d.ts.map +1 -0
- package/dist/services/panel-auth.js +105 -0
- package/dist/services/process-detector.d.ts +16 -9
- package/dist/services/process-detector.d.ts.map +1 -1
- package/dist/services/process-detector.js +136 -66
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +106 -6
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +20 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +71 -2
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts +4 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/constants.js +5 -1
- package/package/aws-client-agent-mcp/dist/constants.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +17 -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 +124 -28
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js +108 -2
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +5 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +53 -3
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- 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 +79 -28
- 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 +35 -0
- 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 +9 -0
- 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.json +1 -1
|
@@ -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,
|
|
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"}
|
package/dist/routes/instance.js
CHANGED
|
@@ -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",
|
|
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",
|
|
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',
|
package/dist/routes/processes.js
CHANGED
|
@@ -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":"
|
|
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
|
+
}
|
package/dist/routes/sessions.js
CHANGED
|
@@ -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":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/terminal.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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 ===
|
|
1150
|
-
p.command.includes(
|
|
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
|
|
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
|
-
|
|
1238
|
+
// ★ 通知 server 会话已就绪
|
|
1239
|
+
await notifyServerSessionReady({
|
|
1240
|
+
agentId,
|
|
1188
1241
|
sessionId,
|
|
1242
|
+
sdkPid,
|
|
1189
1243
|
status: startedStatus,
|
|
1190
|
-
|
|
1191
|
-
|
|
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
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
}
|
|
1249
|
+
// 后台启动失败:清理本地状态 + 通知 server
|
|
1250
|
+
sdkSessions.delete(sessionId);
|
|
1251
|
+
await removePersistedSession(sessionId);
|
|
1203
1252
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
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
|
-
|
|
1210
|
-
|
|
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 专用路由 ============
|
|
@@ -1341,6 +1445,8 @@ terminalRouter.post("/sdk/stop", validateToken, async (req, res) => {
|
|
|
1341
1445
|
// 根据 providerId 获取对应的 adapter
|
|
1342
1446
|
const providerId = entry.providerId || "claude-code";
|
|
1343
1447
|
const adapter = adapterRegistry.get(providerId);
|
|
1448
|
+
// 如果有活跃 AI 请求,先立即中止(fire-and-forget),再清理资源
|
|
1449
|
+
adapter.abortCurrentTurn(sessionId).catch(() => { });
|
|
1344
1450
|
await adapter.terminateSession(sessionId);
|
|
1345
1451
|
sdkSessions.delete(sessionId);
|
|
1346
1452
|
stopTerminalCommandProcess(sessionId);
|
|
@@ -1594,6 +1700,8 @@ terminalRouter.post("/stop", validateToken, async (req, res) => {
|
|
|
1594
1700
|
console.log(`[Runtime] Stopping SDK session ${sessionId}; suppressStatus=${!stopOptions.statusNotified}, reason=${stopOptions.reason || "unspecified"}`);
|
|
1595
1701
|
const providerId = sdkEntry.providerId || "claude-code";
|
|
1596
1702
|
const adapter = adapterRegistry.get(providerId);
|
|
1703
|
+
// 如果有活跃 AI 请求,先立即中止(fire-and-forget),再清理资源
|
|
1704
|
+
adapter.abortCurrentTurn(sessionId).catch(() => { });
|
|
1597
1705
|
await adapter.terminateSession(sessionId);
|
|
1598
1706
|
sdkSessions.delete(sessionId);
|
|
1599
1707
|
stopTerminalCommandProcess(sessionId);
|
|
@@ -1642,6 +1750,8 @@ terminalRouter.post("/stop-all", validateToken, async (_req, res) => {
|
|
|
1642
1750
|
// 根据 providerId 获取对应的 adapter
|
|
1643
1751
|
const providerId = entry.providerId || "claude-code";
|
|
1644
1752
|
const adapter = adapterRegistry.get(providerId);
|
|
1753
|
+
// 如果有活跃 AI 请求,先立即中止(fire-and-forget),再清理资源
|
|
1754
|
+
adapter.abortCurrentTurn(sessionId).catch(() => { });
|
|
1645
1755
|
await adapter.terminateSession(sessionId);
|
|
1646
1756
|
sdkSessions.delete(sessionId);
|
|
1647
1757
|
stopTerminalCommandProcess(sessionId);
|
|
@@ -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":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrH,OAAO,EAOL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAC;AAK/B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,gBAAgB,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;CAC7C;AAED;;;;;;;;;GASG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IAEjD;;OAEG;IACH,OAAO;IAEP;;OAEG;WACW,WAAW,IAAI,mBAAmB;IAWhD;;;;;;;;;;OAUG;IACU,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC/D;;;;;;;OAOG;IACU,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-process-manager.d.ts","sourceRoot":"","sources":["../../src/services/agent-process-manager.ts"],"names":[],"mappings":"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"}
|