opensandbox-code-interpreter 0.1.2.dev0__py3-none-any.whl → 1.1.1__py3-none-any.whl

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.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ API clients and handling SSE streaming for real-time code execution.
22
22
 
23
23
  import json
24
24
  import logging
25
+ import time
25
26
 
26
27
  import httpx
27
28
  from opensandbox.adapters.converter.event_node import EventNode
@@ -32,6 +33,7 @@ from opensandbox.adapters.converter.execution_event_dispatcher import (
32
33
  ExecutionEventDispatcher,
33
34
  )
34
35
  from opensandbox.adapters.converter.response_handler import (
36
+ extract_request_id,
35
37
  handle_api_error,
36
38
  require_parsed,
37
39
  )
@@ -39,6 +41,7 @@ from opensandbox.config import ConnectionConfig
39
41
  from opensandbox.exceptions import InvalidArgumentException, SandboxApiException
40
42
  from opensandbox.models.execd import Execution, ExecutionHandlers
41
43
  from opensandbox.models.sandboxes import SandboxEndpoint
44
+ from opensandbox.transport import unwrap_retry_transport
42
45
 
43
46
  from code_interpreter.adapters.converter.code_execution_converter import (
44
47
  CodeExecutionConverter,
@@ -49,18 +52,28 @@ from code_interpreter.services.code import Codes
49
52
  logger = logging.getLogger(__name__)
50
53
 
51
54
 
55
+ def _normalize_sse_event(event_dict: dict) -> dict:
56
+ if "type" in event_dict and "timestamp" in event_dict:
57
+ return event_dict
58
+ if "code" in event_dict and "message" in event_dict:
59
+ return {
60
+ "type": "error",
61
+ "timestamp": int(time.time() * 1000),
62
+ "error": {
63
+ "ename": str(event_dict["code"]),
64
+ "evalue": str(event_dict["message"]),
65
+ "traceback": [],
66
+ },
67
+ }
68
+ return event_dict
69
+
70
+
52
71
  class CodesAdapter(Codes):
53
72
  """
54
73
  Adapter implementation for code execution service.
55
74
 
56
- This adapter wraps auto-generated API clients and provides the concrete
57
- implementation of the Codes interface. It handles both standard
58
- API calls and SSE streaming for real-time code execution output.
59
-
60
- Similar to CommandServiceAdapter, this adapter uses:
61
- - Generated API clients for simple operations (create_context, interrupt)
62
- - Direct httpx SSE streaming for run
63
- - ExceptionConverter for unified exception handling
75
+ Handles standard API calls plus direct httpx SSE streaming for
76
+ real-time code execution output.
64
77
  """
65
78
 
66
79
  RUN_CODE_PATH = "/code"
@@ -85,10 +98,7 @@ class CodesAdapter(Codes):
85
98
  timeout_seconds = self.connection_config.request_timeout.total_seconds()
86
99
  timeout = httpx.Timeout(timeout_seconds)
87
100
 
88
- headers = {
89
- "User-Agent": self.connection_config.user_agent,
90
- **self.connection_config.headers,
91
- }
101
+ headers = self.execd_endpoint.build_request_headers(self.connection_config)
92
102
 
93
103
  # Execd API does not require authentication
94
104
  self._client = Client(
@@ -96,12 +106,19 @@ class CodesAdapter(Codes):
96
106
  timeout=timeout,
97
107
  )
98
108
 
99
- # Inject httpx client (adapter-owned)
109
+ # Adapter clients must own their transports: aclose() closes them,
110
+ # and the shared/user connection_config transport must survive that.
111
+ self._owns_clients = self.connection_config.owns_transport
112
+ main_transport: httpx.AsyncBaseTransport | None
113
+ if self._owns_clients:
114
+ main_transport = self.connection_config.new_owned_transport()
115
+ else:
116
+ main_transport = self.connection_config.transport
100
117
  self._httpx_client = httpx.AsyncClient(
101
118
  base_url=base_url,
102
119
  headers=headers,
103
120
  timeout=timeout,
104
- transport=self.connection_config.transport,
121
+ transport=main_transport,
105
122
  )
106
123
  self._client.set_async_httpx_client(self._httpx_client)
107
124
 
@@ -111,6 +128,14 @@ class CodesAdapter(Codes):
111
128
  "Accept": "text/event-stream",
112
129
  "Cache-Control": "no-cache",
113
130
  }
