diff --git a/.changeset/fix-pause-jsdoc-test.md b/.changeset/fix-pause-jsdoc-test.md new file mode 100644 index 0000000000..24370e7499 --- /dev/null +++ b/.changeset/fix-pause-jsdoc-test.md @@ -0,0 +1,5 @@ +--- +"e2b": patch +--- + +fix: update pause() JSDoc and add regression tests for apiKey propagation diff --git a/packages/js-sdk/src/connectionConfig.ts b/packages/js-sdk/src/connectionConfig.ts index 298dff1dd9..68a631b443 100644 --- a/packages/js-sdk/src/connectionConfig.ts +++ b/packages/js-sdk/src/connectionConfig.ts @@ -84,10 +84,10 @@ export class ConnectionConfig { readonly headers?: Record constructor(opts?: ConnectionOpts) { - this.apiKey = opts?.apiKey || ConnectionConfig.apiKey + this.apiKey = opts?.apiKey ?? ConnectionConfig.apiKey this.debug = opts?.debug || ConnectionConfig.debug this.domain = opts?.domain || ConnectionConfig.domain - this.accessToken = opts?.accessToken || ConnectionConfig.accessToken + this.accessToken = opts?.accessToken ?? ConnectionConfig.accessToken this.requestTimeoutMs = opts?.requestTimeoutMs ?? REQUEST_TIMEOUT_MS this.logger = opts?.logger this.headers = opts?.headers || {} diff --git a/packages/js-sdk/src/sandbox/index.ts b/packages/js-sdk/src/sandbox/index.ts index 3471dec6cd..043d8bc18b 100644 --- a/packages/js-sdk/src/sandbox/index.ts +++ b/packages/js-sdk/src/sandbox/index.ts @@ -572,11 +572,11 @@ export class Sandbox extends SandboxApi { } /** - * Pause a sandbox by its ID. + * Pause this sandbox. * * @param opts connection options. * - * @returns sandbox ID that can be used to resume the sandbox. + * @returns true if the sandbox was paused successfully, false if it was already paused. * * @example * ```ts @@ -585,14 +585,20 @@ export class Sandbox extends SandboxApi { * ``` */ async pause(opts?: ConnectionOpts): Promise { - return await SandboxApi.pause(this.sandboxId, opts) + return await SandboxApi.pause(this.sandboxId, { + ...this.connectionConfig, + ...opts, + }) } /** * @deprecated Use {@link Sandbox.pause} instead. */ async betaPause(opts?: ConnectionOpts): Promise { - return await SandboxApi.betaPause(this.sandboxId, opts) + return await SandboxApi.betaPause(this.sandboxId, { + ...this.connectionConfig, + ...opts, + }) } /** diff --git a/packages/js-sdk/tests/sandbox/pause.test.ts b/packages/js-sdk/tests/sandbox/pause.test.ts new file mode 100644 index 0000000000..e7e32d3df7 --- /dev/null +++ b/packages/js-sdk/tests/sandbox/pause.test.ts @@ -0,0 +1,103 @@ +import { assert } from 'vitest' + +import { Sandbox } from '../../src' +import { isDebug, sandboxTest } from '../setup' + +/** + * Regression test: Sandbox.connect() with explicit apiKey should allow + * pause() to succeed even when E2B_API_KEY is not set in the environment. + * + * See: https://github.com/e2b-dev/E2B/pull/1218 + */ +sandboxTest.skipIf(isDebug)( + 'pause() uses apiKey from connectionConfig when E2B_API_KEY is not set', + async ({ sandbox }) => { + // Save and clear the environment API key + const savedApiKey = process.env.E2B_API_KEY + delete process.env.E2B_API_KEY + + try { + // Get the apiKey that was used to create this sandbox + const finalApiKey = savedApiKey + if (finalApiKey === undefined) { + throw new Error('apiKey must be defined at this point') + } + + // Connect to the sandbox with an explicit apiKey (E2B_API_KEY is now unset) + const connected = await Sandbox.connect(sandbox.sandboxId, { + apiKey: finalApiKey, + }) + + // Guard: ensure the sandbox is running before we try to pause it + assert.isTrue( + await sandbox.isRunning(), + 'sandbox must be running before pause() is called' + ) + + // pause() should succeed using the apiKey from connectionConfig + // rather than requiring E2B_API_KEY to be set + const paused = await connected.pause() + assert.isTrue( + paused, + 'pause() should return true when successfully pausing a running sandbox' + ) + + // Verify the sandbox is actually paused + assert.isFalse( + await connected.isRunning(), + 'sandbox should be paused after pause()' + ) + } finally { + // Restore the environment API key, including empty strings + process.env.E2B_API_KEY = savedApiKey ?? undefined + } + } +) + +sandboxTest.skipIf(isDebug)( + 'pause() returns false when sandbox is already paused', + async ({ sandbox }) => { + // Pause the sandbox first + const firstPause = await sandbox.pause() + assert.isTrue(firstPause, 'first pause() should return true') + + // Verify the sandbox is actually paused before the second attempt + assert.isFalse( + await sandbox.isRunning(), + 'sandbox should be paused after first pause()' + ) + + // Try to pause again - should return false since already paused + const secondPause = await sandbox.pause() + assert.isFalse( + secondPause, + 'pause() should return false when sandbox is already paused' + ) + } +) + +sandboxTest.skipIf(isDebug)( + 'pause() works on connected sandbox with apiKey in connectionConfig', + async ({ sandbox }) => { + const apiKey = process.env.E2B_API_KEY + if (apiKey === undefined) { + throw new Error('E2B_API_KEY must be set in environment') + } + const finalApiKey = apiKey + + // Connect to the sandbox using apiKey from connectionConfig + const connected = await Sandbox.connect(sandbox.sandboxId, { + apiKey: finalApiKey, + }) + + // Ensure the sandbox is running before pausing + assert.isTrue(await sandbox.isRunning()) + + // Pause the connected sandbox + const paused = await connected.pause() + assert.isTrue(paused, 'pause() should succeed on connected sandbox') + + // Verify it's paused + assert.isFalse(await connected.isRunning()) + } +)