Commit Graph

22 Commits

Author SHA1 Message Date
devin-ai-integration[bot] 5759f17e56 feat(sdk): add E2B client for multiple bound connection configs (#1720)
## Summary

Adds an `E2B` client to both SDKs so a process can talk to several API
keys / domains / deployments without going through environment
variables. The client binds a connection config once and exposes the
resource surfaces off it; the named top-level exports are untouched and
keep reading the environment.

Nothing existing changes (changeset is `minor`): the default export is
still `Sandbox`, `Template(...)` keeps working, and `E2B` is a new named
export. Two follow-ups are tracked for v3: making `E2B` the default
export
([SDK-341](https://linear.app/e2b/issue/SDK-341/sdk-v3-js-make-e2b-the-default-export-instead-of-sandbox))
and dropping the `Template` Proxy in favour of `new Template()`
([SDK-342](https://linear.app/e2b/issue/SDK-342/sdk-v3-js-drop-the-template-proxy-require-new-template)).

```ts
import { E2B } from 'e2b'

const { Sandbox, Volume, Template, Secret } = new E2B({
  apiKey: 'e2b_***',
  domain: 'e2b.dev',
})

const sandbox = await Sandbox.create()
const volume = await Volume.create('my-volume')
const exists = await Template.exists('my-template')
await Template.build(Template().fromPythonImage('3'), 'my-env')
await Secret.create('openai-api-key', 'sk-***')

// Per-call options still win over the client's options.
await Sandbox.create({ apiKey: 'e2b_other***' })
```

```python
from e2b import E2B

client = E2B(api_key="e2b_***", domain="e2b.dev")
Sandbox, Volume, Template = client.Sandbox, client.Volume, client.Template
Secret = client.Secret

sandbox = Sandbox.create()
volume = Volume.create("my-volume")
exists = Template.exists("my-template")
secret = Secret.create("openai-api-key", "sk-***")

# Async variants are exposed too.
AsyncSandbox, AsyncTemplate = client.AsyncSandbox, client.AsyncTemplate
async_sandbox = await AsyncSandbox.create()
await AsyncTemplate.exists("my-template")
```

### Mechanism

`client.Sandbox` / `client.Volume` / `client.Template` / `client.Secret`
(plus the `Async*` variants in Python) are per-client subclasses of the
real classes, carrying the bound opts as class-level state. Nothing
process-global is mutated, so clients are isolated from each other and
from the default path, and `cls`/`this` dispatch is preserved (`create`
on a client class returns an instance of that client class).

```ts
// sandboxApi.ts / volume/index.ts / template/index.ts / secret.ts — one hook per class hierarchy
protected static readonly boundOpts?: ConnectionOpts // undefined on the base classes
protected static resolveOpts<T extends ConnectionOpts>(opts?: T) {
  return ConnectionConfig.mergeOpts(this.boundOpts, opts) // { ...bound, ...definedPerCall }
}

// every static method that built a config from raw opts now does
- const config = new ConnectionConfig(opts)
+ const apiOpts = this.resolveOpts(opts)
+ const config = new ConnectionConfig(apiOpts)
```

```py
# sandbox/main.py, volume_sync.py, volume_async.py, template_{sync,async}/main.py, secret/base.py
_bound_api_params: ApiParams = {}  # empty on the base classes

@classmethod
def _resolve_api_params(cls, **opts: Unpack[ApiParams]) -> ApiParams:
    return merge_api_params(cls._bound_api_params, opts)

- config = ConnectionConfig(**opts)
+ config = ConnectionConfig(**cls._resolve_api_params(**opts))
```

`Template` used to be a factory function whose statics were pre-bound to
`TemplateBase`, which left no class for a client to subclass. It is now
the `TemplateBase` class itself, wrapped in a `Proxy` whose only trap
makes it callable without `new`, so `Template(...)` keeps working (no
breaking change) while `client.Template` is a plain subclass like
Sandbox/Volume and `Template.build(...)` resolves `this` naturally:

```ts
export function callableTemplate<T extends typeof TemplateBase>(cls: T) {
  return new Proxy(cls, { apply: (target, _this, args) => new target(...args) })
}
export const Template = callableTemplate(TemplateBase)          // Template() still returns a builder
this.Template = callableTemplate(class extends TemplateBase { boundOpts })  // client.Template
```

Because the trap only intercepts calls, `new Template()`, statics,
`instanceof` and subclassing all go straight to the class, and the
builder's default file context (`getCallerDirectory()`) still resolves
to the user's frame (the trap's frame is inside the SDK and filtered
like the old factory's).

Two side effects of routing everything through the hook:

- Static methods that resolved config off the base class had to move to
`this`/`cls`: `SandboxApi.createSandbox(...)` →
`this.createSandbox(...)` in JS, `new Volume(...)` → `new this(...)`,
and several Python `@staticmethod`s (`SandboxApi.list`,
`_cls_list_snapshots`, `delete_snapshot`, `Volume._class_get_info` /
`_class_list` / `destroy`, and the `Secret` operations) became
`@classmethod`s. Behavior for the top-level classes is unchanged since
their bound opts are empty.
- `DualMethod.__get__` (the descriptor behind `Volume.get_info` /
`Volume.list` working both on the class and on instances) now binds the
class-level function to the accessed class, so `client.Volume.list()`
sees the subclass' bound params instead of `Volume`'s.

Per-call values explicitly set to `undefined` / `None` are dropped when
merging, so they fall back to the client's opts rather than clearing
them into the env-var path.

### Tests

`packages/js-sdk/tests/client.test.ts` (MSW) and
`packages/python-sdk/tests/test_client.py` (local HTTP server, sync +
async) cover: the client's API key/domain being used instead of the env
vars, per-call precedence, rebinding the class (`const S =
client.Sandbox`), rebound `client.Template`, the client template builder
producing the same Dockerfile as the top-level one, two clients staying
isolated, generated-subclass instances, `client.Secret` (sync + async)
using the bound config, the top-level classes still using the env config
with empty bound opts and the default export still being `Sandbox`.



Link to Devin session:
https://app.devin.ai/sessions/772afa048b814ad784b5dde0a599df46
Requested by: @mishushakov

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mish@e2b.dev <mish@e2b.dev>
Co-authored-by: Mish Ushakov <10400064+mishushakov@users.noreply.github.com>
2026-08-20 18:03:15 +00:00
michael-e2b 2daced65be chore: tag package homepage URLs with UTM parameters (#1724)
The SDK and CLI homepage fields get utm_source=pypi/npm
(utm_campaign=package_homepage) so traffic from the registry pages
attributes to its real source instead of direct. Takes effect on the
next publish of each package.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-19 23:10:48 +00:00
devin-ai-integration[bot] 726ced6ec5 docs: fix duplicate logo on NPM/PyPI by switching to <picture> element (#1466)
## Summary

Fixes the duplicate logo issue on NPM and PyPI caused by #1462. The
`#gh-light-mode-only` / `#gh-dark-mode-only` URL fragments are
GitHub-specific — NPM and PyPI ignore them and render both `<img>` tags.

Switches all three package READMEs (CLI, JS SDK, Python SDK) to
`<picture>` elements:

```html
<picture>
  <source media="(prefers-color-scheme: dark)" srcset=".../logo-white.png">
  <source media="(prefers-color-scheme: light)" srcset=".../logo-black.png">
  <img alt="E2B Logo" src=".../logo-black.png" width="200">
</picture>
```

- **GitHub**: `<picture>` + `prefers-color-scheme` handles theme
switching
- **NPM/PyPI**: `<picture>` not supported, falls back to the single
`<img>` (black logo)

Link to Devin session:
https://app.devin.ai/sessions/4983f23d23934d2c9a51733f5f9920f3
Requested by: @mlejva

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vasek <vasek.mlejnsky@gmail.com>
2026-06-19 19:26:46 +00:00
devin-ai-integration[bot] 0a5d52478c docs: update package logos with theme-aware dark/light variants (#1462)
## Summary

Replace the old `logo-circle.png` in the CLI, JS SDK, and Python SDK
READMEs with the new E2B wordmark logos that adapt to GitHub's theme
setting.

Each package README now uses a `<picture>` element:
```html
<picture>
  <source media="(prefers-color-scheme: dark)" srcset=".../logo-white.png">
  <source media="(prefers-color-scheme: light)" srcset=".../logo-black.png">
  <img alt="E2B Logo" src=".../logo-black.png" width="200">
</picture>
```

- **Light theme** → black logo (`logo-black.png`)
- **Dark theme** → white logo (`logo-white.png`)
- **NPM/PyPI** (no `<picture>` support) → falls back to the black logo
via the `<img>` tag

New logo assets added to `readme-assets/`: `logo-black.png`,
`logo-white.png`.

Includes a patch changeset for `@e2b/cli`, `e2b` (JS SDK), and
`@e2b/python-sdk`.

Link to Devin session:
https://app.devin.ai/sessions/4983f23d23934d2c9a51733f5f9920f3
Requested by: @mlejva

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vasek <vasek.mlejnsky@gmail.com>
2026-06-19 18:48:00 +00:00
Berry 1cc385a767 Link runCode/run_code to docs in READMEs (#1237)
## Summary
- Made `runCode()` and `run_code()` references in READMEs link to the
[code interpreting docs](https://e2b.dev/docs/code-interpreting)
- Updated root README, js-sdk README, and python-sdk README

## Test plan
- [ ] Verify links render correctly on GitHub
- [ ] Confirm docs URL resolves
2026-03-29 15:48:28 +02:00
Berry a240f99db5 Default to base e2b SDK in READMEs (#1232)
## Summary
- Updated root README, js-sdk README, and python-sdk README to show base
`e2b` SDK install and usage as the default
- Code-interpreter is now shown as an optional step for when
`runCode()`/`run_code()` is actually needed
- SDK links in descriptions now point to base `e2b` packages on npm/PyPI

## Why
The base `e2b` package covers commands, files, git, networking, and
sandbox lifecycle. Users who don't need code execution shouldn't be
directed to install `@e2b/code-interpreter` / `e2b-code-interpreter` as
their first step.

## Test plan
- [ ] Verify README renders correctly on GitHub
- [ ] Confirm base SDK examples use correct import syntax
- [ ] Confirm code-interpreter section still shows correct usage for
`runCode()`
2026-03-24 21:04:20 +01:00
Vasek Mlejnsky 96c407e27f Update download badges in README.md (#1133)
Update broken downloads badge

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Documentation-only change that updates external badge image URLs and
styling; no runtime or build behavior is affected.
> 
> **Overview**
> Fixes broken download badges in `README.md` and
`packages/python-sdk/README.md`.
> 
> The PyPI badge is switched from shields.io to a Pepy monthly downloads
badge, and the NPM badge label/styling is updated to explicitly show
*monthly* downloads.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
811324dd583dd068f6a763489ac6c63483c47f87. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2026-02-14 21:10:01 +00:00
Jakub Novák d117ba09e8 SDK v2 - Add beta pause and update Sandbox list (#854)
Introduce beta submodule with beta features - pause and resume
Update Sandbox list to also return paused sandboxes

---------

Co-authored-by: Tomas Valenta <valenta.and.thomas@gmail.com>
2025-08-19 00:37:07 -07:00
0div 1defbc8506 fix typo in legacy docs/READMEs 2024-11-22 14:45:39 -08:00
Vasek Mlejnsky 33992a6c31 Update README.md 2024-10-16 04:23:38 -07:00
Vasek Mlejnsky f502f52fff Update README.md 2024-10-16 04:11:12 -07:00
Vasek Mlejnsky 2be5fb296d Update README.md 2024-10-16 02:44:59 -07:00
Tereza Tizkova c682d94bc8 Update readmes 2024-10-15 16:40:07 -07:00
Tereza Tizkova b5a6f4958e Update readme 2024-10-14 14:10:13 -07:00
Tereza Tizkova 315c264ecd Update SDK readme 2024-10-12 20:49:33 -07:00
Tomas Valenta 60efe38976 Fix package name for downloads' badge in SDK readmes 2024-04-24 22:29:52 -07:00
Vasek Mlejnsky 731a50ff32 Update readmes 2023-11-09 13:39:58 +01:00
Vasek Mlejnsky c42077a8e4 update readmes 2023-11-09 13:29:52 +01:00
Tomas Valenta d8dbce93a7 Chnage html in readmes to fix pypi rendering 2023-08-29 22:56:01 +02:00
Tomas Valenta ce1548b170 Fix casing 2023-08-29 16:52:13 +02:00
Tomas Valenta a3d0939457 Update SDK and repo readmes 2023-08-29 16:49:24 +02:00
Tomas Valenta 922f604dbc Add SDK and CLI [WIP] to the monorepo 2023-08-14 14:54:45 +02:00