@cloudflare/sandbox 0.0.0-d55b0f4 → 0.0.0-d81d2a5
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 +24 -0
- package/Dockerfile +36 -13
- package/README.md +784 -0
- package/container_src/bun.lock +122 -0
- package/container_src/handler/exec.ts +17 -14
- package/container_src/handler/process.ts +1 -1
- package/container_src/index.ts +171 -1
- package/container_src/jupyter-server.ts +336 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/startup.sh +52 -0
- package/container_src/types.ts +8 -3
- package/package.json +1 -1
- package/src/client.ts +47 -64
- package/src/index.ts +13 -4
- package/src/interpreter-types.ts +383 -0
- package/src/interpreter.ts +150 -0
- package/src/jupyter-client.ts +266 -0
- package/src/sandbox.ts +281 -149
- package/src/types.ts +15 -0
package/src/client.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
+
import type { ExecuteRequest } from "../container_src/types";
|
|
1
2
|
import type { Sandbox } from "./index";
|
|
2
3
|
import type {
|
|
4
|
+
BaseExecOptions,
|
|
3
5
|
GetProcessLogsResponse,
|
|
4
6
|
GetProcessResponse,
|
|
5
7
|
ListProcessesResponse,
|
|
6
8
|
StartProcessRequest,
|
|
7
|
-
StartProcessResponse
|
|
9
|
+
StartProcessResponse,
|
|
8
10
|
} from "./types";
|
|
9
11
|
|
|
10
|
-
interface ExecuteRequest {
|
|
11
|
-
command: string;
|
|
12
|
-
sessionId?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
12
|
export interface ExecuteResponse {
|
|
16
13
|
success: boolean;
|
|
17
14
|
stdout: string;
|
|
@@ -21,22 +18,6 @@ export interface ExecuteResponse {
|
|
|
21
18
|
timestamp: string;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
interface SessionResponse {
|
|
25
|
-
sessionId: string;
|
|
26
|
-
message: string;
|
|
27
|
-
timestamp: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface SessionListResponse {
|
|
31
|
-
sessions: Array<{
|
|
32
|
-
sessionId: string;
|
|
33
|
-
hasActiveProcess: boolean;
|
|
34
|
-
createdAt: string;
|
|
35
|
-
}>;
|
|
36
|
-
count: number;
|
|
37
|
-
timestamp: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
21
|
interface CommandsResponse {
|
|
41
22
|
availableCommands: string[];
|
|
42
23
|
timestamp: string;
|
|
@@ -212,7 +193,7 @@ export class HttpClient {
|
|
|
212
193
|
this.baseUrl = this.options.baseUrl!;
|
|
213
194
|
}
|
|
214
195
|
|
|
215
|
-
|
|
196
|
+
protected async doFetch(
|
|
216
197
|
path: string,
|
|
217
198
|
options?: RequestInit
|
|
218
199
|
): Promise<Response> {
|
|
@@ -255,16 +236,19 @@ export class HttpClient {
|
|
|
255
236
|
|
|
256
237
|
async execute(
|
|
257
238
|
command: string,
|
|
258
|
-
sessionId
|
|
239
|
+
options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
|
|
259
240
|
): Promise<ExecuteResponse> {
|
|
260
241
|
try {
|
|
261
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
242
|
+
const targetSessionId = options.sessionId || this.sessionId;
|
|
243
|
+
const executeRequest = {
|
|
244
|
+
command,
|
|
245
|
+
sessionId: targetSessionId,
|
|
246
|
+
cwd: options.cwd,
|
|
247
|
+
env: options.env,
|
|
248
|
+
} satisfies ExecuteRequest;
|
|
262
249
|
|
|
263
250
|
const response = await this.doFetch(`/api/execute`, {
|
|
264
|
-
body: JSON.stringify(
|
|
265
|
-
command,
|
|
266
|
-
sessionId: targetSessionId,
|
|
267
|
-
} as ExecuteRequest),
|
|
251
|
+
body: JSON.stringify(executeRequest),
|
|
268
252
|
headers: {
|
|
269
253
|
"Content-Type": "application/json",
|
|
270
254
|
},
|
|
@@ -305,7 +289,6 @@ export class HttpClient {
|
|
|
305
289
|
}
|
|
306
290
|
}
|
|
307
291
|
|
|
308
|
-
|
|
309
292
|
async executeCommandStream(
|
|
310
293
|
command: string,
|
|
311
294
|
sessionId?: string
|
|
@@ -320,7 +303,7 @@ export class HttpClient {
|
|
|
320
303
|
}),
|
|
321
304
|
headers: {
|
|
322
305
|
"Content-Type": "application/json",
|
|
323
|
-
|
|
306
|
+
Accept: "text/event-stream",
|
|
324
307
|
},
|
|
325
308
|
method: "POST",
|
|
326
309
|
});
|
|
@@ -338,9 +321,7 @@ export class HttpClient {
|
|
|
338
321
|
throw new Error("No response body for streaming request");
|
|
339
322
|
}
|
|
340
323
|
|
|
341
|
-
console.log(
|
|
342
|
-
`[HTTP Client] Started command stream: ${command}`
|
|
343
|
-
);
|
|
324
|
+
console.log(`[HTTP Client] Started command stream: ${command}`);
|
|
344
325
|
|
|
345
326
|
return response.body;
|
|
346
327
|
} catch (error) {
|
|
@@ -392,7 +373,6 @@ export class HttpClient {
|
|
|
392
373
|
}
|
|
393
374
|
}
|
|
394
375
|
|
|
395
|
-
|
|
396
376
|
async mkdir(
|
|
397
377
|
path: string,
|
|
398
378
|
recursive: boolean = false,
|
|
@@ -434,7 +414,6 @@ export class HttpClient {
|
|
|
434
414
|
}
|
|
435
415
|
}
|
|
436
416
|
|
|
437
|
-
|
|
438
417
|
async writeFile(
|
|
439
418
|
path: string,
|
|
440
419
|
content: string,
|
|
@@ -478,7 +457,6 @@ export class HttpClient {
|
|
|
478
457
|
}
|
|
479
458
|
}
|
|
480
459
|
|
|
481
|
-
|
|
482
460
|
async readFile(
|
|
483
461
|
path: string,
|
|
484
462
|
encoding: string = "utf-8",
|
|
@@ -520,7 +498,6 @@ export class HttpClient {
|
|
|
520
498
|
}
|
|
521
499
|
}
|
|
522
500
|
|
|
523
|
-
|
|
524
501
|
async deleteFile(
|
|
525
502
|
path: string,
|
|
526
503
|
sessionId?: string
|
|
@@ -560,7 +537,6 @@ export class HttpClient {
|
|
|
560
537
|
}
|
|
561
538
|
}
|
|
562
539
|
|
|
563
|
-
|
|
564
540
|
async renameFile(
|
|
565
541
|
oldPath: string,
|
|
566
542
|
newPath: string,
|
|
@@ -602,7 +578,6 @@ export class HttpClient {
|
|
|
602
578
|
}
|
|
603
579
|
}
|
|
604
580
|
|
|
605
|
-
|
|
606
581
|
async moveFile(
|
|
607
582
|
sourcePath: string,
|
|
608
583
|
destinationPath: string,
|
|
@@ -644,7 +619,6 @@ export class HttpClient {
|
|
|
644
619
|
}
|
|
645
620
|
}
|
|
646
621
|
|
|
647
|
-
|
|
648
622
|
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
649
623
|
try {
|
|
650
624
|
const response = await this.doFetch(`/api/expose-port`, {
|
|
@@ -670,7 +644,9 @@ export class HttpClient {
|
|
|
670
644
|
|
|
671
645
|
const data: ExposePortResponse = await response.json();
|
|
672
646
|
console.log(
|
|
673
|
-
`[HTTP Client] Port exposed: ${port}${
|
|
647
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
648
|
+
name ? ` (${name})` : ""
|
|
649
|
+
}, Success: ${data.success}`
|
|
674
650
|
);
|
|
675
651
|
|
|
676
652
|
return data;
|
|
@@ -732,9 +708,7 @@ export class HttpClient {
|
|
|
732
708
|
}
|
|
733
709
|
|
|
734
710
|
const data: GetExposedPortsResponse = await response.json();
|
|
735
|
-
console.log(
|
|
736
|
-
`[HTTP Client] Got ${data.count} exposed ports`
|
|
737
|
-
);
|
|
711
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
738
712
|
|
|
739
713
|
return data;
|
|
740
714
|
} catch (error) {
|
|
@@ -871,9 +845,7 @@ export class HttpClient {
|
|
|
871
845
|
}
|
|
872
846
|
|
|
873
847
|
const data: ListProcessesResponse = await response.json();
|
|
874
|
-
console.log(
|
|
875
|
-
`[HTTP Client] Listed ${data.processes.length} processes`
|
|
876
|
-
);
|
|
848
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
877
849
|
|
|
878
850
|
return data;
|
|
879
851
|
} catch (error) {
|
|
@@ -902,7 +874,9 @@ export class HttpClient {
|
|
|
902
874
|
|
|
903
875
|
const data: GetProcessResponse = await response.json();
|
|
904
876
|
console.log(
|
|
905
|
-
`[HTTP Client] Got process ${processId}: ${
|
|
877
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
878
|
+
data.process?.status || "not found"
|
|
879
|
+
}`
|
|
906
880
|
);
|
|
907
881
|
|
|
908
882
|
return data;
|
|
@@ -912,7 +886,9 @@ export class HttpClient {
|
|
|
912
886
|
}
|
|
913
887
|
}
|
|
914
888
|
|
|
915
|
-
async killProcess(
|
|
889
|
+
async killProcess(
|
|
890
|
+
processId: string
|
|
891
|
+
): Promise<{ success: boolean; message: string }> {
|
|
916
892
|
try {
|
|
917
893
|
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
918
894
|
headers: {
|
|
@@ -930,10 +906,11 @@ export class HttpClient {
|
|
|
930
906
|
);
|
|
931
907
|
}
|
|
932
908
|
|
|
933
|
-
const data = await response.json() as {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
909
|
+
const data = (await response.json()) as {
|
|
910
|
+
success: boolean;
|
|
911
|
+
message: string;
|
|
912
|
+
};
|
|
913
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
937
914
|
|
|
938
915
|
return data;
|
|
939
916
|
} catch (error) {
|
|
@@ -942,7 +919,11 @@ export class HttpClient {
|
|
|
942
919
|
}
|
|
943
920
|
}
|
|
944
921
|
|
|
945
|
-
async killAllProcesses(): Promise<{
|
|
922
|
+
async killAllProcesses(): Promise<{
|
|
923
|
+
success: boolean;
|
|
924
|
+
killedCount: number;
|
|
925
|
+
message: string;
|
|
926
|
+
}> {
|
|
946
927
|
try {
|
|
947
928
|
const response = await this.doFetch("/api/process/kill-all", {
|
|
948
929
|
headers: {
|
|
@@ -960,10 +941,12 @@ export class HttpClient {
|
|
|
960
941
|
);
|
|
961
942
|
}
|
|
962
943
|
|
|
963
|
-
const data = await response.json() as {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
944
|
+
const data = (await response.json()) as {
|
|
945
|
+
success: boolean;
|
|
946
|
+
killedCount: number;
|
|
947
|
+
message: string;
|
|
948
|
+
};
|
|
949
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
967
950
|
|
|
968
951
|
return data;
|
|
969
952
|
} catch (error) {
|
|
@@ -991,9 +974,7 @@ export class HttpClient {
|
|
|
991
974
|
}
|
|
992
975
|
|
|
993
976
|
const data: GetProcessLogsResponse = await response.json();
|
|
994
|
-
console.log(
|
|
995
|
-
`[HTTP Client] Got logs for process ${processId}`
|
|
996
|
-
);
|
|
977
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
997
978
|
|
|
998
979
|
return data;
|
|
999
980
|
} catch (error) {
|
|
@@ -1002,11 +983,13 @@ export class HttpClient {
|
|
|
1002
983
|
}
|
|
1003
984
|
}
|
|
1004
985
|
|
|
1005
|
-
async streamProcessLogs(
|
|
986
|
+
async streamProcessLogs(
|
|
987
|
+
processId: string
|
|
988
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
1006
989
|
try {
|
|
1007
990
|
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
1008
991
|
headers: {
|
|
1009
|
-
|
|
992
|
+
Accept: "text/event-stream",
|
|
1010
993
|
"Cache-Control": "no-cache",
|
|
1011
994
|
},
|
|
1012
995
|
method: "GET",
|
package/src/index.ts
CHANGED
|
@@ -5,16 +5,25 @@ export type {
|
|
|
5
5
|
MkdirResponse, MoveFileResponse,
|
|
6
6
|
ReadFileResponse, RenameFileResponse, WriteFileResponse
|
|
7
7
|
} from "./client";
|
|
8
|
-
|
|
8
|
+
// Export code interpreter types
|
|
9
|
+
export type {
|
|
10
|
+
ChartData,
|
|
11
|
+
CodeContext,
|
|
12
|
+
CreateContextOptions,
|
|
13
|
+
Execution,
|
|
14
|
+
ExecutionError,
|
|
15
|
+
OutputMessage,
|
|
16
|
+
Result,
|
|
17
|
+
RunCodeOptions
|
|
18
|
+
} from "./interpreter-types";
|
|
19
|
+
// Export the implementations
|
|
20
|
+
export { ResultImpl } from "./interpreter-types";
|
|
9
21
|
// Re-export request handler utilities
|
|
10
22
|
export {
|
|
11
23
|
proxyToSandbox, type RouteInfo, type SandboxEnv
|
|
12
24
|
} from './request-handler';
|
|
13
|
-
|
|
14
25
|
export { getSandbox, Sandbox } from "./sandbox";
|
|
15
|
-
|
|
16
26
|
// Export SSE parser for converting ReadableStream to AsyncIterable
|
|
17
27
|
export { asyncIterableToSSEStream, parseSSEStream, responseToAsyncIterable } from "./sse-parser";
|
|
18
|
-
|
|
19
28
|
// Export event types for streaming
|
|
20
29
|
export type { ExecEvent, LogEvent } from "./types";
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
// Context Management
|
|
2
|
+
export interface CreateContextOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Programming language for the context
|
|
5
|
+
* @default 'python'
|
|
6
|
+
*/
|
|
7
|
+
language?: 'python' | 'javascript' | 'typescript';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Working directory for the context
|
|
11
|
+
* @default '/workspace'
|
|
12
|
+
*/
|
|
13
|
+
cwd?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Environment variables for the context
|
|
17
|
+
*/
|
|
18
|
+
envVars?: Record<string, string>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Request timeout in milliseconds
|
|
22
|
+
* @default 30000
|
|
23
|
+
*/
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CodeContext {
|
|
28
|
+
/**
|
|
29
|
+
* Unique identifier for the context
|
|
30
|
+
*/
|
|
31
|
+
readonly id: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Programming language of the context
|
|
35
|
+
*/
|
|
36
|
+
readonly language: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Current working directory
|
|
40
|
+
*/
|
|
41
|
+
readonly cwd: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* When the context was created
|
|
45
|
+
*/
|
|
46
|
+
readonly createdAt: Date;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* When the context was last used
|
|
50
|
+
*/
|
|
51
|
+
readonly lastUsed: Date;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Execution Options
|
|
55
|
+
export interface RunCodeOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Context to run the code in. If not provided, uses default context for the language
|
|
58
|
+
*/
|
|
59
|
+
context?: CodeContext;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Language to use if context is not provided
|
|
63
|
+
* @default 'python'
|
|
64
|
+
*/
|
|
65
|
+
language?: 'python' | 'javascript' | 'typescript';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Environment variables for this execution
|
|
69
|
+
*/
|
|
70
|
+
envVars?: Record<string, string>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Execution timeout in milliseconds
|
|
74
|
+
* @default 60000
|
|
75
|
+
*/
|
|
76
|
+
timeout?: number;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* AbortSignal for cancelling execution
|
|
80
|
+
*/
|
|
81
|
+
signal?: AbortSignal;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Callback for stdout output
|
|
85
|
+
*/
|
|
86
|
+
onStdout?: (output: OutputMessage) => void | Promise<void>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Callback for stderr output
|
|
90
|
+
*/
|
|
91
|
+
onStderr?: (output: OutputMessage) => void | Promise<void>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Callback for execution results (charts, tables, etc)
|
|
95
|
+
*/
|
|
96
|
+
onResult?: (result: Result) => void | Promise<void>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Callback for execution errors
|
|
100
|
+
*/
|
|
101
|
+
onError?: (error: ExecutionError) => void | Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Output Messages
|
|
105
|
+
export interface OutputMessage {
|
|
106
|
+
/**
|
|
107
|
+
* The output text
|
|
108
|
+
*/
|
|
109
|
+
text: string;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Timestamp of the output
|
|
113
|
+
*/
|
|
114
|
+
timestamp: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Execution Results
|
|
118
|
+
export interface Result {
|
|
119
|
+
/**
|
|
120
|
+
* Plain text representation
|
|
121
|
+
*/
|
|
122
|
+
text?: string;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* HTML representation (tables, formatted output)
|
|
126
|
+
*/
|
|
127
|
+
html?: string;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* PNG image data (base64 encoded)
|
|
131
|
+
*/
|
|
132
|
+
png?: string;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* JPEG image data (base64 encoded)
|
|
136
|
+
*/
|
|
137
|
+
jpeg?: string;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* SVG image data
|
|
141
|
+
*/
|
|
142
|
+
svg?: string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* LaTeX representation
|
|
146
|
+
*/
|
|
147
|
+
latex?: string;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Markdown representation
|
|
151
|
+
*/
|
|
152
|
+
markdown?: string;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* JavaScript code to execute
|
|
156
|
+
*/
|
|
157
|
+
javascript?: string;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* JSON data
|
|
161
|
+
*/
|
|
162
|
+
json?: any;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Chart data if the result is a visualization
|
|
166
|
+
*/
|
|
167
|
+
chart?: ChartData;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Raw data object
|
|
171
|
+
*/
|
|
172
|
+
data?: any;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Available output formats
|
|
176
|
+
*/
|
|
177
|
+
formats(): string[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Chart Data
|
|
181
|
+
export interface ChartData {
|
|
182
|
+
/**
|
|
183
|
+
* Type of chart
|
|
184
|
+
*/
|
|
185
|
+
type: 'line' | 'bar' | 'scatter' | 'pie' | 'histogram' | 'heatmap' | 'unknown';
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Chart title
|
|
189
|
+
*/
|
|
190
|
+
title?: string;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Chart data (format depends on library)
|
|
194
|
+
*/
|
|
195
|
+
data: any;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Chart layout/configuration
|
|
199
|
+
*/
|
|
200
|
+
layout?: any;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Additional configuration
|
|
204
|
+
*/
|
|
205
|
+
config?: any;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Library that generated the chart
|
|
209
|
+
*/
|
|
210
|
+
library?: 'matplotlib' | 'plotly' | 'altair' | 'seaborn' | 'unknown';
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Base64 encoded image if available
|
|
214
|
+
*/
|
|
215
|
+
image?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Execution Error
|
|
219
|
+
export interface ExecutionError {
|
|
220
|
+
/**
|
|
221
|
+
* Error name/type (e.g., 'NameError', 'SyntaxError')
|
|
222
|
+
*/
|
|
223
|
+
name: string;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Error message
|
|
227
|
+
*/
|
|
228
|
+
value: string;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Stack trace
|
|
232
|
+
*/
|
|
233
|
+
traceback: string[];
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Line number where error occurred
|
|
237
|
+
*/
|
|
238
|
+
lineNumber?: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Serializable execution result
|
|
242
|
+
export interface ExecutionResult {
|
|
243
|
+
code: string;
|
|
244
|
+
logs: {
|
|
245
|
+
stdout: string[];
|
|
246
|
+
stderr: string[];
|
|
247
|
+
};
|
|
248
|
+
error?: ExecutionError;
|
|
249
|
+
executionCount?: number;
|
|
250
|
+
results: Array<{
|
|
251
|
+
text?: string;
|
|
252
|
+
html?: string;
|
|
253
|
+
png?: string;
|
|
254
|
+
jpeg?: string;
|
|
255
|
+
svg?: string;
|
|
256
|
+
latex?: string;
|
|
257
|
+
markdown?: string;
|
|
258
|
+
javascript?: string;
|
|
259
|
+
json?: any;
|
|
260
|
+
chart?: ChartData;
|
|
261
|
+
data?: any;
|
|
262
|
+
}>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Execution Result Container
|
|
266
|
+
export class Execution {
|
|
267
|
+
/**
|
|
268
|
+
* All results from the execution
|
|
269
|
+
*/
|
|
270
|
+
public results: Result[] = [];
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Accumulated stdout and stderr
|
|
274
|
+
*/
|
|
275
|
+
public logs = {
|
|
276
|
+
stdout: [] as string[],
|
|
277
|
+
stderr: [] as string[]
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Execution error if any
|
|
282
|
+
*/
|
|
283
|
+
public error?: ExecutionError;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Execution count (for Jupyter)
|
|
287
|
+
*/
|
|
288
|
+
public executionCount?: number;
|
|
289
|
+
|
|
290
|
+
constructor(
|
|
291
|
+
public readonly code: string,
|
|
292
|
+
public readonly context: CodeContext
|
|
293
|
+
) {}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Convert to a plain object for serialization
|
|
297
|
+
*/
|
|
298
|
+
toJSON(): ExecutionResult {
|
|
299
|
+
return {
|
|
300
|
+
code: this.code,
|
|
301
|
+
logs: this.logs,
|
|
302
|
+
error: this.error,
|
|
303
|
+
executionCount: this.executionCount,
|
|
304
|
+
results: this.results.map(result => ({
|
|
305
|
+
text: result.text,
|
|
306
|
+
html: result.html,
|
|
307
|
+
png: result.png,
|
|
308
|
+
jpeg: result.jpeg,
|
|
309
|
+
svg: result.svg,
|
|
310
|
+
latex: result.latex,
|
|
311
|
+
markdown: result.markdown,
|
|
312
|
+
javascript: result.javascript,
|
|
313
|
+
json: result.json,
|
|
314
|
+
chart: result.chart,
|
|
315
|
+
data: result.data
|
|
316
|
+
}))
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Implementation of Result
|
|
322
|
+
export class ResultImpl implements Result {
|
|
323
|
+
constructor(private raw: any) {}
|
|
324
|
+
|
|
325
|
+
get text(): string | undefined {
|
|
326
|
+
return this.raw.text || this.raw.data?.['text/plain'];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
get html(): string | undefined {
|
|
330
|
+
return this.raw.html || this.raw.data?.['text/html'];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
get png(): string | undefined {
|
|
334
|
+
return this.raw.png || this.raw.data?.['image/png'];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
get jpeg(): string | undefined {
|
|
338
|
+
return this.raw.jpeg || this.raw.data?.['image/jpeg'];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
get svg(): string | undefined {
|
|
342
|
+
return this.raw.svg || this.raw.data?.['image/svg+xml'];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
get latex(): string | undefined {
|
|
346
|
+
return this.raw.latex || this.raw.data?.['text/latex'];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
get markdown(): string | undefined {
|
|
350
|
+
return this.raw.markdown || this.raw.data?.['text/markdown'];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
get javascript(): string | undefined {
|
|
354
|
+
return this.raw.javascript || this.raw.data?.['application/javascript'];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
get json(): any {
|
|
358
|
+
return this.raw.json || this.raw.data?.['application/json'];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
get chart(): ChartData | undefined {
|
|
362
|
+
return this.raw.chart;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
get data(): any {
|
|
366
|
+
return this.raw.data;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
formats(): string[] {
|
|
370
|
+
const formats: string[] = [];
|
|
371
|
+
if (this.text) formats.push('text');
|
|
372
|
+
if (this.html) formats.push('html');
|
|
373
|
+
if (this.png) formats.push('png');
|
|
374
|
+
if (this.jpeg) formats.push('jpeg');
|
|
375
|
+
if (this.svg) formats.push('svg');
|
|
376
|
+
if (this.latex) formats.push('latex');
|
|
377
|
+
if (this.markdown) formats.push('markdown');
|
|
378
|
+
if (this.javascript) formats.push('javascript');
|
|
379
|
+
if (this.json) formats.push('json');
|
|
380
|
+
if (this.chart) formats.push('chart');
|
|
381
|
+
return formats;
|
|
382
|
+
}
|
|
383
|
+
}
|