Skip to content

Commit d4f4352

Browse files
authored
Add getInfo/get_info methods to the SDKs (#625)
- Rebased version of #517
1 parent 5d8b719 commit d4f4352

File tree

12 files changed

+298
-18
lines changed

12 files changed

+298
-18
lines changed

.changeset/khaki-shirts-switch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@e2b/python-sdk': patch
3+
'e2b': patch
4+
---
5+
6+
added getInfo methods

apps/web/src/app/(docs)/docs/sandbox/page.mdx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,37 @@ sandbox.set_timeout(30)
5656
```
5757
</CodeGroup>
5858

59+
## Retrieve sandbox information
60+
61+
You can retrieve sandbox information like sandbox id, template, metadata, started at/end at date by calling the `getInfo` method in JavaScript or `get_info` method in Python.
62+
63+
<CodeGroup>
64+
```js
65+
import { Sandbox } from '@e2b/code-interpreter'
66+
67+
// Create sandbox with and keep it running for 60 seconds.
68+
const sandbox = await Sandbox.create({ timeoutMs: 60_000 })
69+
70+
// Retrieve sandbox information.
71+
const info = await sandbox.getInfo()
72+
73+
// Retrieve sandbox expiration date.
74+
const expirationDate = info.endAt
75+
console.log(expirationDate)
76+
```
77+
78+
```python
79+
from e2b_code_interpreter import Sandbox
80+
81+
# Create sandbox with and keep it running for 60 seconds.
82+
sandbox = Sandbox(timeout=60)
83+
84+
# Retrieve sandbox expiration date.
85+
expiration_date = sandbox.get_info().end_at
86+
print(expiration_date)
87+
```
88+
</CodeGroup>
89+
5990
## Shutdown sandbox
6091

6192
You can shutdown the sandbox any time even before the timeout is up by calling the `kill` method.

packages/js-sdk/src/sandbox/index.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class Sandbox extends SandboxApi {
122122
logger: opts?.logger,
123123
},
124124
{
125-
version: opts?.envdVersion
125+
version: opts?.envdVersion,
126126
}
127127
)
128128
this.files = new Filesystem(
@@ -184,12 +184,15 @@ export class Sandbox extends SandboxApi {
184184
const config = new ConnectionConfig(sandboxOpts)
185185

186186
if (config.debug) {
187-
return new this({ sandboxId: 'debug_sandbox_id', ...config }) as InstanceType<S>
188-
} else {
189-
const sandbox = await this.createSandbox(
190-
template,
191-
sandboxOpts?.timeoutMs ?? this.defaultSandboxTimeoutMs,
192-
sandboxOpts
187+
return new this({
188+
sandboxId: 'debug_sandbox_id',
189+
...config,
190+
}) as InstanceType<S>
191+
} else {
192+
const sandbox = await this.createSandbox(
193+
template,
194+
sandboxOpts?.timeoutMs ?? this.defaultSandboxTimeoutMs,
195+
sandboxOpts
193196
)
194197
return new this({ ...sandbox, ...config }) as InstanceType<S>
195198
}
@@ -356,4 +359,18 @@ export class Sandbox extends SandboxApi {
356359

357360
return url.toString()
358361
}
362+
363+
/**
364+
* Get sandbox information like sandbox id, template, metadata, started at/end at date.
365+
*
366+
* @param opts connection options.
367+
*
368+
* @returns information about the sandbox
369+
*/
370+
async getInfo(opts?: Pick<SandboxOpts, 'requestTimeoutMs'>) {
371+
return await Sandbox.getInfo(this.sandboxId, {
372+
...this.connectionConfig,
373+
...opts,
374+
})
375+
}
359376
}

packages/js-sdk/src/sandbox/sandboxApi.ts

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export interface SandboxListOpts extends SandboxApiOpts {
1515
/**
1616
* Filter the list of sandboxes, e.g. by metadata `metadata:{"key": "value"}`, if there are multiple filters they are combined with AND.
1717
*/
18-
query?: {metadata?: Record<string, string>}
18+
query?: { metadata?: Record<string, string> }
1919
}
2020

2121
/**
@@ -46,6 +46,11 @@ export interface SandboxInfo {
4646
* Sandbox start time.
4747
*/
4848
startedAt: Date
49+
50+
/**
51+
* Sandbox expiration date.
52+
*/
53+
endAt: Date
4954
}
5055

5156
export class SandboxApi {
@@ -94,23 +99,27 @@ export class SandboxApi {
9499
*
95100
* @returns list of running sandboxes.
96101
*/
97-
static async list(
98-
opts?: SandboxListOpts): Promise<SandboxInfo[]> {
102+
static async list(opts?: SandboxListOpts): Promise<SandboxInfo[]> {
99103
const config = new ConnectionConfig(opts)
100104
const client = new ApiClient(config)
101105

102106
let metadata = undefined
103107
if (opts?.query) {
104108
if (opts.query.metadata) {
105-
const encodedPairs: Record<string, string> = Object.fromEntries(Object.entries(opts.query.metadata).map(([key, value]) => [encodeURIComponent(key), encodeURIComponent(value)]))
109+
const encodedPairs: Record<string, string> = Object.fromEntries(
110+
Object.entries(opts.query.metadata).map(([key, value]) => [
111+
encodeURIComponent(key),
112+
encodeURIComponent(value),
113+
])
114+
)
106115
metadata = new URLSearchParams(encodedPairs).toString()
107116
}
108117
}
109118

110119
const res = await client.api.GET('/sandboxes', {
111-
params: {
112-
query: {metadata},
113-
},
120+
params: {
121+
query: { metadata },
122+
},
114123
signal: config.getSignal(opts?.requestTimeoutMs),
115124
})
116125

@@ -129,10 +138,57 @@ export class SandboxApi {
129138
...(sandbox.alias && { name: sandbox.alias }),
130139
metadata: sandbox.metadata ?? {},
131140
startedAt: new Date(sandbox.startedAt),
141+
endAt: new Date(sandbox.endAt),
132142
})) ?? []
133143
)
134144
}
135145

146+
/**
147+
* Get sandbox information like sandbox id, template, metadata, started at/end at date.
148+
*
149+
* @param sandboxId sandbox ID.
150+
* @param opts connection options.
151+
*
152+
* @returns sandbox information.
153+
*/
154+
static async getInfo(
155+
sandboxId: string,
156+
opts?: SandboxApiOpts
157+
): Promise<SandboxInfo> {
158+
const config = new ConnectionConfig(opts)
159+
const client = new ApiClient(config)
160+
161+
const res = await client.api.GET('/sandboxes/{sandboxID}', {
162+
params: {
163+
path: {
164+
sandboxID: sandboxId,
165+
},
166+
},
167+
signal: config.getSignal(opts?.requestTimeoutMs),
168+
})
169+
170+
const err = handleApiError(res)
171+
if (err) {
172+
throw err
173+
}
174+
175+
if (!res.data) {
176+
throw new Error('Sandbox not found')
177+
}
178+
179+
return {
180+
sandboxId: this.getSandboxId({
181+
sandboxId: res.data.sandboxID,
182+
clientId: res.data.clientID,
183+
}),
184+
templateId: res.data.templateID,
185+
...(res.data.alias && { name: res.data.alias }),
186+
metadata: res.data.metadata ?? {},
187+
startedAt: new Date(res.data.startedAt),
188+
endAt: new Date(res.data.endAt),
189+
}
190+
}
191+
136192
/**
137193
* Set the timeout of the specified sandbox.
138194
* After the timeout expires the sandbox will be automatically killed.
@@ -219,7 +275,7 @@ export class SandboxApi {
219275
sandboxId: res.data!.sandboxID,
220276
clientId: res.data!.clientID,
221277
}),
222-
envdVersion: res.data!.envdVersion
278+
envdVersion: res.data!.envdVersion,
223279
}
224280
}
225281

packages/js-sdk/tests/sandbox/timeout.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,10 @@ sandboxTest.skipIf(isDebug)('shorten then lenghten timeout', async ({ sandbox })
1919

2020
await wait(6000)
2121

22-
await sandbox.isRunning()
22+
expect(await sandbox.isRunning()).toBeTruthy()
2323
})
24+
25+
sandboxTest.skipIf(isDebug)('get sandbox timeout', async ({ sandbox }) => {
26+
const { endAt } = await sandbox.getInfo()
27+
expect(endAt).toBeInstanceOf(Date)
28+
})

packages/python-sdk/e2b/sandbox/sandbox_api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class SandboxInfo:
1919
"""Saved sandbox metadata."""
2020
started_at: datetime
2121
"""Sandbox start time."""
22+
end_at: datetime
23+
"""Sandbox expiration date."""
2224

2325

2426
@dataclass

packages/python-sdk/e2b/sandbox_async/main.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from e2b.sandbox_async.filesystem.filesystem import Filesystem
1313
from e2b.sandbox_async.commands.command import Commands
1414
from e2b.sandbox_async.commands.pty import Pty
15-
from e2b.sandbox_async.sandbox_api import SandboxApi
15+
from e2b.sandbox_async.sandbox_api import SandboxApi, SandboxInfo
1616

1717
logger = logging.getLogger(__name__)
1818

@@ -371,3 +371,25 @@ async def set_timeout( # type: ignore
371371
timeout=timeout,
372372
**self.connection_config.__dict__,
373373
)
374+
375+
async def get_info( # type: ignore
376+
self,
377+
request_timeout: Optional[float] = None,
378+
) -> SandboxInfo:
379+
"""
380+
Get sandbox information like sandbox id, template, metadata, started at/end at date.
381+
:param request_timeout: Timeout for the request in **seconds**
382+
:return: Sandbox info
383+
"""
384+
385+
config_dict = self.connection_config.__dict__
386+
config_dict.pop("access_token", None)
387+
config_dict.pop("api_url", None)
388+
389+
if request_timeout:
390+
config_dict["request_timeout"] = request_timeout
391+
392+
return await SandboxApi.get_info(
393+
sandbox_id=self.sandbox_id,
394+
**self.connection_config.__dict__,
395+
)

packages/python-sdk/e2b/sandbox_async/sandbox_api.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from e2b.api import AsyncApiClient, SandboxCreateResponse
88
from e2b.api.client.models import NewSandbox, PostSandboxesSandboxIDTimeoutBody
99
from e2b.api.client.api.sandboxes import (
10+
get_sandboxes_sandbox_id,
1011
post_sandboxes_sandbox_id_timeout,
1112
get_sandboxes,
1213
delete_sandboxes_sandbox_id,
@@ -78,9 +79,61 @@ async def list(
7879
sandbox.metadata if isinstance(sandbox.metadata, dict) else {}
7980
),
8081
started_at=sandbox.started_at,
82+
end_at=sandbox.end_at,
8183
)
8284
for sandbox in res.parsed
8385
]
86+
87+
@classmethod
88+
async def get_info(
89+
cls,
90+
sandbox_id: str,
91+
api_key: Optional[str] = None,
92+
domain: Optional[str] = None,
93+
debug: Optional[bool] = None,
94+
request_timeout: Optional[float] = None,
95+
) -> SandboxInfo:
96+
"""
97+
Get the sandbox info.
98+
:param sandbox_id: Sandbox ID
99+
:param api_key: API key to use for authentication, defaults to `E2B_API_KEY` environment variable
100+
:param domain: Domain to use for the request, defaults to `E2B_DOMAIN` environment variable
101+
:param debug: Debug mode, defaults to `E2B_DEBUG` environment variable
102+
:param request_timeout: Timeout for the request in **seconds**
103+
:return: Sandbox info
104+
"""
105+
config = ConnectionConfig(
106+
api_key=api_key,
107+
domain=domain,
108+
debug=debug,
109+
request_timeout=request_timeout,
110+
)
111+
112+
async with AsyncApiClient(config) as api_client:
113+
res = await get_sandboxes_sandbox_id.asyncio_detailed(
114+
sandbox_id,
115+
client=api_client,
116+
)
117+
118+
if res.status_code >= 300:
119+
raise handle_api_exception(res)
120+
121+
if res.parsed is None:
122+
raise Exception("Body of the request is None")
123+
124+
return SandboxInfo(
125+
sandbox_id=SandboxApi._get_sandbox_id(
126+
res.parsed.sandbox_id,
127+
res.parsed.client_id,
128+
),
129+
template_id=res.parsed.template_id,
130+
name=res.parsed.alias if isinstance(res.parsed.alias, str) else None,
131+
metadata=(
132+
res.parsed.metadata if isinstance(res.parsed.metadata, dict) else {}
133+
),
134+
started_at=res.parsed.started_at,
135+
end_at=res.parsed.end_at,
136+
)
84137

85138
@classmethod
86139
async def _cls_kill(

packages/python-sdk/e2b/sandbox_sync/main.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from e2b.sandbox_sync.filesystem.filesystem import Filesystem
1111
from e2b.sandbox_sync.commands.command import Commands
1212
from e2b.sandbox_sync.commands.pty import Pty
13-
from e2b.sandbox_sync.sandbox_api import SandboxApi
13+
from e2b.sandbox_sync.sandbox_api import SandboxApi, SandboxInfo
1414

1515
logger = logging.getLogger(__name__)
1616

@@ -361,3 +361,24 @@ def set_timeout( # type: ignore
361361
timeout=timeout,
362362
**self.connection_config.__dict__,
363363
)
364+
365+
def get_info( # type: ignore
366+
self,
367+
request_timeout: Optional[float] = None,
368+
) -> SandboxInfo:
369+
"""
370+
Get sandbox information like sandbox id, template, metadata, started at/end at date.
371+
:param request_timeout: Timeout for the request in **seconds**
372+
:return: Sandbox info
373+
"""
374+
config_dict = self.connection_config.__dict__
375+
config_dict.pop("access_token", None)
376+
config_dict.pop("api_url", None)
377+
378+
if request_timeout:
379+
config_dict["request_timeout"] = request_timeout
380+
381+
return SandboxApi.get_info(
382+
sandbox_id=self.sandbox_id,
383+
**self.connection_config.__dict__,
384+
)

0 commit comments

Comments
 (0)