@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.
Files changed (99) hide show
  1. package/README.md +69 -0
  2. package/dist/cli/config-cmd.d.ts +3 -0
  3. package/dist/cli/config-cmd.js +60 -0
  4. package/dist/cli/config-cmd.js.map +1 -0
  5. package/dist/cli/index.d.ts +2 -0
  6. package/dist/cli/index.js +30 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/cli/list-cmd.d.ts +3 -0
  9. package/dist/cli/list-cmd.js +36 -0
  10. package/dist/cli/list-cmd.js.map +1 -0
  11. package/dist/cli/log-cmd.d.ts +3 -0
  12. package/dist/cli/log-cmd.js +118 -0
  13. package/dist/cli/log-cmd.js.map +1 -0
  14. package/dist/cli/start-cmd.d.ts +3 -0
  15. package/dist/cli/start-cmd.js +69 -0
  16. package/dist/cli/start-cmd.js.map +1 -0
  17. package/dist/cli/status-cmd.d.ts +3 -0
  18. package/dist/cli/status-cmd.js +46 -0
  19. package/dist/cli/status-cmd.js.map +1 -0
  20. package/dist/cli/stop-cmd.d.ts +3 -0
  21. package/dist/cli/stop-cmd.js +115 -0
  22. package/dist/cli/stop-cmd.js.map +1 -0
  23. package/dist/cli/update-cmd.d.ts +3 -0
  24. package/dist/cli/update-cmd.js +16 -0
  25. package/dist/cli/update-cmd.js.map +1 -0
  26. package/dist/client/frames.d.ts +111 -0
  27. package/dist/client/frames.js +22 -0
  28. package/dist/client/frames.js.map +1 -0
  29. package/dist/client/handshake.d.ts +7 -0
  30. package/dist/client/handshake.js +83 -0
  31. package/dist/client/handshake.js.map +1 -0
  32. package/dist/client/protocol.d.ts +36 -0
  33. package/dist/client/protocol.js +39 -0
  34. package/dist/client/protocol.js.map +1 -0
  35. package/dist/client/ws-client.d.ts +48 -0
  36. package/dist/client/ws-client.js +272 -0
  37. package/dist/client/ws-client.js.map +1 -0
  38. package/dist/config/index.d.ts +6 -0
  39. package/dist/config/index.js +68 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/config/paths.d.ts +14 -0
  42. package/dist/config/paths.js +41 -0
  43. package/dist/config/paths.js.map +1 -0
  44. package/dist/config/schema.d.ts +17 -0
  45. package/dist/config/schema.js +52 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/daemon/detach.d.ts +17 -0
  48. package/dist/daemon/detach.js +30 -0
  49. package/dist/daemon/detach.js.map +1 -0
  50. package/dist/daemon/launch.d.ts +15 -0
  51. package/dist/daemon/launch.js +70 -0
  52. package/dist/daemon/launch.js.map +1 -0
  53. package/dist/daemon/signal.d.ts +7 -0
  54. package/dist/daemon/signal.js +22 -0
  55. package/dist/daemon/signal.js.map +1 -0
  56. package/dist/handlers/rpc/exec-shell.d.ts +13 -0
  57. package/dist/handlers/rpc/exec-shell.js +52 -0
  58. package/dist/handlers/rpc/exec-shell.js.map +1 -0
  59. package/dist/handlers/rpc/file-ops.d.ts +17 -0
  60. package/dist/handlers/rpc/file-ops.js +163 -0
  61. package/dist/handlers/rpc/file-ops.js.map +1 -0
  62. package/dist/handlers/rpc/router.d.ts +9 -0
  63. package/dist/handlers/rpc/router.js +98 -0
  64. package/dist/handlers/rpc/router.js.map +1 -0
  65. package/dist/handlers/terminal/manager.d.ts +20 -0
  66. package/dist/handlers/terminal/manager.js +75 -0
  67. package/dist/handlers/terminal/manager.js.map +1 -0
  68. package/dist/handlers/terminal/pty-session.d.ts +17 -0
  69. package/dist/handlers/terminal/pty-session.js +76 -0
  70. package/dist/handlers/terminal/pty-session.js.map +1 -0
  71. package/dist/handlers/terminal/router.d.ts +6 -0
  72. package/dist/handlers/terminal/router.js +25 -0
  73. package/dist/handlers/terminal/router.js.map +1 -0
  74. package/dist/instance/id.d.ts +5 -0
  75. package/dist/instance/id.js +17 -0
  76. package/dist/instance/id.js.map +1 -0
  77. package/dist/instance/store.d.ts +33 -0
  78. package/dist/instance/store.js +155 -0
  79. package/dist/instance/store.js.map +1 -0
  80. package/dist/instance/types.d.ts +28 -0
  81. package/dist/instance/types.js +7 -0
  82. package/dist/instance/types.js.map +1 -0
  83. package/dist/logging/logger.d.ts +11 -0
  84. package/dist/logging/logger.js +42 -0
  85. package/dist/logging/logger.js.map +1 -0
  86. package/dist/system/report.d.ts +2 -0
  87. package/dist/system/report.js +61 -0
  88. package/dist/system/report.js.map +1 -0
  89. package/dist/update/self-update.d.ts +8 -0
  90. package/dist/update/self-update.js +41 -0
  91. package/dist/update/self-update.js.map +1 -0
  92. package/dist/utils/info.d.ts +11 -0
  93. package/dist/utils/info.js +21 -0
  94. package/dist/utils/info.js.map +1 -0
  95. package/dist/utils/pem.d.ts +15 -0
  96. package/dist/utils/pem.js +31 -0
  97. package/dist/utils/pem.js.map +1 -0
  98. package/package.json +44 -0
  99. 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":"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"}
@@ -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,5 @@
1
+ /**
2
+ * 生成实例 id:inst-YYYYMMDD-6位hex。
3
+ * 注意:不能用 Math.random()/Date.now()(运行时受限),用 crypto.randomBytes。
4
+ */
5
+ export declare function generateInstanceId(): string;
@@ -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>;