From a98b7996d76f34e4fa97bb1bc9c69d4812daa5fb Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:07:57 +0100 Subject: [PATCH 1/9] secure traffic access --- .changeset/fast-dogs-brush.md | 6 ++ js/src/sandbox.ts | 51 ++++++++--- js/tests/basic.test.ts | 18 +++- js/tests/contexts.test.ts | 89 ++++++++++++++++++- pnpm-lock.yaml | 1 + .../code_interpreter_async.py | 54 ++++++----- .../code_interpreter_sync.py | 50 ++++++----- python/tests/async/test_async_basic.py | 10 +++ python/tests/async/test_async_contexts.py | 57 ++++++++++++ python/tests/conftest.py | 9 +- python/tests/sync/test_basic.py | 10 +++ python/tests/sync/test_contexts.py | 58 ++++++++++++ 12 files changed, 350 insertions(+), 63 deletions(-) create mode 100644 .changeset/fast-dogs-brush.md diff --git a/.changeset/fast-dogs-brush.md b/.changeset/fast-dogs-brush.md new file mode 100644 index 00000000..74bc63ff --- /dev/null +++ b/.changeset/fast-dogs-brush.md @@ -0,0 +1,6 @@ +--- +'@e2b/code-interpreter-python': patch +'@e2b/code-interpreter': patch +--- + +secure traffic access diff --git a/js/src/sandbox.ts b/js/src/sandbox.ts index 0a87d20f..5b6c564b 100644 --- a/js/src/sandbox.ts +++ b/js/src/sandbox.ts @@ -220,8 +220,12 @@ export class Sandbox extends BaseSandbox { }, requestTimeout) : undefined - const headers: Record = { + const headers = { 'Content-Type': 'application/json', + } as Record + + if (this.trafficAccessToken) { + headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken } if (this.envdAccessToken) { headers['X-Access-Token'] = this.envdAccessToken @@ -296,12 +300,18 @@ export class Sandbox extends BaseSandbox { */ async createCodeContext(opts?: CreateCodeContextOpts): Promise { try { + const headers = { + 'Content-Type': 'application/json', + ...this.connectionConfig.headers, + } as Record + + if (this.trafficAccessToken) { + headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken + } + const res = await fetch(`${this.jupyterUrl}/contexts`, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...this.connectionConfig.headers, - }, + headers, body: JSON.stringify({ language: opts?.language, cwd: opts?.cwd, @@ -331,12 +341,18 @@ export class Sandbox extends BaseSandbox { async removeCodeContext(context: Context | string): Promise { try { const id = typeof context === 'string' ? context : context.id + const headers = { + 'Content-Type': 'application/json', + ...this.connectionConfig.headers, + } as Record + + if (this.trafficAccessToken) { + headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken + } + const res = await fetch(`${this.jupyterUrl}/contexts/${id}`, { method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - ...this.connectionConfig.headers, - }, + headers, keepalive: true, signal: this.connectionConfig.getSignal( this.connectionConfig.requestTimeoutMs @@ -364,6 +380,9 @@ export class Sandbox extends BaseSandbox { headers: { 'Content-Type': 'application/json', ...this.connectionConfig.headers, + ...(this.trafficAccessToken + ? { 'E2B-Traffic-Access-Token': this.trafficAccessToken } + : {}), }, keepalive: true, signal: this.connectionConfig.getSignal( @@ -392,12 +411,18 @@ export class Sandbox extends BaseSandbox { async restartCodeContext(context: Context | string): Promise { try { const id = typeof context === 'string' ? context : context.id + const headers = { + 'Content-Type': 'application/json', + ...this.connectionConfig.headers, + } as Record + + if (this.trafficAccessToken) { + headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken + } + const res = await fetch(`${this.jupyterUrl}/contexts/${id}/restart`, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...this.connectionConfig.headers, - }, + headers, keepalive: true, signal: this.connectionConfig.getSignal( this.connectionConfig.requestTimeoutMs diff --git a/js/tests/basic.test.ts b/js/tests/basic.test.ts index c0360d02..a41b61b0 100644 --- a/js/tests/basic.test.ts +++ b/js/tests/basic.test.ts @@ -1,9 +1,23 @@ import { expect } from 'vitest' - -import { sandboxTest } from './setup' +import { isDebug, sandboxTest } from './setup' +import { Sandbox } from '../src' sandboxTest('basic', async ({ sandbox }) => { const result = await sandbox.runCode('x =1; x') expect(result.text).toEqual('1') }) + +sandboxTest.skipIf(isDebug)('secure access', async ({ template }) => { + const sandbox = await Sandbox.create(template, { + network: { + allowPublicTraffic: false, + }, + }) + + const result = await sandbox.runCode('x =1; x') + + expect(result.text).toEqual('1') + + await sandbox.kill() +}) diff --git a/js/tests/contexts.test.ts b/js/tests/contexts.test.ts index 7dda2090..42d91db6 100644 --- a/js/tests/contexts.test.ts +++ b/js/tests/contexts.test.ts @@ -1,6 +1,7 @@ import { expect } from 'vitest' -import { sandboxTest } from './setup' +import { isDebug, sandboxTest } from './setup' +import Sandbox from '../src' sandboxTest('create context with no options', async ({ sandbox }) => { const context = await sandbox.createCodeContext() @@ -61,3 +62,89 @@ sandboxTest('restart context', async ({ sandbox }) => { expect(execution.error?.name).toBe('NameError') expect(execution.error?.value).toBe("name 'x' is not defined") }) + +sandboxTest.skipIf(isDebug)( + 'create context (secure traffic)', + async ({ template }) => { + const sandbox = await Sandbox.create(template, { + network: { + allowPublicTraffic: false, + }, + }) + const context = await sandbox.createCodeContext() + + const contexts = await sandbox.listCodeContexts() + const lastContext = contexts[contexts.length - 1] + + expect(lastContext.id).toBe(context.id) + expect(lastContext.language).toBe(context.language) + expect(lastContext.cwd).toBe(context.cwd) + + await sandbox.kill() + } +) + +sandboxTest.skipIf(isDebug)( + 'remove context (secure traffic)', + async ({ template }) => { + const sandbox = await Sandbox.create(template, { + network: { + allowPublicTraffic: false, + }, + }) + const context = await sandbox.createCodeContext() + + await sandbox.removeCodeContext(context.id) + const contexts = await sandbox.listCodeContexts() + + expect(contexts.map((context) => context.id)).not.toContain(context.id) + + await sandbox.kill() + } +) + +sandboxTest.skipIf(isDebug)( + 'list contexts (secure traffic)', + async ({ template }) => { + const sandbox = await Sandbox.create(template, { + network: { + allowPublicTraffic: false, + }, + }) + const contexts = await sandbox.listCodeContexts() + + // default contexts should include python and javascript + expect(contexts.map((context) => context.language)).toContain('python') + expect(contexts.map((context) => context.language)).toContain('javascript') + + await sandbox.kill() + } +) + +sandboxTest.skipIf(isDebug)( + 'restart context (secure traffic)', + async ({ template }) => { + const sandbox = await Sandbox.create(template, { + network: { + allowPublicTraffic: false, + }, + }) + const context = await sandbox.createCodeContext() + + // set a variable in the context + await sandbox.runCode('x = 1', { context: context }) + + // restart the context + await sandbox.restartCodeContext(context.id) + + // check that the variable no longer exists + const execution = await sandbox.runCode('x', { context: context }) + + // check for an NameError with message "name 'x' is not defined" + expect(execution.error).toBeDefined() + expect(execution.error?.name).toBe('NameError') + expect(execution.error?.value).toBe("name 'x' is not defined") + + await sandbox.kill() + } +) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e3f8587..c1caf8b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1575,6 +1575,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} diff --git a/python/e2b_code_interpreter/code_interpreter_async.py b/python/e2b_code_interpreter/code_interpreter_async.py index 98f59dd0..de938240 100644 --- a/python/e2b_code_interpreter/code_interpreter_async.py +++ b/python/e2b_code_interpreter/code_interpreter_async.py @@ -190,12 +190,15 @@ async def run_code( timeout = None if timeout == 0 else (timeout or DEFAULT_TIMEOUT) request_timeout = request_timeout or self.connection_config.request_timeout context_id = context.id if context else None - - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers = { + "Content-Type": "application/json", + } + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + async with self._client.stream( "POST", f"{self._jupyter_url}/execute", @@ -253,11 +256,13 @@ async def create_code_context( if cwd: data["cwd"] = cwd - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers = { + "Content-Type": "application/json", + } + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = await self._client.post( f"{self._jupyter_url}/contexts", headers=headers, @@ -287,11 +292,13 @@ async def remove_code_context( """ context_id = context.id if isinstance(context, Context) else context - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers = { + "Content-Type": "application/json", + } + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = await self._client.delete( f"{self._jupyter_url}/contexts/{context_id}", headers=headers, @@ -310,11 +317,13 @@ async def list_code_contexts(self) -> List[Context]: :return: List of contexts. """ - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers = { + "Content-Type": "application/json", + } + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = await self._client.get( f"{self._jupyter_url}/contexts", headers=headers, @@ -342,12 +351,13 @@ async def restart_code_context( :return: None """ context_id = context.id if isinstance(context, Context) else context - - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers = { + "Content-Type": "application/json", + } + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = await self._client.post( f"{self._jupyter_url}/contexts/{context_id}/restart", headers=headers, diff --git a/python/e2b_code_interpreter/code_interpreter_sync.py b/python/e2b_code_interpreter/code_interpreter_sync.py index 67492398..3adb2804 100644 --- a/python/e2b_code_interpreter/code_interpreter_sync.py +++ b/python/e2b_code_interpreter/code_interpreter_sync.py @@ -188,11 +188,13 @@ def run_code( request_timeout = request_timeout or self.connection_config.request_timeout context_id = context.id if context else None - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers: Dict[str, str] = {"Content-Type": "application/json"} + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + with self._client.stream( "POST", f"{self._jupyter_url}/execute", @@ -250,11 +252,13 @@ def create_code_context( if cwd: data["cwd"] = cwd - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers: Dict[str, str] = {"Content-Type": "application/json"} + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = self._client.post( f"{self._jupyter_url}/contexts", json=data, @@ -284,11 +288,13 @@ def remove_code_context( """ context_id = context.id if isinstance(context, Context) else context - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers: Dict[str, str] = {"Content-Type": "application/json"} + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = self._client.delete( f"{self._jupyter_url}/contexts/{context_id}", headers=headers, @@ -307,11 +313,13 @@ def list_code_contexts(self) -> List[Context]: :return: List of contexts. """ - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers: Dict[str, str] = {"Content-Type": "application/json"} + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = self._client.get( f"{self._jupyter_url}/contexts", headers=headers, @@ -340,11 +348,13 @@ def restart_code_context( """ context_id = context.id if isinstance(context, Context) else context - headers: Dict[str, str] = {} - if self._envd_access_token: - headers = {"X-Access-Token": self._envd_access_token} - try: + headers: Dict[str, str] = {"Content-Type": "application/json"} + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token + if self.traffic_access_token: + headers["E2B-Traffic-Access-Token"] = self.traffic_access_token + response = self._client.post( f"{self._jupyter_url}/contexts/{context_id}/restart", headers=headers, diff --git a/python/tests/async/test_async_basic.py b/python/tests/async/test_async_basic.py index 486fd219..900f39df 100644 --- a/python/tests/async/test_async_basic.py +++ b/python/tests/async/test_async_basic.py @@ -1,6 +1,16 @@ +import pytest + from e2b_code_interpreter.code_interpreter_async import AsyncSandbox async def test_basic(async_sandbox: AsyncSandbox): result = await async_sandbox.run_code("x =1; x") assert result.text == "1" + + +@pytest.mark.skip_debug +async def test_secure_access(async_sandbox_factory): + # Create sandbox with public traffic disabled (secure access) + async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + result = await async_sandbox.run_code("x =1; x") + assert result.text == "1" diff --git a/python/tests/async/test_async_contexts.py b/python/tests/async/test_async_contexts.py index 6be07734..30f47636 100644 --- a/python/tests/async/test_async_contexts.py +++ b/python/tests/async/test_async_contexts.py @@ -1,3 +1,5 @@ +import pytest + from e2b_code_interpreter.code_interpreter_async import AsyncSandbox @@ -60,3 +62,58 @@ async def test_restart_context(async_sandbox: AsyncSandbox): assert execution.error is not None assert execution.error.name == "NameError" assert execution.error.value == "name 'x' is not defined" + + +@pytest.mark.skip_debug +async def test_create_context_secure_traffic(async_sandbox_factory): + async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + context = await async_sandbox.create_code_context() + + contexts = await async_sandbox.list_code_contexts() + last_context = contexts[-1] + + assert last_context.id == context.id + assert last_context.language == context.language + assert last_context.cwd == context.cwd + + +@pytest.mark.skip_debug +async def test_remove_context_secure_traffic(async_sandbox_factory): + async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + context = await async_sandbox.create_code_context() + + await async_sandbox.remove_code_context(context.id) + + contexts = await async_sandbox.list_code_contexts() + assert context.id not in [ctx.id for ctx in contexts] + + +@pytest.mark.skip_debug +async def test_list_contexts_secure_traffic(async_sandbox_factory): + async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + contexts = await async_sandbox.list_code_contexts() + + # default contexts should include python and javascript + languages = [context.language for context in contexts] + assert "python" in languages + assert "javascript" in languages + + +@pytest.mark.skip_debug +async def test_restart_context_secure_traffic(async_sandbox_factory): + async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + context = await async_sandbox.create_code_context() + + # set a variable in the context + await async_sandbox.run_code("x = 1", context=context) + + # restart the context + await async_sandbox.restart_code_context(context.id) + + # check that the variable no longer exists + execution = await async_sandbox.run_code("x", context=context) + + # check for a NameError with message "name 'x' is not defined" + assert execution.error is not None + assert execution.error.name == "NameError" + assert execution.error.value == "name 'x' is not defined" diff --git a/python/tests/conftest.py b/python/tests/conftest.py index bc853e85..6d48d52a 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -30,8 +30,8 @@ def template(): @pytest.fixture() -def sandbox(template, debug): - sandbox = Sandbox.create(template, timeout=timeout, debug=debug) +def sandbox(template, debug, network): + sandbox = Sandbox.create(template, timeout=timeout, debug=debug, network=network) try: yield sandbox @@ -46,15 +46,14 @@ def sandbox(template, debug): @pytest.fixture -def async_sandbox_factory(request, template, debug, event_loop): +def async_sandbox_factory(request, template, debug, network, event_loop): """Factory for creating async sandboxes with proper cleanup.""" async def factory(template_override=None, **kwargs): template_name = template_override or template kwargs.setdefault("timeout", timeout) kwargs.setdefault("debug", debug) - - sandbox = await AsyncSandbox.create(template_name, **kwargs) + sandbox = await AsyncSandbox.create(template_name, network=network, **kwargs) def kill(): async def _kill(): diff --git a/python/tests/sync/test_basic.py b/python/tests/sync/test_basic.py index 4bfc503d..d2eac37f 100644 --- a/python/tests/sync/test_basic.py +++ b/python/tests/sync/test_basic.py @@ -1,6 +1,16 @@ +import pytest + from e2b_code_interpreter.code_interpreter_sync import Sandbox def test_basic(sandbox: Sandbox): result = sandbox.run_code("x =1; x") assert result.text == "1" + + +@pytest.mark.skip_debug +def test_secure_access(sandbox_factory): + sandbox = sandbox_factory(network={"allow_public_traffic": False}) + # Create sandbox with public traffic disabled (secure access) + result = sandbox.run_code("x =1; x") + assert result.text == "1" diff --git a/python/tests/sync/test_contexts.py b/python/tests/sync/test_contexts.py index a7cbd884..2672af7f 100644 --- a/python/tests/sync/test_contexts.py +++ b/python/tests/sync/test_contexts.py @@ -1,3 +1,5 @@ +import pytest + from e2b_code_interpreter.code_interpreter_sync import Sandbox @@ -60,3 +62,59 @@ def test_restart_context(sandbox: Sandbox): assert execution.error is not None assert execution.error.name == "NameError" assert execution.error.value == "name 'x' is not defined" + + +# Secure traffic tests (public traffic disabled) +@pytest.mark.skip_debug +def test_create_context_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(network={"allow_public_traffic": False}) + context = sandbox.create_code_context() + + contexts = sandbox.list_code_contexts() + last_context = contexts[-1] + + assert last_context.id == context.id + assert last_context.language == context.language + assert last_context.cwd == context.cwd + + +@pytest.mark.skip_debug +def test_remove_context_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(network={"allow_public_traffic": False}) + context = sandbox.create_code_context() + + sandbox.remove_code_context(context.id) + + contexts = sandbox.list_code_contexts() + assert context.id not in [ctx.id for ctx in contexts] + + +@pytest.mark.skip_debug +def test_list_contexts_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(network={"allow_public_traffic": False}) + contexts = sandbox.list_code_contexts() + + # default contexts should include python and javascript + languages = [context.language for context in contexts] + assert "python" in languages + assert "javascript" in languages + + +@pytest.mark.skip_debug +def test_restart_context_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(network={"allow_public_traffic": False}) + context = sandbox.create_code_context() + + # set a variable in the context + sandbox.run_code("x = 1", context=context) + + # restart the context + sandbox.restart_code_context(context.id) + + # check that the variable no longer exists + execution = sandbox.run_code("x", context=context) + + # check for a NameError with message "name 'x' is not defined" + assert execution.error is not None + assert execution.error.name == "NameError" + assert execution.error.value == "name 'x' is not defined" From d7b83b16f0e225727b9f5bd8902c3b181dca5782 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:09:46 +0100 Subject: [PATCH 2/9] changed type syntax --- js/src/sandbox.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/js/src/sandbox.ts b/js/src/sandbox.ts index 5b6c564b..4f4777bb 100644 --- a/js/src/sandbox.ts +++ b/js/src/sandbox.ts @@ -220,9 +220,9 @@ export class Sandbox extends BaseSandbox { }, requestTimeout) : undefined - const headers = { + const headers: Record = { 'Content-Type': 'application/json', - } as Record + } if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken @@ -300,10 +300,10 @@ export class Sandbox extends BaseSandbox { */ async createCodeContext(opts?: CreateCodeContextOpts): Promise { try { - const headers = { + const headers: Record = { 'Content-Type': 'application/json', ...this.connectionConfig.headers, - } as Record + } if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken @@ -341,10 +341,10 @@ export class Sandbox extends BaseSandbox { async removeCodeContext(context: Context | string): Promise { try { const id = typeof context === 'string' ? context : context.id - const headers = { + const headers: Record = { 'Content-Type': 'application/json', ...this.connectionConfig.headers, - } as Record + } if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken @@ -411,10 +411,10 @@ export class Sandbox extends BaseSandbox { async restartCodeContext(context: Context | string): Promise { try { const id = typeof context === 'string' ? context : context.id - const headers = { + const headers: Record = { 'Content-Type': 'application/json', ...this.connectionConfig.headers, - } as Record + } if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken From abe6a0280ba4b8cb485078fcc70ffca99ffb8792 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:27:15 +0100 Subject: [PATCH 3/9] bugbot suggestions --- python/tests/async/test_async_basic.py | 2 +- python/tests/async/test_async_contexts.py | 8 ++++---- python/tests/conftest.py | 8 +++++++- python/tests/sync/test_basic.py | 4 ++-- python/tests/sync/test_contexts.py | 16 ++++++++-------- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/python/tests/async/test_async_basic.py b/python/tests/async/test_async_basic.py index 900f39df..cf1b0bb1 100644 --- a/python/tests/async/test_async_basic.py +++ b/python/tests/async/test_async_basic.py @@ -11,6 +11,6 @@ async def test_basic(async_sandbox: AsyncSandbox): @pytest.mark.skip_debug async def test_secure_access(async_sandbox_factory): # Create sandbox with public traffic disabled (secure access) - async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) result = await async_sandbox.run_code("x =1; x") assert result.text == "1" diff --git a/python/tests/async/test_async_contexts.py b/python/tests/async/test_async_contexts.py index 30f47636..d470025d 100644 --- a/python/tests/async/test_async_contexts.py +++ b/python/tests/async/test_async_contexts.py @@ -66,7 +66,7 @@ async def test_restart_context(async_sandbox: AsyncSandbox): @pytest.mark.skip_debug async def test_create_context_secure_traffic(async_sandbox_factory): - async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) context = await async_sandbox.create_code_context() contexts = await async_sandbox.list_code_contexts() @@ -79,7 +79,7 @@ async def test_create_context_secure_traffic(async_sandbox_factory): @pytest.mark.skip_debug async def test_remove_context_secure_traffic(async_sandbox_factory): - async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) context = await async_sandbox.create_code_context() await async_sandbox.remove_code_context(context.id) @@ -90,7 +90,7 @@ async def test_remove_context_secure_traffic(async_sandbox_factory): @pytest.mark.skip_debug async def test_list_contexts_secure_traffic(async_sandbox_factory): - async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) contexts = await async_sandbox.list_code_contexts() # default contexts should include python and javascript @@ -101,7 +101,7 @@ async def test_list_contexts_secure_traffic(async_sandbox_factory): @pytest.mark.skip_debug async def test_restart_context_secure_traffic(async_sandbox_factory): - async_sandbox = async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) context = await async_sandbox.create_code_context() # set a variable in the context diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 6d48d52a..0ca4be22 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -53,7 +53,8 @@ async def factory(template_override=None, **kwargs): template_name = template_override or template kwargs.setdefault("timeout", timeout) kwargs.setdefault("debug", debug) - sandbox = await AsyncSandbox.create(template_name, network=network, **kwargs) + kwargs.setdefault("network", network) + sandbox = await AsyncSandbox.create(template_name, **kwargs) def kill(): async def _kill(): @@ -84,6 +85,11 @@ def debug(): return os.getenv("E2B_DEBUG") is not None +@pytest.fixture +def network(): + return None + + @pytest.fixture(autouse=True) def skip_by_debug(request, debug): if request.node.get_closest_marker("skip_debug"): diff --git a/python/tests/sync/test_basic.py b/python/tests/sync/test_basic.py index d2eac37f..f9571653 100644 --- a/python/tests/sync/test_basic.py +++ b/python/tests/sync/test_basic.py @@ -9,8 +9,8 @@ def test_basic(sandbox: Sandbox): @pytest.mark.skip_debug -def test_secure_access(sandbox_factory): - sandbox = sandbox_factory(network={"allow_public_traffic": False}) +def test_secure_access(sandbox): + sandbox = sandbox(network={"allow_public_traffic": False}) # Create sandbox with public traffic disabled (secure access) result = sandbox.run_code("x =1; x") assert result.text == "1" diff --git a/python/tests/sync/test_contexts.py b/python/tests/sync/test_contexts.py index 2672af7f..5910ada8 100644 --- a/python/tests/sync/test_contexts.py +++ b/python/tests/sync/test_contexts.py @@ -66,8 +66,8 @@ def test_restart_context(sandbox: Sandbox): # Secure traffic tests (public traffic disabled) @pytest.mark.skip_debug -def test_create_context_secure_traffic(sandbox_factory): - sandbox = sandbox_factory(network={"allow_public_traffic": False}) +def test_create_context_secure_traffic(sandbox): + sandbox = sandbox(network={"allow_public_traffic": False}) context = sandbox.create_code_context() contexts = sandbox.list_code_contexts() @@ -79,8 +79,8 @@ def test_create_context_secure_traffic(sandbox_factory): @pytest.mark.skip_debug -def test_remove_context_secure_traffic(sandbox_factory): - sandbox = sandbox_factory(network={"allow_public_traffic": False}) +def test_remove_context_secure_traffic(sandbox): + sandbox = sandbox(network={"allow_public_traffic": False}) context = sandbox.create_code_context() sandbox.remove_code_context(context.id) @@ -90,8 +90,8 @@ def test_remove_context_secure_traffic(sandbox_factory): @pytest.mark.skip_debug -def test_list_contexts_secure_traffic(sandbox_factory): - sandbox = sandbox_factory(network={"allow_public_traffic": False}) +def test_list_contexts_secure_traffic(sandbox): + sandbox = sandbox(network={"allow_public_traffic": False}) contexts = sandbox.list_code_contexts() # default contexts should include python and javascript @@ -101,8 +101,8 @@ def test_list_contexts_secure_traffic(sandbox_factory): @pytest.mark.skip_debug -def test_restart_context_secure_traffic(sandbox_factory): - sandbox = sandbox_factory(network={"allow_public_traffic": False}) +def test_restart_context_secure_traffic(sandbox): + sandbox = sandbox(network={"allow_public_traffic": False}) context = sandbox.create_code_context() # set a variable in the context From 72a899cc190dc2af8ad4f323a2e1f263b292f49c Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:06:32 +0100 Subject: [PATCH 4/9] update test setup conf --- js/tests/basic.test.ts | 13 +--- js/tests/contexts.test.ts | 43 +++---------- js/tests/setup.ts | 45 +++++++++++--- python/tests/conftest.py | 99 +++++++++++++++--------------- python/tests/sync/test_contexts.py | 16 ++--- 5 files changed, 103 insertions(+), 113 deletions(-) diff --git a/js/tests/basic.test.ts b/js/tests/basic.test.ts index a41b61b0..2f0b9837 100644 --- a/js/tests/basic.test.ts +++ b/js/tests/basic.test.ts @@ -1,6 +1,5 @@ import { expect } from 'vitest' -import { isDebug, sandboxTest } from './setup' -import { Sandbox } from '../src' +import { isDebug, sandboxTest, secureSandboxTest } from './setup' sandboxTest('basic', async ({ sandbox }) => { const result = await sandbox.runCode('x =1; x') @@ -8,16 +7,8 @@ sandboxTest('basic', async ({ sandbox }) => { expect(result.text).toEqual('1') }) -sandboxTest.skipIf(isDebug)('secure access', async ({ template }) => { - const sandbox = await Sandbox.create(template, { - network: { - allowPublicTraffic: false, - }, - }) - +secureSandboxTest.skipIf(isDebug)('secure access', async ({ sandbox }) => { const result = await sandbox.runCode('x =1; x') expect(result.text).toEqual('1') - - await sandbox.kill() }) diff --git a/js/tests/contexts.test.ts b/js/tests/contexts.test.ts index 42d91db6..b0cf9567 100644 --- a/js/tests/contexts.test.ts +++ b/js/tests/contexts.test.ts @@ -1,7 +1,6 @@ import { expect } from 'vitest' -import { isDebug, sandboxTest } from './setup' -import Sandbox from '../src' +import { isDebug, sandboxTest, secureSandboxTest } from './setup' sandboxTest('create context with no options', async ({ sandbox }) => { const context = await sandbox.createCodeContext() @@ -63,14 +62,9 @@ sandboxTest('restart context', async ({ sandbox }) => { expect(execution.error?.value).toBe("name 'x' is not defined") }) -sandboxTest.skipIf(isDebug)( +secureSandboxTest.skipIf(isDebug)( 'create context (secure traffic)', - async ({ template }) => { - const sandbox = await Sandbox.create(template, { - network: { - allowPublicTraffic: false, - }, - }) + async ({ sandbox }) => { const context = await sandbox.createCodeContext() const contexts = await sandbox.listCodeContexts() @@ -79,19 +73,12 @@ sandboxTest.skipIf(isDebug)( expect(lastContext.id).toBe(context.id) expect(lastContext.language).toBe(context.language) expect(lastContext.cwd).toBe(context.cwd) - - await sandbox.kill() } ) -sandboxTest.skipIf(isDebug)( +secureSandboxTest.skipIf(isDebug)( 'remove context (secure traffic)', - async ({ template }) => { - const sandbox = await Sandbox.create(template, { - network: { - allowPublicTraffic: false, - }, - }) + async ({ sandbox }) => { const context = await sandbox.createCodeContext() await sandbox.removeCodeContext(context.id) @@ -103,14 +90,9 @@ sandboxTest.skipIf(isDebug)( } ) -sandboxTest.skipIf(isDebug)( +secureSandboxTest.skipIf(isDebug)( 'list contexts (secure traffic)', - async ({ template }) => { - const sandbox = await Sandbox.create(template, { - network: { - allowPublicTraffic: false, - }, - }) + async ({ sandbox }) => { const contexts = await sandbox.listCodeContexts() // default contexts should include python and javascript @@ -121,14 +103,9 @@ sandboxTest.skipIf(isDebug)( } ) -sandboxTest.skipIf(isDebug)( +secureSandboxTest.skipIf(isDebug)( 'restart context (secure traffic)', - async ({ template }) => { - const sandbox = await Sandbox.create(template, { - network: { - allowPublicTraffic: false, - }, - }) + async ({ sandbox }) => { const context = await sandbox.createCodeContext() // set a variable in the context @@ -144,7 +121,5 @@ sandboxTest.skipIf(isDebug)( expect(execution.error).toBeDefined() expect(execution.error?.name).toBe('NameError') expect(execution.error?.value).toBe("name 'x' is not defined") - - await sandbox.kill() } ) diff --git a/js/tests/setup.ts b/js/tests/setup.ts index b11ce4fa..2f4be79c 100644 --- a/js/tests/setup.ts +++ b/js/tests/setup.ts @@ -1,21 +1,31 @@ -import { Sandbox } from '../src' import { test as base } from 'vitest' - -const timeoutMs = 60_000 - -const template = process.env.E2B_TESTS_TEMPLATE || 'code-interpreter-v1' +import { Sandbox, SandboxOpts } from '../src' interface SandboxFixture { sandbox: Sandbox template: string + sandboxTestId: string + sandboxOpts: Partial } +const template = process.env.E2B_TESTS_TEMPLATE || 'code-interpreter-v1' + export const sandboxTest = base.extend({ - sandbox: [ + template, + sandboxTestId: [ // eslint-disable-next-line no-empty-pattern async ({}, use) => { + const id = `test-${generateRandomString()}` + await use(id) + }, + { auto: true }, + ], + sandboxOpts: {}, + sandbox: [ + async ({ sandboxTestId, sandboxOpts }, use) => { const sandbox = await Sandbox.create(template, { - timeoutMs, + metadata: { sandboxTestId }, + ...sandboxOpts, }) try { await use(sandbox) @@ -31,13 +41,30 @@ export const sandboxTest = base.extend({ } } }, - { auto: true }, + { auto: false }, ], - template, }) export const isDebug = process.env.E2B_DEBUG !== undefined +export const isIntegrationTest = process.env.E2B_INTEGRATION_TEST !== undefined + +export const secureSandboxTest = sandboxTest.extend({ + sandboxOpts: { + secure: true, + network: { + allowPublicTraffic: false, + }, + }, +}) + +function generateRandomString(length: number = 8): string { + return Math.random() + .toString(36) + .substring(2, length + 2) +} export async function wait(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)) } + +export { template } diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 0ca4be22..549c4d57 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -1,27 +1,18 @@ -import pytest -import pytest_asyncio -import os import asyncio +import os -from logging import warning - -from e2b_code_interpreter.code_interpreter_async import AsyncSandbox -from e2b_code_interpreter.code_interpreter_sync import Sandbox +import pytest -timeout = 60 +from e2b_code_interpreter import ( + AsyncSandbox, + Sandbox, +) +import uuid -# Override the event loop so it never closes during test execution -# This helps with pytest-xdist and prevents "Event loop is closed" errors @pytest.fixture(scope="session") -def event_loop(): - """Create a session-scoped event loop for all async tests.""" - try: - loop = asyncio.get_running_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - yield loop - loop.close() +def sandbox_test_id(): + return f"test_{uuid.uuid4()}" @pytest.fixture() @@ -30,45 +21,58 @@ def template(): @pytest.fixture() -def sandbox(template, debug, network): - sandbox = Sandbox.create(template, timeout=timeout, debug=debug, network=network) +def sandbox_factory(request, template, sandbox_test_id): + def factory(*, template_name: str = template, **kwargs): + kwargs.setdefault("secure", False) + kwargs.setdefault("timeout", 5) + + metadata = kwargs.setdefault("metadata", dict()) + metadata.setdefault("sandbox_test_id", sandbox_test_id) + + sandbox = Sandbox.create(template_name, **kwargs) + + request.addfinalizer(lambda: sandbox.kill()) + + return sandbox + + return factory + +@pytest.fixture() +def sandbox(sandbox_factory): + return sandbox_factory() + + +# override the event loop so it never closes +# this helps us with the global-scoped async http transport +@pytest.fixture(scope="session") +def event_loop(): try: - yield sandbox - finally: - try: - sandbox.kill() - except: # noqa: E722 - if not debug: - warning( - "Failed to kill sandbox — this is expected if the test runs with local envd." - ) + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + yield loop + loop.close() @pytest.fixture -def async_sandbox_factory(request, template, debug, network, event_loop): - """Factory for creating async sandboxes with proper cleanup.""" - - async def factory(template_override=None, **kwargs): - template_name = template_override or template - kwargs.setdefault("timeout", timeout) - kwargs.setdefault("debug", debug) - kwargs.setdefault("network", network) +def async_sandbox_factory(request, template, sandbox_test_id, event_loop): + async def factory(*, template_name: str = template, **kwargs): + kwargs.setdefault("timeout", 5) + + metadata = kwargs.setdefault("metadata", dict()) + metadata.setdefault("sandbox_test_id", sandbox_test_id) + sandbox = await AsyncSandbox.create(template_name, **kwargs) def kill(): async def _kill(): - try: - await sandbox.kill() - except: # noqa: E722 - if not debug: - warning( - "Failed to kill sandbox — this is expected if the test runs with local envd." - ) + await sandbox.kill() event_loop.run_until_complete(_kill()) request.addfinalizer(kill) + return sandbox return factory @@ -76,7 +80,6 @@ async def _kill(): @pytest.fixture async def async_sandbox(async_sandbox_factory): - """Default async sandbox fixture.""" return await async_sandbox_factory() @@ -84,12 +87,6 @@ async def async_sandbox(async_sandbox_factory): def debug(): return os.getenv("E2B_DEBUG") is not None - -@pytest.fixture -def network(): - return None - - @pytest.fixture(autouse=True) def skip_by_debug(request, debug): if request.node.get_closest_marker("skip_debug"): diff --git a/python/tests/sync/test_contexts.py b/python/tests/sync/test_contexts.py index 5910ada8..0b8278f5 100644 --- a/python/tests/sync/test_contexts.py +++ b/python/tests/sync/test_contexts.py @@ -66,8 +66,8 @@ def test_restart_context(sandbox: Sandbox): # Secure traffic tests (public traffic disabled) @pytest.mark.skip_debug -def test_create_context_secure_traffic(sandbox): - sandbox = sandbox(network={"allow_public_traffic": False}) +def test_create_context_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(secure=True, network={"allow_public_traffic": False}) context = sandbox.create_code_context() contexts = sandbox.list_code_contexts() @@ -79,8 +79,8 @@ def test_create_context_secure_traffic(sandbox): @pytest.mark.skip_debug -def test_remove_context_secure_traffic(sandbox): - sandbox = sandbox(network={"allow_public_traffic": False}) +def test_remove_context_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(secure=True, network={"allow_public_traffic": False}) context = sandbox.create_code_context() sandbox.remove_code_context(context.id) @@ -90,8 +90,8 @@ def test_remove_context_secure_traffic(sandbox): @pytest.mark.skip_debug -def test_list_contexts_secure_traffic(sandbox): - sandbox = sandbox(network={"allow_public_traffic": False}) +def test_list_contexts_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(secure=True, network={"allow_public_traffic": False}) contexts = sandbox.list_code_contexts() # default contexts should include python and javascript @@ -101,8 +101,8 @@ def test_list_contexts_secure_traffic(sandbox): @pytest.mark.skip_debug -def test_restart_context_secure_traffic(sandbox): - sandbox = sandbox(network={"allow_public_traffic": False}) +def test_restart_context_secure_traffic(sandbox_factory): + sandbox = sandbox_factory(secure=True, network={"allow_public_traffic": False}) context = sandbox.create_code_context() # set a variable in the context From ee532396085b348d7b6f62f8e7a5a5c85764ff28 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:08:02 +0100 Subject: [PATCH 5/9] format --- python/tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 549c4d57..de659f66 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -87,6 +87,7 @@ async def async_sandbox(async_sandbox_factory): def debug(): return os.getenv("E2B_DEBUG") is not None + @pytest.fixture(autouse=True) def skip_by_debug(request, debug): if request.node.get_closest_marker("skip_debug"): From 013110d77f77ba8d86bb245ab664c7a47d337713 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:16:05 +0100 Subject: [PATCH 6/9] cursor suggestion --- python/tests/async/test_async_contexts.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/python/tests/async/test_async_contexts.py b/python/tests/async/test_async_contexts.py index d470025d..0f533f8f 100644 --- a/python/tests/async/test_async_contexts.py +++ b/python/tests/async/test_async_contexts.py @@ -66,7 +66,9 @@ async def test_restart_context(async_sandbox: AsyncSandbox): @pytest.mark.skip_debug async def test_create_context_secure_traffic(async_sandbox_factory): - async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory( + secure=True, network={"allow_public_traffic": False} + ) context = await async_sandbox.create_code_context() contexts = await async_sandbox.list_code_contexts() @@ -79,7 +81,9 @@ async def test_create_context_secure_traffic(async_sandbox_factory): @pytest.mark.skip_debug async def test_remove_context_secure_traffic(async_sandbox_factory): - async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory( + secure=True, network={"allow_public_traffic": False} + ) context = await async_sandbox.create_code_context() await async_sandbox.remove_code_context(context.id) @@ -90,7 +94,9 @@ async def test_remove_context_secure_traffic(async_sandbox_factory): @pytest.mark.skip_debug async def test_list_contexts_secure_traffic(async_sandbox_factory): - async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory( + secure=True, network={"allow_public_traffic": False} + ) contexts = await async_sandbox.list_code_contexts() # default contexts should include python and javascript @@ -101,7 +107,9 @@ async def test_list_contexts_secure_traffic(async_sandbox_factory): @pytest.mark.skip_debug async def test_restart_context_secure_traffic(async_sandbox_factory): - async_sandbox = await async_sandbox_factory(network={"allow_public_traffic": False}) + async_sandbox = await async_sandbox_factory( + secure=True, network={"allow_public_traffic": False} + ) context = await async_sandbox.create_code_context() # set a variable in the context From a01968d805df2f6124a87b6e1151b98603a333f0 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:23:24 +0100 Subject: [PATCH 7/9] increase default timeout --- python/tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index de659f66..a571e00a 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -24,7 +24,7 @@ def template(): def sandbox_factory(request, template, sandbox_test_id): def factory(*, template_name: str = template, **kwargs): kwargs.setdefault("secure", False) - kwargs.setdefault("timeout", 5) + kwargs.setdefault("timeout", 60) metadata = kwargs.setdefault("metadata", dict()) metadata.setdefault("sandbox_test_id", sandbox_test_id) @@ -58,7 +58,7 @@ def event_loop(): @pytest.fixture def async_sandbox_factory(request, template, sandbox_test_id, event_loop): async def factory(*, template_name: str = template, **kwargs): - kwargs.setdefault("timeout", 5) + kwargs.setdefault("timeout", 60) metadata = kwargs.setdefault("metadata", dict()) metadata.setdefault("sandbox_test_id", sandbox_test_id) From b683c17ab83e1d71cfd89ef622f1935d579bd5de Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:27:42 +0100 Subject: [PATCH 8/9] sandbox not callable --- python/tests/sync/test_basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/sync/test_basic.py b/python/tests/sync/test_basic.py index f9571653..30f94983 100644 --- a/python/tests/sync/test_basic.py +++ b/python/tests/sync/test_basic.py @@ -9,8 +9,8 @@ def test_basic(sandbox: Sandbox): @pytest.mark.skip_debug -def test_secure_access(sandbox): - sandbox = sandbox(network={"allow_public_traffic": False}) +def test_secure_access(sandbox_factory): + sandbox = sandbox_factory(secure=True, network={"allow_public_traffic": False}) # Create sandbox with public traffic disabled (secure access) result = sandbox.run_code("x =1; x") assert result.text == "1" From 160b4fcf4c012b39195612102924a3e3dbfeecbb Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 26 Nov 2025 19:06:15 +0100 Subject: [PATCH 9/9] nit --- js/src/sandbox.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/js/src/sandbox.ts b/js/src/sandbox.ts index 4f4777bb..a02b9d9e 100644 --- a/js/src/sandbox.ts +++ b/js/src/sandbox.ts @@ -302,7 +302,6 @@ export class Sandbox extends BaseSandbox { try { const headers: Record = { 'Content-Type': 'application/json', - ...this.connectionConfig.headers, } if (this.trafficAccessToken) { @@ -343,7 +342,6 @@ export class Sandbox extends BaseSandbox { const id = typeof context === 'string' ? context : context.id const headers: Record = { 'Content-Type': 'application/json', - ...this.connectionConfig.headers, } if (this.trafficAccessToken) { @@ -375,15 +373,17 @@ export class Sandbox extends BaseSandbox { */ async listCodeContexts(): Promise { try { + const headers: Record = { + 'Content-Type': 'application/json', + } + + if (this.trafficAccessToken) { + headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken + } + const res = await fetch(`${this.jupyterUrl}/contexts`, { method: 'GET', - headers: { - 'Content-Type': 'application/json', - ...this.connectionConfig.headers, - ...(this.trafficAccessToken - ? { 'E2B-Traffic-Access-Token': this.trafficAccessToken } - : {}), - }, + headers, keepalive: true, signal: this.connectionConfig.getSignal( this.connectionConfig.requestTimeoutMs @@ -413,7 +413,6 @@ export class Sandbox extends BaseSandbox { const id = typeof context === 'string' ? context : context.id const headers: Record = { 'Content-Type': 'application/json', - ...this.connectionConfig.headers, } if (this.trafficAccessToken) {