@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.
Files changed (66) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +495 -0
  3. package/dist/api-server.d.ts +41 -0
  4. package/dist/api-server.d.ts.map +1 -0
  5. package/dist/api-server.js +536 -0
  6. package/dist/api-server.js.map +1 -0
  7. package/dist/auth-middleware.d.ts +31 -0
  8. package/dist/auth-middleware.d.ts.map +1 -0
  9. package/dist/auth-middleware.js +35 -0
  10. package/dist/auth-middleware.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +65 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/client.d.ts +137 -0
  16. package/dist/client.d.ts.map +1 -0
  17. package/dist/client.js +412 -0
  18. package/dist/client.js.map +1 -0
  19. package/dist/file-service.d.ts +94 -0
  20. package/dist/file-service.d.ts.map +1 -0
  21. package/dist/file-service.js +203 -0
  22. package/dist/file-service.js.map +1 -0
  23. package/dist/http-exposure-service.d.ts +71 -0
  24. package/dist/http-exposure-service.d.ts.map +1 -0
  25. package/dist/http-exposure-service.js +172 -0
  26. package/dist/http-exposure-service.js.map +1 -0
  27. package/dist/index.d.ts +59 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +66 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/sandbox-manager.d.ts +76 -0
  32. package/dist/sandbox-manager.d.ts.map +1 -0
  33. package/dist/sandbox-manager.js +161 -0
  34. package/dist/sandbox-manager.js.map +1 -0
  35. package/dist/sandbox.d.ts +118 -0
  36. package/dist/sandbox.d.ts.map +1 -0
  37. package/dist/sandbox.js +303 -0
  38. package/dist/sandbox.js.map +1 -0
  39. package/dist/server.d.ts +7 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +240 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/stream-service.d.ts +35 -0
  44. package/dist/stream-service.d.ts.map +1 -0
  45. package/dist/stream-service.js +136 -0
  46. package/dist/stream-service.js.map +1 -0
  47. package/dist/terminal.d.ts +46 -0
  48. package/dist/terminal.d.ts.map +1 -0
  49. package/dist/terminal.js +264 -0
  50. package/dist/terminal.js.map +1 -0
  51. package/dist/websocket.d.ts +48 -0
  52. package/dist/websocket.d.ts.map +1 -0
  53. package/dist/websocket.js +332 -0
  54. package/dist/websocket.js.map +1 -0
  55. package/package.json +59 -0
  56. package/src/api-server.ts +658 -0
  57. package/src/auth-middleware.ts +65 -0
  58. package/src/cli.ts +71 -0
  59. package/src/client.ts +537 -0
  60. package/src/file-service.ts +273 -0
  61. package/src/http-exposure-service.ts +232 -0
  62. package/src/index.ts +101 -0
  63. package/src/sandbox-manager.ts +202 -0
  64. package/src/sandbox.ts +396 -0
  65. package/src/stream-service.ts +174 -0
  66. 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
+ }