131
+ # SSE bootstraps bypass the retry wrapper: request bodies are
132
+ # not replayable and a non-idempotent status opt-in would cause
133
+ # duplicate execution on a resent SSE POST.
134
+ sse_transport = (
135
+ self.connection_config.new_owned_transport()
136
+ if self._owns_clients
137
+ else self.connection_config.transport
138
+ )
114
139
  self._sse_client = httpx.AsyncClient(
115
140
  headers=sse_headers,
116
141
  timeout=httpx.Timeout(
@@ -119,7 +144,7 @@ class CodesAdapter(Codes):
119
144
  write=timeout_seconds,
120
145
  pool=None,
121
146
  ),
122
- transport=self.connection_config.transport,
147
+ transport=unwrap_retry_transport(sse_transport),
123
148
  )
124
149
 
125
150
  async def _get_client(self):
@@ -135,6 +160,21 @@ class CodesAdapter(Codes):
135
160
  """Return SSE client (read timeout disabled) for execd streaming."""
136
161
  return self._sse_client
137
162
 
163
+ async def aclose(self) -> None:
164
+ """Release the adapter-owned HTTP clients.
165
+
166
+ Never closes the shared ``connection_config.transport``; the sandbox
167
+ stays usable after ``aclose()``. Clients wrapping a user-supplied
168
+ transport are left open (httpx would close the caller's transport
169
+ with them).
170
+ """
171
+ if not self._owns_clients:
172
+ return
173
+ try:
174
+ await self._httpx_client.aclose()
175
+ finally:
176
+ await self._sse_client.aclose()
177
+
138
178
  async def create_context(self, language: str) -> CodeContext:
139
179
  """
140
180
  Creates a new execution context for code interpretation.
@@ -197,7 +237,9 @@ class CodesAdapter(Codes):
197
237
  )
198
238
  handle_api_error(response_obj, "List code contexts")
199
239
  parsed_list = require_parsed(response_obj, list, "List code contexts")
200
- return [CodeExecutionConverter.from_api_code_context(c) for c in parsed_list]
240
+ return [
241
+ CodeExecutionConverter.from_api_code_context(c) for c in parsed_list
242
+ ]
201
243
  except Exception as e:
202
244
  logger.error("Failed to list contexts", exc_info=e)
203
245
  raise ExceptionConverter.to_sandbox_exception(e) from e
@@ -243,14 +285,18 @@ class CodesAdapter(Codes):
243
285
  """
244
286
  Executes code within the specified context using SSE streaming.
245
287
 
246
- Similar to CommandServiceAdapter.run, this uses direct httpx
288
+ Uses direct httpx
247
289
  streaming to handle SSE responses from the execd service.
