Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6504900
Adds filtering sandboxes by state in the SDKs
mishushakov Jan 30, 2025
039b7a3
regenerated api spec
mishushakov Jan 30, 2025
33f2c07
fixes types/unset
mishushakov Jan 30, 2025
60a75a8
pass sandbox.list test for snapshots
mishushakov Feb 1, 2025
1f95e44
updated openapi spec and tests for list
mishushakov Feb 1, 2025
8c096b1
added docs on Sandbox.list
mishushakov Feb 4, 2025
a90ebc4
added changes from #565
mishushakov Feb 4, 2025
76d8a94
added a changeset
mishushakov Feb 4, 2025
5e1c6ab
updated the doc
mishushakov Feb 4, 2025
d688463
use literals for python enums
mishushakov Feb 4, 2025
6bc1c1b
added docs for removing paused sandboxes
mishushakov Feb 5, 2025
8b48134
expanded how to kill arbitrary sandboxes
mishushakov Feb 5, 2025
5f0c179
Fix generator version
jakubno Feb 6, 2025
fe1ffa7
updated openapi spec to match the latest
mishushakov Feb 6, 2025
5803a4f
updated spec, made possible to filter state by array
mishushakov Feb 10, 2025
1898417
removed unnecessary global serializer
mishushakov Feb 10, 2025
20bb345
reversed startedAt nullability
mishushakov Feb 11, 2025
a8ec896
filter sandboxes by state in the cli
mishushakov Feb 11, 2025
f58ad17
added way to filter sandboxes by metadata in the cli
mishushakov Feb 11, 2025
bef333d
updated spec
mishushakov Mar 12, 2025
a08ea6e
sandbox list pagination with asynciterator/asyncgenerator
mishushakov Mar 13, 2025
7c42078
fixes breaking change in the CLI
mishushakov Mar 13, 2025
5af429c
updated Sandbox.list DX
mishushakov Mar 13, 2025
3e60bde
updated mdx docs for Sandbox.list
mishushakov Mar 13, 2025
a6bc67e
renamed cursor to nextToken
mishushakov Mar 14, 2025
bb86d48
added tests for pagination
mishushakov Mar 26, 2025
a12d9f9
remove extra running sandboxes
mishushakov Mar 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/stupid-pens-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@e2b/python-sdk': minor
'e2b': minor
'@e2b/cli': minor
---

Adds filtering sandboxes by state in the SDKs and display of the state in the CLI
24 changes: 12 additions & 12 deletions apps/web/src/app/(docs)/docs/sandbox/connect/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@ To connect to a running sandbox, you first need to retrieve its ID. You can do t
import { Sandbox } from "@e2b/code-interpreter"

// Get all running sandboxes
const runningSandboxes = await Sandbox.list() // $HighlightLine
const { sandboxes } = await Sandbox.list({ state: ['running'] }) // $HighlightLine

