@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.
@@ -0,0 +1,122 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "name": "sandbox-server",
6
+ "dependencies": {
7
+ "@jupyterlab/services": "^7.0.0",
8
+ "uuid": "^9.0.1",
9
+ "ws": "^8.16.0",
10
+ },
11
+ "devDependencies": {
12
+ "@types/uuid": "^9.0.7",
13
+ "@types/ws": "^8.5.10",
14
+ },
15
+ },
16
+ },
17
+ "packages": {
18
+ "@jupyter/ydoc": ["@jupyter/ydoc@3.1.0", "", { "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", "@lumino/disposable": "^1.10.0 || ^2.0.0", "@lumino/signaling": "^1.10.0 || ^2.0.0", "y-protocols": "^1.0.5", "yjs": "^13.5.40" } }, "sha512-+hLNUBZr8Zz8NiuaoYKINDURDJHbjFGxg8EcSU52y+rBe412TsoFxfPSng4eP7w3cZFrVlm7D+8K0nAMHxj0ZQ=="],
19
+
20
+ "@jupyterlab/coreutils": ["@jupyterlab/coreutils@6.4.5", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "sha512-fNEefnqTNP/3alrmGYDb0Mu1heS5Cu39FIDiDDoZuuWv4bsdE2xf7VaStuwjljsQm7VZwi5aeholi1+ciBkZKg=="],
21
+
22
+ "@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.4.5", "", { "dependencies": { "@lumino/coreutils": "^2.2.1" } }, "sha512-VOlm1klsb1LUNp47AL8iDZPP71pXu9a+WqukJFuuWVxg//+iR6wdqTYcunE9Lx/7SZDqMsXGD+doBZC36Lzenw=="],
23
+
24
+ "@jupyterlab/services": ["@jupyterlab/services@7.4.5", "", { "dependencies": { "@jupyter/ydoc": "^3.0.4", "@jupyterlab/coreutils": "^6.4.5", "@jupyterlab/nbformat": "^4.4.5", "@jupyterlab/settingregistry": "^4.4.5", "@jupyterlab/statedb": "^4.4.5", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/polling": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4", "ws": "^8.11.0" } }, "sha512-JEr8+4VS2MqFyYHh4AnpK/g5V0qRBpmnfXmU0p9YFjcIgGR365D8JF0880IJ0e8+jaW88gSAvZFB8x3o9xRoAQ=="],
25
+
26
+ "@jupyterlab/settingregistry": ["@jupyterlab/settingregistry@4.4.5", "", { "dependencies": { "@jupyterlab/nbformat": "^4.4.5", "@jupyterlab/statedb": "^4.4.5", "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-6hEBq3qI99VZwO97W0AM0mP1is57bWC8Vk2xudic1a90wA9G6ovUBzayqSMm/6QhTuIbCY+vruznwhhsLrVMeg=="],
27
+
28
+ "@jupyterlab/statedb": ["@jupyterlab/statedb@4.4.5", "", { "dependencies": { "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4" } }, "sha512-UpqhOujKwoWoxtNBL2Qk0nrw+ORLJ3ckwiJg2eA0CI+n5kO3IBYAnPzak0tSS0mQNspayXr3KAm4GQ80op5yuA=="],
29
+
30
+ "@lumino/algorithm": ["@lumino/algorithm@2.0.3", "", {}, "sha512-DIcF7cIrGEC1Wh8DNjdwaL7IdcNs4Jj1VjO/90oHefeQPszKgc6DSfCxvbQiRanKR6tl/JL7tq4ZRPZES2oVAA=="],
31
+
32
+ "@lumino/commands": ["@lumino/commands@2.3.2", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/domutils": "^2.0.3", "@lumino/keyboard": "^2.0.3", "@lumino/signaling": "^2.1.4", "@lumino/virtualdom": "^2.0.3" } }, "sha512-aAFEiUpp2hrkQU82Z85w1L80g0iDzsQRncLBa+pqVR/k0k1lz6H9F6xZ1ff+lBumZKKtsxBxNomvd0hfxLLqGw=="],
33
+
34
+ "@lumino/coreutils": ["@lumino/coreutils@2.2.1", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-yij4TnxDIum7xfFUsVvZB0oLv4shs2mNbn3juwtEIsruvVBPmurNzKX0Y8z2QetbP2AZ6MSFtBzEKsihf0H0VA=="],
35
+
36
+ "@lumino/disposable": ["@lumino/disposable@2.1.4", "", { "dependencies": { "@lumino/signaling": "^2.1.4" } }, "sha512-qTJiDbglPE2QnG4x4gtBcRbcfKQibxyyinNGKcNDrcK2TGTbbhK5PpMQ8d70l2V2Xw2pb/LfksBAg5pxkJ/G4A=="],
37
+
38
+ "@lumino/domutils": ["@lumino/domutils@2.0.3", "", {}, "sha512-bXAbZg3mf2ZDNdBBpCGDike3U+osRGHePTh8H2Ud2KwaN4g/5IryFJm/TiO4K5IYs91bWF2Zqhf3FsdbZKHlGw=="],
39
+
40
+ "@lumino/keyboard": ["@lumino/keyboard@2.0.3", "", {}, "sha512-bU2OxAR8a9eNBdV0YFjU6/lVVpbOw1gM7yHOuDGDdNu4J0UpKapFoR9gopNGSaHTmTwDtx9RHdFfIAgHwjZ+VQ=="],
41
+
42
+ "@lumino/polling": ["@lumino/polling@2.1.4", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4" } }, "sha512-gSkxlIJ/4/esY2G7bsRrY9A4KimDMHTo0shaD+MCbhd09fZMCWJoDMcA447/dykB1rM5NXgugNLjpdGGL/e8cw=="],
43
+
44
+ "@lumino/properties": ["@lumino/properties@2.0.3", "", {}, "sha512-zkXIU5uYz/ScHCHGl5Bt4gMYsfPxZEduZd80zqDslBWvTIMro3NnzLe66NMnecbdr5N3hDJagYyA8//Qy3XjiA=="],
45
+
46
+ "@lumino/signaling": ["@lumino/signaling@2.1.4", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1" } }, "sha512-nC5Z6d9om369Jkh1Vp3b7C89hV4cjr1fQDVcxhemyKXwc9r6VW7FpKixC+jElcAknP5KLj1FAa8Np+K06mMkEA=="],
47
+
48
+ "@lumino/virtualdom": ["@lumino/virtualdom@2.0.3", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-q2C8eBxPvvOOQjN3KuxZ+vJi082JH/GF7KwMdaWsy5g+7wjKdnXPuLQFTBLOrVqIzmbxBDlLeFr93CEhdQXcyQ=="],
49
+
50
+ "@rjsf/utils": ["@rjsf/utils@5.24.12", "", { "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "react-is": "^18.2.0" }, "peerDependencies": { "react": "^16.14.0 || >=17" } }, "sha512-fDwQB0XkjZjpdFUz5UAnuZj8nnbxDbX5tp+jTOjjJKw2TMQ9gFFYCQ12lSpdhezA2YgEGZfxyYTGW0DKDL5Drg=="],
51
+
52
+ "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
53
+
54
+ "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="],
55
+
56
+ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
57
+
58
+ "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
59
+
60
+ "compute-gcd": ["compute-gcd@1.2.1", "", { "dependencies": { "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2", "validate.io-integer-array": "^1.0.0" } }, "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg=="],
61
+
62
+ "compute-lcm": ["compute-lcm@1.1.2", "", { "dependencies": { "compute-gcd": "^1.2.1", "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2", "validate.io-integer-array": "^1.0.0" } }, "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ=="],
63
+
64
+ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
65
+
66
+ "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="],
67
+
68
+ "isomorphic.js": ["isomorphic.js@0.2.5", "", {}, "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw=="],
69
+
70
+ "json-schema-compare": ["json-schema-compare@0.2.2", "", { "dependencies": { "lodash": "^4.17.4" } }, "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ=="],
71
+
72
+ "json-schema-merge-allof": ["json-schema-merge-allof@0.8.1", "", { "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", "lodash": "^4.17.20" } }, "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w=="],
73
+
74
+ "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
75
+
76
+ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
77
+
78
+ "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
79
+
80
+ "lib0": ["lib0@0.2.114", "", { "dependencies": { "isomorphic.js": "^0.2.4" }, "bin": { "0gentesthtml": "bin/gentesthtml.js", "0serve": "bin/0serve.js", "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js" } }, "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ=="],
81
+
82
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
83
+
84
+ "lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
85
+
86
+ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
87
+
88
+ "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
89
+
90
+ "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="],
91
+
92
+ "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
93
+
94
+ "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
95
+
96
+ "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
97
+
98
+ "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="],
99
+
100
+ "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
101
+
102
+ "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="],
103
+
104
+ "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
105
+
106
+ "validate.io-array": ["validate.io-array@1.0.6", "", {}, "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg=="],
107
+
108
+ "validate.io-function": ["validate.io-function@1.0.2", "", {}, "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ=="],
109
+
110
+ "validate.io-integer": ["validate.io-integer@1.0.5", "", { "dependencies": { "validate.io-number": "^1.0.3" } }, "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ=="],
111
+
112
+ "validate.io-integer-array": ["validate.io-integer-array@1.0.0", "", { "dependencies": { "validate.io-array": "^1.0.3", "validate.io-integer": "^1.0.4" } }, "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA=="],
113
+
114
+ "validate.io-number": ["validate.io-number@1.0.3", "", {}, "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg=="],
115
+
116
+ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
117
+
118
+ "y-protocols": ["y-protocols@1.0.6", "", { "dependencies": { "lib0": "^0.2.85" }, "peerDependencies": { "yjs": "^13.0.0" } }, "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q=="],
119
+
120
+ "yjs": ["yjs@13.6.27", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw=="],
121
+ }
122
+ }
@@ -1,11 +1,10 @@
1
1
  import { type SpawnOptions, spawn } from "node:child_process";
2
- import type { ExecuteRequest, SessionData } from "../types";
2
+ import type { ExecuteOptions, ExecuteRequest, SessionData } from "../types";
3
3
 
4
4
  function executeCommand(
5
5
  sessions: Map<string, SessionData>,
6
6
  command: string,
7
- sessionId?: string,
8
- background?: boolean
7
+ options: ExecuteOptions,
9
8
  ): Promise<{
10
9
  success: boolean;
11
10
  stdout: string;
@@ -16,14 +15,16 @@ function executeCommand(
16
15
  const spawnOptions: SpawnOptions = {
17
16
  shell: true,
18
17
  stdio: ["pipe", "pipe", "pipe"] as const,
19
- detached: background || false,
18
+ detached: options.background || false,
19
+ cwd: options.cwd || "/workspace", // Default to clean /workspace directory
20
+ env: options.env ? { ...process.env, ...options.env } : process.env
20
21
  };
21
22
 
22
23
  const child = spawn(command, spawnOptions);
23
24
 
24
25
  // Store the process reference for cleanup if sessionId is provided
25
- if (sessionId && sessions.has(sessionId)) {
26
- const session = sessions.get(sessionId)!;
26
+ if (options.sessionId && sessions.has(options.sessionId)) {
27
+ const session = sessions.get(options.sessionId)!;
27
28
  session.activeProcess = child;
28
29
  }
29
30
 
@@ -38,7 +39,7 @@ function executeCommand(
38
39
  stderr += data.toString();
39
40
  });
40
41
 
41
- if (background) {
42
+ if (options.background) {
42
43
  // For background processes, unref and return quickly
43
44
  child.unref();
44
45
 
@@ -61,8 +62,8 @@ function executeCommand(
61
62
  // Normal synchronous execution
62
63
  child.on("close", (code) => {
63
64
  // Clear the active process reference
64
- if (sessionId && sessions.has(sessionId)) {
65
- const session = sessions.get(sessionId)!;
65
+ if (options.sessionId && sessions.has(options.sessionId)) {
66
+ const session = sessions.get(options.sessionId)!;
66
67
  session.activeProcess = null;
67
68
  }
68
69
 
@@ -78,8 +79,8 @@ function executeCommand(
78
79
 
79
80
  child.on("error", (error) => {
80
81
  // Clear the active process reference
81
- if (sessionId && sessions.has(sessionId)) {
82
- const session = sessions.get(sessionId)!;
82
+ if (options.sessionId && sessions.has(options.sessionId)) {
83
+ const session = sessions.get(options.sessionId)!;
83
84
  session.activeProcess = null;
84
85
  }
85
86
 
@@ -96,7 +97,7 @@ export async function handleExecuteRequest(
96
97
  ): Promise<Response> {
97
98
  try {
98
99
  const body = (await req.json()) as ExecuteRequest;
99
- const { command, sessionId, background } = body;
100
+ const { command, sessionId, background, cwd, env } = body;
100
101
 
101
102
  if (!command || typeof command !== "string") {
102
103
  return new Response(
@@ -115,7 +116,7 @@ export async function handleExecuteRequest(
115
116
 
116
117
  console.log(`[Server] Executing command: ${command}`);
117
118
 
118
- const result = await executeCommand(sessions, command, sessionId, background);
119
+ const result = await executeCommand(sessions, command, { sessionId, background, cwd, env });
119
120
 
120
121
  return new Response(
121
122
  JSON.stringify({
@@ -158,7 +159,7 @@ export async function handleStreamingExecuteRequest(
158
159
  ): Promise<Response> {
159
160
  try {
160
161
  const body = (await req.json()) as ExecuteRequest;
161
- const { command, sessionId, background } = body;
162
+ const { command, sessionId, background, cwd, env } = body;
162
163
 
163
164
  if (!command || typeof command !== "string") {
164
165
  return new Response(
@@ -185,6 +186,8 @@ export async function handleStreamingExecuteRequest(
185
186
  shell: true,
186
187
  stdio: ["pipe", "pipe", "pipe"] as const,
187
188
  detached: background || false,
189
+ cwd: cwd || "/workspace", // Default to clean /workspace directory
190
+ env: env ? { ...process.env, ...env } : process.env
188
191
  };
189
192
 
190
193
  const child = spawn(command, spawnOptions);
@@ -72,7 +72,7 @@ export async function handleStartProcessRequest(
72
72
  // Start the actual process
73
73
  try {
74
74
  const spawnOptions: SpawnOptions = {
75
- cwd: options.cwd || process.cwd(),
75
+ cwd: options.cwd || "/workspace", // Default to /workspace for consistency with exec commands
76
76
  env: { ...process.env, ...options.env },
77
77
  detached: false,
78
78
  shell: true,
@@ -25,6 +25,7 @@ import {
25
25
  handleStartProcessRequest,
26
26
  handleStreamProcessLogsRequest,
27
27
  } from "./handler/process";
28
+ import { type CreateContextRequest, JupyterServer } from "./jupyter-server";
28
29
  import type { ProcessRecord, SessionData } from "./types";
29
30
 
30
31
  // In-memory session storage (in production, you'd want to use a proper database)
@@ -55,8 +56,28 @@ function cleanupOldSessions() {
55
56
  // Run cleanup every 10 minutes
56
57
  setInterval(cleanupOldSessions, 10 * 60 * 1000);
57
58
 
59
+ // Initialize Jupyter server
60
+ const jupyterServer = new JupyterServer();
61
+ let jupyterInitialized = false;
62
+
63
+ // Initialize Jupyter immediately since startup.sh ensures it's ready
64
+ (async () => {
65
+ try {
66
+ await jupyterServer.initialize();
67
+ jupyterInitialized = true;
68
+ console.log("[Container] Jupyter integration initialized successfully");
69
+ } catch (error) {
70
+ console.error("[Container] Failed to initialize Jupyter:", error);
71
+ // Log more details to help debug
72
+ if (error instanceof Error) {
73
+ console.error("[Container] Error details:", error.message);
74
+ console.error("[Container] Stack trace:", error.stack);
75
+ }
76
+ }
77
+ })();
78
+
58
79
  const server = serve({
59
- fetch(req: Request) {
80
+ async fetch(req: Request) {
60
81
  const url = new URL(req.url);
61
82
  const pathname = url.pathname;
62
83
 
@@ -159,6 +180,7 @@ const server = serve({
159
180
  JSON.stringify({
160
181
  message: "pong",
161
182
  timestamp: new Date().toISOString(),
183
+ jupyter: jupyterInitialized ? "ready" : "not ready",
162
184
  }),
163
185
  {
164
186
  headers: {
@@ -280,7 +302,151 @@ const server = serve({
280
302
  }
281
303
  break;
282
304
 
305
+ // Code interpreter endpoints
306
+ case "/api/contexts":
307
+ if (req.method === "POST") {
308
+ if (!jupyterInitialized) {
309
+ return new Response(
310
+ JSON.stringify({
311
+ error: "Jupyter server is not ready. Please try again in a moment."
312
+ }),
313
+ {
314
+ status: 503,
315
+ headers: {
316
+ "Content-Type": "application/json",
317
+ ...corsHeaders,
318
+ },
319
+ }
320
+ );
321
+ }
322
+ try {
323
+ const body = await req.json() as CreateContextRequest;
324
+ const context = await jupyterServer.createContext(body);
325
+ return new Response(
326
+ JSON.stringify({
327
+ id: context.id,
328
+ language: context.language,
329
+ cwd: context.cwd,
330
+ createdAt: context.createdAt,
331
+ lastUsed: context.lastUsed
332
+ }),
333
+ {
334
+ headers: {
335
+ "Content-Type": "application/json",
336
+ ...corsHeaders,
337
+ },
338
+ }
339
+ );
340
+ } catch (error) {
341
+ console.error("[Container] Error creating context:", error);
342
+ return new Response(
343
+ JSON.stringify({
344
+ error: error instanceof Error ? error.message : "Failed to create context"
345
+ }),
346
+ {
347
+ status: 500,
348
+ headers: {
349
+ "Content-Type": "application/json",
350
+ ...corsHeaders,
351
+ },
352
+ }
353
+ );
354
+ }
355
+ } else if (req.method === "GET") {
356
+ if (!jupyterInitialized) {
357
+ return new Response(
358
+ JSON.stringify({ contexts: [] }),
359
+ {
360
+ headers: {
361
+ "Content-Type": "application/json",
362
+ ...corsHeaders,
363
+ },
364
+ }
365
+ );
366
+ }
367
+ const contexts = await jupyterServer.listContexts();
368
+ return new Response(
369
+ JSON.stringify({ contexts }),
370
+ {
371
+ headers: {
372
+ "Content-Type": "application/json",
373
+ ...corsHeaders,
374
+ },
375
+ }
376
+ );
377
+ }
378
+ break;
379
+
380
+ case "/api/execute/code":
381
+ if (req.method === "POST") {
382
+ if (!jupyterInitialized) {
383
+ return new Response(
384
+ JSON.stringify({
385
+ error: "Jupyter server is not ready. Please try again in a moment."
386
+ }),
387
+ {
388
+ status: 503,
389
+ headers: {
390
+ "Content-Type": "application/json",
391
+ ...corsHeaders,
392
+ },
393
+ }
394
+ );
395
+ }
396
+ try {
397
+ const body = await req.json() as { context_id: string; code: string; language?: string };
398
+ return await jupyterServer.executeCode(body.context_id, body.code, body.language);
399
+ } catch (error) {
400
+ console.error("[Container] Error executing code:", error);
401
+ return new Response(
402
+ JSON.stringify({
403
+ error: error instanceof Error ? error.message : "Failed to execute code"
404
+ }),
405
+ {
406
+ status: 500,
407
+ headers: {
408
+ "Content-Type": "application/json",
409
+ ...corsHeaders,
410
+ },
411
+ }
412
+ );
413
+ }
414
+ }
415
+ break;
416
+
283
417
  default:
418
+ // Handle dynamic routes for contexts
419
+ if (pathname.startsWith("/api/contexts/") && pathname.split('/').length === 4) {
420
+ const contextId = pathname.split('/')[3];
421
+ if (req.method === "DELETE") {
422
+ try {
423
+ await jupyterServer.deleteContext(contextId);
424
+ return new Response(
425
+ JSON.stringify({ success: true }),
426
+ {
427
+ headers: {
428
+ "Content-Type": "application/json",
429
+ ...corsHeaders,
430
+ },
431
+ }
432
+ );
433
+ } catch (error) {
434
+ return new Response(
435
+ JSON.stringify({
436
+ error: error instanceof Error ? error.message : "Failed to delete context"
437
+ }),
438
+ {
439
+ status: error instanceof Error && error.message.includes("not found") ? 404 : 500,
440
+ headers: {
441
+ "Content-Type": "application/json",
442
+ ...corsHeaders,
443
+ },
444
+ }
445
+ );
446
+ }
447
+ }
448
+ }
449
+
284
450
  // Handle dynamic routes for individual processes
285
451
  if (pathname.startsWith("/api/process/")) {
286
452
  const segments = pathname.split('/');
@@ -357,5 +523,9 @@ console.log(` GET /api/process/{id}/logs - Get process logs`);
357
523
  console.log(` GET /api/process/{id}/stream - Stream process logs (SSE)`);
358
524
  console.log(` DELETE /api/process/kill-all - Kill all processes`);
359
525
  console.log(` GET /proxy/{port}/* - Proxy requests to exposed ports`);
526
+ console.log(` POST /api/contexts - Create a code execution context`);
527
+ console.log(` GET /api/contexts - List all contexts`);
528
+ console.log(` DELETE /api/contexts/{id} - Delete a context`);
529
+ console.log(` POST /api/execute/code - Execute code in a context (streaming)`);
360
530
  console.log(` GET /api/ping - Health check`);
361
531
  console.log(` GET /api/commands - List available commands`);