248
290
  """
249
291
  if not code.strip():
250
292
  raise InvalidArgumentException("Code cannot be empty")
251
293
 
252
294
  try:
253
- if context is not None and language is not None and context.language != language:
295
+ if (
296
+ context is not None
297
+ and language is not None
298
+ and context.language != language
299
+ ):
254
300
  raise InvalidArgumentException(
255
301
  f"language '{language}' must match context.language '{context.language}'"
256
302
  )
@@ -280,13 +326,12 @@ class CodesAdapter(Codes):
280
326
  await response.aread()
281
327
  error_body = response.text
282
328
  logger.error(
283
- "Failed to run code. Status: %s, Body: %s",
284
- response.status_code,
285
- error_body,
329
+ f"Failed to run code. Status: {response.status_code}, Body: {error_body}"
286
330
  )
287
331
  raise SandboxApiException(
288
- message=f"Failed to run code. Status code: {response.status_code}",
332
+ message=f"Failed to run code. Status code: {response.status_code}, Body: {error_body}",
289
333
  status_code=response.status_code,
334
+ request_id=extract_request_id(response.headers),
290
335
  )
291
336
 
292
337
  dispatcher = ExecutionEventDispatcher(execution, handlers)
@@ -301,22 +346,20 @@ class CodesAdapter(Codes):
301
346
  data = data[5:].strip()
302
347
 
303
348
  try:
304
- event_dict = json.loads(data)
349
+ event_dict = _normalize_sse_event(json.loads(data))
305
350
  event_node = EventNode(**event_dict)
306
351
  await dispatcher.dispatch(event_node)
307
352
  except json.JSONDecodeError:
308
- logger.debug("Failed to parse SSE line: %s", line)
353
+ logger.debug(f"Failed to parse SSE line: {line}")
309
354
  continue
310
355
  except Exception as e:
311
- logger.error("Error processing event: %s", data, exc_info=e)
356
+ logger.error(f"Error processing event: {data}", exc_info=e)
312
357
  continue
313
358
 
314
359
  return execution
315
360
 
316
361
  except Exception as e:
317
- logger.error(
318
- "Failed to run code (length: %s)", len(code), exc_info=e
319
- )
362
+ logger.error(f"Failed to run code (length: {len(code)})", exc_info=e)
320
363
  raise ExceptionConverter.to_sandbox_exception(e) from e
321
364
 
322
365
  async def interrupt(self, execution_id: str) -> None:
@@ -339,3 +382,23 @@ class CodesAdapter(Codes):
339
382
  except Exception as e:
340
383
  logger.error("Failed to interrupt code execution", exc_info=e)
341
384
  raise ExceptionConverter.to_sandbox_exception(e) from e
385
+
386
+ async def ping(self) -> bool:
387
+ """
388
+ Check if the execd code execution service is alive.
389
+
390
+ Uses the shared generated API client so the ping exercises the same
391
+ HTTP client and endpoint that serve code execution requests.
392
+ """
393
+ try:
394
+ from opensandbox.api.execd.api.health import ping as ping_api
395
+
396
+ client = await self._get_client()
397
+ response_obj = await ping_api.asyncio_detailed(client=client)
398
+
399
+ handle_api_error(response_obj, "Ping code interpreter")
400
+ return True
401
+
402
+ except Exception as e:
403
+ logger.debug(f"Code interpreter ping failed: {e}")
404
+ return False
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -21,12 +21,17 @@ functionality with specialized code execution features, including multi-language
21
21
  support, session management, and variable persistence.
22
22
  """
23
23
 
24
+ import asyncio
25
+ import contextlib
24
26
  import logging
27
+ import time
28
+ from datetime import timedelta
25
29
 
26
30
  from opensandbox.exceptions import (
27
31
  InvalidArgumentException,
28
32
  SandboxException,
29
33
  SandboxInternalException,
34
+ SandboxReadyTimeoutException,
30
35
  )
31
36
  from opensandbox.sandbox import Sandbox
32
37
 
@@ -35,6 +40,21 @@ from code_interpreter.services.code import Codes
35
40
 
36
41
  logger = logging.getLogger(__name__)
37
42
 
43
+ DEFAULT_READY_TIMEOUT = timedelta(seconds=30)
44
+ DEFAULT_HEALTH_CHECK_POLLING_INTERVAL = timedelta(milliseconds=200)
45
+
46
+ # Strict health check script: verifies the code interpreter runtime (Jupyter
47
+ # kernel gateway) is actually serving inside the sandbox. execd starts serving
48
+ # /ping before the entrypoint launches Jupyter, and the setup stage may run
49
+ # short-lived "jupyter kernelspec" helpers, so a daemon ping or a process-name
50
+ # grep cannot prove the runtime is ready. Probing the Jupyter listen port
51
+ # (127.0.0.1:${JUPYTER_PORT:-44771}, same default as the entrypoint) only
52
+ # passes once the server accepts connections.
53
+ RUNTIME_PROCESS_CHECK_COMMAND = (
54
+ "bash -c 'exec 3<>/dev/tcp/127.0.0.1/${JUPYTER_PORT:-44771}' "
55
+ "&& exit 0 || exit 1"
56
+ )
57
+
38
58
 