if (runningSandboxes.length === 0) {
if (sandboxes.length === 0) {
throw new Error("No running sandboxes found")
}

// Get the ID of the sandbox you want to connect to
const sandboxId = runningSandboxes[0].sandboxId
const sandboxId = sandboxes[0].sandboxId
```

```python
from e2b_code_interpreter import Sandbox

# Get all running sandboxes
running_sandboxes = Sandbox.list() # $HighlightLine
running_sandboxes = Sandbox.list(state=['running']) # $HighlightLine

# Get the ID of the sandbox you want to connect to
if len(running_sandboxes) == 0:
if len(running_sandboxes.sandboxes) == 0:
raise Exception("No running sandboxes found")

# Get the ID of the sandbox you want to connect to
sandbox_id = running_sandboxes[0].sandbox_id
sandbox_id = running_sandboxes.sandboxes[0].sandbox_id
```
</CodeGroup>

Expand All @@ -46,14 +46,14 @@ Now that you have the sandbox ID, you can connect to the sandbox using the `Sand
import { Sandbox } from "@e2b/code-interpreter"

// Get all running sandboxes
const runningSandboxes = await Sandbox.list()
const { sandboxes } = await Sandbox.list({ state: ['running'] })

if (runningSandboxes.length === 0) {
if (sandboxes.length === 0) {
throw new Error("No running sandboxes found")
}

// Get the ID of the sandbox you want to connect to
const sandboxId = runningSandboxes[0].sandboxId
const sandboxId = sandboxes[0].sandboxId

// Connect to the sandbox
const sandbox = await Sandbox.connect(sandboxId) // $HighlightLine
Expand All @@ -65,13 +65,13 @@ const sandbox = await Sandbox.connect(sandboxId) // $HighlightLine
from e2b_code_interpreter import Sandbox

# Get all running sandboxes
running_sandboxes = Sandbox.list()
running_sandboxes = Sandbox.list(state=['running'])

# Get the ID of the sandbox you want to connect to
if len(running_sandboxes) == 0:
if len(running_sandboxes.sandboxes) == 0:
raise Exception("No running sandboxes found")

sandbox_id = running_sandboxes[0].sandbox_id
sandbox_id = running_sandboxes.sandboxes[0].sandbox_id

# Connect to the sandbox
sandbox = Sandbox.connect(sandbox_id) # $HighlightLine
Expand Down
15 changes: 9 additions & 6 deletions apps/web/src/app/(docs)/docs/sandbox/list/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const sandbox = await Sandbox.create({
},
})

const runningSandboxes = await Sandbox.list() // $HighlightLine
const runningSandbox = runningSandboxes[0]
const runningSandboxes = await Sandbox.list({ state: ['running'] }) // $HighlightLine
const runningSandbox = runningSandboxes.sandboxes[0]
console.log('Running sandbox metadata:', runningSandbox.metadata)
console.log('Running sandbox id:', runningSandbox.sandboxId)
console.log('Running sandbox started at:', runningSandbox.startedAt)
Expand All @@ -34,8 +34,8 @@ sandbox = Sandbox({
},
})

running_sandboxes = sandbox.list() # $HighlightLine
running_sandbox = running_sandboxes[0]
running_sandboxes = sandbox.list(state=['running']) # $HighlightLine
running_sandbox = running_sandboxes.sandboxes[0]
print('Running sandbox metadata:', running_sandbox.metadata)
print('Running sandbox id:', running_sandbox.sandbox_id)
print('Running sandbox started at:', running_sandbox.started_at)
Expand Down Expand Up @@ -86,7 +86,8 @@ const sandbox = await Sandbox.create({
})

