@treesap/sandbox 0.2.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/CHANGELOG.md +107 -0
- package/README.md +495 -0
- package/dist/api-server.d.ts +41 -0
- package/dist/api-server.d.ts.map +1 -0
- package/dist/api-server.js +536 -0
- package/dist/api-server.js.map +1 -0
- package/dist/auth-middleware.d.ts +31 -0
- package/dist/auth-middleware.d.ts.map +1 -0
- package/dist/auth-middleware.js +35 -0
- package/dist/auth-middleware.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +137 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +412 -0
- package/dist/client.js.map +1 -0
- package/dist/file-service.d.ts +94 -0
- package/dist/file-service.d.ts.map +1 -0
- package/dist/file-service.js +203 -0
- package/dist/file-service.js.map +1 -0
- package/dist/http-exposure-service.d.ts +71 -0
- package/dist/http-exposure-service.d.ts.map +1 -0
- package/dist/http-exposure-service.js +172 -0
- package/dist/http-exposure-service.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox-manager.d.ts +76 -0
- package/dist/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox-manager.js +161 -0
- package/dist/sandbox-manager.js.map +1 -0
- package/dist/sandbox.d.ts +118 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +303 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +240 -0
- package/dist/server.js.map +1 -0
- package/dist/stream-service.d.ts +35 -0
- package/dist/stream-service.d.ts.map +1 -0
- package/dist/stream-service.js +136 -0
- package/dist/stream-service.js.map +1 -0
- package/dist/terminal.d.ts +46 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +264 -0
- package/dist/terminal.js.map +1 -0
- package/dist/websocket.d.ts +48 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +332 -0
- package/dist/websocket.js.map +1 -0
- package/package.json +59 -0
- package/src/api-server.ts +658 -0
- package/src/auth-middleware.ts +65 -0
- package/src/cli.ts +71 -0
- package/src/client.ts +537 -0
- package/src/file-service.ts +273 -0
- package/src/http-exposure-service.ts +232 -0
- package/src/index.ts +101 -0
- package/src/sandbox-manager.ts +202 -0
- package/src/sandbox.ts +396 -0
- package/src/stream-service.ts +174 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Sandbox, ExecOptions } from './sandbox';
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
|
|
4
|
+
export type ExecEventType = 'start' | 'stdout' | 'stderr' | 'complete' | 'error';
|
|
5
|
+
|
|
6
|
+
export interface ExecEvent {
|
|
7
|
+
type: ExecEventType;
|
|
8
|
+
data?: string;
|
|
9
|
+
exitCode?: number;
|
|
10
|
+
error?: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface LogEvent {
|
|
15
|
+
type: 'log';
|
|
16
|
+
data: string;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
stream?: 'stdout' | 'stderr';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Service for creating Server-Sent Events (SSE) streams
|
|
23
|
+
*/
|
|
24
|
+
export class StreamService {
|
|
25
|
+
/**
|
|
26
|
+
* Create an SSE stream for command execution
|
|
27
|
+
*/
|
|
28
|
+
static createExecStream(sandbox: Sandbox, command: string, options: ExecOptions = {}): Readable {
|
|
29
|
+
const stream = new Readable({
|
|
30
|
+
read() {},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const sendEvent = (event: ExecEvent) => {
|
|
34
|
+
const data = JSON.stringify(event);
|
|
35
|
+
stream.push(`data: ${data}\n\n`);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Send start event
|
|
39
|
+
sendEvent({
|
|
40
|
+
type: 'start',
|
|
41
|
+
timestamp: Date.now(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Execute command with streaming
|
|
45
|
+
sandbox
|
|
46
|
+
.exec(command, {
|
|
47
|
+
...options,
|
|
48
|
+
stream: true,
|
|
49
|
+
onOutput: (outputStream, data) => {
|
|
50
|
+
sendEvent({
|
|
51
|
+
type: outputStream,
|
|
52
|
+
data,
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
.then((result) => {
|
|
58
|
+
// Send complete event
|
|
59
|
+
sendEvent({
|
|
60
|
+
type: 'complete',
|
|
61
|
+
exitCode: result.exitCode,
|
|
62
|
+
timestamp: Date.now(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
stream.push(null); // End stream
|
|
66
|
+
})
|
|
67
|
+
.catch((error) => {
|
|
68
|
+
// Send error event
|
|
69
|
+
sendEvent({
|
|
70
|
+
type: 'error',
|
|
71
|
+
error: error.message,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
stream.push(null); // End stream
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return stream;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create an SSE stream for process logs
|
|
83
|
+
*/
|
|
84
|
+
static createProcessLogStream(sandbox: Sandbox, processId: string): Readable {
|
|
85
|
+
const stream = new Readable({
|
|
86
|
+
read() {},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const sendEvent = (event: LogEvent) => {
|
|
90
|
+
const data = JSON.stringify(event);
|
|
91
|
+
stream.push(`data: ${data}\n\n`);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Listen for process output
|
|
95
|
+
const outputListener = (data: any) => {
|
|
96
|
+
if (data.processId === processId) {
|
|
97
|
+
sendEvent({
|
|
98
|
+
type: 'log',
|
|
99
|
+
data: data.data,
|
|
100
|
+
stream: data.stream,
|
|
101
|
+
timestamp: Date.now(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const exitListener = (data: any) => {
|
|
107
|
+
if (data.processId === processId) {
|
|
108
|
+
// End stream when process exits
|
|
109
|
+
stream.push(null);
|
|
110
|
+
cleanup();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Attach listeners
|
|
115
|
+
sandbox.on('process_output', outputListener);
|
|
116
|
+
sandbox.on('process_exit', exitListener);
|
|
117
|
+
|
|
118
|
+
// Cleanup function
|
|
119
|
+
const cleanup = () => {
|
|
120
|
+
sandbox.off('process_output', outputListener);
|
|
121
|
+
sandbox.off('process_exit', exitListener);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Cleanup on stream close
|
|
125
|
+
stream.on('close', cleanup);
|
|
126
|
+
|
|
127
|
+
return stream;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Helper to parse SSE stream on client side
|
|
132
|
+
* This is exported for client library usage
|
|
133
|
+
*/
|
|
134
|
+
static async *parseSSEStream<T = ExecEvent | LogEvent>(
|
|
135
|
+
stream: ReadableStream
|
|
136
|
+
): AsyncGenerator<T> {
|
|
137
|
+
const reader = stream.getReader();
|
|
138
|
+
const decoder = new TextDecoder();
|
|
139
|
+
let buffer = '';
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
while (true) {
|
|
143
|
+
const { done, value } = await reader.read();
|
|
144
|
+
|
|
145
|
+
if (done) {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
buffer += decoder.decode(value, { stream: true });
|
|
150
|
+
|
|
151
|
+
// Split by double newline (SSE event delimiter)
|
|
152
|
+
const events = buffer.split('\n\n');
|
|
153
|
+
buffer = events.pop() || '';
|
|
154
|
+
|
|
155
|
+
for (const event of events) {
|
|
156
|
+
if (event.trim()) {
|
|
157
|
+
// Parse SSE format (data: {json})
|
|
158
|
+
const match = event.match(/^data: (.+)$/m);
|
|
159
|
+
if (match) {
|
|
160
|
+
try {
|
|
161
|
+
const data = JSON.parse(match[1]);
|
|
162
|
+
yield data as T;
|
|
163
|
+
} catch {
|
|
164
|
+
// Ignore parse errors
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} finally {
|
|
171
|
+
reader.releaseLock();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noImplicitAny": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"rootDir": "./src",
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"declarationMap": true,
|
|
17
|
+
"sourceMap": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"jsxImportSource": "hono/jsx",
|
|
20
|
+
"types": ["node"],
|
|
21
|
+
"resolveJsonModule": true,
|
|
22
|
+
"allowJs": true,
|
|
23
|
+
"isolatedModules": true
|
|
24
|
+
},
|
|
25
|
+
"include": [
|
|
26
|
+
"src/**/*.ts",
|
|
27
|
+
"src/**/*.tsx"
|
|
28
|
+
],
|
|
29
|
+
"exclude": [
|
|
30
|
+
"node_modules",
|
|
31
|
+
"dist",
|
|
32
|
+
"examples"
|
|
33
|
+
],
|
|
34
|
+
"ts-node": {
|
|
35
|
+
"esm": true
|
|
36
|
+
}
|
|
37
|
+
}
|