39
59
  class CodeInterpreter:
40
60
  """
@@ -45,7 +65,7 @@ class CodeInterpreter:
45
65
 
46
66
  Key Features:
47
67
 
48
- - Multi-language Code Execution: Support for Python, JavaScript, Bash, Java, Kotlin
68
+ - Multi-language Code Execution: Python, JavaScript, TypeScript, Go, Bash
49
69
  - Session Management: Persistent execution contexts with variable state
50
70
  - Sandbox Integration: Full access to underlying sandbox file system and command execution
51
71
  - Streaming Execution: Real-time code execution with output streaming
@@ -54,11 +74,12 @@ class CodeInterpreter:
54
74
  Usage Example:
55
75
 
56
76
  ```python
57
- # First create a sandbox instance
58
-
77
+ # The sandbox must use the opensandbox/code-interpreter image (or a
78
+ # derivative); the strict health check requires the Jupyter runtime.
59
79
  sandbox = await Sandbox.create(
60
- "python:3.11",
61
- resource={"cpu": "1", "memory": "2Gi"}
80
+ "opensandbox/code-interpreter:latest",
81
+ entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
82
+ resource={"cpu": "1", "memory": "2Gi"},
62
83
  )
63
84
 
64
85
  # Then create a code interpreter wrapping the sandbox
@@ -68,9 +89,10 @@ class CodeInterpreter:
68
89
  from code_interpreter.models.code import SupportedLanguage
69
90
  context = await interpreter.codes.create_context(SupportedLanguage.PYTHON)
70
91
  result = await interpreter.codes.run("print('Hello World')", context=context)
71
- print(result.logs.stdout) # Output: Hello World
92
+ print(result.text) # Output: Hello World
72
93
 
73
94
  # Access underlying sandbox for file operations
95
+ from opensandbox.models.filesystem import WriteEntry
74
96
  await interpreter.sandbox.files.write_files([
75
97
  WriteEntry(path="data.txt", data="Hello")
76
98
  ])
@@ -79,10 +101,16 @@ class CodeInterpreter:
79
101
  context=context,
80
102
  )
81
103
 
82
- # Always clean up resources
104
+ # Clean up: closing the interpreter releases its HTTP clients; the
105
+ # underlying sandbox lifecycle stays caller-owned.
106
+ await interpreter.aclose()
83
107
  await sandbox.kill()
84
108
  await sandbox.close()
85
109
  ```
110
+
111
+ CodeInterpreter also supports the async context-manager protocol; exiting
112
+ the ``async with`` block releases the interpreter's HTTP clients (not the
113
+ sandbox).
86
114
  """
87
115
 
88
116
  def __init__(self, sandbox: Sandbox, code_service: Codes) -> None:
@@ -142,6 +170,24 @@ class CodeInterpreter:
142
170
  """
143
171
  return self._sandbox.commands
144
172
 
173
+ async def aclose(self) -> None:
174
+ """
175
+ Release resources owned by the code interpreter.
176
+
177
+ Closes the HTTP clients used by the code execution service. The
178
+ underlying sandbox is NOT affected; manage its lifecycle separately
179
+ (``sandbox.kill()`` / ``sandbox.close()``). Safe to call multiple times.
180
+ """
181
+ aclose = getattr(self._code_service, "aclose", None)
182
+ if aclose is not None:
183
+ await aclose()
184
+
185
+ async def __aenter__(self) -> "CodeInterpreter":
186
+ return self
187
+
188
+ async def __aexit__(self, exc_type, exc, tb) -> None:
189
+ await self.aclose()
190
+
145
191
  @property
146
192
  def metrics(self):
147
193
  """
