## Summary
- Both JS and Python SDKs now validate that the configured E2B API key
matches `e2b_` followed by 40 hex characters (mirroring the server-side
check in
[`infra/.../keys/key.go`](https://github.com/e2b-dev/infra/blob/main/packages/shared/pkg/keys/key.go#L66))
and throw `AuthenticationError` / `AuthenticationException` with an
example token (`e2b_0000…`) and a link to the API Keys dashboard tab.
- Validation runs inside `ApiClient` / `ApiClient.__init__` whenever an
API key is present, so callers get immediate, actionable feedback
instead of a generic 401 from the server.
- Added unit tests (`validateApiKey.test.ts`,
`test_validate_api_key.py`) and updated existing fixtures that used
placeholder keys like `'test-key'` / `'base-api-key'` to use the valid
format.
## Test plan
- [x] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck`
- [x] `pnpm exec vitest run tests/api/validateApiKey.test.ts
tests/api/handleApiError.test.ts tests/sandbox/abortSignal.test.ts
tests/template/abortSignal.test.ts
tests/sandbox/configPropagation.test.ts tests/connectionConfig.test.ts`
- [x] `poetry run pytest tests/test_validate_api_key.py
tests/test_api_client_transport.py
tests/sync/sandbox_sync/test_config_propagation.py
tests/async/sandbox_async/test_config_propagation.py
tests/test_connection_config.py`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- Sync `spec/openapi.yml` from
[e2b-dev/infra@main](https://github.com/e2b-dev/infra/blob/main/spec/openapi.yml)
and re-run `make codegen`.
- Schema changes surfaced in the generated clients:
`SandboxMetric.memCache` (new required int64 — also exposed on the
public `SandboxMetrics` wrapper in both SDKs), `NodeStatus` gains
`standby`, `TeamUser.email` becomes nullable + deprecated, and `POST
/v3/templates` gains a `403` response.
- Upstream-only spec changes (not generated because the client filters
by tag): new `AuthProviderBearerAuth`/`AuthProviderTeamAuth` security
schemes, new admin endpoints for team API keys, and a `clusterID` query
param on `GET /nodes`.
## Test plan
- [x] \`pnpm run format\`, \`pnpm run lint\`, \`pnpm run typecheck\`
- [x] JS \`tests/sandbox/metrics.test.ts\` against the live API
- [x] Python sync + async \`test_metrics.py\` against the live API
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- Fixes#1349: `Template.fromDockerfile` (JS) and
`Template.from_dockerfile` (Python) silently dropped intermediate
sources from multi-source `COPY`/`ADD`, keeping only the first one and
producing broken images without warning.
- Both parsers now emit one `copy()` call per source to the same
destination (matching Docker semantics), preserving `--chown` across all
calls.
- Added tests in both SDKs (multi-source COPY, and multi-source COPY
with `--chown`), plus changesets for `e2b` and `@e2b/python-sdk`.
## Test plan
- [x] `pnpm run test tests/template/methods/fromDockerfile.test.ts` (JS)
- [x] `poetry run pytest
tests/{async,sync}/template_*/methods/test_from_dockerfile.py` (Python)
- [x] `pnpm run format` / `pnpm run lint`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- `AsyncWatchHandle.stop()` and `AsyncCommandHandle.disconnect()`
previously only cancelled the consumer task and left the underlying
server-streaming gRPC call open — the `await self._events.aclose()` was
commented out as a Python 3.8 `RuntimeError` workaround. On long-lived
sandboxes this leaks one stream per call and eventually produces
`Code.internal: error creating watcher: too many open files`.
- The SDK now pins `python = "^3.10"`, so the workaround is removed.
`stop()`/`disconnect()` cancel the consumer task, await it, then
`aclose()` the async generator. The JS SDK already aborts the underlying
request via `AbortController`, so no JS change is needed.
## Test plan
- [ ] CI: `pnpm run format`, `pnpm run lint`, `pnpm run typecheck`
(passed locally)
- [ ] CI: `tests/async/sandbox_async/files/test_watch.py` and async
command tests still pass
- [ ] Reproduce the leak: in a long-lived async sandbox, repeatedly
create+stop a watcher and confirm fd count no longer climbs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
When `lifecycle.on_timeout` is set it wins; otherwise we fall back to
the `auto_pause` argument.
Previously the Python SDKs subscripted `lifecycle["on_timeout"]`, which
raised `KeyError` if a caller passed a `lifecycle` dict missing that key
(TypedDict is not enforced at runtime). The JS SDK silently used the
whole `lifecycle` object even when `onTimeout` was undefined. In both
cases, mixing `lifecycle` and `auto_pause` had inconsistent and
surprising behavior across the public surfaces (`create` vs
`beta_create`).
Now both SDKs use `.get`/optional chaining on `on_timeout` and only
treat `lifecycle` as authoritative when that field is actually present.
Touched files:
- `packages/python-sdk/e2b/sandbox_async/sandbox_api.py`
- `packages/python-sdk/e2b/sandbox_sync/sandbox_api.py`
- `packages/js-sdk/src/sandbox/sandboxApi.ts`
---------
Co-authored-by: Jakub Novak <jakub@e2b.dev>
## Summary
- Add optional `name` parameter to `createSnapshot` / `create_snapshot`
in the JS and Python SDKs so callers can name the resulting snapshot
template.
- Return the `names` field from the snapshot API on `SnapshotInfo` (both
in `createSnapshot` responses and in `listSnapshots` paginator results)
so callers can discover the namespaced snapshot names.
- Includes a changeset (`patch` for `e2b` and `@e2b/python-sdk`).
## Test plan
- [ ] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck` all pass
locally
- [ ] Integration tests on a sandbox with valid credentials:
`sandbox.createSnapshot({ name: 'my-snap' })` returns non-empty `names`
Resolves https://github.com/e2b-dev/E2B/issues/1249
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- Replace the sequential `for sandbox in sandboxes: await
sandbox.kill()` teardown in `async_sandbox_factory` with
`asyncio.gather(..., return_exceptions=True)` so a failure on one kill
no longer blocks cleanup of the rest, and teardown runs concurrently.
- Hoist the `_test_failed` check above the loop so the failure log is
printed once per fixture rather than per sandbox.
## Test plan
- [ ] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck`
(python-sdk) all pass
- [ ] `pnpm run test` against affected python async sandbox tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Resolves all 31 open [Dependabot
alerts](https://github.com/e2b-dev/e2b/security/dependabot) across the
workspace.
- **npm** — added range-based `pnpm.overrides` to bump vulnerable
transitive deps to their patched versions: postcss, vite, lodash,
brace-expansion, picomatch (2.x + 4.x), yaml, @tootallnate/once,
smol-toml, flatted, and minimatch (3.x/5.x/9.x/10.x).
- **python-sdk** — bumped dev deps in `poetry.lock`: pytest 7.4 → 9.0.3
(with constraint update in `pyproject.toml`), pytest-asyncio 0.23 → 1.3
(required for pytest 9), python-dotenv 1.2.2, pygments 2.20.0, requests
2.33.1, black 26.3.1; removed 4 now-unused `# ty: ignore` directives
that pytest 9's stricter type signatures made obsolete.
## Test plan
- [x] \`pnpm run typecheck\` passes
- [x] \`pnpm run lint\` passes
- [x] \`pnpm run format\` clean
- [x] CLI tests (80/80) and js-sdk/python-sdk unit tests pass;
integration tests not run locally (need \`E2B_API_KEY\`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Consolidates the fix and tests from #1285 and #1293 into a single PR.
- **js-sdk**: `uploadFile` used to pass a Node `Readable` directly to
`fetch`, causing undici to fall back to `Transfer-Encoding: chunked`. S3
presigned PUT URLs reject chunked with 501 NotImplemented. Fix buffers
the archive first so `Content-Length` is set. Includes:
- Regression test that spins up a local HTTP server and asserts
`Content-Length` is set and matches the body, and `Transfer-Encoding` is
not chunked.
- Type-fix for the CLI's typecheck (cast `Pack` →
`AsyncIterable<Buffer>`).
- Dynamic import of `node:stream/consumers` so the browser bundle
doesn't pull it in.
- **python-sdk**: Adds sync + async regression tests for `upload_file`
that guard against the same class of bug (someone swapping
`tar_buffer.getvalue()` for a stream/generator). No Python code change —
the current implementation already passes bytes to `httpx.put(...,
content=...)`.
Authorship of the original JS fix commit preserved (truffle-dev).
Closes#1243.
## Test plan
- [x] `pnpm run test tests/template/uploadFile.test.ts` — passes
- [x] `pnpm run typecheck` / `lint` clean across js-sdk and cli
- [x] `poetry run pytest tests/sync/template_sync/test_upload_file.py
tests/async/template_async/test_upload_file.py -v` — both pass
- [x] `poetry run make format` / `make lint` / `make typecheck` clean
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: truffle <truffleagent@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- Deletes install test files for apt, bun, npm, and pip in both JS and
Python SDKs
- Removes sync and async variants in Python
- Stacktrace tests for these install methods are kept
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Bumps metrics polling from 15s to 30s in Python async/sync and JS SDK
tests so the backend has enough headroom to populate metrics under load
— this test was the new #1 CI offender (5/9 Python runs and 4/10 JS runs
failed).
- Raises the async Python sandbox timeout from 20s to 60s for parity
with sync, and adds per-test timeout overrides
(`@pytest.mark.timeout(60)` / `{ timeout: 60_000 }`) so polling can
complete under the default 30s pytest/vitest cap.
- Happy path is unchanged: the loop still breaks as soon as metrics
appear.
## Test plan
- [x] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck` pass
- [x] `test_sbx_metrics` (Python async) passed locally in 8.4s
- [x] `test_sbx_metrics` (Python sync) passed locally in 15.6s
- [x] `metrics.test.ts` (JS) passed locally in 20.7s
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Fixes#1154
When creating a sandbox with an `mcp` config, the JSON-serialized config
is interpolated directly into a shell command wrapped in single quotes.
Since `json.dumps()` / `JSON.stringify()` do not escape single quotes,
any MCP config value containing a single quote (e.g., API keys, tokens,
URLs) breaks out of shell quoting and allows arbitrary command execution
inside the sandbox.
## Changes
### Python SDK (`sandbox_async/main.py`, `sandbox_sync/main.py`)
- Use `shlex.quote()` to properly escape the JSON config string (4
locations)
- `shlex.quote()` is a stdlib function designed exactly for this purpose
### JS/TS SDK (`sandbox/index.ts`)
- Add a `shellQuote()` helper that escapes single quotes using the
standard `'\'''` pattern (equivalent to Python's `shlex.quote()`)
- Apply it to both MCP config interpolation sites (2 locations)
## Before / After
**Before** (vulnerable):
```
mcp-gateway --config '{"servers": {"test": {"envs": {"KEY": "it's a value"}}}}'
# ^^ breaks out
```
**After** (safe):
```
mcp-gateway --config '{"servers": {"test": {"envs": {"KEY": "it'\''s a value"}}}}'
# ^^^^ properly escaped
```
## Testing
Verified escaping behavior for both Python (`shlex.quote`) and JS
(`shellQuote`) with the PoC from the issue — single quotes in config
values are properly escaped and no longer allow shell breakout.
---------
Co-authored-by: Mish Ushakov <10400064+mishushakov@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- fix typos in hand-written docs and comments
- rename typoed helper variables in the CLI
- fix typoed test identifiers and descriptions in the JS SDK tests
- fix typoed credential warning text in the Python SDK
## Testing
- not run
Closes#1281
## Summary
- Fixes the Python SDK `write_files` docstring (both sync and async)
which incorrectly stated that writing to a non-existing directory would
produce an error
- The backend actually auto-creates parent directories, consistent with
the `write()` docstring and existing tests
(`test_write_to_non_existing_directory`)
## Test plan
- [x] Verified behavior with a test script — both `write()` and
`write_files()` auto-create nested directories
- [x] Existing tests pass (`test_write_to_non_existing_directory`,
`writeFiles creates parent directories`)
## Summary
- Adds optional `gzip` parameter to sandbox file read/write operations
across JS and Python SDKs
- Uploads are gzip-compressed via `CompressionStream` (JS) /
`gzip.compress` (Python) when enabled, downloads request
`Accept-Encoding: gzip`
- Only applies to the octet-stream upload path (envd >= 0.5.7), so older
envd versions are unaffected
- Includes tests for both SDKs covering write+read with gzip, write gzip
+ read plain, multi-file writes, and byte format reads
## Test plan
- [ ] Run JS SDK content encoding tests (`contentEncoding.test.ts`)
- [ ] Run Python async/sync content encoding tests
(`test_content_encoding.py`)
- [ ] Integration test with envd backend supporting `Content-Encoding:
gzip`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Switches sandbox filesystem file uploads from `multipart/form-data` to
`application/octet-stream` in both the JS and Python SDKs
- Each file is now uploaded as raw binary with the path passed as a
query parameter, matching the `application/octet-stream` content type in
the envd API spec
- Multi-file writes send one request per file sequentially
## Test plan
- [ ] Run JS SDK filesystem write tests (`pnpm run test` in
`packages/js-sdk`)
- [ ] Run Python SDK filesystem write tests (`pytest` in
`packages/python-sdk`)
- [ ] Verify single file write, multi-file write, and various data types
(string, bytes, streams)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Increases the default timeout for volume `writeFile`/`write_file`
operations from 60 seconds to 1 hour in both the JS and Python SDKs.
Other volume operations retain the existing 60s default. Users can still
override via `requestTimeoutMs` (JS) or `request_timeout` (Python).
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Made `runCode()` and `run_code()` references in READMEs link to the
[code interpreting docs](https://e2b.dev/docs/code-interpreting)
- Updated root README, js-sdk README, and python-sdk README
## Test plan
- [ ] Verify links render correctly on GitHub
- [ ] Confirm docs URL resolves
## Summary
- Updated root README, js-sdk README, and python-sdk README to show base
`e2b` SDK install and usage as the default
- Code-interpreter is now shown as an optional step for when
`runCode()`/`run_code()` is actually needed
- SDK links in descriptions now point to base `e2b` packages on npm/PyPI
## Why
The base `e2b` package covers commands, files, git, networking, and
sandbox lifecycle. Users who don't need code execution shouldn't be
directed to install `@e2b/code-interpreter` / `e2b-code-interpreter` as
their first step.
## Test plan
- [ ] Verify README renders correctly on GitHub
- [ ] Confirm base SDK examples use correct import syntax
- [ ] Confirm code-interpreter section still shows correct usage for
`runCode()`
extracts the `allow_internet_access`, `lifecycle`, and `network` configs
to the get info responses from the api when present.
Create a sandbox with lifecycle and network rules, check info while
running, pause it, and check info again. Network rules, lifecycle
config, and `allowInternetAccess` all returned while running and paused
```
$ e2b sandbox info xxx --format json
# running
{
"sandboxId": "xxx",
"templateId": "xxx",
"name": "stdin",
"metadata": {},
"allowInternetAccess": true,
"envdVersion": "0.4.3",
"startedAt": "2026-03-19T01:39:56.238Z",
"endAt": "2026-03-19T01:44:56.238Z",
"state": "running",
"cpuCount": 2,
"memoryMB": 1024,
"network": {
"allowOut": ["api.example.com", "cdn.example.com"],
"denyOut": ["0.0.0.0/0"],
"allowPublicTraffic": true
},
"lifecycle": {
"onTimeout": "pause",
"autoResume": true
}
}
# paused
{
"sandboxId": "xxx",
"templateId": "xxx",
"metadata": {},
"allowInternetAccess": true,
"envdVersion": "0.4.3",
"startedAt": "2026-03-19T01:39:56.238Z",
"endAt": "2026-03-19T01:40:27.964Z",
"state": "paused",
"cpuCount": 2,
"memoryMB": 1024,
"network": {
"allowOut": ["api.example.com", "cdn.example.com"],
"denyOut": ["0.0.0.0/0"],
"allowPublicTraffic": true
},
"lifecycle": {
"onTimeout": "pause",
"autoResume": true
}
}
```
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Updates the public `sandbox info` response shape across OpenAPI, JS,
and Python SDKs, which may impact downstream consumers that assume the
previous schema. Risk is moderate since changes are additive/optional
but touch generated models and response mapping logic.
>
> **Overview**
> **Sandbox info responses now include network and lifecycle
configuration when present.** The OpenAPI spec and generated JS schema
extend `SandboxDetail` with `allowInternetAccess`, `network`, and a new
`lifecycle` object (with `SandboxOnTimeout` and `SandboxLifecycle`).
>
> The JS SDK updates `SandboxApi.getFullInfo()` and exported types to
return these fields, introducing `SandboxInfoLifecycle` for info
responses. The Python SDK updates generated client models accordingly,
adds `SandboxLifecycle`/`SandboxOnTimeout` models, and maps
`SandboxDetail.network`/`SandboxDetail.lifecycle` into `SandboxInfo`
(plus exports `SandboxInfoLifecycle`).
>
> A changeset bumps `@e2b/python-sdk` and `e2b` as minor for the
expanded info payload.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
adb22292c08b1db9c8fe60c702f83fee2695af97. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes how instance methods merge and forward connection options (api
key/domain/headers/timeouts) to API calls in both JS and Python SDKs,
which can affect request routing and auth headers. Regression tests
reduce risk but behavior changes could impact callers relying on
previous (incorrect) defaults.
>
> **Overview**
> Fixes **missing propagation of instance `connectionConfig`** when
calling sandbox instance methods (notably `pause`/`betaPause`/`connect`,
plus related methods) so default config is always forwarded and per-call
overrides still win.
>
> In the JS SDK this centralizes option merging via a new
`resolveApiOpts()` helper and updates multiple `SandboxApi.*` calls to
use it; in the Python SDK it updates `Sandbox.connect()` and
`Sandbox.pause()` (sync + async) to pass
`self.connection_config.get_api_params(**opts)`.
>
> Adds regression tests in both SDKs to assert defaults are forwarded
and overrides are applied correctly.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
60aa6ca1c386d99613976269106298267d5dbfbe. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes public error/exception types in both JS and Python SDKs by
introducing new subclasses and remapping 404/NotFound conditions, which
may affect downstream error handling despite deprecation shims.
>
> **Overview**
> **Distinguishes “sandbox not found” from “file/directory not found”
across the SDKs.** Adds `FileNotFound*` and `SandboxNotFound*`
error/exception types (with `NotFound*` marked deprecated) and updates
sandbox lifecycle APIs to throw `SandboxNotFound*` for
missing/non-running sandboxes.
>
> Refactors envd HTTP/RPC error handling in both JS and Python to
support overridable status/code→error maps, and wires filesystem
operations to map 404/`NotFound` into `FileNotFound*`. Tests are updated
accordingly, and patch changesets are added for both packages.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
73da92694c02f71355b1f8625845c82865bf3b1d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Added `fixMissing` option to `aptInstall()` in JS SDK
- Added `fix_missing` parameter to `apt_install()` in Python SDK
- Enables `--fix-missing` flag for `apt-get install` command
🤖 Generated with Claude Code
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Low risk: adds an optional flag passthrough to the generated `apt-get
install` command in both SDKs, with no behavior change unless explicitly
enabled.
>
> **Overview**
> Adds an optional `fixMissing` (JS) / `fix_missing` (Python) parameter
to template `apt` install helpers so callers can emit `apt-get install
--fix-missing` when builds hit transient package download issues.
>
> Updates the JS type definitions/docs accordingly and includes a
changeset bumping `e2b` and `@e2b/python-sdk` as a minor release.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4c0b897e192c3ec6b880ab4e3f1695f1b1289d7b. 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>
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes request timeout semantics for server-stream calls by adding
`write` and conditional `read` timeouts, which could alter behavior for
long-running/slow streams but is limited to client-side networking
configuration.
>
> **Overview**
> **Improves timeout handling for server-stream requests in the Python
SDK.** `Client._prepare_server_stream_request` now builds a richer
`httpcore` timeout extension: `request_timeout` applies to `connect`,
`pool`, and `write`, and the separate `timeout` parameter is mapped to a
`read` timeout (to help prevent hanging streams).
>
> Adds a changeset to publish a patch release documenting the updated
`request_timeout` behavior.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e66ef8e5056cfdb81892222881519e5e63f9e514. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Matt Brockman <matt.brockman@e2b.dev>
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Medium risk because it deletes a large subtree (`apps/web`) and
removes SDK-reference generation/commit steps from the package publish
workflow, which may affect downstream docs/release expectations.
>
> **Overview**
> **Removes the docs web app and generated SDK reference content.** The
PR deletes `apps/web` configs/scripts (Next.js/MDX setup, Sentry config,
prebuild/sitemap generation) and removes the committed `sdk-reference`
MDX pages.
>
> **Simplifies repo automation and ownership.** The package publish
workflow no longer generates/clones/commits SDK reference docs,
`CODEOWNERS` drops web/docs ownership entries, and the root ESLint
config removes `@stylistic/ts` in favor of the built-in `semi` rule.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4158d777b5f3d3fa30b538e434d34ce0e697d473. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->