Commit Graph

31 Commits

Author SHA1 Message Date
Mish Ushakov 4fcf7cb150 feat: sync API specs from infra and belt with Copybara (#1564)
The specs in `spec/` were copied from their source repos by hand and had
drifted ~2,400 lines behind infra, so they are now imported with
Copybara (`copy.bara.sky`, run in a pinned Docker image by
`scripts/fetch-spec.sh`): `make codegen` re-fetches them at the commits
pinned in `spec/infra-ref` and `spec/belt-ref` before generating, and
the generated-files CI check fails if the tracked copies don't match the
pins. Regenerating from the current pins picks up the accumulated spec
changes in the generated JS/Python clients (renamed request schemas,
`SandboxNetworkConfig`, `SandboxIam` workload identity,
`FILE_TYPE_SYMLINK`, access-token auth deprecation, volume path-metadata
tweaks). The one handwritten SDK change follows from that: the public
`FileType` enums gain a `SYMLINK` member (JS and both Python surfaces)
so entries envd reports as symlinks show up in `files.list()` and
`getInfo()`/`get_info()` instead of being silently skipped as unknown
types. The custom `spec/remove_extra_tags.py` tag-filtering script is
replaced by Redocly CLI's `filter-in` decorator (`redocly.yaml`), which
produces identical generated JS output; a `filter-out` decorator
additionally drops any operation or component schema the upstream specs
mark `x-not-implemented: true` (currently the SOCKS5
`SandboxEgressProxyConfig`/`egressProxy` surface, which infra flagged as
spec-only); each SDK's bundle now goes to its own gitignored
`spec/openapi_generated.<api>.yml` instead of both pipelines overwriting
one shared file; Python client models now list fields in spec order
instead of alphabetical (mechanical reordering only — construct models
with keyword args). Spec fetches try whatever GitHub token is available
and fall back to the tracked copies with a warning (the public infra
specs also fetch anonymously); in CI a short-lived belt-scoped token is
minted from the org-wide Autofixer GitHub App (no new secrets), so fork
PRs simply fall back for the belt spec; the CI workflows also cache the
Copybara image alongside the codegen image, and the previously ignored
`CODEGEN_IMAGE` env is honored by the Makefile.

## Usage

```sh
# update the specs: bump a pin, then regenerate
echo <infra-commit-sha> > spec/infra-ref
make codegen

# fetch a single spec without regenerating
pnpm fetch:api-spec     # spec/openapi.yml from infra
pnpm fetch:envd-spec    # spec/envd/ from infra
pnpm fetch:volume-spec  # spec/openapi-volumecontent.yml from belt

# try the latest spec without touching the pin
E2B_INFRA_REF=main pnpm fetch:api-spec

# change which endpoint tags an SDK exposes
$EDITOR redocly.yaml && make codegen
```

```ts
// symlinks are now visible in the filesystem API (JS; same shape in Python)
const entries = await sandbox.files.list('/home/user')
const link = entries.find((e) => e.type === FileType.SYMLINK)
console.log(link?.symlinkTarget)
```

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:37:02 +02:00
Mish Ushakov 00253c39cc feat(python-sdk): migrate envd RPC to the official connectrpc client (#1558)
Replaces the vendored `e2b_connect` client and the custom Go
`protoc-gen-connect-python` plugin with the official Connect RPC client
for Python ([`connectrpc`](https://github.com/connectrpc/connect-py),
transport: `pyqwest`/Rust hyper), and switches the envd messages from
Google's `protobuf` runtime to Buf's
[`protobuf-py`](https://github.com/bufbuild/protobuf-py) (which
`connectrpc` already requires) — the SDK no longer depends on the
conflict-prone `protobuf` package at all, and the protoc binary drops
out of the codegen image. The wire format (same protos, same JSON) is
unchanged. Closing a command or watch stream early now sends
`RST_STREAM`, fixing abandoned streams leaking on the shared HTTP/2
connection, and peer resets surface as typed `ConnectError`s. The
plumbing mirrors the `e2b.api` layout: shared pieces (a JSON codec that
ignores unknown response fields, proxy narrowing, pool tuning) live in
`e2b/envd/client_shared.py`, the flavor-specific pyqwest transports
(wrapped in pyqwest's retry middleware, see the retry note below) and
`create_rpc_client` factories in `e2b/envd/client_sync/` and
`e2b/envd/client_async/`, and the default-header/logging interceptors in
`e2b/envd/interceptors.py`; `e2b/envd/rpc.py` maps `connectrpc` error
codes onto the existing SDK exceptions, so the public API is unchanged
(`sandbox.commands.run(...)`, `files.watch_dir(...)`, etc. work exactly
as before). The REST API and file upload/download keep using `httpx`.

The `proxy` connection option now applies to sandbox RPC calls too —
[pyqwest
0.7.0](https://github.com/curioswitch/pyqwest/releases/tag/v0.7.0) added
an httpx-style `proxy` parameter to its transports, so commands, PTY,
and filesystem watch traffic follow the same proxy as the REST API and
file transfers (an earlier revision of this PR could only fall back to
`http_proxy`/`https_proxy` env vars for RPC):

```python
sandbox = Sandbox.create(proxy="http://user:pass@localhost:8030")
# REST *and* RPC (commands, PTY, watch) traffic goes through the proxy
result = sandbox.commands.run("echo through-the-proxy")
```

Notes:
- `e2b_connect` is no longer shipped in the wheel; code importing it
directly should switch to `connectrpc` (`ConnectError`, `Code`) — SDK
exception types are unchanged.
- The generated `e2b.envd.*.*_pb2` modules are replaced by `protobuf-py`
equivalents (`process_pb`, `filesystem_pb`) with a different message API
(`Oneof` objects, `has_field`); these are internal modules —
`e2b-code-interpreter` and `e2b-desktop` were verified not to import
them.
- RPC transports are cached per proxy URL. `httpx.URL` and `httpx.Proxy`
proxies keep working for RPC calls when they reduce to a proxy URL
(`httpx.Proxy` auth is folded back into the URL userinfo); `httpx.Proxy`
extras that pyqwest can't express — custom headers, an `ssl_context` —
raise `InvalidArgumentException` rather than being silently dropped.
- Plain (non-Connect-encoded) HTTP error responses — an edge proxy or
gateway answering for envd — keep the vendored client's status mapping
even when they carry a JSON body that isn't a valid Connect error (e.g.
a gateway's `{"code": 429}` raises `RateLimitException`, not a
misleading sandbox-timeout); only JSON bodies with a valid Connect
`code` string are left to connectrpc to parse. An envd response that
fails to decode surfaces as a `SandboxException` with a clear message —
the SDK's JSON codec raises a typed `ConnectError(INTERNAL)` at the
source (connectrpc re-raises codec-raised `ConnectError`s unchanged),
rather than the error being reconstructed from `__cause__` heuristics in
the exception mapper.
- pyqwest 0.7.0 explicit transports default to an **empty TLS root
store** (0.6.2 used reqwest's defaults), so the envd transports pass
`tls_include_system_certs=True`; the dependency floor is
`pyqwest>=0.7.0` accordingly.
- Connection retries (`E2B_CONNECTION_RETRIES`, default 3) use pyqwest's
transport-level retry middleware (`pyqwest.middleware.retry`), narrowed
to retry only the builtin `ConnectionError` — raised solely while
establishing the connection, before the request could have reached envd
— with exponential backoff. A retry can therefore never replay a
delivered request, for unary and streaming RPCs alike; the previous
stack's replay of unary calls whose connection dropped mid-request is
dropped deliberately, since it could re-execute a delivered call (e.g.
`SendInput`). Pinned by unit tests plus end-to-end tests driving the
generated stubs through the middleware
(`tests/test_envd_retry_transport.py`).
- For async streaming calls (`commands.run`/`connect`, PTY,
`watch_dir`), `request_timeout` bounds opening the stream — the wait
until envd confirms with a start event, matching the JS SDK's
`requestTimeoutMs` — raising `TimeoutException` and cancelling the
HTTP/2 stream when exceeded (pinned frame-level in
`tests/test_envd_stream_reset.py`). The running stream stays bounded by
the command/watch `timeout`. The sync SDK cannot interrupt its blocking
wait, so `request_timeout` is not applied to sync stream setup — both
setup and the running stream are bounded by `timeout` (unlimited when
`0`).
- The RPC logging interceptor was upstreamed to pyqwest as a logging
middleware
([curioswitch/pyqwest#192](https://github.com/curioswitch/pyqwest/pull/192));
the SDK keeps its own `LoggingInterceptor` until that merges and ships
in a release the SDK can depend on.
- `pyqwest` ships binary wheels for manylinux/musllinux (x86_64,
aarch64), macOS arm64 + x86_64 (Intel wheels landed in 0.7.0), Windows
x64, and PyPy.
- The `RST_STREAM`-on-early-close behavior is pinned by frame-level
regression tests (`tests/test_envd_stream_reset.py`): a plaintext HTTP/2
server records the frames the real generated clients (with the SDK's
codec and interceptors) send — early close via `disconnect()`, close
through the logging interceptor, and abandoning the stream must all send
`RST_STREAM(CANCEL)`; normal completion must send none (sync + async).
- `E2B_MAX_CONNECTIONS` no longer applies to sandbox RPC traffic:
reqwest's pool bounds only idle connections per host
(`E2B_KEEPALIVE_EXPIRY`, `E2B_MAX_KEEPALIVE_CONNECTIONS`), not the total
number of open connections. It still applies to the REST API and file
transfers.
- The sync sandbox modules build one RPC client each and share it across
threads — the connectrpc sync client is stateless per call over the
process-global transport (verified with a 16-thread frame-level test);
only the httpx envd API clients stay per-thread with their transports.
- Also fixes numeric env-var parsing (`E2B_KEEPALIVE_EXPIRY`,
`E2B_MAX_KEEPALIVE_CONNECTIONS`, `E2B_MAX_CONNECTIONS`,
`E2B_CONNECTION_RETRIES`): an empty-string value now falls back to the
default instead of raising `ValueError` at import time.
2026-07-24 05:41:04 -07:00
Mish Ushakov be4eb5fd96 chore(python-sdk): migrate from Poetry to uv (#1513)
Migrates the Python SDK's packaging and CI from Poetry to
[uv](https://docs.astral.sh/uv/): `pyproject.toml` is converted to PEP
621 metadata using uv's native `uv_build` backend (verified to produce a
byte-equivalent wheel containing both `e2b` and `e2b_connect`),
`poetry.lock` is replaced with `uv.lock`, and the `Makefile`,
`package.json` scripts, `.tool-versions`, `CLAUDE.md`, and all six
GitHub workflows now use `uv` (`astral-sh/setup-uv` + `uv
sync`/`build`/`version`/`publish`). It also drops the now-redundant
explicit sync steps (since `uv run` auto-syncs) and removes the orphaned
`pydoc-markdown` dev dependency, whose only consumer was deleted long
ago — trimming 58 packages from the dev lockfile.

## Usage

```sh
cd packages/python-sdk
uv sync          # install deps (replaces `poetry install`)
uv run pytest    # run tests
uv build         # build the wheel/sdist
make lint        # ruff (run via `uv run`)
```

No user-facing SDK change — packaging/tooling only — so no changeset is
included; the published package contents are unchanged.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:00:43 -07:00
Mish Ushakov 6d7e72e3bd feat: add Volume CRUD operations to SDKs (#1126)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: Joe Lombrozo <joe.lombrozo@e2b.dev>
2026-03-25 17:37:46 -07:00
Jakub Dobry a55ca219e9 feat: snapshots (#1111) 2026-02-24 11:59:11 -08:00
Mish Ushakov c38a1819b6 Fix Python SDK type issues with ty type checker (#1122)
## Summary
- Resolved 43 type diagnostics reported by ty (Astral's Python type
checker)
- Fixed Self type issues on class singletons
- Added explicit type annotations for shadowed attributes
- Replaced None with UNSET for auto-generated API parameters
- Fixed method signature alignment for protocol matching
- Added targeted type: ignore suppressions for pattern-based limitations

All checks pass: ty check, ruff format, ruff check.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Mostly typing/CI changes, but some adjustments affect sandbox
connect/pause overload dispatch and API response/parameter handling
(`UNSET` vs `None`), which could alter edge-case runtime behavior.
> 
> **Overview**
> Fixes Python SDK static typing issues for Astral’s `ty` checker and
wires typechecking into CI.
> 
> Adds a new `Typecheck` GitHub Action plus workspace `typecheck`
scripts (TS packages via `tsc`, Python SDK via `make typecheck` running
`ty`), and publishes a patch changeset for `@e2b/python-sdk`.
> 
> Across the Python SDK, adjusts type annotations and overloads (e.g.,
`Self`/singleton typing, `connect` overloads, optional
`user`/token/domain handling), tightens API model parsing with
`cast`/`Optional` checks and `UNSET` usage, and adds a few targeted `ty`
ignore comments in tests/protocols to silence checker limitations.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f66402847c40cee7e44e1aaa7caa97e271ba9978. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-02-12 16:23:39 +00:00
Jakub Dobry 10abab8e96 feat: add template versioning with tags support for JS and Python SDKs (#1080) 2026-01-27 13:41:24 +00:00
Jakub Novák ae48a0adeb Drop support for python 3.9 (#1092)
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Python version floor raised**
> 
> - Require `python ^3.10` in `pyproject.toml` and `.tool-versions`;
update `poetry.lock` metadata and deps to remove 3.9-only
packages/markers
> - Update `Makefile` `datamodel-codegen` to `--target-python-version
3.10`
> - Add changeset entry documenting the drop of Python 3.9 support
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
a75633f2d28fe24e9f33d0605c1059aeea219820. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2026-01-26 12:40:19 +01:00
Joseph Lombrozo d2e22e3748 Some general pytest cleanup (#1021)
- Individual tests must complete in less than 5 minutes
- Add a `make test` option that runs tests
- Upgrade poetry to 2.1.1 (the lock file was generated by this version,
so this just matches what we already expect)
2025-11-13 17:58:30 -08:00
Tomas Valenta 1203ccbad3 Add MCP experimental feature to beta (#936)
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> Adds beta MCP support to JS/Python SDKs with template auto-selection,
config POST, MCP URL helpers, and codegen for MCP types/schemas.
> 
> - **SDKs (Beta MCP support)**:
>   - **JS SDK**:
> - Add `McpServer` types and export; extend `Sandbox.betaCreate` with
`mcp` option that auto-selects `mcp-gateway-v0`, configures MCP via POST
`/config` on port `50005` with retries, and expose `betaGetMcpUrl()`.
> - Add `wait(ms)` util; generate MCP types from `spec/mcp-server.json`;
new `generate:mcp` script and dev dep `json-schema-to-typescript`.
>   - **Python SDK**:
> - Generate and export `McpServer` (TypedDict); extend sync/async
`beta_create` with `mcp` option, auto-select default MCP template, POST
MCP config with retries, and add `beta_get_mcp_url()`; define `mcp_port`
and `default_mcp_template`.
> - Makefile: add `generate-mcp` target; include
`datamodel-code-generator` in tooling; update lockfile/pyproject deps.
> - **Tooling**:
> - Add `spec/mcp-server.json` schema; update codegen Dockerfile to
install `datamodel-code-generator`.
> -
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cf3f175d310d4c242cd2e867efd801f5c04f2099. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Jonas Scholz <Jonas.Scholz@bbscholz.de>
2025-10-09 08:37:32 +00:00
Mish Ushakov 4a2a6c7051 Add Templates SDK (#871)
### Merge Templates SDK into E2B SDK.

This PR merges code from Templates SDK to E2B SDK.

**Changelog**

- Added new error classes: BuildError, FileUploadError.
- Added template API endpoints to API Client generation (Python).
- Updated API Clients error handling to throw BuildError in addition to
SandboxError.
- Exported Template and TemplateAsync classes.
- Moved `getRuntime` utility method from api to utils.
- Browser is supported except file operations (COPY). Incompatible
modules are not bundled, using dynamic imports (tar, glob) in supported
environments (Node, Deno, Bun) instead.
- Basic tests were added, subject to expansion in a follow-up PR.
- Breaking: Before, 401 error was raising `SandboxError`, which was
incorrect, now it will raise `AuthenticationError`. I have also changed
the `AuthenticationError` to extend Error class, not `SandboxError`
anymore as it's now thrown by both Sandbox and Template.

**Usage**

JavaScript

```js
import { Template } from 'e2b';

const template = Template()
  .fromImage('ubuntu:22.04')
  .copy('folder/*.txt', 'folder', { forceUpload: true })
  .setEnvs({
    ENV_1: 'value1',
    ENV_2: 'value2',
  })
  .runCmd('cat folder/test.txt')
  .setWorkdir('/app')
  .setStartCmd('echo "Hello, world!"', Template.waitForTimeout('10s'))

await Template.build(template, {
  alias: 'test-template',
  cpuCount: 1,
  memoryMB: 1024,
  onBuildLogs: (logEntry) => console.log(logEntry.toString()),
})

```

Python

```py
from e2b import Template

template = (
    Template()
    .from_image("ubuntu:22.04")
    .copy("folder/*.txt", "folder", force_upload=True)
    .set_envs(
        {
            "ENV_1": "value1",
            "ENV_2": "value2",
        }
    )
    .run_cmd("cat folder/test.txt")
    .set_workdir("/app")
    .set_start_cmd("echo 'Hello, world!'", Template.wait_for_timeout("10s"))
)

Template.build(
    template,
    alias="test-template",
    cpu_count=1,
    memory_mb=1024,
    on_build_logs=lambda log_entry: print(log_entry),
)
```

Python Async

```py
from e2b import AsyncTemplate

template = (
    AsyncTemplate()
    .from_image("ubuntu:22.04")
    .copy("folder/*.txt", "folder", force_upload=True)
    .set_envs(
        {
            "ENV_1": "value1",
            "ENV_2": "value2",
        }
    )
    .run_cmd("cat folder/test.txt")
    .set_workdir("/app")
    .set_start_cmd("echo 'Hello, world!'", Template.wait_for_timeout("10s"))
)

await AsyncTemplate.build(
    template,
    alias="test-template",
    cpu_count=1,
    memory_mb=1024,
    on_build_logs=lambda log_entry: print(log_entry),
)
```

---

Merge progress

- [x] JavaScript SDK
- [x] Python SDK
- [x] Tests
- [x] Split files for review
2025-09-05 22:00:22 +02:00
Jakub Novák b42ab2a86b Add pipeline for linting and formatting (#883)
Setup linting and formatting in all packages
2025-08-31 12:06:00 -07:00
Mish Ushakov edeafb1cdd Adds files.getInfo / files.get_info methods to retrieve information about directory/files (#724)
**Changelog**

- Adds `files.getInfo`, `files.get_info` methods to the SDKs.
- Adds tests for the new methods
- Adds documentation for the above.

**Examples**

JavaScript

```js
import { Sandbox } from '@e2b/code-interpreter'

const sandbox = await Sandbox.create()

// Create a new file
await sandbox.files.write('test_file.txt', 'Hello, world!')

// Get information about the file
const info = await sandbox.files.getInfo('test_file.txt')

console.log(info)
// {
//   name: 'test_file.txt',
//   type: 'file',
//   path: '/home/user/test_file.txt'
// }
```

Python

```py
from e2b_code_interpreter import Sandbox

sandbox = Sandbox()

# Create a new file
sandbox.files.write('test_file', 'Hello, world!')

# Get information about the file
info = sandbox.files.get_info('test_file')

print(info)
# EntryInfo(name='test_file.txt', type=<FileType.FILE: 'file'>, path='/home/user/test_file.txt')
```

---------

Co-authored-by: Jakub Novak <jakub@e2b.dev>
2025-07-29 03:39:42 -07:00
Jakub Novák 706ebd9af8 Fix generating files with docker (#829)
# Description

Fixes an issue in generating files in Docker. There has been an
incompatibility of `buf` (version `29.5`) and `protoc-gen-es` (version
`2.2.2`).

I updated `protoc-gen-es@` to `2.6.2`

Also refactored the code a little so it's easier to read
Added a CI pipeline job to check all files are properly generated
2025-07-28 13:47:26 +02:00
Mish Ushakov b21f86c543 Added lint workflow for JS, Python SDKs (#759)
- Linted all existing files
- Added GitHub workflow to check everything is linted correctly
2025-06-05 14:52:48 +02:00
Jakub Novak 594bb174d2 Generate only relevant api routes for the client in SDKs 2025-01-17 11:01:40 -08:00
Tomas Valenta 96aeab20a4 [WIP] Switch API client to openapi-python-client for async support 2024-07-05 18:12:28 -07:00
Tomas Valenta c18ada0b9d Python file handling 2024-06-08 23:22:19 +02:00
Tomas Valenta b022dcfb80 [WIP] Upload and download handling 2024-06-02 23:06:37 +02:00
Tomas Valenta 068ff5613f Regenerate APIs 2024-05-31 06:40:42 +02:00
Tomas Valenta 3b4c915a48 Generate clients 2024-05-17 02:05:12 -07:00
Tomas Valenta 002a28870e [WIP] Generate python client 2024-04-29 22:22:33 -07:00
Jakub Novák 7fe1d3b569 Rename api endpoints (#298) 2024-02-02 18:57:40 +01:00
Jakub Novak 8dd2a0f33b Allow extra fields 2024-01-30 14:45:09 +01:00
Jakub Novak 241f1e4329 Update openapi generator 2023-11-21 13:54:53 +01:00
Jakub Novák 388309830b New infra (#212)
Co-authored-by: Tomas Valenta <valenta.and.thomas@gmail.com>
Co-authored-by: Vasek Mlejnsky <vasek.mlejnsky@gmail.com>
2023-11-06 15:07:09 +01:00
Jakub Novák 6feaccf761 Sync is da way (#197)
Test CLI / Build CLI (push) Has been cancelled
2023-10-15 15:24:05 -07:00
Jakub Novák 4ad651feaa Support both pydantic v1 and v2 in python sdk (#176)
Test CLI / Build CLI (push) Has been cancelled
2023-09-29 16:27:26 +02:00
Jakub Novák cb1ef24d84 Update Python SDK to pydantic V2 (#167)
Test CLI / Build CLI (push) Has been cancelled
2023-09-26 11:18:47 +02:00
Jakub Novák 3c6ff27ede Update pydantic to version 2.x.x (#166)
Test CLI / Build CLI (push) Has been cancelled
2023-09-26 11:02:26 +02:00
Tomas Valenta 922f604dbc Add SDK and CLI [WIP] to the monorepo 2023-08-14 14:54:45 +02:00