aws-runtime-bridge 1.5.0 → 1.6.1
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 +1 -1
- package/dist/adapter/AdapterRegistry.d.ts +1 -1
- package/dist/adapter/AdapterRegistry.d.ts.map +1 -1
- package/dist/adapter/AdapterRegistry.js +0 -2
- package/dist/adapter/ClaudeSdkAdapter.d.ts +4 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/ClaudeSdkAdapter.js +11 -2
- package/dist/adapter/CodexSdkAdapter.js +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +2 -2
- package/dist/adapter/types.d.ts +10 -0
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/index.js +14 -43
- package/dist/middleware/auth.d.ts +5 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +9 -1
- package/dist/routes/file-browser.d.ts.map +1 -1
- package/dist/routes/file-browser.js +21 -1
- package/dist/routes/file-browser.test.js +9 -0
- package/dist/routes/instance.d.ts +10 -0
- package/dist/routes/instance.d.ts.map +1 -1
- package/dist/routes/instance.js +93 -2
- package/dist/routes/instance.test.js +50 -0
- package/dist/routes/pty.d.ts +106 -0
- package/dist/routes/pty.d.ts.map +1 -0
- package/dist/routes/pty.js +526 -0
- package/dist/routes/pty.test.d.ts +2 -0
- package/dist/routes/pty.test.d.ts.map +1 -0
- package/dist/routes/pty.test.js +73 -0
- package/dist/routes/sessions.d.ts +1 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +32 -213
- package/dist/routes/terminal.d.ts +32 -3
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +411 -243
- package/dist/routes/terminal.test.js +105 -29
- package/dist/services/agent-process-manager.d.ts +2 -2
- package/dist/services/agent-process-manager.d.ts.map +1 -1
- package/dist/services/agent-process-manager.js +3 -3
- package/dist/services/process-detector.d.ts +2 -4
- package/dist/services/process-detector.d.ts.map +1 -1
- package/dist/services/process-detector.js +9 -16
- package/dist/services/process-registry.d.ts +2 -2
- package/dist/services/process-registry.d.ts.map +1 -1
- package/dist/services/process-registry.js +1 -1
- package/dist/services/session-output.d.ts +15 -5
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +33 -3
- package/dist/services/session-output.test.js +43 -29
- package/dist/services/terminal-persistence.d.ts +9 -0
- package/dist/services/terminal-persistence.d.ts.map +1 -1
- package/dist/services/terminal-persistence.js +20 -0
- package/dist/services/tool-installer.d.ts +10 -0
- package/dist/services/tool-installer.d.ts.map +1 -1
- package/dist/services/tool-installer.js +126 -5
- package/dist/services/tool-installer.test.js +32 -1
- package/dist/services/workspace-files.d.ts +14 -0
- package/dist/services/workspace-files.d.ts.map +1 -1
- package/dist/services/workspace-files.js +52 -0
- package/dist/services/workspace-files.test.js +85 -1
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/routes/sessions.js
CHANGED
|
@@ -1,39 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 会话管理 API 路由
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 提供 SDK Agent 会话的查询和恢复功能
|
|
5
5
|
*/
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
6
7
|
import { Router } from 'express';
|
|
7
|
-
import {
|
|
8
|
-
import { execSync } from 'child_process';
|
|
9
|
-
import pty from 'node-pty';
|
|
8
|
+
import { adapterRegistry } from '../adapter/index.js';
|
|
10
9
|
import { validateToken } from '../middleware/auth.js';
|
|
11
|
-
import { sessions, sendStatus, scheduleOutputFlush, flushSessionOutput } from '../services/session-output.js';
|
|
12
|
-
import { loadPersistedSessions, upsertPersistedSession, removePersistedSession, findPersistedSessionByAgentId } from '../services/terminal-persistence.js';
|
|
13
10
|
import { isProcessRunning, findOrphanProcessByAgentId, findOrphanProcessByWorkspace } from '../services/process-detector.js';
|
|
11
|
+
import { findPersistedSessionByAgentId, loadPersistedSessions, removePersistedSession, } from '../services/terminal-persistence.js';
|
|
14
12
|
import { createLogger } from '../utils/logger.js';
|
|
15
|
-
// 导入 SDK 会话存储
|
|
16
13
|
import { sdkSessions } from './terminal.js';
|
|
17
|
-
// 导入 Adapter 注册�?
|
|
18
|
-
import { adapterRegistry } from '../adapter/index.js';
|
|
19
14
|
const log = createLogger('sessions');
|
|
20
15
|
export const sessionsRouter = Router();
|
|
21
16
|
/**
|
|
22
|
-
*
|
|
17
|
+
* 获取所有 SDK Agent 会话
|
|
23
18
|
* GET /runtime/sessions
|
|
24
19
|
*/
|
|
25
20
|
sessionsRouter.get('/sessions', validateToken, async (_req, res) => {
|
|
26
21
|
try {
|
|
27
|
-
// 1. 获取
|
|
28
|
-
const activePtySessions = Array.from(sessions.entries()).map(([sessionId, session]) => ({
|
|
29
|
-
sessionId,
|
|
30
|
-
agentId: session.agentId,
|
|
31
|
-
workspacePath: session.workspacePath,
|
|
32
|
-
status: 'running',
|
|
33
|
-
mode: 'pty',
|
|
34
|
-
startedAt: null,
|
|
35
|
-
}));
|
|
36
|
-
// 2. 获取 SDK 会话
|
|
22
|
+
// 1. 获取 SDK 会话
|
|
37
23
|
const activeSdkSessions = Array.from(sdkSessions.entries()).map(([sessionId, entry]) => ({
|
|
38
24
|
sessionId,
|
|
39
25
|
agentId: entry.agentId,
|
|
@@ -42,8 +28,8 @@ sessionsRouter.get('/sessions', validateToken, async (_req, res) => {
|
|
|
42
28
|
mode: 'sdk',
|
|
43
29
|
startedAt: null,
|
|
44
30
|
}));
|
|
45
|
-
//
|
|
46
|
-
const activeSessions =
|
|
31
|
+
// 2. SDK-only:活跃会话全部来自 SDK adapter。
|
|
32
|
+
const activeSessions = activeSdkSessions;
|
|
47
33
|
const persistedSessions = await loadPersistedSessions();
|
|
48
34
|
const activeSessionIds = new Set(activeSessions.map(s => s.sessionId));
|
|
49
35
|
const persistedOnly = persistedSessions.filter(s => !activeSessionIds.has(s.sessionId));
|
|
@@ -73,22 +59,7 @@ sessionsRouter.post('/sessions/recover', validateToken, async (req, res) => {
|
|
|
73
59
|
return;
|
|
74
60
|
}
|
|
75
61
|
try {
|
|
76
|
-
// 1.
|
|
77
|
-
const existingSession = Array.from(sessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
78
|
-
if (existingSession) {
|
|
79
|
-
res.json({
|
|
80
|
-
ok: true,
|
|
81
|
-
sessionId: existingSession[0],
|
|
82
|
-
agentId,
|
|
83
|
-
workspacePath: existingSession[1].workspacePath,
|
|
84
|
-
command: '',
|
|
85
|
-
status: 'running',
|
|
86
|
-
recovered: false,
|
|
87
|
-
message: 'Session already active',
|
|
88
|
-
});
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// 2. 检查是否有 SDK 活跃会话
|
|
62
|
+
// 1. 检查是否有 SDK 活跃会话
|
|
92
63
|
const existingSdkSession = Array.from(sdkSessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
93
64
|
if (existingSdkSession) {
|
|
94
65
|
res.json({
|
|
@@ -125,7 +96,7 @@ sessionsRouter.post('/sessions/recover', validateToken, async (req, res) => {
|
|
|
125
96
|
}
|
|
126
97
|
const resolvedWorkspacePath = workspacePath || persistedSession?.workspacePath;
|
|
127
98
|
const resolvedCommand = command || persistedSession?.command || 'claude';
|
|
128
|
-
const resolvedMode = persistedSession?.mode || '
|
|
99
|
+
const resolvedMode = persistedSession?.mode || 'sdk';
|
|
129
100
|
if (!resolvedWorkspacePath) {
|
|
130
101
|
res.status(400).json({ error: 'workspacePath is required (not found in persisted session)' });
|
|
131
102
|
return;
|
|
@@ -136,7 +107,7 @@ sessionsRouter.post('/sessions/recover', validateToken, async (req, res) => {
|
|
|
136
107
|
log.info(`[Recover] Returning orphan process info for agentId=${agentId}`);
|
|
137
108
|
res.json({
|
|
138
109
|
ok: true,
|
|
139
|
-
sessionId: persistedSession?.sessionId ||
|
|
110
|
+
sessionId: persistedSession?.sessionId || '',
|
|
140
111
|
agentId,
|
|
141
112
|
workspacePath: resolvedWorkspacePath,
|
|
142
113
|
command: resolvedCommand,
|
|
@@ -150,69 +121,17 @@ sessionsRouter.post('/sessions/recover', validateToken, async (req, res) => {
|
|
|
150
121
|
});
|
|
151
122
|
return;
|
|
152
123
|
}
|
|
153
|
-
// 6.
|
|
154
|
-
|
|
155
|
-
const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';
|
|
156
|
-
const shellArgs = process.platform === 'win32' ? ['-NoLogo'] : [];
|
|
157
|
-
const terminal = pty.spawn(shell, shellArgs, {
|
|
158
|
-
name: 'xterm-color',
|
|
159
|
-
cols: 120,
|
|
160
|
-
rows: 30,
|
|
161
|
-
cwd: resolvedWorkspacePath,
|
|
162
|
-
env: {
|
|
163
|
-
...process.env,
|
|
164
|
-
AWS_AGENT_ID: String(agentId),
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
sessions.set(sessionId, {
|
|
168
|
-
agentId,
|
|
169
|
-
workspacePath: resolvedWorkspacePath,
|
|
170
|
-
ptyProcess: terminal,
|
|
171
|
-
outputBuffer: '',
|
|
172
|
-
flushTimer: null,
|
|
173
|
-
flushInFlight: null,
|
|
174
|
-
seq: 0,
|
|
175
|
-
});
|
|
176
|
-
await upsertPersistedSession({
|
|
177
|
-
sessionId,
|
|
178
|
-
agentId,
|
|
179
|
-
workspacePath: resolvedWorkspacePath,
|
|
180
|
-
command: resolvedCommand,
|
|
181
|
-
startedAt: new Date().toISOString(),
|
|
182
|
-
status: 'running',
|
|
183
|
-
mode: resolvedMode,
|
|
184
|
-
pid: terminal.pid, // �?记录新进程的 PID
|
|
185
|
-
});
|
|
186
|
-
await sendStatus(agentId, sessionId, 'running');
|
|
187
|
-
terminal.onData((data) => {
|
|
188
|
-
const session = sessions.get(sessionId);
|
|
189
|
-
if (!session) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
session.outputBuffer += data;
|
|
193
|
-
scheduleOutputFlush(sessionId);
|
|
194
|
-
});
|
|
195
|
-
terminal.onExit(async () => {
|
|
196
|
-
const session = sessions.get(sessionId);
|
|
197
|
-
if (session?.flushTimer) {
|
|
198
|
-
clearTimeout(session.flushTimer);
|
|
199
|
-
session.flushTimer = null;
|
|
200
|
-
}
|
|
201
|
-
await flushSessionOutput(sessionId);
|
|
202
|
-
sessions.delete(sessionId);
|
|
203
|
-
// 直接删除持久化会话,而不是改�?stopped,避免后续被恢复
|
|
204
|
-
await removePersistedSession(sessionId);
|
|
205
|
-
await sendStatus(agentId, null, 'stopped');
|
|
206
|
-
});
|
|
207
|
-
terminal.write(`${resolvedCommand}\r`);
|
|
124
|
+
// 6. SDK-only 恢复不再创建本地终端;由上游重新调用 /runtime/start 创建新的 SDK 会话。
|
|
125
|
+
await removePersistedSession(persistedSession?.sessionId || '');
|
|
208
126
|
const result = {
|
|
209
|
-
ok:
|
|
210
|
-
sessionId,
|
|
127
|
+
ok: false,
|
|
128
|
+
sessionId: persistedSession?.sessionId || '',
|
|
211
129
|
agentId,
|
|
212
130
|
workspacePath: resolvedWorkspacePath,
|
|
213
131
|
command: resolvedCommand,
|
|
214
|
-
status: '
|
|
215
|
-
recovered:
|
|
132
|
+
status: 'stopped',
|
|
133
|
+
recovered: false,
|
|
134
|
+
message: 'No active SDK session found; start a new SDK runtime session instead.',
|
|
216
135
|
};
|
|
217
136
|
res.json(result);
|
|
218
137
|
}
|
|
@@ -241,10 +160,9 @@ sessionsRouter.post('/search-orphan', validateToken, async (req, res) => {
|
|
|
241
160
|
return;
|
|
242
161
|
}
|
|
243
162
|
try {
|
|
244
|
-
// 1.
|
|
245
|
-
const activeSession = Array.from(sessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
163
|
+
// 1. 首先检查 runtime-bridge 是否已经有活跃 SDK 会话
|
|
246
164
|
const activeSdkSession = Array.from(sdkSessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
247
|
-
const hasActiveSession =
|
|
165
|
+
const hasActiveSession = activeSdkSession;
|
|
248
166
|
// 2. 搜索标记了该 agentId 的进程(命令�?+ 环境变量 + 进程树匹配)
|
|
249
167
|
const orphanProcess = findOrphanProcessByAgentId(agentId);
|
|
250
168
|
// 3. 如果找到残留进程,返回进程信�?
|
|
@@ -408,23 +326,7 @@ sessionsRouter.get('/sessions/by-agent/:agentId', validateToken, async (req, res
|
|
|
408
326
|
return;
|
|
409
327
|
}
|
|
410
328
|
try {
|
|
411
|
-
// 1.
|
|
412
|
-
const activeSession = Array.from(sessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
413
|
-
if (activeSession) {
|
|
414
|
-
res.json({
|
|
415
|
-
ok: true,
|
|
416
|
-
sessionId: activeSession[0],
|
|
417
|
-
agentId,
|
|
418
|
-
workspacePath: activeSession[1].workspacePath,
|
|
419
|
-
status: 'running',
|
|
420
|
-
isActive: true,
|
|
421
|
-
mode: 'pty',
|
|
422
|
-
pid: activeSession[1].ptyProcess.pid,
|
|
423
|
-
isManaged: true,
|
|
424
|
-
});
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
// 2. 检查活�?SDK 会话
|
|
329
|
+
// 1. 检查活跃 SDK 会话
|
|
428
330
|
const activeSdkSession = Array.from(sdkSessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
429
331
|
if (activeSdkSession) {
|
|
430
332
|
const adapter = adapterRegistry.get(activeSdkSession[1].providerId || 'claude-code');
|
|
@@ -443,7 +345,7 @@ sessionsRouter.get('/sessions/by-agent/:agentId', validateToken, async (req, res
|
|
|
443
345
|
});
|
|
444
346
|
return;
|
|
445
347
|
}
|
|
446
|
-
//
|
|
348
|
+
// 2. 检查持久化会话
|
|
447
349
|
const persistedSession = await findPersistedSessionByAgentId(agentId);
|
|
448
350
|
if (persistedSession) {
|
|
449
351
|
// 检查进程是否仍在运�?
|
|
@@ -494,22 +396,7 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
|
|
|
494
396
|
return;
|
|
495
397
|
}
|
|
496
398
|
try {
|
|
497
|
-
// 1. 检查是否已有活跃
|
|
498
|
-
const existingPtySession = Array.from(sessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
499
|
-
if (existingPtySession) {
|
|
500
|
-
res.json({
|
|
501
|
-
ok: true,
|
|
502
|
-
agentId,
|
|
503
|
-
sessionId: existingPtySession[0],
|
|
504
|
-
mode: 'pty',
|
|
505
|
-
status: 'running',
|
|
506
|
-
isActive: true,
|
|
507
|
-
recovered: false,
|
|
508
|
-
message: 'PTY session already active',
|
|
509
|
-
});
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
// 2. 检查是否已有活跃SDK会话
|
|
399
|
+
// 1. 检查是否已有活跃 SDK 会话
|
|
513
400
|
const existingSdkSession = Array.from(sdkSessions.entries()).find(([, s]) => s.agentId === agentId);
|
|
514
401
|
if (existingSdkSession) {
|
|
515
402
|
res.json({
|
|
@@ -524,7 +411,7 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
|
|
|
524
411
|
});
|
|
525
412
|
return;
|
|
526
413
|
}
|
|
527
|
-
//
|
|
414
|
+
// 2. 查找持久化会�?
|
|
528
415
|
const persistedSession = await findPersistedSessionByAgentId(agentId);
|
|
529
416
|
if (!persistedSession) {
|
|
530
417
|
res.status(404).json({
|
|
@@ -533,7 +420,7 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
|
|
|
533
420
|
});
|
|
534
421
|
return;
|
|
535
422
|
}
|
|
536
|
-
//
|
|
423
|
+
// 3. 检查 SDK 进程是否仍在运行
|
|
537
424
|
let isProcessAlive = false;
|
|
538
425
|
if (persistedSession.mode === 'sdk') {
|
|
539
426
|
// 对于SDK模式,尝试通过agentId查找进程
|
|
@@ -544,10 +431,6 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
|
|
|
544
431
|
isProcessAlive = isProcessRunning(persistedSession.pid);
|
|
545
432
|
}
|
|
546
433
|
}
|
|
547
|
-
else if (persistedSession.pid) {
|
|
548
|
-
// 对于PTY模式,检查PID
|
|
549
|
-
isProcessAlive = isProcessRunning(persistedSession.pid);
|
|
550
|
-
}
|
|
551
434
|
if (!isProcessAlive) {
|
|
552
435
|
log.info(`[takeover-session] Process not alive for agentId=${agentId}, mode=${persistedSession.mode}`);
|
|
553
436
|
res.json({
|
|
@@ -594,68 +477,10 @@ sessionsRouter.post('/takeover-session', validateToken, async (req, res) => {
|
|
|
594
477
|
}
|
|
595
478
|
}
|
|
596
479
|
else {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';
|
|
600
|
-
const shellArgs = process.platform === 'win32' ? ['-NoLogo'] : [];
|
|
601
|
-
const terminal = pty.spawn(shell, shellArgs, {
|
|
602
|
-
name: 'xterm-color',
|
|
603
|
-
cols: 120,
|
|
604
|
-
rows: 30,
|
|
605
|
-
cwd: persistedSession.workspacePath,
|
|
606
|
-
env: {
|
|
607
|
-
...process.env,
|
|
608
|
-
AWS_AGENT_ID: String(agentId),
|
|
609
|
-
},
|
|
610
|
-
});
|
|
611
|
-
sessions.set(sessionId, {
|
|
480
|
+
res.status(410).json({
|
|
481
|
+
error: 'Session takeover is no longer supported; start a new SDK runtime session.',
|
|
612
482
|
agentId,
|
|
613
|
-
|
|
614
|
-
ptyProcess: terminal,
|
|
615
|
-
outputBuffer: '',
|
|
616
|
-
flushTimer: null,
|
|
617
|
-
flushInFlight: null,
|
|
618
|
-
seq: 0,
|
|
619
|
-
});
|
|
620
|
-
await upsertPersistedSession({
|
|
621
|
-
sessionId,
|
|
622
|
-
agentId,
|
|
623
|
-
workspacePath: persistedSession.workspacePath,
|
|
624
|
-
command: persistedSession.command,
|
|
625
|
-
startedAt: new Date().toISOString(),
|
|
626
|
-
status: 'running',
|
|
627
|
-
mode: 'pty',
|
|
628
|
-
pid: terminal.pid,
|
|
629
|
-
});
|
|
630
|
-
await sendStatus(agentId, sessionId, 'running');
|
|
631
|
-
terminal.onData((data) => {
|
|
632
|
-
const session = sessions.get(sessionId);
|
|
633
|
-
if (!session) {
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
session.outputBuffer += data;
|
|
637
|
-
scheduleOutputFlush(sessionId);
|
|
638
|
-
});
|
|
639
|
-
terminal.onExit(async () => {
|
|
640
|
-
const session = sessions.get(sessionId);
|
|
641
|
-
if (session?.flushTimer) {
|
|
642
|
-
clearTimeout(session.flushTimer);
|
|
643
|
-
session.flushTimer = null;
|
|
644
|
-
}
|
|
645
|
-
await flushSessionOutput(sessionId);
|
|
646
|
-
sessions.delete(sessionId);
|
|
647
|
-
await removePersistedSession(sessionId);
|
|
648
|
-
await sendStatus(agentId, null, 'stopped');
|
|
649
|
-
});
|
|
650
|
-
res.json({
|
|
651
|
-
ok: true,
|
|
652
|
-
agentId,
|
|
653
|
-
sessionId,
|
|
654
|
-
mode: 'pty',
|
|
655
|
-
status: 'running',
|
|
656
|
-
isActive: true,
|
|
657
|
-
recovered: true,
|
|
658
|
-
pid: terminal.pid,
|
|
483
|
+
mode: 'sdk',
|
|
659
484
|
});
|
|
660
485
|
}
|
|
661
486
|
}
|
|
@@ -689,15 +514,10 @@ sessionsRouter.post('/terminate-tree', validateToken, async (req, res) => {
|
|
|
689
514
|
targetSessionId = persistedSession.sessionId;
|
|
690
515
|
}
|
|
691
516
|
// 2. 检查是否是活跃会话
|
|
692
|
-
const activeSession = sessions.get(targetSessionId);
|
|
693
517
|
const activeSdkSession = sdkSessions.get(targetSessionId);
|
|
694
518
|
let pid;
|
|
695
519
|
let mode;
|
|
696
|
-
if (
|
|
697
|
-
pid = activeSession.ptyProcess.pid;
|
|
698
|
-
mode = 'pty';
|
|
699
|
-
}
|
|
700
|
-
else if (activeSdkSession) {
|
|
520
|
+
if (activeSdkSession) {
|
|
701
521
|
const adapter = adapterRegistry.get(activeSdkSession.providerId || 'claude-code');
|
|
702
522
|
pid = adapter.getSessionPid?.(targetSessionId);
|
|
703
523
|
mode = 'sdk';
|
|
@@ -710,7 +530,7 @@ sessionsRouter.post('/terminate-tree', validateToken, async (req, res) => {
|
|
|
710
530
|
return;
|
|
711
531
|
}
|
|
712
532
|
pid = persistedSession.pid;
|
|
713
|
-
mode = persistedSession.mode || '
|
|
533
|
+
mode = persistedSession.mode || 'sdk';
|
|
714
534
|
}
|
|
715
535
|
// 3. 终止进程树
|
|
716
536
|
const { terminateProcessTree, waitForProcessExit } = await import('../services/process-detector.js');
|
|
@@ -769,9 +589,8 @@ sessionsRouter.get('/health/:agentId', validateToken, async (req, res) => {
|
|
|
769
589
|
return;
|
|
770
590
|
}
|
|
771
591
|
// 2. 检查是否是活跃会话
|
|
772
|
-
const activeSession = sessions.get(persistedSession.sessionId);
|
|
773
592
|
const activeSdkSession = sdkSessions.get(persistedSession.sessionId);
|
|
774
|
-
const isManaged = !!
|
|
593
|
+
const isManaged = !!activeSdkSession;
|
|
775
594
|
// 3. 获取健康状态
|
|
776
595
|
const { checkProcessHealth } = await import('../services/process-detector.js');
|
|
777
596
|
const healthResponse = {
|
|
@@ -779,7 +598,7 @@ sessionsRouter.get('/health/:agentId', validateToken, async (req, res) => {
|
|
|
779
598
|
mode: persistedSession.mode,
|
|
780
599
|
workspacePath: persistedSession.workspacePath,
|
|
781
600
|
isManaged,
|
|
782
|
-
isActive: !!
|
|
601
|
+
isActive: !!activeSdkSession,
|
|
783
602
|
};
|
|
784
603
|
if (persistedSession.pid) {
|
|
785
604
|
const health = await checkProcessHealth(persistedSession.pid, agentId);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 终端 API 路由
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 提供 SDK Agent 会话的启动、输入、调整大小和停止功能
|
|
5
|
+
* 终端 UI 仅作为 SDK Agent 的消息输入/状态输出载体
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { TextDecoder } from 'node:util';
|
|
8
|
+
import { type AdapterSessionConfig, type ProviderEvent } from '../adapter/index.js';
|
|
8
9
|
export declare const terminalRouter: import("express-serve-static-core").Router;
|
|
9
10
|
export interface SdkSessionEntry {
|
|
10
11
|
agentId: string;
|
|
@@ -12,7 +13,35 @@ export interface SdkSessionEntry {
|
|
|
12
13
|
sessionId: string;
|
|
13
14
|
config: AdapterSessionConfig;
|
|
14
15
|
providerId: string;
|
|
16
|
+
seq: number;
|
|
15
17
|
}
|
|
16
18
|
export declare const sdkSessions: Map<string, SdkSessionEntry>;
|
|
19
|
+
/**
|
|
20
|
+
* 标准化终端输入:将前端回车符还原为 shell 可执行命令文本。
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeTerminalCommandInput(value: unknown): string;
|
|
23
|
+
/**
|
|
24
|
+
* 解析终端命令输出解码编码。
|
|
25
|
+
* 主流程:优先使用环境变量覆盖 -> Windows 默认 GB18030 -> 其他平台默认 UTF-8。
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveTerminalOutputEncoding(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
|
|
28
|
+
/**
|
|
29
|
+
* 创建终端输出流式解码器,避免多字节中文被 chunk 边界截断。
|
|
30
|
+
*/
|
|
31
|
+
export declare function createTerminalOutputDecoder(encoding?: string): TextDecoder;
|
|
32
|
+
/**
|
|
33
|
+
* 解码终端输出 chunk;end=true 时刷新解码器内部剩余字节。
|
|
34
|
+
*/
|
|
35
|
+
export declare function decodeTerminalOutputChunk(decoder: TextDecoder, chunk?: Buffer, end?: boolean): string;
|
|
36
|
+
/**
|
|
37
|
+
* 解析 cd 命令目标;返回 undefined 表示不是目录切换命令,null 表示仅查看当前目录。
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseTerminalDirectoryChangeTarget(command: string): string | null | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* 生成显示在终端输出流中的工作目录提示符。
|
|
42
|
+
*/
|
|
43
|
+
export declare function formatTerminalPrompt(currentDirectory: string): string;
|
|
44
|
+
export declare function buildRuntimeEnv(agentId: string, workspacePath: string | undefined, baseEnv: NodeJS.ProcessEnv, envOverrides?: Record<string, unknown>): Record<string, string>;
|
|
45
|
+
export declare function formatSdkOutputEvent(event: ProviderEvent): string | undefined;
|
|
17
46
|
export declare function resolveSdkProviderId(command?: string): string;
|
|
18
47
|
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAEL,KAAK,oBAAoB,
|
|
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;AAKxC,OAAO,EAEL,KAAK,oBAAoB,EAEzB,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAC;AAc7B,eAAO,MAAM,cAAc,4CAAW,CAAC;AAIvC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAGD,eAAO,MAAM,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAa,CAAC;AAenE;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpE;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,SAAkC,GAAG,WAAW,CAMnG;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,UAAQ,GAAG,MAAM,CAQnG;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAiB7F;AA0CD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAErE;AAyGD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+BxB;AA8BD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAU7E;AAYD,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7D"}
|