@team-work/sandbox-client 0.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/cli/config-cmd.d.ts +3 -0
- package/dist/cli/config-cmd.js +60 -0
- package/dist/cli/config-cmd.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +30 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/list-cmd.d.ts +3 -0
- package/dist/cli/list-cmd.js +36 -0
- package/dist/cli/list-cmd.js.map +1 -0
- package/dist/cli/log-cmd.d.ts +3 -0
- package/dist/cli/log-cmd.js +118 -0
- package/dist/cli/log-cmd.js.map +1 -0
- package/dist/cli/start-cmd.d.ts +3 -0
- package/dist/cli/start-cmd.js +69 -0
- package/dist/cli/start-cmd.js.map +1 -0
- package/dist/cli/status-cmd.d.ts +3 -0
- package/dist/cli/status-cmd.js +46 -0
- package/dist/cli/status-cmd.js.map +1 -0
- package/dist/cli/stop-cmd.d.ts +3 -0
- package/dist/cli/stop-cmd.js +115 -0
- package/dist/cli/stop-cmd.js.map +1 -0
- package/dist/cli/update-cmd.d.ts +3 -0
- package/dist/cli/update-cmd.js +16 -0
- package/dist/cli/update-cmd.js.map +1 -0
- package/dist/client/frames.d.ts +111 -0
- package/dist/client/frames.js +22 -0
- package/dist/client/frames.js.map +1 -0
- package/dist/client/handshake.d.ts +7 -0
- package/dist/client/handshake.js +83 -0
- package/dist/client/handshake.js.map +1 -0
- package/dist/client/protocol.d.ts +36 -0
- package/dist/client/protocol.js +39 -0
- package/dist/client/protocol.js.map +1 -0
- package/dist/client/ws-client.d.ts +48 -0
- package/dist/client/ws-client.js +272 -0
- package/dist/client/ws-client.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.js +68 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/paths.d.ts +14 -0
- package/dist/config/paths.js +41 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/schema.d.ts +17 -0
- package/dist/config/schema.js +52 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/daemon/detach.d.ts +17 -0
- package/dist/daemon/detach.js +30 -0
- package/dist/daemon/detach.js.map +1 -0
- package/dist/daemon/launch.d.ts +15 -0
- package/dist/daemon/launch.js +70 -0
- package/dist/daemon/launch.js.map +1 -0
- package/dist/daemon/signal.d.ts +7 -0
- package/dist/daemon/signal.js +22 -0
- package/dist/daemon/signal.js.map +1 -0
- package/dist/handlers/rpc/exec-shell.d.ts +13 -0
- package/dist/handlers/rpc/exec-shell.js +52 -0
- package/dist/handlers/rpc/exec-shell.js.map +1 -0
- package/dist/handlers/rpc/file-ops.d.ts +17 -0
- package/dist/handlers/rpc/file-ops.js +163 -0
- package/dist/handlers/rpc/file-ops.js.map +1 -0
- package/dist/handlers/rpc/router.d.ts +9 -0
- package/dist/handlers/rpc/router.js +98 -0
- package/dist/handlers/rpc/router.js.map +1 -0
- package/dist/handlers/terminal/manager.d.ts +20 -0
- package/dist/handlers/terminal/manager.js +75 -0
- package/dist/handlers/terminal/manager.js.map +1 -0
- package/dist/handlers/terminal/pty-session.d.ts +17 -0
- package/dist/handlers/terminal/pty-session.js +76 -0
- package/dist/handlers/terminal/pty-session.js.map +1 -0
- package/dist/handlers/terminal/router.d.ts +6 -0
- package/dist/handlers/terminal/router.js +25 -0
- package/dist/handlers/terminal/router.js.map +1 -0
- package/dist/instance/id.d.ts +5 -0
- package/dist/instance/id.js +17 -0
- package/dist/instance/id.js.map +1 -0
- package/dist/instance/store.d.ts +33 -0
- package/dist/instance/store.js +155 -0
- package/dist/instance/store.js.map +1 -0
- package/dist/instance/types.d.ts +28 -0
- package/dist/instance/types.js +7 -0
- package/dist/instance/types.js.map +1 -0
- package/dist/logging/logger.d.ts +11 -0
- package/dist/logging/logger.js +42 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/system/report.d.ts +2 -0
- package/dist/system/report.js +61 -0
- package/dist/system/report.js.map +1 -0
- package/dist/update/self-update.d.ts +8 -0
- package/dist/update/self-update.js +41 -0
- package/dist/update/self-update.js.map +1 -0
- package/dist/utils/info.d.ts +11 -0
- package/dist/utils/info.js +21 -0
- package/dist/utils/info.js.map +1 -0
- package/dist/utils/pem.d.ts +15 -0
- package/dist/utils/pem.js +31 -0
- package/dist/utils/pem.js.map +1 -0
- package/package.json +44 -0
- package/scripts/fix-pty-perms.js +30 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.uploadFile = uploadFile;
|
|
4
|
+
exports.fileUploadAborted = fileUploadAborted;
|
|
5
|
+
exports.downloadFile = downloadFile;
|
|
6
|
+
exports.readFile = readFile;
|
|
7
|
+
exports.writeFile = writeFile;
|
|
8
|
+
exports.listDir = listDir;
|
|
9
|
+
exports.makeDir = makeDir;
|
|
10
|
+
exports.remove = remove;
|
|
11
|
+
exports.stat = stat;
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
/**
|
|
15
|
+
* 文件操作:既有 6 类同步单帧回包 + 分片上传/下载(二进制 base64)。
|
|
16
|
+
* 分片上传:逐片 appendFileSync 到 .part 文件,isLast 时 rename 落盘(内存 O(chunk))。
|
|
17
|
+
* 分片下载:CLI 按 chunkSize 读文件,base64 编码逐片回吐;支持 offset/length 参数做 Range 段级下载。
|
|
18
|
+
*
|
|
19
|
+
* 路径校验:拒绝 null 字节、拒绝相对解析异常。不做路径白名单(sandbox 已鉴权,CLI 信任其指令)。
|
|
20
|
+
* 错误一律抛 → router 回 rpc-result{ok:false,error}。
|
|
21
|
+
*/
|
|
22
|
+
function assertPath(path) {
|
|
23
|
+
if (typeof path !== 'string' || !path)
|
|
24
|
+
throw new Error('path required');
|
|
25
|
+
if (path.includes('\0'))
|
|
26
|
+
throw new Error('invalid path');
|
|
27
|
+
return path;
|
|
28
|
+
}
|
|
29
|
+
// ---- 分片上传:逐片 append 到 .part 文件,isLast 时 rename 落盘(内存 O(chunk)) ----
|
|
30
|
+
/** 分片上传会话(按 path 索引,保存 fd + 累积大小) */
|
|
31
|
+
const uploadSessions = new Map();
|
|
32
|
+
function uploadFile(path, chunkDataB64, isLast) {
|
|
33
|
+
const p = assertPath(path);
|
|
34
|
+
if (typeof chunkDataB64 !== 'string')
|
|
35
|
+
throw new Error('chunkData must be base64 string');
|
|
36
|
+
const chunk = Buffer.from(chunkDataB64, 'base64');
|
|
37
|
+
const prev = uploadSessions.get(p);
|
|
38
|
+
if (!prev) {
|
|
39
|
+
// 首次分片:创建 .part 文件
|
|
40
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(p, '..'), { recursive: true });
|
|
41
|
+
const fd = (0, node_fs_1.openSync)(p + '.part', 'w');
|
|
42
|
+
uploadSessions.set(p, { fd, size: chunk.length });
|
|
43
|
+
(0, node_fs_1.appendFileSync)(fd, chunk);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
(0, node_fs_1.appendFileSync)(prev.fd, chunk);
|
|
47
|
+
prev.size += chunk.length;
|
|
48
|
+
}
|
|
49
|
+
if (isLast) {
|
|
50
|
+
const sess = uploadSessions.get(p);
|
|
51
|
+
uploadSessions.delete(p);
|
|
52
|
+
(0, node_fs_1.closeSync)(sess.fd);
|
|
53
|
+
// 原子 rename:.part → 目标路径
|
|
54
|
+
(0, node_fs_1.renameSync)(p + '.part', p);
|
|
55
|
+
}
|
|
56
|
+
return { ok: true, path: p };
|
|
57
|
+
}
|
|
58
|
+
function fileUploadAborted(path) {
|
|
59
|
+
const p = assertPath(path);
|
|
60
|
+
const sess = uploadSessions.get(p);
|
|
61
|
+
if (sess) {
|
|
62
|
+
(0, node_fs_1.closeSync)(sess.fd);
|
|
63
|
+
try {
|
|
64
|
+
(0, node_fs_1.unlinkSync)(p + '.part');
|
|
65
|
+
}
|
|
66
|
+
catch { /* ignore */ }
|
|
67
|
+
uploadSessions.delete(p);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 分片下载。支持 offset/length 做 Range 段级下载(段内仍分片回吐):
|
|
72
|
+
* - offset(字节):起始位置,默认 0
|
|
73
|
+
* - length(字节):读取长度,默认到文件尾
|
|
74
|
+
* isLast=true 表示本段(或整个文件)的最后一片。
|
|
75
|
+
*/
|
|
76
|
+
function downloadFile(path, chunkB64, offset, length) {
|
|
77
|
+
const p = assertPath(path);
|
|
78
|
+
if (!(0, node_fs_1.existsSync)(p))
|
|
79
|
+
throw new Error(`not found: ${p}`);
|
|
80
|
+
const st = (0, node_fs_1.statSync)(p);
|
|
81
|
+
const start = Math.max(0, Number(offset ?? 0) || 0);
|
|
82
|
+
const len = Number(length ?? 0) > 0 ? Number(length) : st.size - start;
|
|
83
|
+
const end = Math.min(st.size, start + len);
|
|
84
|
+
let pos = start;
|
|
85
|
+
// 10 MiB 原始字节 → base64 ~13.3 MiB;分片回吐避免单帧过大(覆盖绝大多数文件)
|
|
86
|
+
const chunkSize = 10 * 1024 * 1024;
|
|
87
|
+
const fd = (0, node_fs_1.openSync)(p, 'r');
|
|
88
|
+
try {
|
|
89
|
+
const buf = Buffer.alloc(chunkSize);
|
|
90
|
+
let isLast = false;
|
|
91
|
+
while (pos < end) {
|
|
92
|
+
const want = Math.min(chunkSize, end - pos);
|
|
93
|
+
const bytes = (0, node_fs_1.readSync)(fd, buf, 0, want, pos);
|
|
94
|
+
if (bytes === 0)
|
|
95
|
+
break;
|
|
96
|
+
const slice = bytes < want ? buf.subarray(0, bytes) : buf.subarray(0, want);
|
|
97
|
+
pos += bytes;
|
|
98
|
+
isLast = pos >= end;
|
|
99
|
+
chunkB64(slice.toString('base64'), isLast);
|
|
100
|
+
if (isLast)
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
if (!isLast && start >= end) {
|
|
104
|
+
// 空段/越界:仍回一个结束以让调用方正常收尾
|
|
105
|
+
chunkB64('', true);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
(0, node_fs_1.closeSync)(fd);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ---- 既有同步操作 ----
|
|
113
|
+
function readFile(path) {
|
|
114
|
+
const p = assertPath(path);
|
|
115
|
+
return (0, node_fs_1.readFileSync)(p, 'utf8');
|
|
116
|
+
}
|
|
117
|
+
function writeFile(path, content) {
|
|
118
|
+
const p = assertPath(path);
|
|
119
|
+
if (typeof content !== 'string')
|
|
120
|
+
throw new Error('content must be string');
|
|
121
|
+
(0, node_fs_1.writeFileSync)(p, content);
|
|
122
|
+
return { ok: true, path: p };
|
|
123
|
+
}
|
|
124
|
+
function listDir(path) {
|
|
125
|
+
const p = assertPath(path);
|
|
126
|
+
const names = (0, node_fs_1.readdirSync)(p);
|
|
127
|
+
const entries = [];
|
|
128
|
+
for (const name of names) {
|
|
129
|
+
const full = (0, node_path_1.join)(p, name);
|
|
130
|
+
let isDir = false;
|
|
131
|
+
let size = 0;
|
|
132
|
+
try {
|
|
133
|
+
const st = (0, node_fs_1.lstatSync)(full);
|
|
134
|
+
isDir = st.isDirectory();
|
|
135
|
+
size = st.size;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// 无权限等:仍列出但 size 0
|
|
139
|
+
}
|
|
140
|
+
entries.push({ name, isDir, size });
|
|
141
|
+
}
|
|
142
|
+
return entries;
|
|
143
|
+
}
|
|
144
|
+
function makeDir(path) {
|
|
145
|
+
const p = assertPath(path);
|
|
146
|
+
(0, node_fs_1.mkdirSync)(p, { recursive: true });
|
|
147
|
+
return { ok: true, path: p };
|
|
148
|
+
}
|
|
149
|
+
function remove(path) {
|
|
150
|
+
const p = assertPath(path);
|
|
151
|
+
(0, node_fs_1.rmSync)(p, { recursive: true, force: true });
|
|
152
|
+
return { ok: true, path: p };
|
|
153
|
+
}
|
|
154
|
+
function stat(path) {
|
|
155
|
+
const p = assertPath(path);
|
|
156
|
+
const st = (0, node_fs_1.statSync)(p);
|
|
157
|
+
return {
|
|
158
|
+
size: st.size,
|
|
159
|
+
isDir: st.isDirectory(),
|
|
160
|
+
mtime: st.mtimeMs,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=file-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-ops.js","sourceRoot":"","sources":["../../../src/handlers/rpc/file-ops.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Logger } from '../../logging/logger.js';
|
|
2
|
+
import { RpcFrame } from '../../client/frames.js';
|
|
3
|
+
/**
|
|
4
|
+
* RPC 路由:按 action 分发,统一回包为 rpc-result(文件操作)或 rpc-stream+rpc-end(exec_shell)。
|
|
5
|
+
* 单个 RPC 异常回 rpc-result{ok:false,error},不向上抛、不关连接。
|
|
6
|
+
*/
|
|
7
|
+
type Send = (frame: string) => void;
|
|
8
|
+
export declare function handleRpc(frame: RpcFrame, send: Send, logger: Logger): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleRpc = handleRpc;
|
|
4
|
+
const frames_js_1 = require("../../client/frames.js");
|
|
5
|
+
const protocol_js_1 = require("../../client/protocol.js");
|
|
6
|
+
const exec_shell_js_1 = require("./exec-shell.js");
|
|
7
|
+
const file_ops_js_1 = require("./file-ops.js");
|
|
8
|
+
function sendResult(reqId, ok, data, error, send) {
|
|
9
|
+
const frame = { type: 'rpc-result', reqId, ok, ...(data !== undefined ? { data } : {}), ...(error ? { error } : {}) };
|
|
10
|
+
send((0, frames_js_1.serializeFrame)(frame));
|
|
11
|
+
}
|
|
12
|
+
function handleRpc(frame, send, logger) {
|
|
13
|
+
const { reqId, action, params } = frame;
|
|
14
|
+
try {
|
|
15
|
+
switch (action) {
|
|
16
|
+
case protocol_js_1.RpcAction.READ_FILE: {
|
|
17
|
+
const data = (0, file_ops_js_1.readFile)(params.path);
|
|
18
|
+
sendResult(reqId, true, data, undefined, send);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
case protocol_js_1.RpcAction.WRITE_FILE: {
|
|
22
|
+
const data = (0, file_ops_js_1.writeFile)(params.path, params.content);
|
|
23
|
+
sendResult(reqId, true, data, undefined, send);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
case protocol_js_1.RpcAction.LIST_DIR: {
|
|
27
|
+
const data = (0, file_ops_js_1.listDir)(params.path);
|
|
28
|
+
sendResult(reqId, true, data, undefined, send);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
case protocol_js_1.RpcAction.MAKE_DIR: {
|
|
32
|
+
const data = (0, file_ops_js_1.makeDir)(params.path);
|
|
33
|
+
sendResult(reqId, true, data, undefined, send);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
case protocol_js_1.RpcAction.REMOVE: {
|
|
37
|
+
const data = (0, file_ops_js_1.remove)(params.path);
|
|
38
|
+
sendResult(reqId, true, data, undefined, send);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
case protocol_js_1.RpcAction.STAT: {
|
|
42
|
+
const data = (0, file_ops_js_1.stat)(params.path);
|
|
43
|
+
sendResult(reqId, true, data, undefined, send);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
case protocol_js_1.RpcAction.UPLOAD_FILE: {
|
|
47
|
+
// 分片上传:append 到内存 buffer,isLast 时落盘
|
|
48
|
+
const data = (0, file_ops_js_1.uploadFile)(params.path, params.chunkData, params.isLast);
|
|
49
|
+
sendResult(reqId, true, data, undefined, send);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
case protocol_js_1.RpcAction.DOWNLOAD_FILE: {
|
|
53
|
+
// 分片下载:逐片 base64 回吐到 rpc-stream;支持 offset/length 做 Range 段级下载
|
|
54
|
+
let done = false;
|
|
55
|
+
(0, file_ops_js_1.downloadFile)(params.path, (chunkB64, isLast) => {
|
|
56
|
+
send((0, frames_js_1.serializeFrame)({
|
|
57
|
+
type: 'rpc-stream',
|
|
58
|
+
reqId,
|
|
59
|
+
stream: 'stdout',
|
|
60
|
+
data: chunkB64, // 纯 base64,不包装成 JSON
|
|
61
|
+
}));
|
|
62
|
+
if (isLast) {
|
|
63
|
+
done = true;
|
|
64
|
+
send((0, frames_js_1.serializeFrame)({ type: 'rpc-end', reqId, exitCode: 0 }));
|
|
65
|
+
}
|
|
66
|
+
}, params.offset, params.length);
|
|
67
|
+
if (!done) {
|
|
68
|
+
// 空文件:没有分片,直接结束
|
|
69
|
+
send((0, frames_js_1.serializeFrame)({ type: 'rpc-end', reqId, exitCode: 0 }));
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case protocol_js_1.RpcAction.EXEC_SHELL: {
|
|
74
|
+
(0, exec_shell_js_1.execShell)(params.command, params.cwd, params.timeout, {
|
|
75
|
+
onStream: (stream, data) => send((0, frames_js_1.serializeFrame)({ type: 'rpc-stream', reqId, stream, data })),
|
|
76
|
+
onEnd: (exitCode) => {
|
|
77
|
+
const endFrame = exitCode !== undefined
|
|
78
|
+
? { type: 'rpc-end', reqId, exitCode }
|
|
79
|
+
: { type: 'rpc-end', reqId };
|
|
80
|
+
send((0, frames_js_1.serializeFrame)(endFrame));
|
|
81
|
+
},
|
|
82
|
+
onError: (error) => sendResult(reqId, false, undefined, error, send),
|
|
83
|
+
}, logger);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
default: {
|
|
87
|
+
sendResult(reqId, false, undefined, `unknown action: ${action}`, send);
|
|
88
|
+
logger.warn({ action }, 'unknown rpc action');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
94
|
+
sendResult(reqId, false, undefined, msg, send);
|
|
95
|
+
logger.warn({ action, err: msg }, 'rpc handler error');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/handlers/rpc/router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Logger } from '../../logging/logger.js';
|
|
2
|
+
type Send = (frame: string) => void;
|
|
3
|
+
/**
|
|
4
|
+
* 多会话管理:单 ws 连接复用多个 pty,按 sessionId 索引。
|
|
5
|
+
* terminal.open → 创建;input → 写;resize → 调整;close → kill+清理。
|
|
6
|
+
* 退出时自动从 Map 删除。
|
|
7
|
+
*/
|
|
8
|
+
export declare class TerminalManager {
|
|
9
|
+
private sessions;
|
|
10
|
+
private logger;
|
|
11
|
+
private send;
|
|
12
|
+
constructor(send: Send, logger: Logger);
|
|
13
|
+
open(sessionId: string, cols: number, rows: number): void;
|
|
14
|
+
input(sessionId: string, data: string): void;
|
|
15
|
+
resize(sessionId: string, cols: number, rows: number): void;
|
|
16
|
+
close(sessionId: string): void;
|
|
17
|
+
/** 连接断开时清理全部会话 */
|
|
18
|
+
closeAll(): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TerminalManager = void 0;
|
|
4
|
+
const frames_js_1 = require("../../client/frames.js");
|
|
5
|
+
const pty_session_js_1 = require("./pty-session.js");
|
|
6
|
+
/**
|
|
7
|
+
* 多会话管理:单 ws 连接复用多个 pty,按 sessionId 索引。
|
|
8
|
+
* terminal.open → 创建;input → 写;resize → 调整;close → kill+清理。
|
|
9
|
+
* 退出时自动从 Map 删除。
|
|
10
|
+
*/
|
|
11
|
+
class TerminalManager {
|
|
12
|
+
sessions = new Map();
|
|
13
|
+
logger;
|
|
14
|
+
send;
|
|
15
|
+
constructor(send, logger) {
|
|
16
|
+
this.send = send;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
}
|
|
19
|
+
open(sessionId, cols, rows) {
|
|
20
|
+
if (this.sessions.has(sessionId)) {
|
|
21
|
+
this.logger.warn({ sessionId }, 'terminal.open for existing session, ignore');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let session;
|
|
25
|
+
try {
|
|
26
|
+
session = new pty_session_js_1.PtySession(sessionId, cols, rows, {
|
|
27
|
+
onData: (data) => this.send((0, frames_js_1.serializeFrame)({ type: 'terminal.data', sessionId, data })),
|
|
28
|
+
onExit: (exitCode) => {
|
|
29
|
+
this.send((0, frames_js_1.serializeFrame)({ type: 'terminal.exit', sessionId, exitCode }));
|
|
30
|
+
this.sessions.delete(sessionId);
|
|
31
|
+
this.logger.info({ sessionId, exitCode }, 'pty exited');
|
|
32
|
+
},
|
|
33
|
+
}, this.logger);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
// pty.spawn 同步抛(如 spawn-helper 丢失可执行位 → posix_spawnp EACCES、
|
|
37
|
+
// shell/cwd 不存在等):不能让单会话失败把整个守护进程拖崩(否则所有终端断、
|
|
38
|
+
// CLI 离线)。回一条 terminal.exit 让 web 侧优雅收尾,并落日志便于排查。
|
|
39
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40
|
+
this.logger.error({ sessionId, err: msg, cols, rows }, 'pty spawn failed, sending terminal.exit');
|
|
41
|
+
this.send((0, frames_js_1.serializeFrame)({ type: 'terminal.exit', sessionId, exitCode: -1 }));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.sessions.set(sessionId, session);
|
|
45
|
+
}
|
|
46
|
+
input(sessionId, data) {
|
|
47
|
+
const s = this.sessions.get(sessionId);
|
|
48
|
+
if (!s) {
|
|
49
|
+
this.logger.warn({ sessionId }, 'terminal.input for unknown session');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
s.write(data);
|
|
53
|
+
}
|
|
54
|
+
resize(sessionId, cols, rows) {
|
|
55
|
+
const s = this.sessions.get(sessionId);
|
|
56
|
+
if (!s)
|
|
57
|
+
return;
|
|
58
|
+
s.resize(cols, rows);
|
|
59
|
+
}
|
|
60
|
+
close(sessionId) {
|
|
61
|
+
const s = this.sessions.get(sessionId);
|
|
62
|
+
if (!s)
|
|
63
|
+
return;
|
|
64
|
+
s.kill();
|
|
65
|
+
this.sessions.delete(sessionId);
|
|
66
|
+
}
|
|
67
|
+
/** 连接断开时清理全部会话 */
|
|
68
|
+
closeAll() {
|
|
69
|
+
for (const s of this.sessions.values())
|
|
70
|
+
s.kill();
|
|
71
|
+
this.sessions.clear();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.TerminalManager = TerminalManager;
|
|
75
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/handlers/terminal/manager.ts"],"names":[],"mappings":";;;AACA,sDAA8F;AAC9F,qDAA8C;AAI9C;;;;GAIG;AACH,MAAa,eAAe;IAClB,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;IACzC,MAAM,CAAS;IACf,IAAI,CAAO;IAEnB,YAAY,IAAU,EAAE,MAAc;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,SAAiB,EAAE,IAAY,EAAE,IAAY;QAChD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,4CAA4C,CAAC,CAAC;YAC9E,OAAO;QACT,CAAC;QACD,IAAI,OAAmB,CAAC;QACxB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,2BAAU,CACtB,SAAS,EACT,IAAI,EACJ,IAAI,EACJ;gBACE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAA,0BAAc,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAuB,CAAC,CAAC;gBAC5G,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE;oBACnB,IAAI,CAAC,IAAI,CAAC,IAAA,0BAAc,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAuB,CAAC,CAAC,CAAC;oBAC/F,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,YAAY,CAAC,CAAC;gBAC1D,CAAC;aACF,EACD,IAAI,CAAC,MAAM,CACZ,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,6DAA6D;YAC7D,6CAA6C;YAC7C,kDAAkD;YAClD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,yCAAyC,CAAC,CAAC;YAClG,IAAI,CAAC,IAAI,CAAC,IAAA,0BAAc,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAuB,CAAC,CAAC,CAAC;YACnG,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,SAAiB,EAAE,IAAY;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,oCAAoC,CAAC,CAAC;YACtE,OAAO;QACT,CAAC;QACD,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,SAAiB,EAAE,IAAY,EAAE,IAAY;QAClD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,SAAiB;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,CAAC,CAAC,IAAI,EAAE,CAAC;QACT,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,kBAAkB;IAClB,QAAQ;QACN,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF;AAtED,0CAsEC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Logger } from '../../logging/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* 单个伪终端会话封装:node-pty spawn,输出转发为 terminal.data,退出转发 terminal.exit。
|
|
4
|
+
* 按 sessionId 索引,由 manager 维护。
|
|
5
|
+
*/
|
|
6
|
+
export interface PtyCallbacks {
|
|
7
|
+
onData: (data: string) => void;
|
|
8
|
+
onExit: (exitCode: number) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare class PtySession {
|
|
11
|
+
private term;
|
|
12
|
+
private logger;
|
|
13
|
+
constructor(sessionId: string, cols: number, rows: number, cb: PtyCallbacks, logger: Logger);
|
|
14
|
+
write(data: string): void;
|
|
15
|
+
resize(cols: number, rows: number): void;
|
|
16
|
+
kill(): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PtySession = void 0;
|
|
37
|
+
const pty = __importStar(require("node-pty"));
|
|
38
|
+
class PtySession {
|
|
39
|
+
term;
|
|
40
|
+
logger;
|
|
41
|
+
constructor(sessionId, cols, rows, cb, logger) {
|
|
42
|
+
this.logger = logger.child({ sessionId });
|
|
43
|
+
const shell = process.env.SHELL || (process.platform === 'win32' ? 'powershell.exe' : 'bash');
|
|
44
|
+
this.term = pty.spawn(shell, [], {
|
|
45
|
+
name: 'xterm-256color',
|
|
46
|
+
cols: Math.max(1, cols),
|
|
47
|
+
rows: Math.max(1, rows),
|
|
48
|
+
cwd: process.env.HOME || process.cwd(),
|
|
49
|
+
env: process.env,
|
|
50
|
+
});
|
|
51
|
+
this.term.onData((data) => cb.onData(data));
|
|
52
|
+
this.term.onExit(({ exitCode }) => cb.onExit(exitCode));
|
|
53
|
+
this.logger.info({ shell, cols, rows }, 'pty spawned');
|
|
54
|
+
}
|
|
55
|
+
write(data) {
|
|
56
|
+
this.term.write(data);
|
|
57
|
+
}
|
|
58
|
+
resize(cols, rows) {
|
|
59
|
+
try {
|
|
60
|
+
this.term.resize(Math.max(1, cols), Math.max(1, rows));
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
this.logger.warn({ err: err instanceof Error ? err.message : String(err) }, 'pty resize failed');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
kill() {
|
|
67
|
+
try {
|
|
68
|
+
this.term.kill();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// 已退出则忽略
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.PtySession = PtySession;
|
|
76
|
+
//# sourceMappingURL=pty-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty-session.js","sourceRoot":"","sources":["../../../src/handlers/terminal/pty-session.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8CAAgC;AAchC,MAAa,UAAU;IACb,IAAI,CAAW;IACf,MAAM,CAAS;IAEvB,YACE,SAAiB,EACjB,IAAY,EACZ,IAAY,EACZ,EAAgB,EAChB,MAAc;QAEd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC9F,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE;YAC/B,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC;YACvB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC;YACvB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE;YACtC,GAAG,EAAE,OAAO,CAAC,GAA6B;SAC3C,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,mBAAmB,CAAC,CAAC;QACnG,CAAC;IACH,CAAC;IAED,IAAI;QACF,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;CACF;AA5CD,gCA4CC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ServerToCliFrame } from '../../client/frames.js';
|
|
2
|
+
import { TerminalManager } from './manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* 终端帧路由:terminal.open/input/resize/close → manager。
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleTerminalFrame(frame: ServerToCliFrame, manager: TerminalManager): boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleTerminalFrame = handleTerminalFrame;
|
|
4
|
+
/**
|
|
5
|
+
* 终端帧路由:terminal.open/input/resize/close → manager。
|
|
6
|
+
*/
|
|
7
|
+
function handleTerminalFrame(frame, manager) {
|
|
8
|
+
switch (frame.type) {
|
|
9
|
+
case 'terminal.open':
|
|
10
|
+
manager.open(frame.sessionId, frame.cols, frame.rows);
|
|
11
|
+
return true;
|
|
12
|
+
case 'terminal.input':
|
|
13
|
+
manager.input(frame.sessionId, frame.data);
|
|
14
|
+
return true;
|
|
15
|
+
case 'terminal.resize':
|
|
16
|
+
manager.resize(frame.sessionId, frame.cols, frame.rows);
|
|
17
|
+
return true;
|
|
18
|
+
case 'terminal.close':
|
|
19
|
+
manager.close(frame.sessionId);
|
|
20
|
+
return true;
|
|
21
|
+
default:
|
|
22
|
+
return false; // 非终端帧
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/handlers/terminal/router.ts"],"names":[],"mappings":";;AAYA,kDAoBC;AAvBD;;GAEG;AACH,SAAgB,mBAAmB,CACjC,KAAuB,EACvB,OAAwB;IAExB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,eAAe;YAClB,OAAO,CAAC,IAAI,CAAE,KAA2B,CAAC,SAAS,EAAG,KAA2B,CAAC,IAAI,EAAG,KAA2B,CAAC,IAAI,CAAC,CAAC;YAC3H,OAAO,IAAI,CAAC;QACd,KAAK,gBAAgB;YACnB,OAAO,CAAC,KAAK,CAAE,KAA4B,CAAC,SAAS,EAAG,KAA4B,CAAC,IAAI,CAAC,CAAC;YAC3F,OAAO,IAAI,CAAC;QACd,KAAK,iBAAiB;YACpB,OAAO,CAAC,MAAM,CAAE,KAA6B,CAAC,SAAS,EAAG,KAA6B,CAAC,IAAI,EAAG,KAA6B,CAAC,IAAI,CAAC,CAAC;YACnI,OAAO,IAAI,CAAC;QACd,KAAK,gBAAgB;YACnB,OAAO,CAAC,KAAK,CAAE,KAA4B,CAAC,SAAS,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC;QACd;YACE,OAAO,KAAK,CAAC,CAAC,OAAO;IACzB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateInstanceId = generateInstanceId;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
/**
|
|
6
|
+
* 生成实例 id:inst-YYYYMMDD-6位hex。
|
|
7
|
+
* 注意:不能用 Math.random()/Date.now()(运行时受限),用 crypto.randomBytes。
|
|
8
|
+
*/
|
|
9
|
+
function generateInstanceId() {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const y = now.getUTCFullYear();
|
|
12
|
+
const m = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
13
|
+
const d = String(now.getUTCDate()).padStart(2, '0');
|
|
14
|
+
const hex = (0, node_crypto_1.randomBytes)(3).toString('hex'); // 6 位
|
|
15
|
+
return `inst-${y}${m}${d}-${hex}`;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/instance/id.ts"],"names":[],"mappings":";;AAMA,gDAOC;AAbD,6CAA0C;AAE1C;;;GAGG;AACH,SAAgB,kBAAkB;IAChC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;IAC/B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,IAAA,yBAAW,EAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;IAClD,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { InstanceRecord, InstanceStatus, WsState } from './types.js';
|
|
2
|
+
export declare function tokenHash(privateKey: string): string;
|
|
3
|
+
export declare function loadInstance(id: string): InstanceRecord | null;
|
|
4
|
+
export declare function saveInstance(rec: InstanceRecord): void;
|
|
5
|
+
export declare function removeInstance(id: string): void;
|
|
6
|
+
/** 列出所有实例状态记录 */
|
|
7
|
+
export declare function listInstances(): InstanceRecord[];
|
|
8
|
+
/** 更新实例部分字段 */
|
|
9
|
+
export declare function updateInstance(id: string, patch: Partial<InstanceRecord>): InstanceRecord | null;
|
|
10
|
+
export declare function setStatus(id: string, status: InstanceStatus, wsState?: WsState): void;
|
|
11
|
+
/** 进程是否存活(kill 0 探测,不发信号) */
|
|
12
|
+
export declare function isPidAlive(pid: number): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 清理 PID 已死的残留记录——daemon 正常退出已自删,但崩溃(SIGKILL/段错误)
|
|
15
|
+
* 来不及自删时会留下 stale "running" 记录。此函数自愈:遍历全部记录,PID 死则删文件。
|
|
16
|
+
* 返回清理条数。
|
|
17
|
+
*/
|
|
18
|
+
export declare function purgeDeadInstances(): number;
|
|
19
|
+
export interface KillResult {
|
|
20
|
+
/** 杀掉的实例记录,无则 null */
|
|
21
|
+
rec: InstanceRecord | null;
|
|
22
|
+
/** 是否成功终止 */
|
|
23
|
+
ok: boolean;
|
|
24
|
+
/** 说明(为何杀/结果) */
|
|
25
|
+
note: string;
|
|
26
|
+
}
|
|
27
|
+
/** 按 sandboxId 找到仍在运行的实例(PID 存活、status=running) */
|
|
28
|
+
export declare function findRunningBySandboxId(sandboxId: string): InstanceRecord | null;
|
|
29
|
+
/**
|
|
30
|
+
* 终止某实例进程并清理其记录。同步等待 SIGTERM,超时 SIGKILL(最多约 5s)。
|
|
31
|
+
* 用于「同 sandboxId 只允许一个进程」时,start 前杀掉旧实例。
|
|
32
|
+
*/
|
|
33
|
+
export declare function killInstance(id: string): Promise<KillResult>;
|