// List running sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
const runningSandboxes = await Sandbox.list({
const { sandboxes } = await Sandbox.list({
state: ['running'],
filters: { userId: '123', env: 'dev' } // $HighlightLine
})
```
Expand All @@ -103,7 +104,9 @@ sandbox = Sandbox(
)

# List running sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
running_sandboxes = Sandbox.list(filters={
running_sandboxes = Sandbox.list(
state=['running'],
filters={
"userId": "123", "env": "dev" # $HighlightLine
})
```
Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/app/(docs)/docs/sandbox/metadata/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ const sandbox = await Sandbox.create({
})

// List running sandboxes and access metadata.
const runningSandboxes = await Sandbox.list()
const { sandboxes } = await Sandbox.list({ state: ['running'] })
// Will print:
// {
// 'userId': '123',
// }
console.log(runningSandboxes[0].metadata)
console.log(sandboxes[0].metadata)
```
```python
from e2b_code_interpreter import Sandbox
Expand All @@ -41,12 +41,12 @@ sandbox = Sandbox(
)

# List running sandboxes and access metadata.
running_sandboxes = Sandbox.list()
running_sandboxes = Sandbox.list(state=['running'])
# Will print:
# {
# 'userId': '123',
# }
print(running_sandboxes[0].metadata)
print(running_sandboxes.sandboxes[0].metadata)
```
</CodeGroup>

Expand Down
78 changes: 78 additions & 0 deletions apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx

@mlejva mlejva Feb 4, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mishushakov will deleting paused sandboxes be included in a separate PR? I'm asking because the new docs addition doesn't mention anything about deleting paused sandboxes.

Other than that, this looks good to me on the docs level

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this will be a separate PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I have decided to add it here as well to avoid merge conflicts

Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,84 @@ print('Sandbox resumed', same_sbx.sandbox_id) # $HighlightLine
```
</CodeGroup>

## 4. Listing paused sandboxes
You can list all paused sandboxes by calling the `Sandbox.list` method by supplying the `state` parameter.

<CodeGroup>
```js
import { Sandbox } from '@e2b/code-interpreter'
// or use Core: https://github.com/e2b-dev/e2b
// import { Sandbox } from 'e2b'
//
// or use Desktop: https://github.com/e2b-dev/desktop
// import { Sandbox } from '@e2b/desktop'

// List all paused sandboxes
const { sandboxes } = await Sandbox.list({ state: ['paused'] }) // $HighlightLine
console.log('Paused sandboxes', sandboxes) // $HighlightLine
```
```python
from e2b import Sandbox
# or use Core: https://github.com/e2b-dev/e2b
# from e2b import Sandbox
#
# or use Desktop: https://github.com/e2b-dev/desktop
# from e2b_desktop import Sandbox

# List all paused sandboxes
sandboxes = Sandbox.list(state=['paused']) # $HighlightLine
print('Paused sandboxes', sandboxes.sandboxes) # $HighlightLine
```
</CodeGroup>

## 5. Removing paused sandboxes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is js and python little bit different, also could you unify word id, sometimes you have it in lowercase and it other instance in uppercase

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me an example?


You can remove paused (and running!) sandboxes by calling the `kill` method on the Sandbox instance.

<CodeGroup>
```js
import { Sandbox } from '@e2b/code-interpreter'
// or use Core: https://github.com/e2b-dev/e2b
// import { Sandbox } from 'e2b'
//
// or use Desktop: https://github.com/e2b-dev/desktop
// import { Sandbox } from '@e2b/desktop'

const sbx = await Sandbox.create()
console.log('Sandbox created', sbx.sandboxId)

// Pause the sandbox
// You can save the sandbox ID in your database
// to resume the sandbox later
const sandboxId = await sbx.pause()

// Remove the sandbox
await sbx.kill() // $HighlightLine

// Remove sandbox by id
await Sandbox.kill(sandboxId) // $HighlightLine
```
```python
from e2b import Sandbox
# or use Core: https://github.com/e2b-dev/e2b
# from e2b import Sandbox
#
# or use Desktop: https://github.com/e2b-dev/desktop
# from e2b_desktop import Sandbox

sbx = Sandbox()

# Pause the sandbox
sandbox_id = sbx.pause()

# Remove the sandbox
sbx.kill() # $HighlightLine

# Remove sandbox by id
Sandbox.kill(sandbox_id) # $HighlightLine
```
</CodeGroup>

## Sandbox's timeout
When you resume a sandbox, the sandbox's timeout is reset to the default timeout of an E2B sandbox - 5 minutes.

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/code/js/basics/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ await sandbox.keepAlive(60_000)

// Later, can be even from another process
// List all running sandboxes
const runningSandboxes = await Sandbox.list()
const runningSandboxes = await Sandbox.list({ state: ['running'] })
// Find the sandbox by metadata
const found = runningSandboxes.find(s => s.metadata?.userID === 'uniqueID')
if (found) {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/code/python/basics/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

# Later, can be even from another process
# List all running sandboxes
running_sandboxes = Sandbox.list()
running_sandboxes = Sandbox.list(state=['running'])

# Find the sandbox by metadata
for running_sandbox in running_sandboxes:
for running_sandbox in running_sandboxes.sandboxes:
if running_sandbox.metadata.get("user_id", "") == 'uniqueID':
sandbox = Sandbox.reconnect(running_sandbox.sandbox_id)
break
Expand Down
18 changes: 9 additions & 9 deletions packages/cli/src/commands/sandbox/kill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as commander from 'commander'
import { ensureAPIKey } from 'src/api'
import { asBold } from 'src/utils/format'
import * as e2b from 'e2b'
import { SandboxInfo } from 'e2b'

async function killSandbox(sandboxID: string, apiKey: string) {
const killed = await e2b.Sandbox.kill(sandboxID, { apiKey })
Expand All @@ -17,7 +18,7 @@ export const killCommand = new commander.Command('kill')
.description('kill sandbox')
.argument(
'[sandboxID]',
`kill the sandbox specified by ${asBold('[sandboxID]')}`,
`kill the sandbox specified by ${asBold('[sandboxID]')}`
)
.alias('kl')
.option('-a, --all', 'kill all running sandboxes')
Expand All @@ -28,31 +29,30 @@ export const killCommand = new commander.Command('kill')
if (!sandboxID && !all) {
console.error(
`You need to specify ${asBold('[sandboxID]')} or use ${asBold(
'-a/--all',
)} flag`,
'-a/--all'
)} flag`
)
process.exit(1)
}

if (all && sandboxID) {
console.error(
`You cannot use ${asBold('-a/--all')} flag while specifying ${asBold(
'[sandboxID]',
)}`,
'[sandboxID]'
)}`
)
process.exit(1)
}

if (all) {
const sandboxes = await e2b.Sandbox.list({ apiKey })

const { sandboxes } = await e2b.Sandbox.list({ apiKey })
if (sandboxes.length === 0) {
console.log('No running sandboxes')
console.log('No sandboxes found')
process.exit(0)
}

await Promise.all(
sandboxes.map((sandbox) => killSandbox(sandbox.sandboxId, apiKey)),
sandboxes.map((sandbox) => killSandbox(sandbox.sandboxId, apiKey))
)
} else {
await killSandbox(sandboxID, apiKey)
Expand Down
35 changes: 29 additions & 6 deletions packages/cli/src/commands/sandbox/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ import { handleE2BRequestError } from '../../utils/errors'
export const listCommand = new commander.Command('list')
.description('list all running sandboxes')
.alias('ls')
.action(async () => {
.option('-s, --state <state>', 'filter by state', (value) => value.split(','))
.option('-f, --filters <filters>', 'filter by metadata', (value) =>
value.replace(/,/g, '&')
)
.action(async (options) => {
try {
const sandboxes = await listSandboxes()
const sandboxes = await listSandboxes({
state: options.state,
filters: options.filters,
})

if (!sandboxes?.length) {
console.log('No running sandboxes.')
console.log('No sandboxes found')
} else {
const table = new tablePrinter.Table({
title: 'Running sandboxes',
Expand All @@ -28,6 +35,7 @@ export const listCommand = new commander.Command('list')
{ name: 'alias', alignment: 'left', title: 'Alias' },
{ name: 'startedAt', alignment: 'left', title: 'Started at' },
{ name: 'endAt', alignment: 'left', title: 'End at' },
{ name: 'state', alignment: 'left', title: 'State' },
{ name: 'cpuCount', alignment: 'left', title: 'vCPUs' },
{ name: 'memoryMB', alignment: 'left', title: 'RAM MiB' },
{ name: 'metadata', alignment: 'left', title: 'Metadata' },
Expand All @@ -39,6 +47,8 @@ export const listCommand = new commander.Command('list')
sandboxID: `${sandbox.sandboxID}-${sandbox.clientID}`,
startedAt: new Date(sandbox.startedAt).toLocaleString(),
endAt: new Date(sandbox.endAt).toLocaleString(),
state:
sandbox.state.charAt(0).toUpperCase() + sandbox.state.slice(1), // capitalize
metadata: JSON.stringify(sandbox.metadata),
}))
.sort(
Expand Down Expand Up @@ -81,13 +91,26 @@ export const listCommand = new commander.Command('list')
}
})

export async function listSandboxes(): Promise<
e2b.components['schemas']['RunningSandbox'][]
type ListSandboxesOptions = {
state?: e2b.components['schemas']['SandboxState'][]
filters?: string
}

export async function listSandboxes({
state,
filters,
}: ListSandboxesOptions = {}): Promise<
e2b.components['schemas']['ListedSandbox'][]
> {
ensureAPIKey()

const signal = connectionConfig.getSignal()
const res = await client.api.GET('/sandboxes', { signal })
const res = await client.api.GET('/sandboxes', {
params: {
query: { state, query: filters },
},
signal,
})

handleE2BRequestError(res.error, 'Error getting running sandboxes')

Expand Down
Loading