@@ -169,14 +215,114 @@ class CodeInterpreter:
169
215
  """
170
216
  return self._code_service
171
217
 
218
+ async def ping(self) -> bool:
219
+ """
220
+ Check if the code execution service (execd) is responsive.
221
+
222
+ Returns:
223
+ True if the code execution service is responsive, False otherwise
224
+ """
225
+ return await self._code_service.ping()
226
+
227
+ async def is_healthy(self) -> bool:
228
+ """
229
+ Check if the code interpreter is healthy (strict check).
230
+
231
+ Healthy means both:
232
+
233
+ - the code execution service (execd) answers ``GET /ping``; and
234
+ - the code interpreter runtime (Jupyter kernel gateway) is serving
235
+ inside the sandbox, verified by probing its listen port through the
236
+ execd command API.
237
+
238
+ Exceptions raised by either leg are treated as unhealthy.
239
+
240
+ Returns:
241
+ True if healthy, False otherwise
242
+ """
243
+ try:
244
+ return await self.ping() and await self._is_runtime_process_alive()
245
+ except Exception:
246
+ return False
247
+
248
+ async def _is_runtime_process_alive(self) -> bool:
249
+ """
250
+ Check if the code interpreter runtime (Jupyter) is serving.
251
+
252
+ Probes the runtime listen port through the sandbox command service and
253
+ treats a failed command (non-zero exit surfaced as an execution error)
254
+ as an unhealthy runtime.
255
+ """
256
+ try:
257
+ execution = await self._sandbox.commands.run(
258
+ RUNTIME_PROCESS_CHECK_COMMAND
259
+ )
260
+ return execution.error is None
261
+ except Exception:
262
+ return False
263
+
264
+ async def check_ready(
265
+ self,
266
+ timeout: timedelta,
267
+ polling_interval: timedelta,
268
+ ) -> None:
269
+ """
270
+ Wait for the code interpreter to pass the strict health check with polling.
271
+
272
+ Raises:
273
+ SandboxReadyTimeoutException: if the health check doesn't pass within timeout
274
+ """
275
+ logger.info(
276
+ f"Waiting for code interpreter {self.id} to pass health check "
277
+ f"(timeout: {timeout.total_seconds()}s)"
278
+ )
279
+
280
+ deadline = time.monotonic() + timeout.total_seconds()
281
+ attempt = 0
282
+
283
+ while time.monotonic() < deadline:
284
+ attempt += 1
285
+ if await self.is_healthy():
286
+ logger.info(
287
+ f"Code interpreter {self.id} passed health check "
288
+ f"after {attempt} attempts"
289
+ )
290
+ return
291
+
292
+ remaining = deadline - time.monotonic()
293
+ if remaining <= 0:
294
+ break
295
+ await asyncio.sleep(min(polling_interval.total_seconds(), remaining))
296
+
297
+ raise SandboxReadyTimeoutException(
298
+ f"Code interpreter {self.id} health check timed out after "
299
+ f"{timeout.total_seconds()}s ({attempt} attempts). The code execution "
300
+ f"service (execd) or the interpreter runtime (Jupyter) did not become "
301
+ f"ready. Pass skip_health_check=True to skip this check."
302
+ )
303
+
172
304
  @classmethod
173
- async def create(cls, sandbox: Sandbox) -> "CodeInterpreter":
305
+ async def create(
306
+ cls,
307
+ sandbox: Sandbox,
308
+ *,
309
+ ready_timeout: timedelta = DEFAULT_READY_TIMEOUT,
310
+ health_check_polling_interval: timedelta = DEFAULT_HEALTH_CHECK_POLLING_INTERVAL,
311
+ skip_health_check: bool = False,
312
+ ) -> "CodeInterpreter":
174
313
  """
175
314
  Creates a CodeInterpreter from an existing Sandbox instance.
176
315
 
177
316
  This factory method handles the creation and initialization of CodeInterpreter
178
317
  services, including the code execution service and language configuration.
179
318
 
319
+ By default a strict health check runs before the interpreter is returned:
320
+ the code execution service (execd) must answer ``GET /ping`` AND the
321
+ code interpreter runtime (Jupyter kernel gateway) must be serving,
322
+ both within ``ready_timeout``. execd starts serving before the runtime
323
+ launches, so the daemon ping alone is not enough. Set
324
+ ``skip_health_check=True`` to opt out.
325
+
180
326
  CodeInterpreter must be created by wrapping an existing Sandbox instance with
181
327
  code execution capabilities. This design ensures clear separation of concerns:
182
328
  - Sandbox handles infrastructure (containers, resources, networking)
