@cloudflare/sandbox 0.0.0-4aceb32 → 0.0.0-5213e94

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @cloudflare/sandbox
2
2
 
3
+ ## 0.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#53](https://github.com/cloudflare/sandbox-sdk/pull/53) [`c87db11`](https://github.com/cloudflare/sandbox-sdk/commit/c87db117693a86cfb667bf09fb7720d6a6e0524d) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Improve jupyterlab config to speed up startup
8
+
9
+ ## 0.2.2
10
+
11
+ ### Patch Changes
12
+
13
+ - [#51](https://github.com/cloudflare/sandbox-sdk/pull/51) [`4aceb32`](https://github.com/cloudflare/sandbox-sdk/commit/4aceb3215c836f59afcb88b2b325016b3f623f46) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Handle intermittent interpreter failures and decouple jupyter startup
14
+
3
15
  ## 0.2.1
4
16
 
5
17
  ### Patch Changes
package/Dockerfile CHANGED
@@ -37,6 +37,7 @@ RUN apt-get update && apt-get install -y \
37
37
  ca-certificates \
38
38
  gnupg \
39
39
  lsb-release \
40
+ strace \
40
41
  && rm -rf /var/lib/apt/lists/*
41
42
 
42
43
  # Set Python 3.11 as default python3
@@ -56,17 +57,20 @@ RUN apt-get update && apt-get install -y ca-certificates curl gnupg \
56
57
  COPY --from=bun-source /usr/local/bin/bun /usr/local/bin/bun
57
58
  COPY --from=bun-source /usr/local/bin/bunx /usr/local/bin/bunx
58
59
 
59
- # Install Jupyter and kernels
60
+ # Install minimal Jupyter components
60
61
  RUN pip3 install --no-cache-dir \
61
- jupyter \
62
- jupyterlab \
62
+ jupyter-server \
63
+ jupyter-client \
63
64
  ipykernel \
64
- notebook \
65
+ orjson \
66
+ && python3 -m ipykernel install --user --name python3
67
+
68
+ # Install scientific packages
69
+ RUN pip3 install --no-cache-dir \
65
70
  matplotlib \
66
71
  numpy \
67
72
  pandas \
68
- seaborn \
69
- && python3 -m ipykernel install --user --name python3
73
+ seaborn
70
74
 
71
75
  # Install JavaScript kernel (ijavascript) - using E2B's fork
72
76
  RUN npm install -g --unsafe-perm git+https://github.com/e2b-dev/ijavascript.git \
package/README.md CHANGED
@@ -72,7 +72,7 @@ npm install @cloudflare/sandbox
72
72
  1. **Create a Dockerfile** (temporary requirement, will be removed in future releases):
73
73
 
74
74
  ```dockerfile
75
- FROM docker.io/cloudflare/sandbox:0.2.1
75
+ FROM docker.io/cloudflare/sandbox:0.2.3
76
76
 
77
77
  # Expose the ports you want to expose
78
78
  EXPOSE 3000
@@ -133,16 +133,26 @@ export class JupyterService {
133
133
  * Pre-warm context pools for better performance
134
134
  */
135
135
  private async warmContextPools() {
136
+ // Delay pre-warming to avoid startup rush that triggers Bun WebSocket bug
137
+ await new Promise((resolve) => setTimeout(resolve, 2000));
138
+
136
139
  try {
137
140
  console.log(
138
141
  "[JupyterService] Pre-warming context pools for better performance"
139
142
  );
140
143
 
141
- // Enable pool warming with min sizes
144
+ // Warm one at a time with delay between them
142
145
  await this.jupyterServer.enablePoolWarming("python", 1);
146
+
147
+ // Small delay between different language kernels
148
+ await new Promise((resolve) => setTimeout(resolve, 1000));
149
+
143
150
  await this.jupyterServer.enablePoolWarming("javascript", 1);
144
151
  } catch (error) {
145
152
  console.error("[JupyterService] Error pre-warming context pools:", error);
153
+ console.error(
154
+ "[JupyterService] Pre-warming failed but service continues"
155
+ );
146
156
  }
147
157
  }
148
158
 
@@ -0,0 +1,48 @@
1
+ """
2
+ Minimal Jupyter configuration focused on kernel-only usage
3
+ """
4
+
5
+ c = get_config() # noqa
6
+
7
+ # Disable all authentication - we handle security at container level
8
+ c.ServerApp.token = ''
9
+ c.ServerApp.password = ''
10
+ c.IdentityProvider.token = ''
11
+ c.ServerApp.allow_origin = '*'
12
+ c.ServerApp.allow_remote_access = True
13
+ c.ServerApp.disable_check_xsrf = True
14
+ c.ServerApp.allow_root = True
15
+ c.ServerApp.allow_credentials = True
16
+
17
+ # Also set NotebookApp settings for compatibility
18
+ c.NotebookApp.token = ''
19
+ c.NotebookApp.password = ''
20
+ c.NotebookApp.allow_origin = '*'
21
+ c.NotebookApp.allow_remote_access = True
22
+ c.NotebookApp.disable_check_xsrf = True
23
+ c.NotebookApp.allow_credentials = True
24
+
25
+ # Performance settings
26
+ c.ServerApp.iopub_data_rate_limit = 1000000000 # E2B uses 1GB/s
27
+
28
+ # Minimal logging
29
+ c.Application.log_level = 'ERROR'
30
+
31
+ # Disable browser
32
+ c.ServerApp.open_browser = False
33
+
34
+ # Optimize for container environment
35
+ c.ServerApp.ip = '0.0.0.0'
36
+ c.ServerApp.port = 8888
37
+
38
+ # Kernel optimizations
39
+ c.KernelManager.shutdown_wait_time = 0.0
40
+ c.MappingKernelManager.cull_idle_timeout = 0
41
+ c.MappingKernelManager.cull_interval = 0
42
+
43
+ # Disable terminals
44
+ c.ServerApp.terminals_enabled = False
45
+
46
+ # Disable all extensions to speed up startup
47
+ c.ServerApp.jpserver_extensions = {}
48
+ c.ServerApp.nbserver_extensions = {}
@@ -2,7 +2,8 @@
2
2
 
3
3
  # Function to check if Jupyter is ready
4
4
  check_jupyter_ready() {
5
- curl -s http://localhost:8888/api > /dev/null 2>&1
5
+ # Check if API is responsive and kernelspecs are available
6
+ curl -s http://localhost:8888/api/kernelspecs > /dev/null 2>&1
6
7
  }
7
8
 
8
9
  # Function to notify Bun server that Jupyter is ready
@@ -12,19 +13,10 @@ notify_jupyter_ready() {
12
13
  echo "[Startup] Jupyter is ready, notified Bun server"
13
14
  }
14
15
 
15
- # Start Jupyter notebook server in background
16
+ # Start Jupyter server in background
16
17
  echo "[Startup] Starting Jupyter server..."
17
- jupyter notebook \
18
- --ip=0.0.0.0 \
19
- --port=8888 \
20
- --no-browser \
21
- --allow-root \
22
- --NotebookApp.token='' \
23
- --NotebookApp.password='' \
24
- --NotebookApp.allow_origin='*' \
25
- --NotebookApp.disable_check_xsrf=True \
26
- --NotebookApp.allow_remote_access=True \
27
- --NotebookApp.allow_credentials=True \
18
+ jupyter server \
19
+ --config=/container-server/jupyter_config.py \
28
20
  > /tmp/jupyter.log 2>&1 &
29
21
 
30
22
  JUPYTER_PID=$!
@@ -37,18 +29,21 @@ BUN_PID=$!
37
29
  # Monitor Jupyter readiness in background
38
30
  (
39
31
  echo "[Startup] Monitoring Jupyter readiness in background..."
40
- MAX_ATTEMPTS=30
32
+ MAX_ATTEMPTS=60
41
33
  ATTEMPT=0
42
- DELAY=0.5
43
- MAX_DELAY=5
44
-
34
+
35
+ # Track start time for reporting
36
+ START_TIME=$(date +%s.%N)
37
+
45
38
  while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
46
39
  if check_jupyter_ready; then
47
40
  notify_jupyter_ready
48
- echo "[Startup] Jupyter server is ready after $ATTEMPT attempts"
41
+ END_TIME=$(date +%s.%N)
42
+ ELAPSED=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}")
43
+ echo "[Startup] Jupyter server is ready after $ELAPSED seconds ($ATTEMPT attempts)"
49
44
  break
50
45
  fi
51
-
46
+
52
47
  # Check if Jupyter process is still running
53
48
  if ! kill -0 $JUPYTER_PID 2>/dev/null; then
54
49
  echo "[Startup] WARNING: Jupyter process died. Check /tmp/jupyter.log for details"
@@ -56,21 +51,27 @@ BUN_PID=$!
56
51
  # Don't exit - let Bun server continue running in degraded mode
57
52
  break
58
53
  fi
59
-
54
+
60
55
  ATTEMPT=$((ATTEMPT + 1))
61
- echo "[Startup] Jupyter not ready yet (attempt $ATTEMPT/$MAX_ATTEMPTS, delay ${DELAY}s)"
62
-
63
- # Sleep with exponential backoff
64
- sleep $DELAY
65
-
66
- # Increase delay exponentially with jitter, cap at MAX_DELAY
67
- DELAY=$(awk "BEGIN {printf \"%.2f\", $DELAY * 1.5 + (rand() * 0.5)}")
68
- # Use awk for comparison since bc might not be available
69
- if [ $(awk "BEGIN {print ($DELAY > $MAX_DELAY)}") -eq 1 ]; then
70
- DELAY=$MAX_DELAY
56
+
57
+ # Start with faster checks
58
+ if [ $ATTEMPT -eq 1 ]; then
59
+ DELAY=0.5 # Start at 0.5s
60
+ else
61
+ # Exponential backoff with 1.3x multiplier (less aggressive than 1.5x)
62
+ DELAY=$(awk "BEGIN {printf \"%.2f\", $DELAY * 1.3}")
63
+ # Cap at 2s max (instead of 5s)
64
+ if [ $(awk "BEGIN {print ($DELAY > 2)}") -eq 1 ]; then
65
+ DELAY=2
66
+ fi
71
67
  fi
68
+
69
+ # Log with current delay for transparency
70
+ echo "[Startup] Jupyter not ready yet (attempt $ATTEMPT/$MAX_ATTEMPTS, next check in ${DELAY}s)"
71
+
72
+ sleep $DELAY
72
73
  done
73
-
74
+
74
75
  if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
75
76
  echo "[Startup] WARNING: Jupyter failed to become ready within attempts"
76
77
  echo "[Startup] Jupyter logs:"
@@ -80,4 +81,4 @@ BUN_PID=$!
80
81
  ) &
81
82
 
82
83
  # Wait for Bun server (main process)
83
- wait $BUN_PID
84
+ wait $BUN_PID
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/sandbox",
3
- "version": "0.0.0-4aceb32",
3
+ "version": "0.0.0-5213e94",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cloudflare/sandbox-sdk"
package/src/sandbox.ts CHANGED
@@ -37,7 +37,7 @@ export function getSandbox(ns: DurableObjectNamespace<Sandbox>, id: string) {
37
37
 
38
38
  export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
39
39
  defaultPort = 3000; // Default port for the container's Bun server
40
- sleepAfter = "3m"; // Sleep the sandbox if no requests are made in this timeframe
40
+ sleepAfter = "20m"; // Keep container warm for 20 minutes to avoid cold starts
41
41
  client: JupyterClient;
42
42
  private sandboxName: string | null = null;
43
43
  private codeInterpreter: CodeInterpreter;