Commit Graph

4943 Commits

Author SHA1 Message Date
Mish Ushakov 09e12b3f65 feat(sdk): set-once integration attribution via ConnectionConfig.setIntegration (#1524)
Replaces the per-call `integration` connection option with a set-once,
process-wide setter — `ConnectionConfig.setIntegration()` in JS and
`ConnectionConfig.set_integration()` in Python — so integrations
wrapping the SDK tag themselves once at startup and every request
carries the identifier in the `User-Agent` header, with no threading
through individual SDK calls. The setter is internal and hidden from
generated docs; the `integration` option is removed from
`ConnectionConfigOpts` (kept as a deprecated alias of `ConnectionOpts`)
and from the Python constructor, and the round-trip machinery from #1459
is no longer needed since rebuilt configs read the process-wide value.
User-Agent handling now follows a single rule in both SDKs via one
shared helper per SDK: an explicitly provided `User-Agent` always wins,
otherwise the SDK sends its own tagged with the current integration —
and SDK-built values are recomputed whenever a config is rebuilt, so
clearing or changing the integration propagates. Tests cover
attribution, clearing, config rebuilds, and custom User-Agent precedence
in both SDKs, with changesets for `e2b` and `@e2b/python-sdk` (minor).
CLI attribution using this setter will follow in a separate PR.

Usage (internal integrations only):

```ts
import { ConnectionConfig } from 'e2b'
ConnectionConfig.setIntegration('e2b-code-interpreter/0.1.0') // once at startup
```

```python
from e2b import ConnectionConfig
ConnectionConfig.set_integration("e2b-code-interpreter/0.1.0")  # once at startup
```

A caller-supplied `User-Agent` (via `headers`/`apiHeaders`) is preserved
in both SDKs:

```ts
const sbx = await Sandbox.create({ apiHeaders: { 'User-Agent': 'my-app/1.0' } })
// requests carry: my-app/1.0
```

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@e2b/cli@2.13.2 @e2b/python-sdk@2.32.0 e2b@2.33.0
2026-07-11 15:52:09 +02:00
Mish Ushakov 07041ccffc test: skip live volume tests unless ENABLE_VOLUME_TESTS is set (#1526)
Live volume tests create real volumes against the API; this gates them
behind an `ENABLE_VOLUME_TESTS` env var so they skip by default. In the
JS SDK, the `volumeTest` fixture is chained with
`.skipIf(process.env.ENABLE_VOLUME_TESTS === undefined)`, skipping all
of `tests/volume/file.test.ts`. In the Python SDK, the `volume` and
`async_volume` fixtures call `pytest.skip` when the env var is unset,
gating `tests/{sync/volume_sync,async/volume_async}/test_file.py`.
Mocked and unit volume tests (msw-based `volume.test.ts`,
`test_volume.py`, `test_volume_content.py`, `test_volume_client.py`,
`test_volume_connection_config.py`) still run unconditionally. To run
the live tests: `ENABLE_VOLUME_TESTS=1 pnpm run test` or
`ENABLE_VOLUME_TESTS=1 poetry run pytest`.

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

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 10:38:05 -07:00
Mish Ushakov 0bd06d86d2 chore(js-sdk,cli): modernize tsconfig and adopt TypeScript 7 (side-by-side) (#1536)
Supersedes #1516 (same modernization at TypeScript 6.0). Rebased onto
`main` now that the build runs on **tsdown** (#1515).

## What & why

Adopt **TypeScript 7** for both packages and modernize the compiler
config.

TypeScript 7.0's native compiler [ships no programmatic API
yet](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#running-side-by-side-with-typescript-6.0)
(it lands in 7.1), so anything built on the TS compiler API breaks on it
— here that's tsdown's `.d.ts` generation and the codegen scripts
(`openapi-typescript`, `json-schema-to-typescript`). Per the official
guidance, TS 7 is installed **side-by-side** with TS 6:

```json
"@typescript/native": "npm:typescript@^7.0.2",      // native tsc — used for type-checking
"typescript": "npm:@typescript/typescript6@^6.0.2"  // TS6 w/ compiler API — used by tooling
```

- `tsc --noEmit` (typecheck) → **native TypeScript 7.0.2** (verified:
`tsc --version` → 7.0.2)
- `import 'typescript'` → **TypeScript 6.0** *with* the compiler API →
tsdown dts + codegen keep working
- Bonus: tsdown's dts no longer prints the "TypeScript 7.0 does not yet
have a stable API and is experimental" warning (it's on the 6.0 API now)

**Internal build-config change only — no public API or runtime behavior
changes.**

## Compiler options: before → after

### `packages/js-sdk/tsconfig.json`
| option | before | after |
|---|---|---|
| `target` | `es6` | `es2022` |
| `lib` | `["dom","ESNext"]` | `["dom","es2022"]` |
| `module` | _(unset)_ | `esnext` |
| `moduleResolution` | `node` | `bundler` |
| `allowJs` | `true` | **removed** (no `.js` sources) |
| `allowSyntheticDefaultImports` | `true` | **removed** (implied by
`esModuleInterop`) |
| `useDefineForClassFields` | _(false, implied by es6)_ | **`false` (now
explicit)** — see note |

### `packages/cli/tsconfig.json`
| option | before | after |
|---|---|---|
| `moduleResolution` | `node` | `bundler` |
| `strictNullChecks`, `strictFunctionTypes`, `strictBindCallApply`,
`strictPropertyInitialization`, `noImplicitThis`, `alwaysStrict` |
`true` | **removed** (implied by `strict`) |
| `downlevelIteration` | `true` | **removed** (removed in TS 7; no-op at
`es2022`) |
| `baseUrl` | `"."` | **removed** (removed in TS 7) |
| `paths` | `{ e2b }` | `{ src, "src/*", e2b }` (replaces `baseUrl` for
the existing `src/...` import style) |
| `outDir` | `"dist"` | **removed** (unused under `tsc --noEmit`) |
| `exclude` | _(none)_ | `["dist","node_modules"]` (so the built bundle
is never type-checked) |

`target`/`lib` for the CLI were already `es2022`.

## Notes / decisions

- **Why side-by-side, not a plain `typescript@7` bump:** TS 7.0 is the
native (Go) compiler rewrite — feature-identical to 6.0 for
type-checking, no programmatic API until 7.1. A plain bump crashed both
codegen tools (`Cannot read properties of undefined (reading
'createKeywordTypeNode')`). Side-by-side gives native-TS-7 checking
while keeping the TS-6 API for tooling. Once 7.1 ships the API and the
tools update, this collapses back to a single `typescript@7` dep.
- **`useDefineForClassFields: false` is pinned explicitly.** Raising
js-sdk's `target` to `es2022` flips this default to `true`, changing
class-field emit and shifting stack frames. The template builder
resolves the caller's directory and per-step traces via **fixed-depth**
stack walking (`getCallerDirectory` in `src/template/index.ts`), so the
extra frames threw it off by one — resolving `.copy('folder/*', …)`
against the wrong base dir and mis-attributing build steps
(`tests/template/build.test.ts` + `stacktrace.test.ts`). Pinning `false`
keeps the exact pre-existing field semantics (es6 already implied
`false`); adopting `define` semantics should be a separate, deliberately
tested change.
- **Target stays at `es2022`, not `es2023`.** `engines` still allow Node
20 (`>=20.18.1 <21 || >=22`).
- **`moduleResolution: "bundler"`** typechecks + builds cleanly in both
packages. The CLI's `baseUrl`-based bare imports (`from 'src/user'`,
`from 'src'`) are preserved via `paths`; the bundled output still
resolves them (build verified, binary smoke-tested).

## Not done (intentionally)

- **`verbatimModuleSyntax`** — ~177 `import type` conversions; left as a
follow-up.
- **Shared `tsconfig.base.json`** — the two configs diverge too much to
factor out cleanly.

## Verification
- `pnpm run typecheck`  both packages, on **native TS 7.0.2**
- `pnpm run build`  both packages (js-sdk ESM + CJS + **DTS**; cli CJS;
binary smoke-tested)
- codegen  `openapi-typescript` + `json2ts` run and produce identical
output (idempotent)
- `pnpm run lint`  both packages
- `pnpm run test` — `template/build` + `template/stacktrace` now pass
(`stacktrace` verified locally 30/30); remaining local failures are all
`E2B_API_KEY`-gated live tests, unaffected by this change

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 18:35:15 +00:00
Mish Ushakov 504c60999f ci: key Playwright browser cache on Playwright version (#1538)
## Why

The Playwright browser cache in `js_sdk_tests.yml` keyed on the Node
version + a hash of `packages/js-sdk/package.json`. Node bumps (e.g.
#1515) and release-bot version bumps rotated the key, so PRs kept
re-downloading Chromium — ~3 minutes per Windows job, twice per run
(staging + production) — e.g. [this
run](https://github.com/e2b-dev/E2B/actions/runs/29036879724/job/86183938330?pr=1536).
The churn also created a fresh ~250 MB cache entry per OS on every
release.

## What

Browser binaries depend only on the Playwright version, so the cache is
now keyed on the installed Playwright version (read from `node_modules`
after `pnpm install`), and the two OS-conditional cache steps are
collapsed into one. The key only rotates when Playwright itself is
upgraded, which is exactly when a re-download is needed. On a cache hit,
the `pretest` `playwright install` becomes a no-op skip instead of a
download.

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

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:34:06 +02:00
Alex Drankou 9a638ae907 docs(readme): add UTM tracking to e2b.dev links (#1537)
Adds UTM parameters to the e2b.dev link(s) in the README so
GitHub-referral traffic is attributed per repo.


`utm_source=github&utm_medium=referral&utm_campaign=readme&utm_content=<repo>`

Applies to apex e2b.dev links (root and subpaths). Subdomains and
already-tagged links are untouched, and URL anchors are preserved.
2026-07-09 19:56:16 +02:00
Mish Ushakov 49367c8491 build: switch from tsup to tsdown (#1515)
Switches the build tooling for `packages/js-sdk` and `packages/cli` from
`tsup` (esbuild) to `tsdown` (rolldown), replacing each `tsup.config.js`
with a `tsdown.config.ts` and updating the `build`/`dev` scripts and
devDependencies. The published artifact layout is intentionally
unchanged — the SDK still ships `dist/index.js` (CJS), `dist/index.mjs`
(ESM) and `dist/index.d.ts`/`.d.mts`, and the CLI still ships an
executable `dist/index.js` plus `dist/templates` — kept identical via
`fixedExtension: false`. CLI dependency bundling is preserved by mapping
the old `noExternal` to tsdown's `deps.alwaysBundle` (still excluding
the ESM-only, dynamically-imported `inquirer`), and template copying
moves from an `onSuccess` shell step to tsdown's `copy` option.

Also aligns Node versions: `engines.node` for both packages is set to
`20 || >=22`, the CLI build targets `node20`, and the pinned `nodejs` in
`.tool-versions` is bumped to `22.11.0`. The large `pnpm-lock.yaml` diff
is expected — it swaps the tsup/esbuild dependency tree for tsdown's
rolldown tree (no lockfile format change).

## Verification
- Both packages build cleanly with output filenames identical to the
previous tsup builds.
- `typecheck`, `lint` (oxlint) and `build` pass for both packages; the
built CLI runs (`--version`).
- Built js-sdk imports correctly in both CJS (`require`) and ESM
(`import`), exposing the default `Sandbox` export and all named exports.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 19:04:03 +02:00
Mish Ushakov e6c4e7e9d5 chore(js): modernize Connect/Protobuf and React test deps (#1512)
## What

Modernizes the JS SDK's dependencies while remaining fully compatible
with the current supported Node range (`>=20.18.1`) — no engine changes
and no breaking impact for consumers.

- **`@connectrpc/connect` / `@connectrpc/connect-web`:** `2.0.0-rc.3` →
`^2.1.2` (off the pre-release pin onto the stable line, and switched to
a `^` range).
- **`@bufbuild/protobuf`:** `^2.6.2` → `^2.12.1`.
- **React test deps:** `react` / `@types/react` → `^19.2.0`, and
`react-dom` / `@types/react-dom` added at `^19.2.0` (previously
auto-installed as v18 peers). Dev/test-only — no runtime impact.
- **CI:** standardized `actions/setup-node` (mixed v3/v4/v6) to `v6`
across all workflows; the three `@v3` uses were on the deprecated Node16
action runtime.

No public SDK API changes — the sandbox filesystem and command RPCs use
the same Connect transport configuration.

## Why undici / Node floor were dropped from this PR

An earlier revision also bumped `undici` 7 → 8 and raised the Node floor
to `>=22.19.0`. Usage data shows **Node 20 is still the single largest
SDK runtime (~39% of sandbox creations)**, so dropping it would break
the largest consumer segment via `engine-strict` install failures.
undici 8 was the *only* change forcing Node 22, and undici `7.28.0`
(already the latest 7.x) supports Node 20 — so undici stays at `^7.28.0`
and the engine floor is unchanged. undici 8 is a good candidate for a
future major once Node 20 usage declines.

## Verification

- typecheck, lint (oxlint), and build pass
- 22 mocked Connect/undici transport unit tests pass
- 106 live filesystem/command tests pass over connectrpc `2.1.2` +
undici `7.28.0`

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 16:37:50 +00:00
github-actions[bot] 0feb926937 [skip ci] Release new versions 2026-07-08 13:37:26 +00:00
dependabot[bot] 5d84a8e7d2 chore(deps-dev): bump black from 23.7.0 to 26.3.1 in /packages/python-sdk in the uv group across 1 directory (#1530)
Bumps the uv group with 1 update in the /packages/python-sdk directory:
[black](https://github.com/psf/black).

Updates `black` from 23.7.0 to 26.3.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/psf/black/releases">black's
releases</a>.</em></p>
<blockquote>
<h2>26.3.1</h2>
<h3>Stable style</h3>
<ul>
<li>Prevent Jupyter notebook magic masking collisions from corrupting
cells by using
exact-length placeholders for short magics and aborting if a placeholder
can no longer
be unmasked safely (<a
href="https://redirect.github.com/psf/black/issues/5038">#5038</a>)</li>
</ul>
<h3>Configuration</h3>
<ul>
<li>Always hash cache filename components derived from
<code>--python-cell-magics</code> so custom
magic names cannot affect cache paths (<a
href="https://redirect.github.com/psf/black/issues/5038">#5038</a>)</li>
</ul>
<h3><em>Blackd</em></h3>
<ul>
<li>Disable browser-originated requests by default, add configurable
origin allowlisting
and request body limits, and bound executor submissions to improve
backpressure
(<a
href="https://redirect.github.com/psf/black/issues/5039">#5039</a>)</li>
</ul>
<h2>26.3.0</h2>
<h3>Stable style</h3>
<ul>
<li>Don't double-decode input, causing non-UTF-8 files to be corrupted
(<a
href="https://redirect.github.com/psf/black/issues/4964">#4964</a>)</li>
<li>Fix crash on standalone comment in lambda default arguments (<a
href="https://redirect.github.com/psf/black/issues/4993">#4993</a>)</li>
<li>Preserve parentheses when <code># type: ignore</code> comments would
be merged with other
comments on the same line, preventing AST equivalence failures (<a
href="https://redirect.github.com/psf/black/issues/4888">#4888</a>)</li>
</ul>
<h3>Preview style</h3>
<ul>
<li>Fix bug where <code>if</code> guards in <code>case</code> blocks
were incorrectly split when the pattern had
a trailing comma (<a
href="https://redirect.github.com/psf/black/issues/4884">#4884</a>)</li>
<li>Fix <code>string_processing</code> crashing on unassigned long
string literals with trailing
commas (one-item tuples) (<a
href="https://redirect.github.com/psf/black/issues/4929">#4929</a>)</li>
<li>Simplify implementation of the power operator &quot;hugging&quot;
logic (<a
href="https://redirect.github.com/psf/black/issues/4918">#4918</a>)</li>
</ul>
<h3>Packaging</h3>
<ul>
<li>Fix shutdown errors in PyInstaller builds on macOS by disabling
multiprocessing in
frozen environments (<a
href="https://redirect.github.com/psf/black/issues/4930">#4930</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Introduce winloop for windows as an alternative to uvloop (<a
href="https://redirect.github.com/psf/black/issues/4996">#4996</a>)</li>
<li>Remove deprecated function <code>uvloop.install()</code> in favor of
<code>uvloop.new_event_loop()</code>
(<a
href="https://redirect.github.com/psf/black/issues/4996">#4996</a>)</li>
<li>Rename <code>maybe_install_uvloop</code> function to
<code>maybe_use_uvloop</code> to simplify loop
installation and creation of either a uvloop/winloop evenloop or default
eventloop
(<a
href="https://redirect.github.com/psf/black/issues/4996">#4996</a>)</li>
</ul>
<h3>Output</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/psf/black/blob/main/CHANGES.md">black's
changelog</a>.</em></p>
<blockquote>
<h2>Version 26.3.1</h2>
<h3>Stable style</h3>
<ul>
<li>Prevent Jupyter notebook magic masking collisions from corrupting
cells by using
exact-length placeholders for short magics and aborting if a placeholder
can no longer
be unmasked safely (<a
href="https://redirect.github.com/psf/black/issues/5038">#5038</a>)</li>
</ul>
<h3>Configuration</h3>
<ul>
<li>Always hash cache filename components derived from
<code>--python-cell-magics</code> so custom
magic names cannot affect cache paths (<a
href="https://redirect.github.com/psf/black/issues/5038">#5038</a>)</li>
</ul>
<h3><em>Blackd</em></h3>
<ul>
<li>Disable browser-originated requests by default, add configurable
origin allowlisting
and request body limits, and bound executor submissions to improve
backpressure
(<a
href="https://redirect.github.com/psf/black/issues/5039">#5039</a>)</li>
</ul>
<h2>Version 26.3.0</h2>
<h3>Stable style</h3>
<ul>
<li>Don't double-decode input, causing non-UTF-8 files to be corrupted
(<a
href="https://redirect.github.com/psf/black/issues/4964">#4964</a>)</li>
<li>Fix crash on standalone comment in lambda default arguments (<a
href="https://redirect.github.com/psf/black/issues/4993">#4993</a>)</li>
<li>Preserve parentheses when <code># type: ignore</code> comments would
be merged with other
comments on the same line, preventing AST equivalence failures (<a
href="https://redirect.github.com/psf/black/issues/4888">#4888</a>)</li>
</ul>
<h3>Preview style</h3>
<ul>
<li>Fix bug where <code>if</code> guards in <code>case</code> blocks
were incorrectly split when the pattern had
a trailing comma (<a
href="https://redirect.github.com/psf/black/issues/4884">#4884</a>)</li>
<li>Fix <code>string_processing</code> crashing on unassigned long
string literals with trailing
commas (one-item tuples) (<a
href="https://redirect.github.com/psf/black/issues/4929">#4929</a>)</li>
<li>Simplify implementation of the power operator &quot;hugging&quot;
logic (<a
href="https://redirect.github.com/psf/black/issues/4918">#4918</a>)</li>
</ul>
<h3>Packaging</h3>
<ul>
<li>Fix shutdown errors in PyInstaller builds on macOS by disabling
multiprocessing in
frozen environments (<a
href="https://redirect.github.com/psf/black/issues/4930">#4930</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Introduce winloop for windows as an alternative to uvloop (<a
href="https://redirect.github.com/psf/black/issues/4996">#4996</a>)</li>
<li>Remove deprecated function <code>uvloop.install()</code> in favor of
<code>uvloop.new_event_loop()</code>
(<a
href="https://redirect.github.com/psf/black/issues/4996">#4996</a>)</li>
<li>Rename <code>maybe_install_uvloop</code> function to
<code>maybe_use_uvloop</code> to simplify loop
installation and creation of either a uvloop/winloop eventloop or
default eventloop
(<a
href="https://redirect.github.com/psf/black/issues/4996">#4996</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/psf/black/commit/c6755bb741b6481d6b3d3bb563c83fa060db96c9"><code>c6755bb</code></a>
Prepare release 26.3.1 (<a
href="https://redirect.github.com/psf/black/issues/5046">#5046</a>)</li>
<li><a
href="https://github.com/psf/black/commit/69973fd6950985fbeb1090d96da717dc4d8380b0"><code>69973fd</code></a>
Harden blackd browser-facing request handling (<a
href="https://redirect.github.com/psf/black/issues/5039">#5039</a>)</li>
<li><a
href="https://github.com/psf/black/commit/4937fe6cf241139ddbfc16b0bdbb5b422798909d"><code>4937fe6</code></a>
Fix some shenanigans with the cache file and IPython (<a
href="https://redirect.github.com/psf/black/issues/5038">#5038</a>)</li>
<li><a
href="https://github.com/psf/black/commit/2e641d174469c505d5ae905e75d4c769597e681f"><code>2e641d1</code></a>
docs: remove outdated Black Playground references (<a
href="https://redirect.github.com/psf/black/issues/5044">#5044</a>)</li>
<li><a
href="https://github.com/psf/black/commit/c014b22a2d5e0632587b47b81151658bddfa0b88"><code>c014b22</code></a>
Remove unused internal code (<a
href="https://redirect.github.com/psf/black/issues/5041">#5041</a>)</li>
<li><a
href="https://github.com/psf/black/commit/0dae20b2d009f2f03de8696d06b0c947d3abafc9"><code>0dae20b</code></a>
Add new changelog (<a
href="https://redirect.github.com/psf/black/issues/5036">#5036</a>)</li>
<li><a
href="https://github.com/psf/black/commit/c5c1cbddd92cecb554ac2a77a24139dd76831030"><code>c5c1cbd</code></a>
Minor release patches (<a
href="https://redirect.github.com/psf/black/issues/5035">#5035</a>)</li>
<li><a
href="https://github.com/psf/black/commit/7e5a828c37d71b6a6666e28eed444816def6a8f4"><code>7e5a828</code></a>
docs: clarify relationship between Black style and PEP 8 (<a
href="https://redirect.github.com/psf/black/issues/5025">#5025</a>)</li>
<li><a
href="https://github.com/psf/black/commit/69705deb8776e7c5e585668da106d1abe2cb8d77"><code>69705de</code></a>
docs: add clearer pyproject configuration guidance (<a
href="https://redirect.github.com/psf/black/issues/5026">#5026</a>)</li>
<li><a
href="https://github.com/psf/black/commit/35ea67920b7f6ac8e09be1c47278752b1e827f76"><code>35ea679</code></a>
Prepare release 26.3.0 (<a
href="https://redirect.github.com/psf/black/issues/5032">#5032</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/psf/black/compare/23.7.0...26.3.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=black&package-manager=uv&previous-version=23.7.0&new-version=26.3.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/e2b-dev/E2B/network/alerts).

</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mish Ushakov <10400064+mishushakov@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@e2b/cli@2.13.1 e2b@2.32.0 @e2b/python-sdk@2.31.0
2026-07-07 18:45:02 +02: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 a6b1cf4bcf fix(python-sdk): strip colon-separated SGR escape codes in build logs (#1522)
### What

Cherry-picks the fix from #1519.

`strip_ansi_escape_codes` in the Python SDK only matched
semicolon-separated CSI
parameters, so colon-separated SGR sequences leaked literal escape
garbage into
template build-log messages. This widens the parameter class from `;` to
`[;:]`
so colon-separated sequences are stripped too, matching the JS SDK's
`stripAnsi`.

Modern terminals emit colon-separated SGR sequences:

- 256-color: `\x1b[38:5:82m`
- truecolor: `\x1b[38:2::255:0:0m`
- curly underline: `\x1b[4:3m`

The two SDKs share one source (chalk/ansi-regex) and the JS twin was
already
updated to support colons (`packages/js-sdk/src/utils.ts:95`, comment:
"supports
; and :"); the Python port lagged behind. `strip_ansi_escape_codes` is
consumed
by `LogEntry.__post_init__`
(`packages/python-sdk/e2b/template/logger.py`), so
the leftover escape bytes showed up in Python build logs only.

### The one-line fix

```python
# packages/python-sdk/e2b/template/utils.py:319
- r"(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))",
+ r"(?:(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))",
```

### Usage example (before / after)

```python
from e2b.template.utils import strip_ansi_escape_codes

# 256-color, colon-separated
strip_ansi_escape_codes("\x1b[38:5:82mX\x1b[0m")
# before: ":5:82mX"   after: "X"

# truecolor, colon-separated
strip_ansi_escape_codes("\x1b[38:2::255:0:0mRED\x1b[0m")
# before: ":2::255:0:0mRED"   after: "RED"

# semicolon variants already worked and still do
strip_ansi_escape_codes("\x1b[38;5;82mX\x1b[0m")  # "X"  (unchanged)
```

### Tests

Unit tests at

`packages/python-sdk/tests/shared/template/utils/test_strip_ansi_escape_codes.py`
(no API key / sandbox): colon-256, colon-truecolor, curly-underline,
plus
basic/semicolon regressions. All 7 pass locally.

### Changeset

`.changeset/python-strip-ansi-colon.md` (patch on `@e2b/python-sdk`).

### Notes

Original PR: #1519 (by @anxkhn). Opened against a fresh branch off
`main` per
request, rather than merging #1519 directly.

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

Co-authored-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
2026-07-02 10:45:49 -07:00
Mish Ushakov 2b7dd17f10 feat(sdk): add gzip option to template copy layer (#1482)
Adds a `gzip` option to the template `.copy()` / `copyItems` layer that
controls whether copied files are gzipped before upload, threaded from
the copy call through the build-time tar stream in both the JS SDK and
the sync/async Python SDKs. It is enabled by default to preserve
existing behavior, so passing `gzip: false` (`gzip=False`) uploads an
uncompressed tar — useful for already-compressed payloads where gzip
adds CPU cost without shrinking the upload. The option name matches
node-tar's own `gzip` option and the existing sandbox filesystem `gzip`
kwarg. Gzip is deliberately excluded from the file cache hash, so
toggling it does not bust the build cache. Tests in both SDKs were
updated for the new argument and extended with `gzip: false` cases
asserting the archive is not gzipped yet still extracts, and a changeset
(`minor` for both packages) is included.

> [!NOTE]
> The server that extracts these uploaded archives lives in another repo
and must auto-detect compression (peek the gzip `0x1f 0x8b` magic)
rather than assuming gzip; confirm it handles plain tars before release.

## Usage

```ts
// JS/TS
template.copy('model.bin', '/app/', { gzip: false })
template.copyItems([{ src: 'a.bin', dest: '/app/', gzip: false }])
```

```python
# Python (sync & async)
template.copy('model.bin', '/app/', gzip=False)
template.copy_items([{ 'src': 'a.bin', 'dest': '/app/', 'gzip': False }])
```

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:50:21 +02:00
Mish Ushakov a39db3bb36 chore: switch from eslint to oxlint (#1514)
Replaces ESLint (and its `@typescript-eslint/*` and `unused-imports`
plugins) with [oxlint](https://oxc.rs) across the `js-sdk` and `cli`
packages. A root `.oxlintrc.json` replaces the three `.eslintrc.cjs`
files, the package `lint` scripts now run `oxlint`, the related
devDependencies are swapped for `oxlint`, and the lint CI path filter is
updated accordingly. Formatting rules
(`quotes`/`semi`/`linebreak-style`) are dropped because Prettier already
enforces them, and `no-unused-vars` is set to error to preserve the
previous unused-imports check. The one behavior change is that
`@typescript-eslint/member-ordering` has no oxlint equivalent and is no
longer enforced. `lint`, `typecheck`, and `prettier` all pass clean for
both packages.

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

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:34:39 +02:00
Mish Ushakov 9b4a74388d chore(cli): remove unused dockerfile-ast dependency (#1509)
The CLI declared `dockerfile-ast` as a dependency but never imported it
— all Dockerfile parsing in the CLI goes through the `e2b` SDK, which
keeps its own (newer) `dockerfile-ast` dependency. This drops the
redundant copy from `packages/cli/package.json`, removing
`dockerfile-ast@0.6.1` and its sub-deps from the lockfile while
`dockerfile-ast@0.7.1` (used by the js-sdk) stays. No behavior change;
CLI typecheck and lint pass, and a `@e2b/cli` patch changeset is
included.

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

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 05:15:00 -07:00
Mish Ushakov c385566c29 fix(python-sdk): correct Sandbox.list() docstring (also lists paused) (#1511)
Integration branch PR for #1500. Merges the docstring fix into `main`.

Once #1500 is merged into `python-sdk-list-docstring-base`, this PR will
carry those changes into `main`.

---------

Co-authored-by: Leinux <tristone13th@outlook.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:10:33 +00:00
mattteufel-e2b d071bb78c8 fix(cli): use absolute import in generated Python build scripts (#1505)
Fixes the generated Python build scripts to use an absolute import (from
template import template) instead of a relative one, which broke python
build_dev.py with ImportError: attempted relative import with no known
parent package since the files are emitted as flat siblings with no
package. This reverts an unintended change from #954 that was flagged by
Cursor Bugbot at the time but not addressed. Fixes #1477.
2026-06-29 22:46:31 +02:00
Mish Ushakov 2869febdee feat(cli): add config override flags to template migrate (#1494)
Adds override flags to `e2b template migrate` so the generated SDK files
don't have to inherit everything from `e2b.toml`: `--name`/`-n`
(template name), `--cmd`/`-c` (start command), `--ready-cmd` (ready
command), `--cpu-count`, and `--memory-mb`. Each flag falls back to the
corresponding config value when omitted, and `--memory-mb` is validated
to be even. Includes tests covering the overrides and the odd-memory
rejection, plus a changeset for `@e2b/cli`.

## Usage

```bash
e2b template migrate \
  --language typescript \
  --name my-custom-name \
  --cmd "node server.js" \
  --ready-cmd "curl localhost:3000" \
  --cpu-count 4 \
  --memory-mb 2048
```

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 15:36:46 +02:00
Matt Brockman f160f08c7b Keep integration attribution on connection config (#1459)
moves integration attirbution to more private thing to avoid confusing people with first class kwargs
2026-06-26 18:30:35 -07:00
mattteufel-e2b 42538836f3 ci: show skipped tests as skipped instead of green pass (#1486)
green passed tests is misleading. indicate when tests skipped.
2026-06-26 14:51:44 -07:00
Lukáš Huvar bb45f185f1 Introduce generic paginator base class for JS and Python SDKs (#1491)
Extracts the cursor-based pagination state machine into a reusable base
class — `Paginator` in the JS SDK's `utils`, `PaginatorBase` in
`e2b/utils.py` — that owns `hasNext`/`nextToken` and the `x-next-token`
header handling, and migrates the sandbox and snapshot paginators onto
it. Each concrete paginator now just implements `nextItems`/`next_items`
to fetch its own page, so future list endpoints (templates, builds,
etc.) can add pagination by subclassing without reimplementing the
bookkeeping. Applied equivalently to the JS SDK and both Python sync and
async implementations, with unit tests covering the shared base. There
are no public API changes — `Sandbox.list()` / `listSnapshots()` and the
existing paginator types behave identically.

## Usage (unchanged)

```ts
const paginator = Sandbox.list()
while (paginator.hasNext) {
  const sandboxes = await paginator.nextItems()
  console.log(sandboxes)
}
```

```python
paginator = Sandbox.list()
while paginator.has_next:
    sandboxes = paginator.next_items()
    print(sandboxes)
```
2026-06-26 14:53:56 +02:00
Mish Ushakov 5c8c3ad7fc ci: split release workflow into production and candidate workflows (#1483)
## Why

GitHub Actions cannot conditionally show `workflow_dispatch` inputs
based on other inputs, so the single **Release** form always displayed
the six candidate-only fields even when running a production release —
confusing for anyone doing their first release.

## What

Split the combined workflow into two so each form matches its intent:
- **`release.yml` ("Release")** — production only; the `mode` dropdown
and all candidate fields are removed, leaving a form with no inputs.
- **`release-candidate.yml` ("Release candidate")** — new file
containing only the RC inputs (js-sdk, python-sdk, cli, tag, preid,
skip-tests), with the now-redundant "(candidate only)" label suffixes
dropped.

People choose by sidebar name instead of a dropdown, and the `mode ==/!=
'candidate'` job guards are gone since workflow selection does that job.

Two follow-ups from review to keep behavior intact across the split:
- **Concurrency:** both files use a shared literal group `release-${{
github.ref }}` (instead of `${{ github.workflow }}-…`) so production and
candidate releases on the same ref still serialize.
- **RC versioning:** `publish_candidates.yml` now derives RC version
suffixes from `github.run_id` instead of `github.run_number`.
`run_number` is per-workflow-file and would reset to 1 for the new
workflow, causing RC versions to go backwards (npm dist-tag downgrade /
publish collisions); `run_id` is globally unique and monotonic.

> [!NOTE]
> Any automation or docs that ran the old workflow with `-f
mode=candidate` must now target `release-candidate.yml` (no `mode`
field).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:17:49 +02:00
Mish Ushakov bb1696871b Stream template build-context upload from disk instead of buffering in memory (#1435)
## Summary

Template builds previously buffered the entire gzipped build-context tar
archive in memory before uploading it. This PR spools the archive to a
temporary file and streams it from disk during upload — in the JS SDK
and both sync and async Python SDKs — so memory usage no longer scales
with the size of the build context.

The upload keeps an explicit `Content-Length` header (taken from the
spooled file's size), which S3 presigned PUT URLs require — they reject
`Transfer-Encoding: chunked` with `501 NotImplemented` (#1243).

## Changes

- **JS** (`packages/js-sdk/src/template/`):
`tarFileStream`/`tarFileStreamUpload` are replaced by `tarFileToStream`,
which writes the archive to a temp file and returns a self-cleaning read
stream plus its `size`. The spooled temp file deletes itself once the
stream is closed (consumed, errored, or destroyed) via the stream's
`close` event — mirroring the Python SDK's `tar_file_stream`. `buildApi`
streams this body with `duplex: 'half'` and an explicit `Content-Length`
from `size`; if `fetch` throws before consuming the body, it destroys
the stream to trigger the same cleanup. There is no separate cleanup
callback, so a cleanup failure can no longer mask the upload result.
- **Python** (`packages/python-sdk/e2b/template/utils.py`,
`template_async/build_api.py`, `template_sync/build_api.py`):
`tar_file_stream` now writes to a `tempfile.TemporaryFile` instead of
`io.BytesIO` and returns the file object positioned at the start; the
upload streams from it with an explicit `Content-Length` and closes it
(deleting the temp file) when done.
- Tests updated for the new return shapes (JS `tarFileToStream.test.ts`,
`uploadFile.test.ts`; Python upload/tar tests), including assertions
that the spooled archive is removed on both the consume and destroy
paths.

## Usage

No API changes — `Template.build()` / template builds behave the same,
just without holding the build context in memory:

```ts
await Template.build(template, { alias: 'my-template' })
```

```python
Template.build(template, alias="my-template")
```

Split out of #1433, which covers streaming for sandbox/volume file
uploads and downloads.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-25 20:56:10 +02:00
Mish Ushakov 8b8a224f8b feat(python-sdk): add logger option for request/debug logging (#1409)
Adds a `logger` option (a standard library `logging.Logger`) to
`Sandbox.create`/`AsyncSandbox.create` and the static
`Sandbox.connect(sandbox_id, ...)`, wired into the API client, the envd
client, the volume content client, and the RPC (ConnectRPC) path. The
logger is stored on the sandbox and propagates to all of its later
operations — including control-plane calls like
`kill`/`pause`/`set_timeout`/`get_info` (via `get_api_params`) — so
logging keeps working after construction; mirroring the JS SDK, `logger`
is a construction-time option and not a public per-request parameter
those methods accept from the caller, and nothing is logged unless a
logger is supplied. The stdlib `logging.Logger` is used directly as the
adapter (no ported JS `Logger` interface), and log levels match JS:
requests at `INFO`, successful API and unary RPC responses at `INFO`,
streamed RPC messages at `DEBUG`, failed API responses (status >= 400)
at `ERROR`. The always-on module-level (`e2b.*`) request logging at the
transport layer was removed in favor of this opt-in client-layer
logging, and volume content operations continue to accept `logger` per
call via `VolumeApiParams` to match the JS Volume API. Includes a
changeset and unit tests in `tests/test_logging_option.py`.

## Usage

```python
import logging
from e2b import Sandbox

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("my-app.e2b")

sbx = Sandbox.create(logger=logger)
sbx.commands.run("echo hello")   # RPC logged via `logger`
sbx.set_timeout(60)              # control-plane call also logged via `logger`
```

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Matt Brockman <matt.brockman@e2b.dev>
2026-06-25 20:43:04 +02:00
github-actions[bot] ec260376dc [skip ci] Release new versions 2026-06-25 18:04:08 +00:00
Mish Ushakov de0c401626 fix(sdk): correct filesystem watch handle callback and timeout behavior (#1480) @e2b/python-sdk@2.30.0 e2b@2.31.0 2026-06-25 19:51:53 +02:00
Babis Chalios 7e7e9514df feat(sdk): filesystem-only auto-pause via lifecycle.onTimeout object form (#1471)
## Filesystem-only auto-pause (`onTimeout` object form)

Adds an object form to the sandbox **lifecycle** `onTimeout`
(`on_timeout` in Python) that controls the snapshot kind taken when a
sandbox auto-pauses on timeout, via `keepMemory` (`keep_memory`).

`onTimeout` now accepts either the existing bare action (`'pause'` /
`'kill'`) or the object form `{ action, keepMemory }`. When `keepMemory`
is `false` (with `action: 'pause'`), a timeout auto-pause takes a
**filesystem-only** snapshot (no memory) instead of a full memory one,
so the sandbox cold-boots (reboots) from disk on resume — losing running
processes and open connections. Defaults to `true` (full memory
snapshot), so existing callers are unaffected. **The bare string form is
unchanged.**

It's the create-time / auto-pause counterpart to the explicit
`pause(keepMemory=false)` from #1465: same `keepMemory` naming, mapped
onto the `autoPauseMemory` create field.

### Type safety
The object form is a **discriminated union** on `action`: `keepMemory`
is only valid with `action: 'pause'`. Pairing it with `action: 'kill'`
is a **compile-time type error** (TS) / static error (`ty`), and is
additionally rejected at runtime (`InvalidArgumentError` /
`InvalidArgumentException`) for untyped callers.

### Behavior & validation
- `keepMemory` only applies to a `pause` action.
- **Incompatible with auto-resume** — auto-resume wakes a paused sandbox
on inbound traffic by restoring its memory snapshot in place; a
filesystem-only snapshot has no memory to restore (resuming cold-boots
it), so it must be resumed explicitly via `connect()`. Combining
`keepMemory: false` with `autoResume` is rejected client-side.

### Usage
```ts
// JS/TS — filesystem-only auto-pause on timeout
const sbx = await Sandbox.create({
  lifecycle: { onTimeout: { action: 'pause', keepMemory: false } },
})

// bare string form still works (full memory snapshot)
const sbx2 = await Sandbox.create({ lifecycle: { onTimeout: 'pause' } })
```
```python
# Python
sbx = Sandbox.create(
    lifecycle={"on_timeout": {"action": "pause", "keep_memory": False}}
)
```

### Changes
- `spec/openapi.yml`: `autoPauseMemory` on the create body (+
regenerated JS/Python clients).
- JS `SandboxOnTimeout` discriminated union (`'pause' | 'kill' | {
action: 'pause'; keepMemory? } | { action: 'kill' }`) and the Python
`SandboxOnTimeoutPause` / `SandboxOnTimeoutKill` TypedDicts, wired
through `createSandbox` / `_create_sandbox` (sync + async) to
`autoPauseMemory`, with the client-side guards.
- Tests: payload serialization + validation (offline, incl. the `action:
'kill'` type/runtime guard) and live cold-boot e2e in both SDKs;
changeset (`e2b` + `@e2b/python-sdk`, minor).

### Backend dependency
The live e2e tests exercise the real auto-pause→cold-boot path and
require the infra-side `autoPauseMemory` support (e2b-dev/infra#3055),
now merged and deployed.

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

---------

Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 17:16:56 +00:00
Babis Chalios cb5a3870b6 feat(sdk): filesystem-only snapshots (pause memory:false) (#1465)
## Summary

Adds an optional **`memory`** flag to `pause` in both the JS and Python
SDKs. When `memory` is `false`, the pause captures **only the
filesystem** (no memory snapshot); resuming such a snapshot **cold-boots
(reboots)** the sandbox from disk — losing in-memory state, running
processes, and open connections. Defaults to `true` (full memory
snapshot), so existing callers are unaffected.

This is the SDK surface for the filesystem-only snapshot feature on the
infra side.

## Usage

```ts
// JS / TS
const sbx = await Sandbox.create()
await sbx.pause({ memory: false })   // filesystem-only snapshot
const resumed = await sbx.connect()  // resumes by cold-booting from disk
```

```python
# Python (sync)
sbx = Sandbox()
sbx.pause(memory=False)              # filesystem-only snapshot
resumed = sbx.connect()              # resumes by cold-booting from disk

# Python (async)
sbx = await AsyncSandbox.create()
await sbx.pause(memory=False)
resumed = await sbx.connect()
```

`memory` defaults to `true` — `pause()` / `pause({})` behave exactly as
before.

## What changed

- **spec**: optional `memory: boolean` (default `true`) on `POST
/sandboxes/{sandboxID}/pause` (`SandboxPauseRequest`); both API clients
regenerated via `make codegen`.
- **JS**: `Sandbox.pause` / `betaPause` accept `{ memory }` →
`SandboxApi.pause` sends the request body.
- **Python**: `pause(memory=...)` / `beta_pause` → `_cls_pause` (sync +
async) sends `SandboxPauseRequest(memory=...)`.
- **Tests**: filesystem-only pause+resume reboots the guest while the
filesystem survives — JS (`tests/sandbox/snapshot.test.ts`) and Python
sync + async. All pass against a local stack; `format` / `lint` /
`typecheck` clean.
- **Changeset**: `minor` for `e2b` and `@e2b/python-sdk`.

## Note (related infra observation, not addressed here)

While testing, a filesystem-only **resume cold-boots into a different
default exec context** (`root` / `/root`) than a memory resume (`user` /
`/home/user`). The filesystem itself is fully intact; tests use absolute
paths to be robust to this. Worth confirming on the infra reboot path
whether the template's default user should be restored after a cold
boot.

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

---------

Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 10:41:57 +00:00
github-actions[bot] 31a93bed0c [skip ci] Release new versions 2026-06-25 06:10:56 +00:00
Ben Fornefeld 5370d54bfa feat(cli): replace access token auth with Hydra OAuth flow (#1481)
## Summary

Restructures the CLI config schema to v1 with nested , , and sections.
Replaces the legacy e2b access token auth with a Hydra OAuth flow using
refresh tokens. Token expiry is decoded from the JWT claim at runtime
instead of being stored.

## Changes

- **New config schema (v1)**: , , , , ,  (ISO timestamp)
- **Token refresh**: decodes from the JWT access token, refreshes via
Hydra when expired, writes only (not )
- **Deprecated config handling**: Old flat configs without are deleted
with a re-login prompt. No migration path — users re-authenticate.
- ****: Set on  and , not on token refresh
- ****: New helper for direct error throwing without type narrowing;
auth commands use it instead of
- **Type-safe team responses**: Removed  casts, use  type extraction
- **Logout**: Revokes refresh token via Hydra before deleting config;
fixed crash when deprecated config already deleted by
- **Removed**: Token expiry display from ,  from 

## Config example

```json
{
  "version": 1,
  "identity": { "email": "user@example.com" },
  "oauth": { "token_endpoint": "https://hydra.../oauth2/token", "client_id": "..." },
  "tokens": { "access_token": "...", "refresh_token": "..." },
  "last_refresh": "2024-06-24T12:00:00.000Z",
  "teamName": "...", "teamId": "...", "teamApiKey": "..."
}
```

## Test plan

- [x] `pnpm run typecheck` passes
- [x] `pnpm exec eslint` passes on changed files
- [x] `pnpm exec prettier --check` passes
- [x] `pnpm exec vitest run tests/user_config_permissions.test.ts`
passes
- [ ] Manual: `e2b auth login` writes v1 config
- [ ] Manual: token refresh via `e2b auth configure` with expired JWT
- [ ] Manual: old flat config triggers deprecation and re-login

Depends on: dashboard PR adding the Hydra OAuth CLI flow

---------

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@e2b/cli@2.13.0 @e2b/python-sdk@2.29.6 e2b@2.30.6
2026-06-24 18:03:55 -07:00
Mish Ushakov 2a98cce8c7 fix(js-sdk): stop CommandHandle.disconnect() leaking the output subscription (#1474)
## Description

This PR fixes two related issues in the command handle's event handling.

### 1. JS `CommandHandle.disconnect()` leaked the output subscription

`disconnect()` was fire-and-forget — it only triggered the transport
abort and relied entirely on HTTP/2 abort propagation to stop events,
which is unreliable under keepalive: `onStdout`/`onStderr`/`onPty` could
keep firing for output produced after `disconnect()` returned.

`disconnect()` now sets a cooperative `disconnected` flag and aborts the
transport. The flag is checked before every callback dispatch in the
event loop, so once `disconnect()` returns no callback fires for output
that arrives (or was buffered) after the call — even if the underlying
abort hasn't torn the stream down yet. It does **not** wait for the
event handler to drain, so it returns promptly even for an idle command
(e.g. `sleep`) whose stream produces no further output, never blocks on
an in-flight callback, and does not deadlock when awaited from inside a
callback.

The async Python SDK was already correct here (`disconnect()` cancels
the event-handling task), and the sync Python SDK has no background
subscription (events are consumed only while the caller iterates). The
added Python tests confirm both.

### 2. Exit code was lost when a disconnected consumer stopped on a
flushed `end`-event chunk

When the `end` event flushes trailing decoder bytes (an incomplete
multibyte sequence → replacement character) and the consumer stops
iterating on the first flushed chunk, the generator was aborted before
the result was assigned, so `wait()` failed as if the process never
produced a result. The `end` handler now records the result **before**
yielding the flushed chunks, across the JS, async Python, and sync
Python SDKs.

## Usage

```js
const handle = await sandbox.commands.run(daemon, { background: true, stdin: true, onStdout })
await sandbox.commands.sendStdin(handle.pid, 'turn1\n')
await handle.disconnect() // resolves promptly; onStdout will not fire again
await sandbox.commands.sendStdin(handle.pid, 'turn2\n') // turn2 output never reaches onStdout
```

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:03:56 +00:00
Matt Brockman 21af1f8a15 fix(python): avoid quadratic stdout/stderr accumulation in command ha… (#1472)
merged from https://github.com/e2b-dev/E2B/pull/1457

## Problem

`AsyncCommandHandle` / `CommandHandle` accumulate streamed output with
`self._stdout += out` per chunk. Because `self._stdout` is an instance
attribute (`STORE_ATTR`), CPython's in-place string-concatenation
optimization — which only applies to local `STORE_FAST` targets —
doesn't apply, so each append re-copies the entire buffer. For commands
that emit large volumes of output this becomes O(n²) in total bytes, and
in async contexts it stalls the event loop for hundreds of ms per chunk
near the tail.

## Fix

Buffer decoded chunks in a `list[str]` and `"".join()` them on read.
This restores linear-time accumulation and keeps streaming responsive,
with no change to the resulting `stdout`/`stderr` values or the public
API.

## Notes

- Applies the same change to both the sync and async command handles.
- Pure internal change; the incremental UTF-8 decoding behavior is
preserved.
- Changeset included (`@e2b/python-sdk`, patch).

Co-authored-by: davidzeng-pplx <david.zeng@perplexity.ai>
2026-06-23 13:08:43 +02:00
Mish Ushakov dabac31cab Reuse toUploadBody in JS volume writeFile (#1473)
Replace the inlined stream/buffer logic in the JS volume `writeFile`
with the shared `toUploadBody` helper, matching the `sandbox/filesystem`
write path and dropping the now-unused local `runtime` and `toBlob`
imports. The helper already returns a `ReadableStream` only when the
body should be streamed (non-browser stream input) and otherwise buffers
into a Blob, so deriving `isStream` from `body instanceof
ReadableStream` is byte-for-byte equivalent to the old check. This is a
pure refactor with no behavior change, keeping the two write paths from
drifting.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 13:02:38 +02:00
github-actions[bot] 7d4d620fa8 [skip ci] Release new versions 2026-06-22 19:39:46 +00:00
Mish Ushakov c1415f3ec7 Stream volume file uploads and downloads instead of buffering in memory (#1453)
Follow-up to #1433. Builds on the shared streaming infrastructure
introduced there (`FILE_TIMEOUT_MS`, request-controller/stream-cleanup
helpers in `connectionConfig`, `io_utils` chunk iterators, the `runtime`
guard) and applies the same streaming model to volumes.

> [!NOTE]
> Based on `mishushakov/stream-write-file-upload` (#1433). Merge that PR
first; this PR's diff will then retarget to `main` automatically.

## What changed

- **`Volume.writeFile()` / `Volume.write_file()`** — stream the request
body instead of buffering it in memory.
- JS: `ReadableStream` data is streamed outside the browser
(half-duplex); browsers still buffer since they can't stream request
bodies.
- Python: file-like objects are streamed in chunks (async wraps them in
an async iterator; sync passes them to httpx directly, text-mode IO is
encoded chunk-by-chunk).
- **`Volume.readFile(format="stream")` / `read_file(format="stream")`**
— the request timeout now bounds only the initial handshake, not the
body read, matching the sandbox `files.read` stream path. A dropped
connection during the handshake surfaces the same typed, health-checked
error; JS supports `signal` to cancel an in-flight stream and cancels
unconsumed bodies on error so the pooled connection is released.

## Usage

JS — stream a file straight to a volume without buffering:
```ts
import { createReadStream } from 'node:fs'
import { Readable } from 'node:stream'

const stream = Readable.toWeb(createReadStream('large-input.bin'))
await volume.writeFile('/data/large-input.bin', stream)

// read back as a stream; the body lives until consumed/cancelled
const out = await volume.readFile('/data/large-input.bin', { format: 'stream' })
for await (const chunk of out) {
  // process chunk
}
```

Python — stream a file-like object:
```python
with open("large-input.bin", "rb") as f:
    volume.write_file("/data/large-input.bin", f)  # streamed, not read() into memory

for chunk in volume.read_file("/data/large-input.bin", format="stream"):
    ...  # process chunk
```

## Testing

- `pnpm run format`, `pnpm run lint`, `pnpm run typecheck` pass.
- Added volume streaming tests (JS `tests/volume/file.test.ts`; Python
sync/async `test_file.py` text-stream cases).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@e2b/python-sdk@2.29.5 e2b@2.30.5
2026-06-22 12:11:03 -07:00
Mish Ushakov 60feee3cf6 Stream SDK file uploads and downloads instead of buffering in memory (#1433)
## Description

Removes full in-memory buffering from the SDK **sandbox** file-transfer
paths, in both JS and Python (sync + async).

**Streamed uploads** — `Sandbox.files.write` / `write_files` streams
`ReadableStream` (JS, outside the browser) and file-like (Python) input
to the sandbox with chunk-by-chunk gzip compression, instead of
buffering the whole body in memory. `useOctetStream`/`use_octet_stream`
now defaults to auto-detect — octet-stream when any entry is streamable
(so streamed uploads aren't silently buffered), `multipart/form-data`
otherwise; browsers always use `multipart/form-data` since streaming
request bodies aren't supported there. A streamed upload is bounded by a
per-chunk timeout on the wire (Python's per-write `httpx` timeout,
default the request timeout); a stalled upload the wire can't observe is
bounded server-side. On Python's `AsyncSandbox`, the blocking file reads
and gzip compression of a streamed upload now run in a worker thread so
a large upload doesn't stall the event loop.

**Streamed downloads** — `Sandbox.files.read(format="stream")` now
streams the response body from the sandbox instead of downloading it
into memory before iterating (Python sync + async), and the 60s request
timeout no longer kills the stream while it's being consumed:
- The request timeout now bounds only the initial handshake.
- The body is bounded by a per-chunk **idle-read timeout** on the wire —
a per-`read()` option (`streamIdleTimeoutMs` in JS,
`stream_idle_timeout` in Python; default the request timeout — 60s —
`0`/`None` to disable). It's armed only while waiting on a network read
and cleared the moment a chunk arrives, so it aborts only when the
server stops sending mid-stream; a slow or paused consumer never trips
it (a held-but-unread stream is reclaimed server-side, not by this
timer).
- A dropped connection during the handshake surfaces the same typed,
health-checked error as non-stream reads. In JS, `signal` can still
cancel an in-flight stream.
- The stream holds its pooled connection until it is consumed to the
end, cancelled/closed, errors, or the idle timeout fires — consume it
fully, use the context manager, or close it. (This replaces the earlier
GC-finalizer net.) Python returns a
`FileStreamReader`/`AsyncFileStreamReader` supporting deterministic
cleanup via `close()`/`aclose()` and (async) context-manager use; both
still satisfy `Iterator[bytes]`/`AsyncIterator[bytes]`, so existing
iteration is unchanged.

**Empty files** — JS `Sandbox.files.read()` with `blob` or `stream`
format now returns a format-correct empty value (empty `Blob` / empty
`ReadableStream`) for empty files instead of `""`.

> [!NOTE]
> The equivalent **volume** streaming changes
(`Volume.writeFile`/`write_file`, `Volume.readFile`/`read_file` streams)
live in a follow-up PR, #1453, which is based on this branch.

## Usage

```ts
// JS: upload a large file without holding it in memory
const file = createReadStream('large.bin')
await sandbox.files.write('large.bin', Readable.toWeb(file), { gzip: true })

// JS: consume a download for longer than 60s without it being killed
const stream = await sandbox.files.read('large.bin', { format: 'stream' })
for await (const chunk of stream) { /* ... */ }

// JS: tune (or disable) the per-chunk idle-read timeout for a read
const stream = await sandbox.files.read('large.bin', {
  format: 'stream',
  streamIdleTimeoutMs: 120_000, // 0 to disable
})

// JS: empty files now return format-correct empty values
const blob = await sandbox.files.read('empty.txt', { format: 'blob' }) // Blob (size 0), not ''
```

```python
# Python: streamed upload and download
with open("large.bin", "rb") as f:
    sandbox.files.write("large.bin", f, gzip=True)

for chunk in sandbox.files.read("large.bin", format="stream"):
    ...

# Python: deterministic cleanup when not reading the stream to the end
with sandbox.files.read("large.bin", format="stream") as stream:
    first_chunk = next(iter(stream))  # connection released on block exit

# Python: tune (or disable) the per-chunk idle-read timeout for a read
for chunk in sandbox.files.read(
    "large.bin", format="stream", stream_idle_timeout=120.0  # None to disable
):
    ...
```

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:45:32 +02:00
Mish Ushakov 46615194c1 ci: rebase before push in release commit step to avoid push race (#1469)
## Problem

The release workflow's "Commit new versions" step
(`.github/workflows/publish_packages.yml`) ran `git commit -am … && git
push` with no rebase. If any PR merged into the target branch while a
release was in flight, the remote moved ahead and the push failed as a
non-fast-forward — failing the whole release.

## Fix

Run `git pull --rebase origin "${GITHUB_REF_NAME}"` before `git push`,
so the release commit is replayed on top of the latest remote state.

```yaml
git commit -am "[skip ci] Release new versions" || exit 0
git pull --rebase origin "${GITHUB_REF_NAME}"
git push
```

Note: a narrow window remains if a PR merges between the rebase and the
push (sub-second), which would still fail; a retry loop would fully
eliminate it but adds complexity. Happy to add one if preferred.

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

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:16:21 -07:00
Mish Ushakov 3cb6ca5f92 [skip ci] Release new versions (sync repo with published packages) (#1468)
## Why

The Release run
[27844631141](https://github.com/e2b-dev/E2B/actions/runs/27844631141/job/82412481993)
**published all packages successfully** but then failed at the final
*"Commit new versions"* step — the version-bump commit-back to \`main\`
was rejected as non-fast-forward (another PR landed on \`main\` during
the release window).

As a result the registries are ahead of the repo:

| Package | Published | Repo (main) before this PR |
|---|---|---|
| \`e2b\` (JS) | 2.30.4 (npm) | 2.30.3 |
| \`@e2b/cli\` | 2.12.2 (npm) | 2.12.1 |
| \`e2b\` (Python) | 2.29.4 (PyPI) | 2.29.3 |

The changeset \`fix-logo-pypi-npm.md\` was also never consumed and is
still on \`main\`.

## What this PR does

Replays exactly what the failed *"Commit new versions"* step would have
committed — i.e. \`pnpm run version\` (changeset version +
\`postVersion\` poetry sync) + lockfile update:

- Bumps \`e2b\` → 2.30.4, \`@e2b/cli\` → 2.12.2, \`@e2b/python-sdk\` →
2.29.4 (matching what's already published)
- Deletes the consumed changeset \`fix-logo-pypi-npm.md\`
- Updates \`pnpm-lock.yaml\` (CLI's \`e2b\` dep → 2.30.4)

No new packages are published by merging this — it only syncs the repo
to the registries. **Do not re-run the Release workflow** for this
changeset; the versions already exist on npm/PyPI.
2026-06-22 10:15:33 -07:00
Mish Ushakov 8171a03765 fix(python-sdk): let api_headers Authorization win over deprecated access_token (#1464)
The Python SDK's `ApiClient` applied the deprecated `access_token`
*after* merging `config.headers` (which includes `api_headers`), so a
custom `Authorization` passed via `api_headers` was silently overwritten
— the opposite of the JS SDK, where a custom `Authorization` wins. This
moves the deprecated access token before `config.headers` so
`api_headers` now takes precedence, matching JS. No change when only
`access_token` is set.

```python
# Custom Authorization via api_headers now wins over the deprecated access_token
ConnectionConfig(api_key="e2b_...", access_token="old", api_headers={"Authorization": "Bearer custom"})
# -> Authorization: Bearer custom
```

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 14:53:37 +02:00
devin-ai-integration[bot] 5a755078c2 docs: update main README logo with theme-aware dark/light variants (#1470)
## Summary

Replace the old full-width SDK banner images (`e2b-sdk-light.png` /
`e2b-sdk-dark.png`) in the main README with the new E2B wordmark logos
(`logo-black.png` / `logo-white.png`), using `#gh-light-mode-only` /
`#gh-dark-mode-only` for GitHub theme switching.

No changeset needed — this is a repo-level README change only (not
published to NPM/PyPI).

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 13:26:06 -07:00
Mish Ushakov 58566d45f6 ci: skip CI on docs-only changes (#1467)
## Summary

PR-gated workflows filter changed paths to decide whether to run, but
their package/spec globs (`packages/**`, `spec/**`) matched every `.md`
file in those directories — so docs-only changes (e.g. a package README)
triggered full test/lint/typecheck/codegen runs.

This appends the picomatch extglob `**/!(*.md)` to those directory globs
so Markdown no longer matches, across:

- **sdk_tests.yml** — JS/Python/CLI suites (prod + staging)
- **lint.yml** — lint/format only touch `src/`, `tests/`, and Python
code, never Markdown
- **typecheck.yml** — typecheck only covers `.ts`/`.py`
- **generated_files.yml** — codegen derives from `spec/`, unaffected by
docs

The exclusion is baked into each glob rather than added as a `!**/*.md`
rule because that only subtracts under `predicate-quantifier: every`,
which is global to the step and would break the OR between the shared
and package globs. PRs touching code (or code **and** docs together)
still run as before.

Note: `pkg_artifacts.yml` builds packages on every PR with no path
filter at all — left as-is since gating it would require adding a
`changes` job and change its always-runs behavior.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 19:27:29 +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>
@e2b/cli@2.12.2 @e2b/python-sdk@2.29.4 e2b@2.30.4
2026-06-19 19:26:46 +00:00
github-actions[bot] 2c48e927c2 [skip ci] Release new versions 2026-06-19 19:04:05 +00:00
Mish Ushakov e03d1b88fc ci: Slack notifications on release start and success with itinerary (#1463)
## What

The release workflow only pinged Slack on failure. This adds two
notifications to the `monitoring-releases` channel, each including an
itinerary of what is being released:

- **Release Started** (`report-start`) — posts as soon as a production
release is triggered.
- **Release Succeeded** (`report-success`) — posts when the release
publishes successfully.

The itinerary (package name + target version) is computed once in
`preflight` via `changeset status` and exposed as a job output, so both
notifications stay consistent. The jobs only fire for production
releases (`release == 'true'` / `publish` success), never for RC
publishes.

## Example Slack messages

**Started**
> 🚀 A new release has been triggered 
>
> *Releasing:*
> • JS SDK (e2b) v2.30.3
> • Python SDK (e2b) v2.29.3
> • CLI (@e2b/cli) v2.12.1

**Succeeded**
> 🚀 🎉 A new version has been released successfully!
:ship-it-parrot:
>
> *Released:*
> • JS SDK (e2b) v2.30.3
> • Python SDK (e2b) v2.29.3
> • CLI (@e2b/cli) v2.12.1

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@e2b/cli@2.12.1 @e2b/python-sdk@2.29.3 e2b@2.30.3
2026-06-19 11:50:42 -07: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
dependabot[bot] 73826a3089 chore(deps): bump undici from 7.25.0 to 7.28.0 in the npm_and_yarn group across 1 directory (#1461)
Bumps the npm_and_yarn group with 1 update in the / directory:
[undici](https://github.com/nodejs/undici).

Updates `undici` from 7.25.0 to 7.28.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nodejs/undici/releases">undici's
releases</a>.</em></p>
<blockquote>
<h2>v7.28.0</h2>
<h1>⚠️ Security Release</h1>
<p>This release line addresses <strong>7 security advisories</strong>,
all shipped in <strong>v7.28.0</strong>.</p>
<blockquote>
<p><strong>Action required:</strong> Upgrade to <strong>undici
7.28.0</strong> or later.</p>
<pre lang="sh"><code>npm install undici@^7.28.0
</code></pre>
</blockquote>
<p>The v7 line is <strong>not</strong> affected by GHSA-38rv-x7px-6hhq
(CVE-2026-9675), which is
an 8.x-only regression.</p>
<blockquote>
<p><strong>Note on GHSA-hm92-r4w5-c3mj:</strong> this fix shipped in
<strong>v7.28.0</strong>, not the
earlier 7.2x line — the vulnerable single-pool code was still present
through
<code>v7.27.2</code>. The per-origin pool fix is
<a
href="https://github.com/nodejs/undici/commit/3805b8f8"><code>3805b8f8</code></a>
(<a
href="https://redirect.github.com/nodejs/undici/pull/5041">#5041</a>).</p>
</blockquote>
<h2>Summary</h2>
<table>
<thead>
<tr>
<th>Advisory</th>
<th>CVE</th>
<th>Severity (CVSS)</th>
<th>Fixed in</th>
<th>Fix commit</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-vxpw-j846-p89q">GHSA-vxpw-j846-p89q</a></td>
<td>CVE-2026-12151</td>
<td>High (7.5)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/8cb10f98"><code>8cb10f98</code></a></td>
</tr>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-vmh5-mc38-953g">GHSA-vmh5-mc38-953g</a></td>
<td>CVE-2026-9697</td>
<td>High (7.4)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/04201f89"><code>04201f89</code></a></td>
</tr>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-hm92-r4w5-c3mj">GHSA-hm92-r4w5-c3mj</a></td>
<td>CVE-2026-6734</td>
<td>High (7.5)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/3805b8f8"><code>3805b8f8</code></a></td>
</tr>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-pr7r-676h-xcf6">GHSA-pr7r-676h-xcf6</a></td>
<td>CVE-2026-9678</td>
<td>Moderate (5.9)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/85a24055"><code>85a24055</code></a></td>
</tr>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-p88m-4jfj-68fv">GHSA-p88m-4jfj-68fv</a></td>
<td>CVE-2026-9679</td>
<td>Moderate (5.9)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/d0574cc4"><code>d0574cc4</code></a></td>
</tr>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-g8m3-5g58-fq7m">GHSA-g8m3-5g58-fq7m</a></td>
<td>CVE-2026-11525</td>
<td>Low (3.7)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/d0574cc4"><code>d0574cc4</code></a></td>
</tr>
<tr>
<td><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-35p6-xmwp-9g52">GHSA-35p6-xmwp-9g52</a></td>
<td>CVE-2026-6733</td>
<td>Low (3.7)</td>
<td>7.28.0</td>
<td><a
href="https://github.com/nodejs/undici/commit/ea8930cf"><code>ea8930cf</code></a></td>
</tr>
</tbody>
</table>
<hr />
<h2>High severity</h2>
<h3>WebSocket DoS via fragment count bypass — CVE-2026-12151</h3>
<p><strong><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-vxpw-j846-p89q">GHSA-vxpw-j846-p89q</a></strong>
· CWE-400, CWE-770
<strong>Fix:</strong> <a
href="https://github.com/nodejs/undici/commit/8cb10f98"><code>8cb10f98</code></a>
<em>websocket: limit the number of fragments in a message</em> (part of
backport <a
href="https://github.com/nodejs/undici/commit/a027a4a0"><code>a027a4a0</code></a>
<em>Backport WebSocket maxPayloadSize fixes to v7.x</em>, <a
href="https://redirect.github.com/nodejs/undici/pull/5423">#5423</a>)</p>
<p>A malicious WebSocket server can stream a large number of small or
empty
continuation frames. Undici enforced a limit on cumulative payload size
but did
not limit the <em>number</em> of fragments per message, leading to
unbounded memory
growth and denial of service.</p>
<ul>
<li><strong>Affected:</strong> applications using <code>new
WebSocket(...)</code> or <code>WebSocketStream</code>
against untrusted endpoints.</li>
<li><strong>Workaround:</strong> none — upgrade is required.</li>
</ul>
<h3>TLS certificate validation bypass in SOCKS5 ProxyAgent —
CVE-2026-9697</h3>
<p><strong><a
href="https://github.com/nodejs/undici/security/advisories/GHSA-vmh5-mc38-953g">GHSA-vmh5-mc38-953g</a></strong>
· CWE-295</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/nodejs/undici/commit/f9eba0ad9134e1c0977848476bba9d49734696e4"><code>f9eba0a</code></a>
Bumped v7.28.0 (<a
href="https://redirect.github.com/nodejs/undici/issues/5430">#5430</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/a027a4a04c6c055877d1abaf5f60ee4917e7e01f"><code>a027a4a</code></a>
Backport WebSocket maxPayloadSize fixes to v7.x (<a
href="https://redirect.github.com/nodejs/undici/issues/5423">#5423</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/8cb10f983eb6005dd53f3744d95d3b6d7dbcee0f"><code>8cb10f9</code></a>
websocket: limit the number of fragments in a message</li>
<li><a
href="https://github.com/nodejs/undici/commit/04201f8947041f0f4f2ac865dbdb1677e46a8844"><code>04201f8</code></a>
fix: honor requestTls when proxy is SOCKS5</li>
<li><a
href="https://github.com/nodejs/undici/commit/fcd642ff613ea9030dec87cf622e68d4b1ae9847"><code>fcd642f</code></a>
fix(socks5): preserve dispatch backpressure return value (<a
href="https://redirect.github.com/nodejs/undici/issues/5166">#5166</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/bc98c97906abf26fa1e959b2f6111b53ade0e18f"><code>bc98c97</code></a>
fix(socks5): use configured connector in Socks5ProxyAgent (<a
href="https://redirect.github.com/nodejs/undici/issues/5168">#5168</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/9e1c74372a2b27cacd92d27c13a83a6d84f10e0e"><code>9e1c743</code></a>
fix(socks5): encode embedded IPv4 tails in IPv6 literals correctly (<a
href="https://redirect.github.com/nodejs/undici/issues/5099">#5099</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/376c8be27cb40cc17ccaad6b6ebb317fa7148d65"><code>376c8be</code></a>
fix(socks5): enforce authenticated state before CONNECT (<a
href="https://redirect.github.com/nodejs/undici/issues/5097">#5097</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/3805b8f8518882991044048c256e005dc3c10a85"><code>3805b8f</code></a>
fix(socks5-proxy-agent): use per-origin pools to prevent cross-origin
routing...</li>
<li><a
href="https://github.com/nodejs/undici/commit/85a240551c9feb8b8a0ecc56c84b2b3015add8a9"><code>85a2405</code></a>
fix(cache): trim qualified field names</li>
<li>Additional commits viewable in <a
href="https://github.com/nodejs/undici/compare/v7.25.0...v7.28.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=undici&package-manager=npm_and_yarn&previous-version=7.25.0&new-version=7.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/e2b-dev/E2B/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 00:24:32 +02:00
Ben Fornefeld 02bcf83adf chore: remove Supabase (#1460) 2026-06-18 22:31:33 +02:00
Mish Ushakov f3e7f33973 refactor(sdks): tidy SDK auth and deprecate ConnectionConfig access token (#1452)
## Summary

The access token was only ever used by the CLI, never by any SDK
operation — sandbox, template, and volume calls all authenticate with
the API key. This cleans up the auth plumbing and **deprecates** (rather
than removes) the access token on `ConnectionConfig`, so there's no
breaking change for direct SDK consumers.

## Changes

- **Deprecated** the `accessToken` (JS) / `access_token` (Python) option
on `ConnectionConfig`. It still works exactly as before — when set (or
via `E2B_ACCESS_TOKEN`) the `Authorization: Bearer` header is still sent
— but `apiHeaders` is now the recommended way to pass custom auth.
- **Clear error when the API key is missing**, pointing to the API Keys
tab (`https://e2b.dev/dashboard?tab=keys`). In JS this is gated by a
`requireApiKey` option (default `true`) so callers that authenticate
differently — like the CLI hitting `/teams` with an access token — can
opt out; in Python the API key is always required.
- Removed the unused access-token toggle from the API clients:
`requireAccessToken` (JS) / `require_access_token` (Python). No caller
ever set it to a non-default value, so behavior is unchanged.
- The CLI now passes the access token to the `/teams` endpoint via
`apiHeaders` instead of the deprecated option, and opts out of the
API-key requirement on its own clients.
- Decoupled the sandbox-scoped envd access token from
`ConnectionConfig`: `EnvdApiClient` now owns its own `envdAccessToken`
field and sets the `X-Access-Token` header itself, removing a redundant
manually-set header.

## Recommended usage

```ts
// Deprecated
new ConnectionConfig({ accessToken: 'my-token' })

// Preferred
new ConnectionConfig({ apiHeaders: { Authorization: 'Bearer my-token' } })
```

```python
# Deprecated
ConnectionConfig(access_token="my-token")

# Preferred
ConnectionConfig(api_headers={"Authorization": "Bearer my-token"})
```

## Verification

`pnpm run typecheck`, `pnpm run lint`, Python `make typecheck`, and the
unit tests all pass — including new tests for the API-key requirement
(and its opt-out) in both SDKs. Confirmed the `Authorization: Bearer`
header is still sent for both the deprecated option and
`E2B_ACCESS_TOKEN`.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:20:21 +02:00
github-actions[bot] 90724836a1 [skip ci] Release new versions 2026-06-17 23:01:34 +00:00
Matt Brockman 4619f8ca11 cicd/add wait for status for public traffic network tests (#1456)
when running server in sandbox, sometimes slow to start (>3s) so need to wait for status instead
2026-06-17 15:47:34 -07:00
Matt Brockman 75e27420a2 cicd/fix test_commit_creates_commit timeout (#1455)
does a bunch of actions and times out sometimes
2026-06-17 22:13:56 +00:00