@@ -184,6 +330,10 @@ class CodeInterpreter:
184
330
 
185
331
  Args:
186
332
  sandbox: Existing sandbox instance to wrap with code execution capabilities
333
+ ready_timeout: Maximum time to wait for the code execution service health check
334
+ health_check_polling_interval: Time between health check attempts
335
+ skip_health_check: If True, do not wait for the code execution service
336
+ to become ready; the returned interpreter may fail on first use
187
337
 
188
338
  Returns:
189
339
  CodeInterpreter instance wrapping the sandbox
@@ -191,24 +341,41 @@ class CodeInterpreter:
191
341
  Raises:
192
342
  InvalidArgumentException: If sandbox is not provided
193
343
  SandboxException: If creation fails
344
+ SandboxReadyTimeoutException: If the code execution service health check
345
+ times out and ``skip_health_check`` is False
194
346
  SandboxInternalException: If internal service initialization fails
195
347
  """
196
348
  if sandbox is None:
197
349
  raise InvalidArgumentException("Sandbox instance must be provided")
198
350
 
199
- logger.info("Creating code interpreter from sandbox: %s", sandbox.id)
351
+ logger.info(f"Creating code interpreter from sandbox: {sandbox.id}")
200
352
 
201
353
  factory = AdapterFactory(sandbox.connection_config)
202
354
 
203
355
  try:
204
356
  # Connect to the execd daemon endpoint for code execution services
205
357
  from opensandbox.constants import DEFAULT_EXECD_PORT
358
+
206
359
  code_interpreter_endpoint = await sandbox.get_endpoint(DEFAULT_EXECD_PORT)
207
- code_execution_service = factory.create_code_execution_service(code_interpreter_endpoint)
360
+ code_execution_service = factory.create_code_execution_service(
361
+ code_interpreter_endpoint
362
+ )
363
+
364
+ interpreter = cls(sandbox, code_execution_service)
365
+ try:
366
+ if not skip_health_check:
367
+ await interpreter.check_ready(
368
+ ready_timeout, health_check_polling_interval
369
+ )
370
+ except Exception:
371
+ # Don't leak the service's clients on a failed creation.
372
+ with contextlib.suppress(Exception):
373
+ await interpreter.aclose()
374
+ raise
208
375
 
209
- logger.info("Code interpreter %s created successfully", sandbox.id)
376
+ logger.info(f"Code interpreter {sandbox.id} created successfully")
210
377
 
211
- return cls(sandbox, code_execution_service)
378
+ return interpreter
212
379
  except Exception as e:
213
380
  if isinstance(e, SandboxException):
214
381
  raise
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ class SupportedLanguageSync:
27
27
  GO = "go"
28
28
  TYPESCRIPT = "typescript"
29
29
  BASH = "bash"
30
+ JAVASCRIPT = "javascript"
30
31
 
31
32
 
32
33
  class CodeContextSync(BaseModel):
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -38,10 +38,10 @@ class Codes(Protocol):
38
38
  Supported Languages:
39
39
 
40
40
  - Python: Full Python 3.x support with package management
41
- - JavaScript/Node.js: ES6+ with npm package support
41
+ - JavaScript: ES6+ with npm package support
42
+ - TypeScript
43
+ - Go
42
44
  - Bash: Shell scripting with full system access
43
- - Java: Compilation and execution with classpath management
44
- - Kotlin: Script and compiled Kotlin execution
45
45
 
46
46
  Key Features:
47
47
 
@@ -212,3 +212,17 @@ class Codes(Protocol):
212
212
  SandboxException: If interruption fails
213
213
  """
214
214
  ...
215
+
216
+ async def ping(self) -> bool:
217
+ """
218
+ Check if the code execution service (execd) is alive.
219
+
220
+ The ping targets the execd daemon endpoint used by this service
221
+ (``GET /ping``). It does not verify that a specific language runtime
222
+ is ready, only that the daemon serving code execution requests is
223
+ responsive.
224
+
225
+ Returns:
226
+ True if the code execution service is responsive, False otherwise
227
+ """
228
+ ...
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.