diff --git a/.changeset/stupid-pens-judge.md b/.changeset/stupid-pens-judge.md
new file mode 100644
index 000000000..f1d895ebd
--- /dev/null
+++ b/.changeset/stupid-pens-judge.md
@@ -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
diff --git a/apps/web/src/app/(docs)/docs/sandbox/connect/page.mdx b/apps/web/src/app/(docs)/docs/sandbox/connect/page.mdx
index 63d54e0f0..14526f9b3 100644
--- a/apps/web/src/app/(docs)/docs/sandbox/connect/page.mdx
+++ b/apps/web/src/app/(docs)/docs/sandbox/connect/page.mdx
@@ -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
```
@@ -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
@@ -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
diff --git a/apps/web/src/app/(docs)/docs/sandbox/list/page.mdx b/apps/web/src/app/(docs)/docs/sandbox/list/page.mdx
index 7aa236695..059a1b195 100644
--- a/apps/web/src/app/(docs)/docs/sandbox/list/page.mdx
+++ b/apps/web/src/app/(docs)/docs/sandbox/list/page.mdx
@@ -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)
@@ -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)
@@ -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
})
```
@@ -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
})
```
diff --git a/apps/web/src/app/(docs)/docs/sandbox/metadata/page.mdx b/apps/web/src/app/(docs)/docs/sandbox/metadata/page.mdx
index 3eb890e91..cecc1df51 100644
--- a/apps/web/src/app/(docs)/docs/sandbox/metadata/page.mdx
+++ b/apps/web/src/app/(docs)/docs/sandbox/metadata/page.mdx
@@ -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
@@ -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)
```
diff --git a/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx b/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx
index 345b3f11a..e93dd09e1 100644
--- a/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx
+++ b/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx
@@ -128,6 +128,84 @@ print('Sandbox resumed', same_sbx.sandbox_id) # $HighlightLine
```
+## 4. Listing paused sandboxes
+You can list all paused sandboxes by calling the `Sandbox.list` method by supplying the `state` parameter.
+
+
+```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
+```
+
+
+## 5. Removing paused sandboxes
+
+You can remove paused (and running!) sandboxes by calling the `kill` method on the Sandbox instance.
+
+
+```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
+```
+
+
## Sandbox's timeout
When you resume a sandbox, the sandbox's timeout is reset to the default timeout of an E2B sandbox - 5 minutes.
diff --git a/apps/web/src/code/js/basics/metadata.js b/apps/web/src/code/js/basics/metadata.js
index afd3befb6..9080d2bf2 100644
--- a/apps/web/src/code/js/basics/metadata.js
+++ b/apps/web/src/code/js/basics/metadata.js
@@ -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) {
diff --git a/apps/web/src/code/python/basics/metadata.py b/apps/web/src/code/python/basics/metadata.py
index e6a74f31b..e6ef929f0 100644
--- a/apps/web/src/code/python/basics/metadata.py
+++ b/apps/web/src/code/python/basics/metadata.py
@@ -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
diff --git a/packages/cli/src/commands/sandbox/kill.ts b/packages/cli/src/commands/sandbox/kill.ts
index 278365926..47f4e6355 100644
--- a/packages/cli/src/commands/sandbox/kill.ts
+++ b/packages/cli/src/commands/sandbox/kill.ts
@@ -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 })
@@ -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')
@@ -28,8 +29,8 @@ 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)
}
@@ -37,22 +38,21 @@ export const killCommand = new commander.Command('kill')
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)
diff --git a/packages/cli/src/commands/sandbox/list.ts b/packages/cli/src/commands/sandbox/list.ts
index 7f12e67e4..049bca428 100644
--- a/packages/cli/src/commands/sandbox/list.ts
+++ b/packages/cli/src/commands/sandbox/list.ts
@@ -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 ', 'filter by state', (value) => value.split(','))
+ .option('-f, --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',
@@ -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' },
@@ -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(
@@ -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')
diff --git a/packages/cli/src/commands/sandbox/logs.ts b/packages/cli/src/commands/sandbox/logs.ts
index 52bc75713..267472fc4 100644
--- a/packages/cli/src/commands/sandbox/logs.ts
+++ b/packages/cli/src/commands/sandbox/logs.ts
@@ -31,7 +31,7 @@ export function waitForSandboxEnd(sandboxID: string) {
break
}
- const response = await listSandboxes()
+ const response = await listSandboxes({ state: ['running'] })
const sandbox = response.find(
(s) => s.sandboxID === getShortID(sandboxID)
)
@@ -153,7 +153,7 @@ export const logsCommand = new commander.Command('logs')
console.log(`\nLogs for sandbox ${asBold(sandboxID)}:`)
}
- const isRunningPromise = listSandboxes()
+ const isRunningPromise = listSandboxes({ state: ['running'] })
.then((r) => r.find((s) => s.sandboxID === getShortID(sandboxID)))
.then((s) => !!s)
diff --git a/packages/cli/src/commands/sandbox/metrics.ts b/packages/cli/src/commands/sandbox/metrics.ts
index a1fbaee7b..8f46cc395 100644
--- a/packages/cli/src/commands/sandbox/metrics.ts
+++ b/packages/cli/src/commands/sandbox/metrics.ts
@@ -66,7 +66,7 @@ export const metricsCommand = new commander.Command('metrics')
console.log(`\nMetrics for sandbox ${asBold(sandboxID)}:`)
}
- const isRunningPromise = listSandboxes()
+ const isRunningPromise = listSandboxes({ state: ['running'] })
.then((r) => r.find((s) => s.sandboxID === getShortID(sandboxID)))
.then((s) => !!s)
diff --git a/packages/js-sdk/package.json b/packages/js-sdk/package.json
index d3094710c..bea84790a 100644
--- a/packages/js-sdk/package.json
+++ b/packages/js-sdk/package.json
@@ -28,7 +28,7 @@
"dev": "tsup --watch",
"example": "tsx example.mts",
"test": "vitest run",
- "generate": "python ./../../spec/remove_extra_tags.py sandboxes templates && openapi-typescript ../../spec/openapi_generated.yml -x api_key --support-array-length --alphabetize --output src/api/schema.gen.ts",
+ "generate": "python ./../../spec/remove_extra_tags.py sandboxes templates && openapi-typescript ../../spec/openapi_generated.yml -x api_key --array-length --alphabetize --output src/api/schema.gen.ts",
"generate-envd-api": "openapi-typescript ../../spec/envd/envd.yaml -x api_key --support-array-length --alphabetize --output src/envd/schema.gen.ts",
"generate-ref": "./scripts/generate_sdk_ref.sh",
"check-deps": "knip",
@@ -50,7 +50,7 @@
"dotenv": "^16.4.5",
"knip": "^5.17.3",
"npm-check-updates": "^16.14.20",
- "openapi-typescript": "^6.7.6",
+ "openapi-typescript": "^7.6.1",
"playwright": "^1.48.0",
"react": "^18.3.1",
"tsup": "^8.0.2",
diff --git a/packages/js-sdk/src/api/index.ts b/packages/js-sdk/src/api/index.ts
index 2d31fd108..1a5af427e 100644
--- a/packages/js-sdk/src/api/index.ts
+++ b/packages/js-sdk/src/api/index.ts
@@ -59,6 +59,12 @@ class ApiClient {
Authorization: `Bearer ${config.accessToken}`,
}),
},
+ querySerializer: {
+ array: {
+ style: 'form',
+ explode: false,
+ },
+ },
})
if (config.logger) {
diff --git a/packages/js-sdk/src/api/schema.gen.ts b/packages/js-sdk/src/api/schema.gen.ts
index e7d120e6e..d5fc6336e 100644
--- a/packages/js-sdk/src/api/schema.gen.ts
+++ b/packages/js-sdk/src/api/schema.gen.ts
@@ -3,684 +3,1062 @@
* Do not make direct changes to the file.
*/
-
export interface paths {
- "/sandboxes": {
- /** @description List all running sandboxes */
- get: {
- parameters: {
- query?: {
- /** @description A query used to filter the sandboxes (e.g. "user=abc&app=prod"). Query and each key and values must be URL encoded. */
- query?: string;
- };
- };
- responses: {
- /** @description Successfully returned all running sandboxes */
- 200: {
- content: {
- "application/json": components["schemas"]["RunningSandbox"][];
- };
- };
- 400: components["responses"]["400"];
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
- };
- /** @description Create a sandbox from the template */
- post: {
- requestBody: {
- content: {
- "application/json": components["schemas"]["NewSandbox"];
- };
- };
- responses: {
- /** @description The sandbox was created successfully */
- 201: {
- content: {
- "application/json": components["schemas"]["Sandbox"];
- };
- };
- 400: components["responses"]["400"];
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
- };
- };
- "/sandboxes/{sandboxID}": {
- /** @description Get a sandbox by id */
- get: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- responses: {
- /** @description Successfully returned the sandbox */
- 200: {
- content: {
- "application/json": components["schemas"]["RunningSandbox"];
- };
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 500: components["responses"]["500"];
- };
- };
- /** @description Kill a sandbox */
- delete: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- responses: {
- /** @description The sandbox was killed successfully */
- 204: {
- content: never;
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 500: components["responses"]["500"];
- };
- };
- };
- "/sandboxes/{sandboxID}/logs": {
- /** @description Get sandbox logs */
- get: {
- parameters: {
- query?: {
- /** @description Starting timestamp of the logs that should be returned in milliseconds */
- start?: number;
- /** @description Maximum number of logs that should be returned */
- limit?: number;
- };
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- responses: {
- /** @description Successfully returned the sandbox logs */
- 200: {
- content: {
- "application/json": components["schemas"]["SandboxLogs"];
- };
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description List all sandboxes */
+ get: {
+ parameters: {
+ query?: {
+ /** @description Maximum number of items to return per page */
+ limit?: number;
+ /** @description Cursor to start the list from */
+ nextToken?: string;
+ /** @description A query used to filter the sandboxes (e.g. "user=abc&app=prod"). Query and each key and values must be URL encoded. */
+ query?: string;
+ /** @description Filter sandboxes by one or more states */
+ state?: components["schemas"]["SandboxState"][];
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned all running sandboxes */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ListedSandbox"][];
+ };
+ };
+ 400: components["responses"]["400"];
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ /** @description Create a sandbox from the template */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["NewSandbox"];
+ };
+ };
+ responses: {
+ /** @description The sandbox was created successfully */
+ 201: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Sandbox"];
+ };
+ };
+ 400: components["responses"]["400"];
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/sandboxes/{sandboxID}/metrics": {
- /** @description Get sandbox metrics */
- get: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- responses: {
- /** @description Successfully returned the sandbox metrics */
- 200: {
- content: {
- "application/json": components["schemas"]["SandboxMetric"][];
- };
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/{sandboxID}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description Get a sandbox by id */
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned the sandbox */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ListedSandbox"];
+ };
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ post?: never;
+ /** @description Kill a sandbox */
+ delete: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description The sandbox was killed successfully */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 500: components["responses"]["500"];
+ };
+ };
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/sandboxes/{sandboxID}/pause": {
- /** @description Pause the sandbox */
- post: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- responses: {
- /** @description The sandbox was paused successfully and can be resumed */
- 204: {
- content: never;
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 409: components["responses"]["409"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/{sandboxID}/logs": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description Get sandbox logs */
+ get: {
+ parameters: {
+ query?: {
+ /** @description Maximum number of logs that should be returned */
+ limit?: number;
+ /** @description Starting timestamp of the logs that should be returned in milliseconds */
+ start?: number;
+ };
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned the sandbox logs */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SandboxLogs"];
+ };
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/sandboxes/{sandboxID}/refreshes": {
- /** @description Refresh the sandbox extending its time to live */
- post: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- requestBody?: {
- content: {
- "application/json": {
- /** @description Duration for which the sandbox should be kept alive in seconds */
- duration?: number;
- };
- };
- };
- responses: {
- /** @description Successfully refreshed the sandbox */
- 204: {
- content: never;
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- };
+ "/sandboxes/{sandboxID}/metrics": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description Get sandbox metrics */
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned the sandbox metrics */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SandboxMetric"][];
+ };
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/sandboxes/{sandboxID}/resume": {
- /** @description Resume the sandbox */
- post: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- requestBody: {
- content: {
- "application/json": components["schemas"]["ResumedSandbox"];
- };
- };
- responses: {
- /** @description The sandbox was resumed successfully */
- 201: {
- content: {
- "application/json": components["schemas"]["Sandbox"];
- };
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 409: components["responses"]["409"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/{sandboxID}/pause": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** @description Pause the sandbox */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description The sandbox was paused successfully and can be resumed */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 409: components["responses"]["409"];
+ 500: components["responses"]["500"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/sandboxes/{sandboxID}/timeout": {
- /** @description Set the timeout for the sandbox. The sandbox will expire x seconds from the time of the request. Calling this method multiple times overwrites the TTL, each time using the current timestamp as the starting point to measure the timeout duration. */
- post: {
- parameters: {
- path: {
- sandboxID: components["parameters"]["sandboxID"];
- };
- };
- requestBody?: {
- content: {
- "application/json": {
- /**
- * Format: int32
- * @description Timeout in seconds from the current time after which the sandbox should expire
- */
- timeout: number;
- };
- };
- };
- responses: {
- /** @description Successfully set the sandbox timeout */
- 204: {
- content: never;
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/{sandboxID}/refreshes": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** @description Refresh the sandbox extending its time to live */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: {
+ content: {
+ "application/json": {
+ /** @description Duration for which the sandbox should be kept alive in seconds */
+ duration?: number;
+ };
+ };
+ };
+ responses: {
+ /** @description Successfully refreshed the sandbox */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/templates": {
- /** @description List all templates */
- get: {
- parameters: {
- query?: {
- teamID?: string;
- };
- };
- responses: {
- /** @description Successfully returned all templates */
- 200: {
- content: {
- "application/json": components["schemas"]["Template"][];
- };
- };
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/{sandboxID}/resume": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** @description Resume the sandbox */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ResumedSandbox"];
+ };
+ };
+ responses: {
+ /** @description The sandbox was resumed successfully */
+ 201: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Sandbox"];
+ };
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 409: components["responses"]["409"];
+ 500: components["responses"]["500"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- /** @description Create a new template */
- post: {
- requestBody: {
- content: {
- "application/json": components["schemas"]["TemplateBuildRequest"];
- };
- };
- responses: {
- /** @description The build was accepted */
- 202: {
- content: {
- "application/json": components["schemas"]["Template"];
- };
- };
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/{sandboxID}/timeout": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** @description Set the timeout for the sandbox. The sandbox will expire x seconds from the time of the request. Calling this method multiple times overwrites the TTL, each time using the current timestamp as the starting point to measure the timeout duration. */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ sandboxID: components["parameters"]["sandboxID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: {
+ content: {
+ "application/json": {
+ /**
+ * Format: int32
+ * @description Timeout in seconds from the current time after which the sandbox should expire
+ */
+ timeout: number;
+ };
+ };
+ };
+ responses: {
+ /** @description Successfully set the sandbox timeout */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 500: components["responses"]["500"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/templates/{templateID}": {
- /** @description Rebuild an template */
- post: {
- parameters: {
- path: {
- templateID: components["parameters"]["templateID"];
- };
- };
- requestBody: {
- content: {
- "application/json": components["schemas"]["TemplateBuildRequest"];
- };
- };
- responses: {
- /** @description The build was accepted */
- 202: {
- content: {
- "application/json": components["schemas"]["Template"];
- };
- };
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
+ "/sandboxes/metrics": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description List all running sandboxes with metrics */
+ get: {
+ parameters: {
+ query?: {
+ /** @description A query used to filter the sandboxes (e.g. "user=abc&app=prod"). Query and each key and values must be URL encoded. */
+ query?: string;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned all running sandboxes with metrics */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["RunningSandboxWithMetrics"][];
+ };
+ };
+ 400: components["responses"]["400"];
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- /** @description Delete a template */
- delete: {
- parameters: {
- path: {
- templateID: components["parameters"]["templateID"];
- };
- };
- responses: {
- /** @description The template was deleted successfully */
- 204: {
- content: never;
- };
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
+ "/templates": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description List all templates */
+ get: {
+ parameters: {
+ query?: {
+ teamID?: string;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned all templates */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Template"][];
+ };
+ };
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ /** @description Create a new template */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["TemplateBuildRequest"];
+ };
+ };
+ responses: {
+ /** @description The build was accepted */
+ 202: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Template"];
+ };
+ };
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- /** @description Update template */
- patch: {
- parameters: {
- path: {
- templateID: components["parameters"]["templateID"];
- };
- };
- requestBody: {
- content: {
- "application/json": components["schemas"]["TemplateUpdateRequest"];
- };
- };
- responses: {
- /** @description The template was updated successfully */
- 200: {
- content: never;
- };
- 400: components["responses"]["400"];
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
+ "/templates/{templateID}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** @description Rebuild an template */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ templateID: components["parameters"]["templateID"];
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["TemplateBuildRequest"];
+ };
+ };
+ responses: {
+ /** @description The build was accepted */
+ 202: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Template"];
+ };
+ };
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ /** @description Delete a template */
+ delete: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ templateID: components["parameters"]["templateID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description The template was deleted successfully */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ options?: never;
+ head?: never;
+ /** @description Update template */
+ patch: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ templateID: components["parameters"]["templateID"];
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["TemplateUpdateRequest"];
+ };
+ };
+ responses: {
+ /** @description The template was updated successfully */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 400: components["responses"]["400"];
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ trace?: never;
};
- };
- "/templates/{templateID}/builds/{buildID}": {
- /** @description Start the build */
- post: {
- parameters: {
- path: {
- templateID: components["parameters"]["templateID"];
- buildID: components["parameters"]["buildID"];
- };
- };
- responses: {
- /** @description The build has started */
- 202: {
- content: never;
- };
- 401: components["responses"]["401"];
- 500: components["responses"]["500"];
- };
+ "/templates/{templateID}/builds/{buildID}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** @description Start the build */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ buildID: components["parameters"]["buildID"];
+ templateID: components["parameters"]["templateID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description The build has started */
+ 202: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 401: components["responses"]["401"];
+ 500: components["responses"]["500"];
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
- "/templates/{templateID}/builds/{buildID}/status": {
- /** @description Get template build info */
- get: {
- parameters: {
- query?: {
- /** @description Index of the starting build log that should be returned with the template */
- logsOffset?: number;
- };
- path: {
- templateID: components["parameters"]["templateID"];
- buildID: components["parameters"]["buildID"];
- };
- };
- responses: {
- /** @description Successfully returned the template */
- 200: {
- content: {
- "application/json": components["schemas"]["TemplateBuild"];
- };
- };
- 401: components["responses"]["401"];
- 404: components["responses"]["404"];
- 500: components["responses"]["500"];
- };
+ "/templates/{templateID}/builds/{buildID}/status": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description Get template build info */
+ get: {
+ parameters: {
+ query?: {
+ /** @description Index of the starting build log that should be returned with the template */
+ logsOffset?: number;
+ };
+ header?: never;
+ path: {
+ buildID: components["parameters"]["buildID"];
+ templateID: components["parameters"]["templateID"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully returned the template */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["TemplateBuild"];
+ };
+ };
+ 401: components["responses"]["401"];
+ 404: components["responses"]["404"];
+ 500: components["responses"]["500"];
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
};
- };
}
-
export type webhooks = Record;
-
export interface components {
- schemas: {
- /**
- * Format: int32
- * @description CPU cores for the sandbox
- */
- CPUCount: number;
- EnvVars: {
- [key: string]: string;
- };
- Error: {
- /**
- * Format: int32
- * @description Error code
- */
- code: number;
- /** @description Error */
- message: string;
- };
- /**
- * Format: int32
- * @description Memory for the sandbox in MB
- */
- MemoryMB: number;
- NewSandbox: {
- envVars?: components["schemas"]["EnvVars"];
- metadata?: components["schemas"]["SandboxMetadata"];
- /** @description Identifier of the required template */
- templateID: string;
- /**
- * Format: int32
- * @description Time to live for the sandbox in seconds.
- * @default 15
- */
- timeout?: number;
- };
- Node: {
- /**
- * Format: int32
- * @description Number of allocated CPU cores
- */
- allocatedCPU: number;
- /**
- * Format: int32
- * @description Amount of allocated memory in MiB
- */
- allocatedMemoryMiB: number;
- /** @description Identifier of the node */
- nodeID: string;
- /**
- * Format: int32
- * @description Number of sandboxes running on the node
- */
- sandboxCount: number;
- status: components["schemas"]["NodeStatus"];
- };
- NodeDetail: {
- /** @description List of cached builds id on the node */
- cachedBuilds: string[];
- /** @description Identifier of the node */
- nodeID: string;
- /** @description List of sandboxes running on the node */
- sandboxes: components["schemas"]["RunningSandbox"][];
- status: components["schemas"]["NodeStatus"];
- };
- /**
- * @description Status of the node
- * @enum {string}
- */
- NodeStatus: "ready" | "draining";
- NodeStatusChange: {
- status: components["schemas"]["NodeStatus"];
- };
- ResumedSandbox: {
- /**
- * Format: int32
- * @description Time to live for the sandbox in seconds.
- * @default 15
- */
- timeout?: number;
- };
- RunningSandbox: {
- /** @description Alias of the template */
- alias?: string;
- /** @description Identifier of the client */
- clientID: string;
- cpuCount: components["schemas"]["CPUCount"];
- /**
- * Format: date-time
- * @description Time when the sandbox will expire
- */
- endAt: string;
- memoryMB: components["schemas"]["MemoryMB"];
- metadata?: components["schemas"]["SandboxMetadata"];
- /** @description Identifier of the sandbox */
- sandboxID: string;
- /**
- * Format: date-time
- * @description Time when the sandbox was started
- */
- startedAt: string;
- /** @description Identifier of the template from which is the sandbox created */
- templateID: string;
- };
- Sandbox: {
- /** @description Alias of the template */
- alias?: string;
- /** @description Identifier of the client */
- clientID: string;
- /** @description Version of the envd running in the sandbox */
- envdVersion: string;
- /** @description Identifier of the sandbox */
- sandboxID: string;
- /** @description Identifier of the template from which is the sandbox created */
- templateID: string;
- };
- /** @description Log entry with timestamp and line */
- SandboxLog: {
- /** @description Log line content */
- line: string;
- /**
- * Format: date-time
- * @description Timestamp of the log entry
- */
- timestamp: string;
- };
- SandboxLogs: {
- /** @description Logs of the sandbox */
- logs: components["schemas"]["SandboxLog"][];
- };
- SandboxMetadata: {
- [key: string]: string;
- };
- /** @description Metric entry with timestamp and line */
- SandboxMetric: {
- /**
- * Format: int32
- * @description Number of CPU cores
- */
- cpuCount: number;
- /**
- * Format: float
- * @description CPU usage percentage
- */
- cpuUsedPct: number;
- /**
- * Format: int64
- * @description Total memory in MiB
- */
- memTotalMiB: number;
- /**
- * Format: int64
- * @description Memory used in MiB
- */
- memUsedMiB: number;
- /**
- * Format: date-time
- * @description Timestamp of the metric entry
- */
- timestamp: string;
- };
- Team: {
- /** @description API key for the team */
- apiKey: string;
- /** @description Whether the team is the default team */
- isDefault: boolean;
- /** @description Name of the team */
- name: string;
- /** @description Identifier of the team */
- teamID: string;
- };
- TeamUser: {
- /** @description Email of the user */
- email: string;
- /**
- * Format: uuid
- * @description Identifier of the user
- */
- id: string;
- };
- Template: {
- /** @description Aliases of the template */
- aliases?: string[];
- /**
- * Format: int32
- * @description Number of times the template was built
- */
- buildCount: number;
- /** @description Identifier of the last successful build for given template */
- buildID: string;
- cpuCount: components["schemas"]["CPUCount"];
- /**
- * Format: date-time
- * @description Time when the template was created
- */
- createdAt: string;
- createdBy: components["schemas"]["TeamUser"] | null;
- /**
- * Format: date-time
- * @description Time when the template was last used
- */
- lastSpawnedAt: string;
- memoryMB: components["schemas"]["MemoryMB"];
- /** @description Whether the template is public or only accessible by the team */
- public: boolean;
- /**
- * Format: int64
- * @description Number of times the template was used
- */
- spawnCount: number;
- /** @description Identifier of the template */
- templateID: string;
- /**
- * Format: date-time
- * @description Time when the template was last updated
- */
- updatedAt: string;
- };
- TemplateBuild: {
- /** @description Identifier of the build */
- buildID: string;
- /**
- * @description Build logs
- * @default []
- */
- logs: string[];
- /**
- * @description Status of the template
- * @enum {string}
- */
- status: "building" | "ready" | "error";
- /** @description Identifier of the template */
- templateID: string;
- };
- TemplateBuildRequest: {
- /** @description Alias of the template */
- alias?: string;
- cpuCount?: components["schemas"]["CPUCount"];
- /** @description Dockerfile for the template */
- dockerfile: string;
- memoryMB?: components["schemas"]["MemoryMB"];
- /** @description Start command to execute in the template after the build */
- startCmd?: string;
- /** @description Identifier of the team */
- teamID?: string;
- };
- TemplateUpdateRequest: {
- /** @description Whether the template is public or only accessible by the team */
- public?: boolean;
- };
- };
- responses: {
- /** @description Bad request */
- 400: {
- content: {
- "application/json": components["schemas"]["Error"];
- };
- };
- /** @description Authentication error */
- 401: {
- content: {
- "application/json": components["schemas"]["Error"];
- };
- };
- /** @description Not found */
- 404: {
- content: {
- "application/json": components["schemas"]["Error"];
- };
+ schemas: {
+ /**
+ * Format: int32
+ * @description CPU cores for the sandbox
+ */
+ CPUCount: number;
+ EnvVars: {
+ [key: string]: string;
+ };
+ Error: {
+ /**
+ * Format: int32
+ * @description Error code
+ */
+ code: number;
+ /** @description Error */
+ message: string;
+ };
+ ListedSandbox: {
+ /** @description Alias of the template */
+ alias?: string;
+ /** @description Identifier of the client */
+ clientID: string;
+ cpuCount: components["schemas"]["CPUCount"];
+ /**
+ * Format: date-time
+ * @description Time when the sandbox will expire
+ */
+ endAt: string;
+ memoryMB: components["schemas"]["MemoryMB"];
+ metadata?: components["schemas"]["SandboxMetadata"];
+ /** @description Identifier of the sandbox */
+ sandboxID: string;
+ /**
+ * Format: date-time
+ * @description Time when the sandbox was started
+ */
+ startedAt: string;
+ state: components["schemas"]["SandboxState"];
+ /** @description Identifier of the template from which is the sandbox created */
+ templateID: string;
+ };
+ /**
+ * Format: int32
+ * @description Memory for the sandbox in MB
+ */
+ MemoryMB: number;
+ NewSandbox: {
+ /**
+ * @description Automatically pauses the sandbox after the timeout
+ * @default false
+ */
+ autoPause: boolean;
+ envVars?: components["schemas"]["EnvVars"];
+ metadata?: components["schemas"]["SandboxMetadata"];
+ /** @description Identifier of the required template */
+ templateID: string;
+ /**
+ * Format: int32
+ * @description Time to live for the sandbox in seconds.
+ * @default 15
+ */
+ timeout: number;
+ };
+ Node: {
+ /**
+ * Format: int32
+ * @description Number of allocated CPU cores
+ */
+ allocatedCPU: number;
+ /**
+ * Format: int32
+ * @description Amount of allocated memory in MiB
+ */
+ allocatedMemoryMiB: number;
+ /**
+ * Format: uint64
+ * @description Number of sandbox create fails
+ */
+ createFails: number;
+ /** @description Identifier of the node */
+ nodeID: string;
+ /**
+ * Format: int32
+ * @description Number of sandboxes running on the node
+ */
+ sandboxCount: number;
+ /**
+ * Format: int
+ * @description Number of starting Sandboxes
+ */
+ sandboxStartingCount: number;
+ status: components["schemas"]["NodeStatus"];
+ };
+ NodeDetail: {
+ /** @description List of cached builds id on the node */
+ cachedBuilds: string[];
+ /**
+ * Format: uint64
+ * @description Number of sandbox create fails
+ */
+ createFails: number;
+ /** @description Identifier of the node */
+ nodeID: string;
+ /** @description List of sandboxes running on the node */
+ sandboxes: components["schemas"]["ListedSandbox"][];
+ status: components["schemas"]["NodeStatus"];
+ };
+ /**
+ * @description Status of the node
+ * @enum {string}
+ */
+ NodeStatus: "ready" | "draining" | "connecting" | "unhealthy";
+ NodeStatusChange: {
+ status: components["schemas"]["NodeStatus"];
+ };
+ ResumedSandbox: {
+ /**
+ * @description Automatically pauses the sandbox after the timeout
+ * @default false
+ */
+ autoPause: boolean;
+ /**
+ * Format: int32
+ * @description Time to live for the sandbox in seconds.
+ * @default 15
+ */
+ timeout: number;
+ };
+ RunningSandboxWithMetrics: {
+ /** @description Alias of the template */
+ alias?: string;
+ /** @description Identifier of the client */
+ clientID: string;
+ cpuCount: components["schemas"]["CPUCount"];
+ /**
+ * Format: date-time
+ * @description Time when the sandbox will expire
+ */
+ endAt: string;
+ memoryMB: components["schemas"]["MemoryMB"];
+ metadata?: components["schemas"]["SandboxMetadata"];
+ metrics?: components["schemas"]["SandboxMetric"][];
+ /** @description Identifier of the sandbox */
+ sandboxID: string;
+ /**
+ * Format: date-time
+ * @description Time when the sandbox was started
+ */
+ startedAt: string;
+ /** @description Identifier of the template from which is the sandbox created */
+ templateID: string;
+ };
+ Sandbox: {
+ /** @description Alias of the template */
+ alias?: string;
+ /** @description Identifier of the client */
+ clientID: string;
+ /** @description Version of the envd running in the sandbox */
+ envdVersion: string;
+ /** @description Identifier of the sandbox */
+ sandboxID: string;
+ /** @description Identifier of the template from which is the sandbox created */
+ templateID: string;
+ };
+ /** @description Log entry with timestamp and line */
+ SandboxLog: {
+ /** @description Log line content */
+ line: string;
+ /**
+ * Format: date-time
+ * @description Timestamp of the log entry
+ */
+ timestamp: string;
+ };
+ SandboxLogs: {
+ /** @description Logs of the sandbox */
+ logs: components["schemas"]["SandboxLog"][];
+ };
+ SandboxMetadata: {
+ [key: string]: string;
+ };
+ /** @description Metric entry with timestamp and line */
+ SandboxMetric: {
+ /**
+ * Format: int32
+ * @description Number of CPU cores
+ */
+ cpuCount: number;
+ /**
+ * Format: float
+ * @description CPU usage percentage
+ */
+ cpuUsedPct: number;
+ /**
+ * Format: int64
+ * @description Total memory in MiB
+ */
+ memTotalMiB: number;
+ /**
+ * Format: int64
+ * @description Memory used in MiB
+ */
+ memUsedMiB: number;
+ /**
+ * Format: date-time
+ * @description Timestamp of the metric entry
+ */
+ timestamp: string;
+ };
+ /**
+ * @description State of the sandbox
+ * @enum {string}
+ */
+ SandboxState: "running" | "paused";
+ Team: {
+ /** @description API key for the team */
+ apiKey: string;
+ /** @description Whether the team is the default team */
+ isDefault: boolean;
+ /** @description Name of the team */
+ name: string;
+ /** @description Identifier of the team */
+ teamID: string;
+ };
+ TeamUser: {
+ /** @description Email of the user */
+ email: string;
+ /**
+ * Format: uuid
+ * @description Identifier of the user
+ */
+ id: string;
+ };
+ Template: {
+ /** @description Aliases of the template */
+ aliases?: string[];
+ /**
+ * Format: int32
+ * @description Number of times the template was built
+ */
+ buildCount: number;
+ /** @description Identifier of the last successful build for given template */
+ buildID: string;
+ cpuCount: components["schemas"]["CPUCount"];
+ /**
+ * Format: date-time
+ * @description Time when the template was created
+ */
+ createdAt: string;
+ createdBy: components["schemas"]["TeamUser"] | null;
+ /**
+ * Format: date-time
+ * @description Time when the template was last used
+ */
+ lastSpawnedAt: string;
+ memoryMB: components["schemas"]["MemoryMB"];
+ /** @description Whether the template is public or only accessible by the team */
+ public: boolean;
+ /**
+ * Format: int64
+ * @description Number of times the template was used
+ */
+ spawnCount: number;
+ /** @description Identifier of the template */
+ templateID: string;
+ /**
+ * Format: date-time
+ * @description Time when the template was last updated
+ */
+ updatedAt: string;
+ };
+ TemplateBuild: {
+ /** @description Identifier of the build */
+ buildID: string;
+ /**
+ * @description Build logs
+ * @default []
+ */
+ logs: string[];
+ /**
+ * @description Status of the template
+ * @enum {string}
+ */
+ status: "building" | "ready" | "error";
+ /** @description Identifier of the template */
+ templateID: string;
+ };
+ TemplateBuildRequest: {
+ /** @description Alias of the template */
+ alias?: string;
+ cpuCount?: components["schemas"]["CPUCount"];
+ /** @description Dockerfile for the template */
+ dockerfile: string;
+ memoryMB?: components["schemas"]["MemoryMB"];
+ /** @description Start command to execute in the template after the build */
+ startCmd?: string;
+ /** @description Identifier of the team */
+ teamID?: string;
+ };
+ TemplateUpdateRequest: {
+ /** @description Whether the template is public or only accessible by the team */
+ public?: boolean;
+ };
};
- /** @description Conflict */
- 409: {
- content: {
- "application/json": components["schemas"]["Error"];
- };
+ responses: {
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Not found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Conflict */
+ 409: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Server error */
+ 500: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
};
- /** @description Server error */
- 500: {
- content: {
- "application/json": components["schemas"]["Error"];
- };
+ parameters: {
+ buildID: string;
+ nodeID: string;
+ sandboxID: string;
+ templateID: string;
};
- };
- parameters: {
- buildID: string;
- nodeID: string;
- sandboxID: string;
- templateID: string;
- };
- requestBodies: never;
- headers: never;
- pathItems: never;
+ requestBodies: never;
+ headers: never;
+ pathItems: never;
}
-
export type $defs = Record;
-
-export type external = Record;
-
export type operations = Record;
diff --git a/packages/js-sdk/src/sandbox/sandboxApi.ts b/packages/js-sdk/src/sandbox/sandboxApi.ts
index 0a8aadf9c..aeadb3db1 100644
--- a/packages/js-sdk/src/sandbox/sandboxApi.ts
+++ b/packages/js-sdk/src/sandbox/sandboxApi.ts
@@ -16,6 +16,21 @@ export interface SandboxListOpts extends SandboxApiOpts {
* Filter the list of sandboxes by metadata, e.g. `{"key": "value"}`, if there are multiple filters they are combined with AND.
*/
filters?: Record
+
+ /**
+ * Filter the list of sandboxes by state.
+ */
+ state?: Array<'running' | 'paused'> | undefined
+
+ /**
+ * Number of sandboxes to return.
+ */
+ limit?: number
+
+ /**
+ * Token to the next page.
+ */
+ nextToken?: string
}
/**
@@ -46,6 +61,11 @@ export interface SandboxInfo {
* Sandbox start time.
*/
startedAt: Date
+
+ /**
+ * Sandbox state.
+ */
+ state: 'running' | 'paused'
}
export class SandboxApi {
@@ -94,22 +114,37 @@ export class SandboxApi {
*
* @returns list of running sandboxes.
*/
- static async list(
- opts?: SandboxListOpts): Promise {
- const config = new ConnectionConfig(opts)
+ static async list(opts: SandboxListOpts = {}): Promise<{
+ sandboxes: SandboxInfo[],
+ hasMoreItems: boolean,
+ nextToken: string | undefined,
+ iterator: AsyncGenerator
+ }> {
+ const { filters, state, limit, nextToken, requestTimeoutMs } = opts
+ const config = new ConnectionConfig({ requestTimeoutMs })
const client = new ApiClient(config)
let query = undefined
- if (opts?.filters) {
- const encodedPairs: Record = Object.fromEntries(Object.entries(opts.filters).map(([key, value]) => [encodeURIComponent(key),encodeURIComponent(value)]))
+ if (filters) {
+ const encodedPairs: Record = Object.fromEntries(
+ Object.entries(filters).map(([key, value]) => [
+ encodeURIComponent(key),
+ encodeURIComponent(value),
+ ])
+ )
query = new URLSearchParams(encodedPairs).toString()
}
const res = await client.api.GET('/sandboxes', {
- params: {
- query: {query},
+ params: {
+ query: {
+ query,
+ state,
+ limit,
+ nextToken,
},
- signal: config.getSignal(opts?.requestTimeoutMs),
+ },
+ signal: config.getSignal(requestTimeoutMs),
})
const err = handleApiError(res)
@@ -117,18 +152,46 @@ export class SandboxApi {
throw err
}
- return (
- res.data?.map((sandbox: components['schemas']['RunningSandbox']) => ({
- sandboxId: this.getSandboxId({
- sandboxId: sandbox.sandboxID,
- clientId: sandbox.clientID,
- }),
- templateId: sandbox.templateID,
- ...(sandbox.alias && { name: sandbox.alias }),
- metadata: sandbox.metadata ?? {},
- startedAt: new Date(sandbox.startedAt),
- })) ?? []
- )
+ const nextPageToken = res.response.headers.get('x-next-token') || undefined
+ const hasMoreItems = !!nextPageToken
+
+ const sandboxes = (res.data ?? []).map(sandbox => ({
+ sandboxId: this.getSandboxId({
+ sandboxId: sandbox.sandboxID,
+ clientId: sandbox.clientID,
+ }),
+ templateId: sandbox.templateID,
+ ...(sandbox.alias && { name: sandbox.alias }),
+ metadata: sandbox.metadata ?? {},
+ startedAt: new Date(sandbox.startedAt),
+ state: sandbox.state,
+ }))
+
+ return {
+ sandboxes,
+ hasMoreItems,
+ nextToken: nextPageToken,
+ iterator: this.listIterator({ limit, nextToken: nextPageToken, filters, state, requestTimeoutMs })
+ }
+ }
+
+ private static async *listIterator(options: SandboxListOpts = {}): AsyncGenerator {
+ let nextPage = true
+ let token = options.nextToken
+
+ while (nextPage) {
+ const { sandboxes, hasMoreItems, nextToken } = await this.list({
+ ...options,
+ nextToken: token,
+ })
+
+ nextPage = hasMoreItems
+ token = nextToken
+
+ for (const sandbox of sandboxes) {
+ yield sandbox
+ }
+ }
}
/**
@@ -207,13 +270,13 @@ export class SandboxApi {
}
/**
- * Pause the sandbox specified by sandbox ID.
- *
- * @param sandboxId sandbox ID.
- * @param opts connection options.
- *
- * @returns `true` if the sandbox got paused, `false` if the sandbox was already paused.
- */
+ * Pause the sandbox specified by sandbox ID.
+ *
+ * @param sandboxId sandbox ID.
+ * @param opts connection options.
+ *
+ * @returns `true` if the sandbox got paused, `false` if the sandbox was already paused.
+ */
protected static async pauseSandbox(
sandboxId: string,
opts?: SandboxApiOpts
@@ -247,7 +310,6 @@ export class SandboxApi {
return true
}
-
protected static async resumeSandbox(
sandboxId: string,
timeoutMs: number,
@@ -264,6 +326,7 @@ export class SandboxApi {
},
body: {
timeout: this.timeoutToSeconds(timeoutMs),
+ autoPause: false,
},
signal: config.getSignal(opts?.requestTimeoutMs),
})
@@ -305,6 +368,7 @@ export class SandboxApi {
metadata: opts?.metadata,
envVars: opts?.envs,
timeout: this.timeoutToSeconds(timeoutMs),
+ autoPause: false,
},
signal: config.getSignal(opts?.requestTimeoutMs),
})
diff --git a/packages/js-sdk/tests/api/kill.test.ts b/packages/js-sdk/tests/api/kill.test.ts
index 2e928befe..8a3d2f108 100644
--- a/packages/js-sdk/tests/api/kill.test.ts
+++ b/packages/js-sdk/tests/api/kill.test.ts
@@ -6,8 +6,8 @@ import { Sandbox } from '../../src'
sandboxTest.skipIf(isDebug)('kill existing sandbox', async ({ sandbox }) => {
await Sandbox.kill(sandbox.sandboxId)
- const list = await Sandbox.list()
- expect(list.map(s => s.sandboxId)).not.toContain(sandbox.sandboxId)
+ const { sandboxes } = await Sandbox.list()
+ expect(sandboxes.map((s) => s.sandboxId)).not.toContain(sandbox.sandboxId)
})
sandboxTest.skipIf(isDebug)('kill non-existing sandbox', async () => {
diff --git a/packages/js-sdk/tests/api/list.test.ts b/packages/js-sdk/tests/api/list.test.ts
index e8e56d5bb..f42ae3d34 100644
--- a/packages/js-sdk/tests/api/list.test.ts
+++ b/packages/js-sdk/tests/api/list.test.ts
@@ -4,37 +4,212 @@ import { Sandbox } from '../../src'
import { sandboxTest, isDebug } from '../setup.js'
sandboxTest.skipIf(isDebug)('list sandboxes', async ({ sandbox }) => {
- const sandboxes = await Sandbox.list()
+ const { sandboxes } = await Sandbox.list()
+
assert.isAtLeast(sandboxes.length, 1)
assert.include(
sandboxes.map((s) => s.sandboxId),
sandbox.sandboxId
)
-
- // Check that sandboxes are sorted by startedAt in descending order (newest first)
- for (let i = 0; i < sandboxes.length - 1; i++) {
- assert.isAtLeast(
- new Date(sandboxes[i + 1].startedAt).getTime(),
- new Date(sandboxes[i].startedAt).getTime(),
- 'Sandboxes should be sorted by startedAt in descending order'
- )
- }
})
sandboxTest.skipIf(isDebug)('list sandboxes with filter', async () => {
const uniqueId = Date.now().toString()
- // Create an extra sandbox with a uniqueId
- const extraSbx = await Sandbox.create({ })
+ const extraSbx = await Sandbox.create({ metadata: { uniqueId } })
+
try {
- const sbx = await Sandbox.create({metadata: {uniqueId: uniqueId}})
+ const { sandboxes } = await Sandbox.list({ filters: { uniqueId } })
+
+ assert.equal(sandboxes.length, 1)
+ assert.equal(sandboxes[0].sandboxId, extraSbx.sandboxId)
+ } finally {
+ await extraSbx.kill()
+ }
+})
+
+sandboxTest.skipIf(isDebug)('list paused sandboxes', async ({ sandbox }) => {
+ const pausedSandbox = await sandbox.pause()
+ const pausedSandboxId = pausedSandbox.split('-')[0] + '-' + '00000000'
+ const { sandboxes } = await Sandbox.list({ state: ['paused'] })
+
+ assert.isAtLeast(sandboxes.length, 1)
+ assert.include(
+ sandboxes.map((s) => s.sandboxId),
+ pausedSandboxId
+ )
+})
+
+sandboxTest.skipIf(isDebug)('list running sandboxes', async ({ sandbox }) => {
+ const extraSbx = await Sandbox.create()
+ const { sandboxes } = await Sandbox.list({ state: ['running'] })
+
+ assert.isAtLeast(sandboxes.length, 1)
+ assert.include(
+ sandboxes.map((s) => s.sandboxId),
+ extraSbx.sandboxId
+ )
+})
+
+sandboxTest.skipIf(isDebug)(
+ 'list sandboxes with limit',
+ async ({ sandbox }) => {
+ const { sandboxes } = await Sandbox.list({ limit: 1 })
+ assert.equal(sandboxes.length, 1)
+ assert.include(
+ sandboxes.map((s) => s.sandboxId),
+ sandbox.sandboxId
+ )
+ }
+)
+
+sandboxTest.skipIf(isDebug)(
+ 'paginate running sandboxes',
+ async ({ sandbox }) => {
+ const extraSbx = await Sandbox.create()
+
try {
- const sandboxes = await Sandbox.list({filters: {uniqueId}})
+ const { sandboxes, hasMoreItems, nextToken } = await Sandbox.list({
+ state: ['running'],
+ limit: 1,
+ })
+
+ // check first page
assert.equal(sandboxes.length, 1)
- assert.equal(sandboxes[0].sandboxId, sbx.sandboxId)
+ assert.equal(sandboxes[0].state, 'running')
+ assert.isTrue(hasMoreItems)
+ assert.notEqual(nextToken, undefined)
+
+ // new sandbox should be on first page
+ assert.include(
+ sandboxes.map((s) => s.sandboxId),
+ extraSbx.sandboxId
+ )
+
+ // fetch second page
+ const {
+ sandboxes: sandboxes2,
+ hasMoreItems: hasMoreItems2,
+ nextToken: nextToken2,
+ } = await Sandbox.list({
+ state: ['running'],
+ nextToken: nextToken,
+ limit: 1,
+ })
+
+ // check second page
+ assert.equal(sandboxes2.length, 1)
+ assert.equal(sandboxes2[0].state, 'running')
+ assert.isFalse(hasMoreItems2)
+ assert.equal(nextToken2, undefined)
+
+ // past sandbox should be on second page
+ assert.include(
+ sandboxes2.map((s) => s.sandboxId),
+ sandbox.sandboxId
+ )
} finally {
- await sbx.kill()
+ await extraSbx.kill()
}
- } finally {
- await extraSbx.kill()
}
-})
+)
+
+sandboxTest.skipIf(isDebug)(
+ 'paginate paused sandboxes',
+ async ({ sandbox }) => {
+ // pause the current sandbox
+ await sandbox.pause()
+
+ // create a new sandbox
+ const extraSbx = await Sandbox.create()
+ await extraSbx.pause()
+
+ const { sandboxes, hasMoreItems, nextToken } = await Sandbox.list({
+ state: ['paused'],
+ limit: 1,
+ })
+
+ // check first page
+ assert.equal(sandboxes.length, 1)
+ assert.equal(sandboxes[0].state, 'paused')
+ assert.isTrue(hasMoreItems)
+ assert.notEqual(nextToken, undefined)
+
+ // new sandbox should be on first page
+ assert.include(
+ sandboxes.map((s) => s.sandboxId),
+ extraSbx.sandboxId
+ )
+
+ // fetch second page
+ const {
+ sandboxes: sandboxes2,
+ hasMoreItems: hasMoreItems2,
+ nextToken: nextToken2,
+ } = await Sandbox.list({
+ state: ['paused'],
+ nextToken: nextToken,
+ limit: 1,
+ })
+
+ // check second page
+ assert.equal(sandboxes2.length, 1)
+ assert.equal(sandboxes2[0].state, 'paused')
+ assert.isFalse(hasMoreItems2)
+ assert.equal(nextToken2, undefined)
+
+ // past sandbox should be on second page
+ assert.include(
+ sandboxes2.map((s) => s.sandboxId),
+ sandbox.sandboxId
+ )
+ }
+)
+
+sandboxTest.skipIf(isDebug)(
+ 'paginate paused and running sandboxes',
+ async ({ sandbox }) => {
+ // create a new sandbox
+ const extraSbx = await Sandbox.create()
+ await extraSbx.pause()
+
+ const { sandboxes, hasMoreItems, nextToken } = await Sandbox.list({
+ state: ['paused', 'running'],
+ limit: 1,
+ })
+
+ // check first page
+ assert.equal(sandboxes.length, 1)
+ assert.equal(sandboxes[0].state, 'paused')
+ assert.isTrue(hasMoreItems)
+ assert.notEqual(nextToken, undefined)
+
+ // new sandbox should be on first page
+ assert.include(
+ sandboxes.map((s) => s.sandboxId),
+ extraSbx.sandboxId
+ )
+
+ // fetch second page
+ const {
+ sandboxes: sandboxes2,
+ hasMoreItems: hasMoreItems2,
+ nextToken: nextToken2,
+ } = await Sandbox.list({
+ state: ['paused'],
+ nextToken: nextToken,
+ limit: 1,
+ })
+
+ // check second page
+ assert.equal(sandboxes2.length, 1)
+ assert.equal(sandboxes2[0].state, 'running')
+ assert.isFalse(hasMoreItems2)
+ assert.equal(nextToken2, undefined)
+
+ // past sandbox should be on second page
+ assert.include(
+ sandboxes2.map((s) => s.sandboxId),
+ sandbox.sandboxId
+ )
+ }
+)
diff --git a/packages/js-sdk/tests/sandbox/create.test.ts b/packages/js-sdk/tests/sandbox/create.test.ts
index 47a9b371e..847cfd582 100644
--- a/packages/js-sdk/tests/sandbox/create.test.ts
+++ b/packages/js-sdk/tests/sandbox/create.test.ts
@@ -21,10 +21,9 @@ test.skipIf(isDebug)('metadata', async () => {
}
const sbx = await Sandbox.create(template, { timeoutMs: 5_000, metadata })
-
try {
- const sbxs = await Sandbox.list()
- const sbxInfo = sbxs.find((s) => s.sandboxId === sbx.sandboxId)
+ const { sandboxes } = await Sandbox.list()
+ const sbxInfo = sandboxes.find((s) => s.sandboxId === sbx.sandboxId)
assert.deepEqual(sbxInfo?.metadata, metadata)
} finally {
diff --git a/packages/js-sdk/tests/sandbox/kill.test.ts b/packages/js-sdk/tests/sandbox/kill.test.ts
index b59d4e7ac..f5ffa44b1 100644
--- a/packages/js-sdk/tests/sandbox/kill.test.ts
+++ b/packages/js-sdk/tests/sandbox/kill.test.ts
@@ -6,6 +6,6 @@ import { sandboxTest, isDebug } from '../setup.js'
sandboxTest.skipIf(isDebug)('kill', async ({ sandbox }) => {
await sandbox.kill()
- const list = await Sandbox.list()
- expect(list.map(s => s.sandboxId)).not.toContain(sandbox.sandboxId)
+ const { sandboxes } = await Sandbox.list()
+ expect(sandboxes.map((s) => s.sandboxId)).not.toContain(sandbox.sandboxId)
})
diff --git a/packages/python-sdk/Makefile b/packages/python-sdk/Makefile
index c1b169c53..8192a619b 100644
--- a/packages/python-sdk/Makefile
+++ b/packages/python-sdk/Makefile
@@ -1,9 +1,9 @@
generate-api:
python ./../../spec/remove_extra_tags.py sandboxes
- openapi-python-client generate --output-path e2b/api/api --overwrite --path ../../spec/openapi_generated.yml
+ openapi-python-client generate --output-path e2b/api/api --overwrite --path ../../spec/openapi_generated.yml --config openapi-generator.yml
rm -rf e2b/api/client
mv e2b/api/api/e2b_api_client e2b/api/client
rm -rf e2b/api/api
init:
- pip install openapi-python-client
+ pip install openapi-python-client==0.23.1
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py b/packages/python-sdk/e2b/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py
index 30a419c00..b5deb5cff 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union
+from typing import Any, Optional, Union
import httpx
@@ -10,8 +10,8 @@
def _get_kwargs(
sandbox_id: str,
-) -> Dict[str, Any]:
- _kwargs: Dict[str, Any] = {
+) -> dict[str, Any]:
+ _kwargs: dict[str, Any] = {
"method": "delete",
"url": f"/sandboxes/{sandbox_id}",
}
@@ -20,13 +20,13 @@ def _get_kwargs(
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
- if response.status_code == HTTPStatus.NO_CONTENT:
+ if response.status_code == 204:
return None
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
return None
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
return None
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
return None
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes.py b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes.py
index 0c2efe612..1620c1f6a 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes.py
@@ -1,25 +1,42 @@
from http import HTTPStatus
-from typing import Any, Dict, List, Optional, Union, cast
+from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
-from ...models.running_sandbox import RunningSandbox
+from ...models.listed_sandbox import ListedSandbox
+from ...models.sandbox_state import SandboxState
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
query: Union[Unset, str] = UNSET,
-) -> Dict[str, Any]:
- params: Dict[str, Any] = {}
+ state: Union[Unset, list[SandboxState]] = UNSET,
+ next_token: Union[Unset, str] = UNSET,
+ limit: Union[Unset, int] = 1000,
+) -> dict[str, Any]:
+ params: dict[str, Any] = {}
params["query"] = query
+ json_state: Union[Unset, list[str]] = UNSET
+ if not isinstance(state, Unset):
+ json_state = []
+ for state_item_data in state:
+ state_item: str = state_item_data
+ json_state.append(state_item)
+
+ params["state"] = json_state
+
+ params["nextToken"] = next_token
+
+ params["limit"] = limit
+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
- _kwargs: Dict[str, Any] = {
+ _kwargs: dict[str, Any] = {
"method": "get",
"url": "/sandboxes",
"params": params,
@@ -30,23 +47,23 @@ def _get_kwargs(
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
-) -> Optional[Union[Any, List["RunningSandbox"]]]:
- if response.status_code == HTTPStatus.OK:
+) -> Optional[Union[Any, list["ListedSandbox"]]]:
+ if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
- response_200_item = RunningSandbox.from_dict(response_200_item_data)
+ response_200_item = ListedSandbox.from_dict(response_200_item_data)
response_200.append(response_200_item)
return response_200
- if response.status_code == HTTPStatus.BAD_REQUEST:
+ if response.status_code == 400:
response_400 = cast(Any, None)
return response_400
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
response_401 = cast(Any, None)
return response_401
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
response_500 = cast(Any, None)
return response_500
if client.raise_on_unexpected_status:
@@ -57,7 +74,7 @@ def _parse_response(
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
-) -> Response[Union[Any, List["RunningSandbox"]]]:
+) -> Response[Union[Any, list["ListedSandbox"]]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
@@ -70,22 +87,31 @@ def sync_detailed(
*,
client: AuthenticatedClient,
query: Union[Unset, str] = UNSET,
-) -> Response[Union[Any, List["RunningSandbox"]]]:
- """List all running sandboxes
+ state: Union[Unset, list[SandboxState]] = UNSET,
+ next_token: Union[Unset, str] = UNSET,
+ limit: Union[Unset, int] = 1000,
+) -> Response[Union[Any, list["ListedSandbox"]]]:
+ """List all sandboxes
Args:
query (Union[Unset, str]):
+ state (Union[Unset, list[SandboxState]]):
+ next_token (Union[Unset, str]):
+ limit (Union[Unset, int]): Default: 1000.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Response[Union[Any, List['RunningSandbox']]]
+ Response[Union[Any, list['ListedSandbox']]]
"""
kwargs = _get_kwargs(
query=query,
+ state=state,
+ next_token=next_token,
+ limit=limit,
)
response = client.get_httpx_client().request(
@@ -99,23 +125,32 @@ def sync(
*,
client: AuthenticatedClient,
query: Union[Unset, str] = UNSET,
-) -> Optional[Union[Any, List["RunningSandbox"]]]:
- """List all running sandboxes
+ state: Union[Unset, list[SandboxState]] = UNSET,
+ next_token: Union[Unset, str] = UNSET,
+ limit: Union[Unset, int] = 1000,
+) -> Optional[Union[Any, list["ListedSandbox"]]]:
+ """List all sandboxes
Args:
query (Union[Unset, str]):
+ state (Union[Unset, list[SandboxState]]):
+ next_token (Union[Unset, str]):
+ limit (Union[Unset, int]): Default: 1000.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Union[Any, List['RunningSandbox']]
+ Union[Any, list['ListedSandbox']]
"""
return sync_detailed(
client=client,
query=query,
+ state=state,
+ next_token=next_token,
+ limit=limit,
).parsed
@@ -123,22 +158,31 @@ async def asyncio_detailed(
*,
client: AuthenticatedClient,
query: Union[Unset, str] = UNSET,
-) -> Response[Union[Any, List["RunningSandbox"]]]:
- """List all running sandboxes
+ state: Union[Unset, list[SandboxState]] = UNSET,
+ next_token: Union[Unset, str] = UNSET,
+ limit: Union[Unset, int] = 1000,
+) -> Response[Union[Any, list["ListedSandbox"]]]:
+ """List all sandboxes
Args:
query (Union[Unset, str]):
+ state (Union[Unset, list[SandboxState]]):
+ next_token (Union[Unset, str]):
+ limit (Union[Unset, int]): Default: 1000.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Response[Union[Any, List['RunningSandbox']]]
+ Response[Union[Any, list['ListedSandbox']]]
"""
kwargs = _get_kwargs(
query=query,
+ state=state,
+ next_token=next_token,
+ limit=limit,
)
response = await client.get_async_httpx_client().request(**kwargs)
@@ -150,23 +194,32 @@ async def asyncio(
*,
client: AuthenticatedClient,
query: Union[Unset, str] = UNSET,
-) -> Optional[Union[Any, List["RunningSandbox"]]]:
- """List all running sandboxes
+ state: Union[Unset, list[SandboxState]] = UNSET,
+ next_token: Union[Unset, str] = UNSET,
+ limit: Union[Unset, int] = 1000,
+) -> Optional[Union[Any, list["ListedSandbox"]]]:
+ """List all sandboxes
Args:
query (Union[Unset, str]):
+ state (Union[Unset, list[SandboxState]]):
+ next_token (Union[Unset, str]):
+ limit (Union[Unset, int]): Default: 1000.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Union[Any, List['RunningSandbox']]
+ Union[Any, list['ListedSandbox']]
"""
return (
await asyncio_detailed(
client=client,
query=query,
+ state=state,
+ next_token=next_token,
+ limit=limit,
)
).parsed
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_metrics.py b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_metrics.py
new file mode 100644
index 000000000..06eb52e60
--- /dev/null
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_metrics.py
@@ -0,0 +1,172 @@
+from http import HTTPStatus
+from typing import Any, Optional, Union, cast
+
+import httpx
+
+from ... import errors
+from ...client import AuthenticatedClient, Client
+from ...models.running_sandbox_with_metrics import RunningSandboxWithMetrics
+from ...types import UNSET, Response, Unset
+
+
+def _get_kwargs(
+ *,
+ query: Union[Unset, str] = UNSET,
+) -> dict[str, Any]:
+ params: dict[str, Any] = {}
+
+ params["query"] = query
+
+ params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
+
+ _kwargs: dict[str, Any] = {
+ "method": "get",
+ "url": "/sandboxes/metrics",
+ "params": params,
+ }
+
+ return _kwargs
+
+
+def _parse_response(
+ *, client: Union[AuthenticatedClient, Client], response: httpx.Response
+) -> Optional[Union[Any, list["RunningSandboxWithMetrics"]]]:
+ if response.status_code == 200:
+ response_200 = []
+ _response_200 = response.json()
+ for response_200_item_data in _response_200:
+ response_200_item = RunningSandboxWithMetrics.from_dict(response_200_item_data)
+
+ response_200.append(response_200_item)
+
+ return response_200
+ if response.status_code == 400:
+ response_400 = cast(Any, None)
+ return response_400
+ if response.status_code == 401:
+ response_401 = cast(Any, None)
+ return response_401
+ if response.status_code == 500:
+ response_500 = cast(Any, None)
+ return response_500
+ if client.raise_on_unexpected_status:
+ raise errors.UnexpectedStatus(response.status_code, response.content)
+ else:
+ return None
+
+
+def _build_response(
+ *, client: Union[AuthenticatedClient, Client], response: httpx.Response
+) -> Response[Union[Any, list["RunningSandboxWithMetrics"]]]:
+ return Response(
+ status_code=HTTPStatus(response.status_code),
+ content=response.content,
+ headers=response.headers,
+ parsed=_parse_response(client=client, response=response),
+ )
+
+
+def sync_detailed(
+ *,
+ client: AuthenticatedClient,
+ query: Union[Unset, str] = UNSET,
+) -> Response[Union[Any, list["RunningSandboxWithMetrics"]]]:
+ """List all running sandboxes with metrics
+
+ Args:
+ query (Union[Unset, str]):
+
+ Raises:
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
+
+ Returns:
+ Response[Union[Any, list['RunningSandboxWithMetrics']]]
+ """
+
+ kwargs = _get_kwargs(
+ query=query,
+ )
+
+ response = client.get_httpx_client().request(
+ **kwargs,
+ )
+
+ return _build_response(client=client, response=response)
+
+
+def sync(
+ *,
+ client: AuthenticatedClient,
+ query: Union[Unset, str] = UNSET,
+) -> Optional[Union[Any, list["RunningSandboxWithMetrics"]]]:
+ """List all running sandboxes with metrics
+
+ Args:
+ query (Union[Unset, str]):
+
+ Raises:
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
+
+ Returns:
+ Union[Any, list['RunningSandboxWithMetrics']]
+ """
+
+ return sync_detailed(
+ client=client,
+ query=query,
+ ).parsed
+
+
+async def asyncio_detailed(
+ *,
+ client: AuthenticatedClient,
+ query: Union[Unset, str] = UNSET,
+) -> Response[Union[Any, list["RunningSandboxWithMetrics"]]]:
+ """List all running sandboxes with metrics
+
+ Args:
+ query (Union[Unset, str]):
+
+ Raises:
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
+
+ Returns:
+ Response[Union[Any, list['RunningSandboxWithMetrics']]]
+ """
+
+ kwargs = _get_kwargs(
+ query=query,
+ )
+
+ response = await client.get_async_httpx_client().request(**kwargs)
+
+ return _build_response(client=client, response=response)
+
+
+async def asyncio(
+ *,
+ client: AuthenticatedClient,
+ query: Union[Unset, str] = UNSET,
+) -> Optional[Union[Any, list["RunningSandboxWithMetrics"]]]:
+ """List all running sandboxes with metrics
+
+ Args:
+ query (Union[Unset, str]):
+
+ Raises:
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
+
+ Returns:
+ Union[Any, list['RunningSandboxWithMetrics']]
+ """
+
+ return (
+ await asyncio_detailed(
+ client=client,
+ query=query,
+ )
+ ).parsed
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id.py b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id.py
index 7ab59ee7e..069e4acf9 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id.py
@@ -1,18 +1,18 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union, cast
+from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
-from ...models.running_sandbox import RunningSandbox
+from ...models.listed_sandbox import ListedSandbox
from ...types import Response
def _get_kwargs(
sandbox_id: str,
-) -> Dict[str, Any]:
- _kwargs: Dict[str, Any] = {
+) -> dict[str, Any]:
+ _kwargs: dict[str, Any] = {
"method": "get",
"url": f"/sandboxes/{sandbox_id}",
}
@@ -22,18 +22,18 @@ def _get_kwargs(
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
-) -> Optional[Union[Any, RunningSandbox]]:
- if response.status_code == HTTPStatus.OK:
- response_200 = RunningSandbox.from_dict(response.json())
+) -> Optional[Union[Any, ListedSandbox]]:
+ if response.status_code == 200:
+ response_200 = ListedSandbox.from_dict(response.json())
return response_200
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
response_401 = cast(Any, None)
return response_401
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
response_404 = cast(Any, None)
return response_404
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
response_500 = cast(Any, None)
return response_500
if client.raise_on_unexpected_status:
@@ -44,7 +44,7 @@ def _parse_response(
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
-) -> Response[Union[Any, RunningSandbox]]:
+) -> Response[Union[Any, ListedSandbox]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
@@ -57,7 +57,7 @@ def sync_detailed(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Response[Union[Any, RunningSandbox]]:
+) -> Response[Union[Any, ListedSandbox]]:
"""Get a sandbox by id
Args:
@@ -68,7 +68,7 @@ def sync_detailed(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Response[Union[Any, RunningSandbox]]
+ Response[Union[Any, ListedSandbox]]
"""
kwargs = _get_kwargs(
@@ -86,7 +86,7 @@ def sync(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Optional[Union[Any, RunningSandbox]]:
+) -> Optional[Union[Any, ListedSandbox]]:
"""Get a sandbox by id
Args:
@@ -97,7 +97,7 @@ def sync(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Union[Any, RunningSandbox]
+ Union[Any, ListedSandbox]
"""
return sync_detailed(
@@ -110,7 +110,7 @@ async def asyncio_detailed(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Response[Union[Any, RunningSandbox]]:
+) -> Response[Union[Any, ListedSandbox]]:
"""Get a sandbox by id
Args:
@@ -121,7 +121,7 @@ async def asyncio_detailed(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Response[Union[Any, RunningSandbox]]
+ Response[Union[Any, ListedSandbox]]
"""
kwargs = _get_kwargs(
@@ -137,7 +137,7 @@ async def asyncio(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Optional[Union[Any, RunningSandbox]]:
+) -> Optional[Union[Any, ListedSandbox]]:
"""Get a sandbox by id
Args:
@@ -148,7 +148,7 @@ async def asyncio(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Union[Any, RunningSandbox]
+ Union[Any, ListedSandbox]
"""
return (
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py
index 7c70e28b7..11d665612 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union, cast
+from typing import Any, Optional, Union, cast
import httpx
@@ -14,8 +14,8 @@ def _get_kwargs(
*,
start: Union[Unset, int] = UNSET,
limit: Union[Unset, int] = 1000,
-) -> Dict[str, Any]:
- params: Dict[str, Any] = {}
+) -> dict[str, Any]:
+ params: dict[str, Any] = {}
params["start"] = start
@@ -23,7 +23,7 @@ def _get_kwargs(
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
- _kwargs: Dict[str, Any] = {
+ _kwargs: dict[str, Any] = {
"method": "get",
"url": f"/sandboxes/{sandbox_id}/logs",
"params": params,
@@ -35,17 +35,17 @@ def _get_kwargs(
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Any, SandboxLogs]]:
- if response.status_code == HTTPStatus.OK:
+ if response.status_code == 200:
response_200 = SandboxLogs.from_dict(response.json())
return response_200
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
response_401 = cast(Any, None)
return response_401
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
response_404 = cast(Any, None)
return response_404
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
response_500 = cast(Any, None)
return response_500
if client.raise_on_unexpected_status:
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py
index 85851eda1..76905092d 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, List, Optional, Union, cast
+from typing import Any, Optional, Union, cast
import httpx
@@ -11,8 +11,8 @@
def _get_kwargs(
sandbox_id: str,
-) -> Dict[str, Any]:
- _kwargs: Dict[str, Any] = {
+) -> dict[str, Any]:
+ _kwargs: dict[str, Any] = {
"method": "get",
"url": f"/sandboxes/{sandbox_id}/metrics",
}
@@ -22,8 +22,8 @@ def _get_kwargs(
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
-) -> Optional[Union[Any, List["SandboxMetric"]]]:
- if response.status_code == HTTPStatus.OK:
+) -> Optional[Union[Any, list["SandboxMetric"]]]:
+ if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
@@ -32,13 +32,13 @@ def _parse_response(
response_200.append(response_200_item)
return response_200
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
response_401 = cast(Any, None)
return response_401
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
response_404 = cast(Any, None)
return response_404
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
response_500 = cast(Any, None)
return response_500
if client.raise_on_unexpected_status:
@@ -49,7 +49,7 @@ def _parse_response(
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
-) -> Response[Union[Any, List["SandboxMetric"]]]:
+) -> Response[Union[Any, list["SandboxMetric"]]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
@@ -62,7 +62,7 @@ def sync_detailed(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Response[Union[Any, List["SandboxMetric"]]]:
+) -> Response[Union[Any, list["SandboxMetric"]]]:
"""Get sandbox metrics
Args:
@@ -73,7 +73,7 @@ def sync_detailed(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Response[Union[Any, List['SandboxMetric']]]
+ Response[Union[Any, list['SandboxMetric']]]
"""
kwargs = _get_kwargs(
@@ -91,7 +91,7 @@ def sync(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Optional[Union[Any, List["SandboxMetric"]]]:
+) -> Optional[Union[Any, list["SandboxMetric"]]]:
"""Get sandbox metrics
Args:
@@ -102,7 +102,7 @@ def sync(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Union[Any, List['SandboxMetric']]
+ Union[Any, list['SandboxMetric']]
"""
return sync_detailed(
@@ -115,7 +115,7 @@ async def asyncio_detailed(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Response[Union[Any, List["SandboxMetric"]]]:
+) -> Response[Union[Any, list["SandboxMetric"]]]:
"""Get sandbox metrics
Args:
@@ -126,7 +126,7 @@ async def asyncio_detailed(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Response[Union[Any, List['SandboxMetric']]]
+ Response[Union[Any, list['SandboxMetric']]]
"""
kwargs = _get_kwargs(
@@ -142,7 +142,7 @@ async def asyncio(
sandbox_id: str,
*,
client: AuthenticatedClient,
-) -> Optional[Union[Any, List["SandboxMetric"]]]:
+) -> Optional[Union[Any, list["SandboxMetric"]]]:
"""Get sandbox metrics
Args:
@@ -153,7 +153,7 @@ async def asyncio(
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
- Union[Any, List['SandboxMetric']]
+ Union[Any, list['SandboxMetric']]
"""
return (
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes.py b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes.py
index 70fa540da..0a4000b74 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union, cast
+from typing import Any, Optional, Union, cast
import httpx
@@ -13,10 +13,10 @@
def _get_kwargs(
*,
body: NewSandbox,
-) -> Dict[str, Any]:
- headers: Dict[str, Any] = {}
+) -> dict[str, Any]:
+ headers: dict[str, Any] = {}
- _kwargs: Dict[str, Any] = {
+ _kwargs: dict[str, Any] = {
"method": "post",
"url": "/sandboxes",
}
@@ -33,17 +33,17 @@ def _get_kwargs(
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Any, Sandbox]]:
- if response.status_code == HTTPStatus.CREATED:
+ if response.status_code == 201:
response_201 = Sandbox.from_dict(response.json())
return response_201
- if response.status_code == HTTPStatus.BAD_REQUEST:
+ if response.status_code == 400:
response_400 = cast(Any, None)
return response_400
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
response_401 = cast(Any, None)
return response_401
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
response_500 = cast(Any, None)
return response_500
if client.raise_on_unexpected_status:
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py
index 8133ed132..8635ebc60 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union
+from typing import Any, Optional, Union
import httpx
@@ -10,8 +10,8 @@
def _get_kwargs(
sandbox_id: str,
-) -> Dict[str, Any]:
- _kwargs: Dict[str, Any] = {
+) -> dict[str, Any]:
+ _kwargs: dict[str, Any] = {
"method": "post",
"url": f"/sandboxes/{sandbox_id}/pause",
}
@@ -20,15 +20,15 @@ def _get_kwargs(
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
- if response.status_code == HTTPStatus.NO_CONTENT:
+ if response.status_code == 204:
return None
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
return None
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
return None
- if response.status_code == HTTPStatus.CONFLICT:
+ if response.status_code == 409:
return None
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
return None
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py
index 163bcf177..ab90668d1 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union
+from typing import Any, Optional, Union
import httpx
@@ -13,10 +13,10 @@ def _get_kwargs(
sandbox_id: str,
*,
body: PostSandboxesSandboxIDRefreshesBody,
-) -> Dict[str, Any]:
- headers: Dict[str, Any] = {}
+) -> dict[str, Any]:
+ headers: dict[str, Any] = {}
- _kwargs: Dict[str, Any] = {
+ _kwargs: dict[str, Any] = {
"method": "post",
"url": f"/sandboxes/{sandbox_id}/refreshes",
}
@@ -31,11 +31,11 @@ def _get_kwargs(
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
- if response.status_code == HTTPStatus.NO_CONTENT:
+ if response.status_code == 204:
return None
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
return None
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
return None
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py
index 1433aa513..c50fc9c07 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union, cast
+from typing import Any, Optional, Union, cast
import httpx
@@ -14,10 +14,10 @@ def _get_kwargs(
sandbox_id: str,
*,
body: ResumedSandbox,
-) -> Dict[str, Any]:
- headers: Dict[str, Any] = {}
+) -> dict[str, Any]:
+ headers: dict[str, Any] = {}
- _kwargs: Dict[str, Any] = {
+ _kwargs: dict[str, Any] = {
"method": "post",
"url": f"/sandboxes/{sandbox_id}/resume",
}
@@ -34,20 +34,20 @@ def _get_kwargs(
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Any, Sandbox]]:
- if response.status_code == HTTPStatus.CREATED:
+ if response.status_code == 201:
response_201 = Sandbox.from_dict(response.json())
return response_201
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
response_401 = cast(Any, None)
return response_401
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
response_404 = cast(Any, None)
return response_404
- if response.status_code == HTTPStatus.CONFLICT:
+ if response.status_code == 409:
response_409 = cast(Any, None)
return response_409
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
response_500 = cast(Any, None)
return response_500
if client.raise_on_unexpected_status:
diff --git a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py
index 148d97c87..63beecdcb 100644
--- a/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py
+++ b/packages/python-sdk/e2b/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py
@@ -1,5 +1,5 @@
from http import HTTPStatus
-from typing import Any, Dict, Optional, Union
+from typing import Any, Optional, Union
import httpx
@@ -13,10 +13,10 @@ def _get_kwargs(
sandbox_id: str,
*,
body: PostSandboxesSandboxIDTimeoutBody,
-) -> Dict[str, Any]:
- headers: Dict[str, Any] = {}
+) -> dict[str, Any]:
+ headers: dict[str, Any] = {}
- _kwargs: Dict[str, Any] = {
+ _kwargs: dict[str, Any] = {
"method": "post",
"url": f"/sandboxes/{sandbox_id}/timeout",
}
@@ -31,13 +31,13 @@ def _get_kwargs(
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
- if response.status_code == HTTPStatus.NO_CONTENT:
+ if response.status_code == 204:
return None
- if response.status_code == HTTPStatus.UNAUTHORIZED:
+ if response.status_code == 401:
return None
- if response.status_code == HTTPStatus.NOT_FOUND:
+ if response.status_code == 404:
return None
- if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
+ if response.status_code == 500:
return None
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
diff --git a/packages/python-sdk/e2b/api/client/client.py b/packages/python-sdk/e2b/api/client/client.py
index 63a2493b9..e80446f10 100644
--- a/packages/python-sdk/e2b/api/client/client.py
+++ b/packages/python-sdk/e2b/api/client/client.py
@@ -1,5 +1,5 @@
import ssl
-from typing import Any, Dict, Optional, Union
+from typing import Any, Optional, Union
import httpx
from attrs import define, evolve, field
@@ -36,16 +36,16 @@ class Client:
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
_base_url: str = field(alias="base_url")
- _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
- _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
+ _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
+ _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout")
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl")
_follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
- _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
+ _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
_client: Optional[httpx.Client] = field(default=None, init=False)
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
- def with_headers(self, headers: Dict[str, str]) -> "Client":
+ def with_headers(self, headers: dict[str, str]) -> "Client":
"""Get a new client matching this one with additional headers"""
if self._client is not None:
self._client.headers.update(headers)
@@ -53,7 +53,7 @@ def with_headers(self, headers: Dict[str, str]) -> "Client":
self._async_client.headers.update(headers)
return evolve(self, headers={**self._headers, **headers})
- def with_cookies(self, cookies: Dict[str, str]) -> "Client":
+ def with_cookies(self, cookies: dict[str, str]) -> "Client":
"""Get a new client matching this one with additional cookies"""
if self._client is not None:
self._client.cookies.update(cookies)
@@ -70,7 +70,7 @@ def with_timeout(self, timeout: httpx.Timeout) -> "Client":
return evolve(self, timeout=timeout)
def set_httpx_client(self, client: httpx.Client) -> "Client":
- """Manually the underlying httpx.Client
+ """Manually set the underlying httpx.Client
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
"""
@@ -166,12 +166,12 @@ class AuthenticatedClient:
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
_base_url: str = field(alias="base_url")
- _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
- _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
+ _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
+ _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout")
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl")
_follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
- _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
+ _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
_client: Optional[httpx.Client] = field(default=None, init=False)
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
@@ -179,7 +179,7 @@ class AuthenticatedClient:
prefix: str = "Bearer"
auth_header_name: str = "Authorization"
- def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient":
+ def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient":
"""Get a new client matching this one with additional headers"""
if self._client is not None:
self._client.headers.update(headers)
@@ -187,7 +187,7 @@ def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient":
self._async_client.headers.update(headers)
return evolve(self, headers={**self._headers, **headers})
- def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient":
+ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient":
"""Get a new client matching this one with additional cookies"""
if self._client is not None:
self._client.cookies.update(cookies)
@@ -204,7 +204,7 @@ def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient":
return evolve(self, timeout=timeout)
def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient":
- """Manually the underlying httpx.Client
+ """Manually set the underlying httpx.Client
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
"""
diff --git a/packages/python-sdk/e2b/api/client/models/__init__.py b/packages/python-sdk/e2b/api/client/models/__init__.py
index 4f8bae902..4aed1029f 100644
--- a/packages/python-sdk/e2b/api/client/models/__init__.py
+++ b/packages/python-sdk/e2b/api/client/models/__init__.py
@@ -1,6 +1,7 @@
"""Contains all the data models used in inputs/outputs"""
from .error import Error
+from .listed_sandbox import ListedSandbox
from .new_sandbox import NewSandbox
from .node import Node
from .node_detail import NodeDetail
@@ -9,11 +10,12 @@
from .post_sandboxes_sandbox_id_refreshes_body import PostSandboxesSandboxIDRefreshesBody
from .post_sandboxes_sandbox_id_timeout_body import PostSandboxesSandboxIDTimeoutBody
from .resumed_sandbox import ResumedSandbox
-from .running_sandbox import RunningSandbox
+from .running_sandbox_with_metrics import RunningSandboxWithMetrics
from .sandbox import Sandbox
from .sandbox_log import SandboxLog
from .sandbox_logs import SandboxLogs
from .sandbox_metric import SandboxMetric
+from .sandbox_state import SandboxState
from .team import Team
from .team_user import TeamUser
from .template import Template
@@ -24,6 +26,7 @@
__all__ = (
"Error",
+ "ListedSandbox",
"NewSandbox",
"Node",
"NodeDetail",
@@ -32,11 +35,12 @@
"PostSandboxesSandboxIDRefreshesBody",
"PostSandboxesSandboxIDTimeoutBody",
"ResumedSandbox",
- "RunningSandbox",
+ "RunningSandboxWithMetrics",
"Sandbox",
"SandboxLog",
"SandboxLogs",
"SandboxMetric",
+ "SandboxState",
"Team",
"TeamUser",
"Template",
diff --git a/packages/python-sdk/e2b/api/client/models/error.py b/packages/python-sdk/e2b/api/client/models/error.py
index b9680dc0c..1dcf14741 100644
--- a/packages/python-sdk/e2b/api/client/models/error.py
+++ b/packages/python-sdk/e2b/api/client/models/error.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -16,14 +16,14 @@ class Error:
code: int
message: str
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
code = self.code
message = self.message
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -35,7 +35,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
code = d.pop("code")
@@ -50,7 +50,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return error
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/running_sandbox.py b/packages/python-sdk/e2b/api/client/models/listed_sandbox.py
similarity index 80%
rename from packages/python-sdk/e2b/api/client/models/running_sandbox.py
rename to packages/python-sdk/e2b/api/client/models/listed_sandbox.py
index c329511b5..9107c1312 100644
--- a/packages/python-sdk/e2b/api/client/models/running_sandbox.py
+++ b/packages/python-sdk/e2b/api/client/models/listed_sandbox.py
@@ -1,17 +1,18 @@
import datetime
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from dateutil.parser import isoparse
+from ..models.sandbox_state import SandboxState, check_sandbox_state
from ..types import UNSET, Unset
-T = TypeVar("T", bound="RunningSandbox")
+T = TypeVar("T", bound="ListedSandbox")
@_attrs_define
-class RunningSandbox:
+class ListedSandbox:
"""
Attributes:
client_id (str): Identifier of the client
@@ -20,6 +21,7 @@ class RunningSandbox:
memory_mb (int): Memory for the sandbox in MB
sandbox_id (str): Identifier of the sandbox
started_at (datetime.datetime): Time when the sandbox was started
+ state (SandboxState): State of the sandbox
template_id (str): Identifier of the template from which is the sandbox created
alias (Union[Unset, str]): Alias of the template
metadata (Union[Unset, Any]):
@@ -31,12 +33,13 @@ class RunningSandbox:
memory_mb: int
sandbox_id: str
started_at: datetime.datetime
+ state: SandboxState
template_id: str
alias: Union[Unset, str] = UNSET
metadata: Union[Unset, Any] = UNSET
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
client_id = self.client_id
cpu_count = self.cpu_count
@@ -49,13 +52,15 @@ def to_dict(self) -> Dict[str, Any]:
started_at = self.started_at.isoformat()
+ state: str = self.state
+
template_id = self.template_id
alias = self.alias
metadata = self.metadata
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -65,6 +70,7 @@ def to_dict(self) -> Dict[str, Any]:
"memoryMB": memory_mb,
"sandboxID": sandbox_id,
"startedAt": started_at,
+ "state": state,
"templateID": template_id,
}
)
@@ -76,7 +82,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
client_id = d.pop("clientID")
@@ -90,29 +96,32 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
started_at = isoparse(d.pop("startedAt"))
+ state = check_sandbox_state(d.pop("state"))
+
template_id = d.pop("templateID")
alias = d.pop("alias", UNSET)
metadata = d.pop("metadata", UNSET)
- running_sandbox = cls(
+ listed_sandbox = cls(
client_id=client_id,
cpu_count=cpu_count,
end_at=end_at,
memory_mb=memory_mb,
sandbox_id=sandbox_id,
started_at=started_at,
+ state=state,
template_id=template_id,
alias=alias,
metadata=metadata,
)
- running_sandbox.additional_properties = d
- return running_sandbox
+ listed_sandbox.additional_properties = d
+ return listed_sandbox
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/new_sandbox.py b/packages/python-sdk/e2b/api/client/models/new_sandbox.py
index 10cf61310..aa79cac3f 100644
--- a/packages/python-sdk/e2b/api/client/models/new_sandbox.py
+++ b/packages/python-sdk/e2b/api/client/models/new_sandbox.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -13,33 +13,39 @@ class NewSandbox:
"""
Attributes:
template_id (str): Identifier of the required template
+ auto_pause (Union[Unset, bool]): Automatically pauses the sandbox after the timeout Default: False.
env_vars (Union[Unset, Any]):
metadata (Union[Unset, Any]):
timeout (Union[Unset, int]): Time to live for the sandbox in seconds. Default: 15.
"""
template_id: str
+ auto_pause: Union[Unset, bool] = False
env_vars: Union[Unset, Any] = UNSET
metadata: Union[Unset, Any] = UNSET
timeout: Union[Unset, int] = 15
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
template_id = self.template_id
+ auto_pause = self.auto_pause
+
env_vars = self.env_vars
metadata = self.metadata
timeout = self.timeout
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"templateID": template_id,
}
)
+ if auto_pause is not UNSET:
+ field_dict["autoPause"] = auto_pause
if env_vars is not UNSET:
field_dict["envVars"] = env_vars
if metadata is not UNSET:
@@ -50,10 +56,12 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
template_id = d.pop("templateID")
+ auto_pause = d.pop("autoPause", UNSET)
+
env_vars = d.pop("envVars", UNSET)
metadata = d.pop("metadata", UNSET)
@@ -62,6 +70,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
new_sandbox = cls(
template_id=template_id,
+ auto_pause=auto_pause,
env_vars=env_vars,
metadata=metadata,
timeout=timeout,
@@ -71,7 +80,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return new_sandbox
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/node.py b/packages/python-sdk/e2b/api/client/models/node.py
index 8c207759f..4dcf581d5 100644
--- a/packages/python-sdk/e2b/api/client/models/node.py
+++ b/packages/python-sdk/e2b/api/client/models/node.py
@@ -1,9 +1,9 @@
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
-from ..models.node_status import NodeStatus
+from ..models.node_status import NodeStatus, check_node_status
T = TypeVar("T", bound="Node")
@@ -14,37 +14,47 @@ class Node:
Attributes:
allocated_cpu (int): Number of allocated CPU cores
allocated_memory_mi_b (int): Amount of allocated memory in MiB
+ create_fails (int): Number of sandbox create fails
node_id (str): Identifier of the node
sandbox_count (int): Number of sandboxes running on the node
+ sandbox_starting_count (int): Number of starting Sandboxes
status (NodeStatus): Status of the node
"""
allocated_cpu: int
allocated_memory_mi_b: int
+ create_fails: int
node_id: str
sandbox_count: int
+ sandbox_starting_count: int
status: NodeStatus
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
allocated_cpu = self.allocated_cpu
allocated_memory_mi_b = self.allocated_memory_mi_b
+ create_fails = self.create_fails
+
node_id = self.node_id
sandbox_count = self.sandbox_count
- status = self.status.value
+ sandbox_starting_count = self.sandbox_starting_count
+
+ status: str = self.status
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"allocatedCPU": allocated_cpu,
"allocatedMemoryMiB": allocated_memory_mi_b,
+ "createFails": create_fails,
"nodeID": node_id,
"sandboxCount": sandbox_count,
+ "sandboxStartingCount": sandbox_starting_count,
"status": status,
}
)
@@ -52,23 +62,29 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
allocated_cpu = d.pop("allocatedCPU")
allocated_memory_mi_b = d.pop("allocatedMemoryMiB")
+ create_fails = d.pop("createFails")
+
node_id = d.pop("nodeID")
sandbox_count = d.pop("sandboxCount")
- status = NodeStatus(d.pop("status"))
+ sandbox_starting_count = d.pop("sandboxStartingCount")
+
+ status = check_node_status(d.pop("status"))
node = cls(
allocated_cpu=allocated_cpu,
allocated_memory_mi_b=allocated_memory_mi_b,
+ create_fails=create_fails,
node_id=node_id,
sandbox_count=sandbox_count,
+ sandbox_starting_count=sandbox_starting_count,
status=status,
)
@@ -76,7 +92,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return node
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/node_detail.py b/packages/python-sdk/e2b/api/client/models/node_detail.py
index a2326e054..2430e60ba 100644
--- a/packages/python-sdk/e2b/api/client/models/node_detail.py
+++ b/packages/python-sdk/e2b/api/client/models/node_detail.py
@@ -1,12 +1,12 @@
-from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, cast
+from typing import TYPE_CHECKING, Any, TypeVar, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
-from ..models.node_status import NodeStatus
+from ..models.node_status import NodeStatus, check_node_status
if TYPE_CHECKING:
- from ..models.running_sandbox import RunningSandbox
+ from ..models.listed_sandbox import ListedSandbox
T = TypeVar("T", bound="NodeDetail")
@@ -16,21 +16,25 @@
class NodeDetail:
"""
Attributes:
- cached_builds (List[str]): List of cached builds id on the node
+ cached_builds (list[str]): List of cached builds id on the node
+ create_fails (int): Number of sandbox create fails
node_id (str): Identifier of the node
- sandboxes (List['RunningSandbox']): List of sandboxes running on the node
+ sandboxes (list['ListedSandbox']): List of sandboxes running on the node
status (NodeStatus): Status of the node
"""
- cached_builds: List[str]
+ cached_builds: list[str]
+ create_fails: int
node_id: str
- sandboxes: List["RunningSandbox"]
+ sandboxes: list["ListedSandbox"]
status: NodeStatus
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
cached_builds = self.cached_builds
+ create_fails = self.create_fails
+
node_id = self.node_id
sandboxes = []
@@ -38,13 +42,14 @@ def to_dict(self) -> Dict[str, Any]:
sandboxes_item = sandboxes_item_data.to_dict()
sandboxes.append(sandboxes_item)
- status = self.status.value
+ status: str = self.status
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"cachedBuilds": cached_builds,
+ "createFails": create_fails,
"nodeID": node_id,
"sandboxes": sandboxes,
"status": status,
@@ -54,25 +59,28 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
- from ..models.running_sandbox import RunningSandbox
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
+ from ..models.listed_sandbox import ListedSandbox
d = src_dict.copy()
- cached_builds = cast(List[str], d.pop("cachedBuilds"))
+ cached_builds = cast(list[str], d.pop("cachedBuilds"))
+
+ create_fails = d.pop("createFails")
node_id = d.pop("nodeID")
sandboxes = []
_sandboxes = d.pop("sandboxes")
for sandboxes_item_data in _sandboxes:
- sandboxes_item = RunningSandbox.from_dict(sandboxes_item_data)
+ sandboxes_item = ListedSandbox.from_dict(sandboxes_item_data)
sandboxes.append(sandboxes_item)
- status = NodeStatus(d.pop("status"))
+ status = check_node_status(d.pop("status"))
node_detail = cls(
cached_builds=cached_builds,
+ create_fails=create_fails,
node_id=node_id,
sandboxes=sandboxes,
status=status,
@@ -82,7 +90,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return node_detail
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/node_status.py b/packages/python-sdk/e2b/api/client/models/node_status.py
index 87a77905b..718895f5a 100644
--- a/packages/python-sdk/e2b/api/client/models/node_status.py
+++ b/packages/python-sdk/e2b/api/client/models/node_status.py
@@ -1,9 +1,16 @@
-from enum import Enum
+from typing import Literal, cast
+NodeStatus = Literal["connecting", "draining", "ready", "unhealthy"]
-class NodeStatus(str, Enum):
- DRAINING = "draining"
- READY = "ready"
+NODE_STATUS_VALUES: set[NodeStatus] = {
+ "connecting",
+ "draining",
+ "ready",
+ "unhealthy",
+}
- def __str__(self) -> str:
- return str(self.value)
+
+def check_node_status(value: str) -> NodeStatus:
+ if value in NODE_STATUS_VALUES:
+ return cast(NodeStatus, value)
+ raise TypeError(f"Unexpected value {value!r}. Expected one of {NODE_STATUS_VALUES!r}")
diff --git a/packages/python-sdk/e2b/api/client/models/node_status_change.py b/packages/python-sdk/e2b/api/client/models/node_status_change.py
index 43628b809..8e38fb264 100644
--- a/packages/python-sdk/e2b/api/client/models/node_status_change.py
+++ b/packages/python-sdk/e2b/api/client/models/node_status_change.py
@@ -1,9 +1,9 @@
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
-from ..models.node_status import NodeStatus
+from ..models.node_status import NodeStatus, check_node_status
T = TypeVar("T", bound="NodeStatusChange")
@@ -16,12 +16,12 @@ class NodeStatusChange:
"""
status: NodeStatus
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
- status = self.status.value
+ def to_dict(self) -> dict[str, Any]:
+ status: str = self.status
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -32,9 +32,9 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
- status = NodeStatus(d.pop("status"))
+ status = check_node_status(d.pop("status"))
node_status_change = cls(
status=status,
@@ -44,7 +44,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return node_status_change
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py b/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py
index 39536231a..5cb0807cf 100644
--- a/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py
+++ b/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -16,12 +16,12 @@ class PostSandboxesSandboxIDRefreshesBody:
"""
duration: Union[Unset, int] = UNSET
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
duration = self.duration
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if duration is not UNSET:
@@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
duration = d.pop("duration", UNSET)
@@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return post_sandboxes_sandbox_id_refreshes_body
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_timeout_body.py b/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_timeout_body.py
index 5d0b87607..77907033b 100644
--- a/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_timeout_body.py
+++ b/packages/python-sdk/e2b/api/client/models/post_sandboxes_sandbox_id_timeout_body.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -14,12 +14,12 @@ class PostSandboxesSandboxIDTimeoutBody:
"""
timeout: int
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
timeout = self.timeout
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
timeout = d.pop("timeout")
@@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return post_sandboxes_sandbox_id_timeout_body
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/resumed_sandbox.py b/packages/python-sdk/e2b/api/client/models/resumed_sandbox.py
index ae872a6d1..c8481750b 100644
--- a/packages/python-sdk/e2b/api/client/models/resumed_sandbox.py
+++ b/packages/python-sdk/e2b/api/client/models/resumed_sandbox.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -12,29 +12,38 @@
class ResumedSandbox:
"""
Attributes:
+ auto_pause (Union[Unset, bool]): Automatically pauses the sandbox after the timeout Default: False.
timeout (Union[Unset, int]): Time to live for the sandbox in seconds. Default: 15.
"""
+ auto_pause: Union[Unset, bool] = False
timeout: Union[Unset, int] = 15
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
+
+ def to_dict(self) -> dict[str, Any]:
+ auto_pause = self.auto_pause
- def to_dict(self) -> Dict[str, Any]:
timeout = self.timeout
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
+ if auto_pause is not UNSET:
+ field_dict["autoPause"] = auto_pause
if timeout is not UNSET:
field_dict["timeout"] = timeout
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
+ auto_pause = d.pop("autoPause", UNSET)
+
timeout = d.pop("timeout", UNSET)
resumed_sandbox = cls(
+ auto_pause=auto_pause,
timeout=timeout,
)
@@ -42,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return resumed_sandbox
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/running_sandbox_with_metrics.py b/packages/python-sdk/e2b/api/client/models/running_sandbox_with_metrics.py
new file mode 100644
index 000000000..95925acac
--- /dev/null
+++ b/packages/python-sdk/e2b/api/client/models/running_sandbox_with_metrics.py
@@ -0,0 +1,153 @@
+import datetime
+from typing import TYPE_CHECKING, Any, TypeVar, Union
+
+from attrs import define as _attrs_define
+from attrs import field as _attrs_field
+from dateutil.parser import isoparse
+
+from ..types import UNSET, Unset
+
+if TYPE_CHECKING:
+ from ..models.sandbox_metric import SandboxMetric
+
+
+T = TypeVar("T", bound="RunningSandboxWithMetrics")
+
+
+@_attrs_define
+class RunningSandboxWithMetrics:
+ """
+ Attributes:
+ client_id (str): Identifier of the client
+ cpu_count (int): CPU cores for the sandbox
+ end_at (datetime.datetime): Time when the sandbox will expire
+ memory_mb (int): Memory for the sandbox in MB
+ sandbox_id (str): Identifier of the sandbox
+ started_at (datetime.datetime): Time when the sandbox was started
+ template_id (str): Identifier of the template from which is the sandbox created
+ alias (Union[Unset, str]): Alias of the template
+ metadata (Union[Unset, Any]):
+ metrics (Union[Unset, list['SandboxMetric']]):
+ """
+
+ client_id: str
+ cpu_count: int
+ end_at: datetime.datetime
+ memory_mb: int
+ sandbox_id: str
+ started_at: datetime.datetime
+ template_id: str
+ alias: Union[Unset, str] = UNSET
+ metadata: Union[Unset, Any] = UNSET
+ metrics: Union[Unset, list["SandboxMetric"]] = UNSET
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
+
+ def to_dict(self) -> dict[str, Any]:
+ client_id = self.client_id
+
+ cpu_count = self.cpu_count
+
+ end_at = self.end_at.isoformat()
+
+ memory_mb = self.memory_mb
+
+ sandbox_id = self.sandbox_id
+
+ started_at = self.started_at.isoformat()
+
+ template_id = self.template_id
+
+ alias = self.alias
+
+ metadata = self.metadata
+
+ metrics: Union[Unset, list[dict[str, Any]]] = UNSET
+ if not isinstance(self.metrics, Unset):
+ metrics = []
+ for metrics_item_data in self.metrics:
+ metrics_item = metrics_item_data.to_dict()
+ metrics.append(metrics_item)
+
+ field_dict: dict[str, Any] = {}
+ field_dict.update(self.additional_properties)
+ field_dict.update(
+ {
+ "clientID": client_id,
+ "cpuCount": cpu_count,
+ "endAt": end_at,
+ "memoryMB": memory_mb,
+ "sandboxID": sandbox_id,
+ "startedAt": started_at,
+ "templateID": template_id,
+ }
+ )
+ if alias is not UNSET:
+ field_dict["alias"] = alias
+ if metadata is not UNSET:
+ field_dict["metadata"] = metadata
+ if metrics is not UNSET:
+ field_dict["metrics"] = metrics
+
+ return field_dict
+
+ @classmethod
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
+ from ..models.sandbox_metric import SandboxMetric
+
+ d = src_dict.copy()
+ client_id = d.pop("clientID")
+
+ cpu_count = d.pop("cpuCount")
+
+ end_at = isoparse(d.pop("endAt"))
+
+ memory_mb = d.pop("memoryMB")
+
+ sandbox_id = d.pop("sandboxID")
+
+ started_at = isoparse(d.pop("startedAt"))
+
+ template_id = d.pop("templateID")
+
+ alias = d.pop("alias", UNSET)
+
+ metadata = d.pop("metadata", UNSET)
+
+ metrics = []
+ _metrics = d.pop("metrics", UNSET)
+ for metrics_item_data in _metrics or []:
+ metrics_item = SandboxMetric.from_dict(metrics_item_data)
+
+ metrics.append(metrics_item)
+
+ running_sandbox_with_metrics = cls(
+ client_id=client_id,
+ cpu_count=cpu_count,
+ end_at=end_at,
+ memory_mb=memory_mb,
+ sandbox_id=sandbox_id,
+ started_at=started_at,
+ template_id=template_id,
+ alias=alias,
+ metadata=metadata,
+ metrics=metrics,
+ )
+
+ running_sandbox_with_metrics.additional_properties = d
+ return running_sandbox_with_metrics
+
+ @property
+ def additional_keys(self) -> list[str]:
+ return list(self.additional_properties.keys())
+
+ def __getitem__(self, key: str) -> Any:
+ return self.additional_properties[key]
+
+ def __setitem__(self, key: str, value: Any) -> None:
+ self.additional_properties[key] = value
+
+ def __delitem__(self, key: str) -> None:
+ del self.additional_properties[key]
+
+ def __contains__(self, key: str) -> bool:
+ return key in self.additional_properties
diff --git a/packages/python-sdk/e2b/api/client/models/sandbox.py b/packages/python-sdk/e2b/api/client/models/sandbox.py
index baf5b4890..3514d71b1 100644
--- a/packages/python-sdk/e2b/api/client/models/sandbox.py
+++ b/packages/python-sdk/e2b/api/client/models/sandbox.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -24,9 +24,9 @@ class Sandbox:
sandbox_id: str
template_id: str
alias: Union[Unset, str] = UNSET
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
client_id = self.client_id
envd_version = self.envd_version
@@ -37,7 +37,7 @@ def to_dict(self) -> Dict[str, Any]:
alias = self.alias
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -53,7 +53,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
client_id = d.pop("clientID")
@@ -77,7 +77,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return sandbox
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/sandbox_log.py b/packages/python-sdk/e2b/api/client/models/sandbox_log.py
index 44c990e9a..ea192436d 100644
--- a/packages/python-sdk/e2b/api/client/models/sandbox_log.py
+++ b/packages/python-sdk/e2b/api/client/models/sandbox_log.py
@@ -1,5 +1,5 @@
import datetime
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -19,14 +19,14 @@ class SandboxLog:
line: str
timestamp: datetime.datetime
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
line = self.line
timestamp = self.timestamp.isoformat()
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -38,7 +38,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
line = d.pop("line")
@@ -53,7 +53,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return sandbox_log
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/sandbox_logs.py b/packages/python-sdk/e2b/api/client/models/sandbox_logs.py
index 0594fdcc8..0766a371b 100644
--- a/packages/python-sdk/e2b/api/client/models/sandbox_logs.py
+++ b/packages/python-sdk/e2b/api/client/models/sandbox_logs.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar
+from typing import TYPE_CHECKING, Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -14,19 +14,19 @@
class SandboxLogs:
"""
Attributes:
- logs (List['SandboxLog']): Logs of the sandbox
+ logs (list['SandboxLog']): Logs of the sandbox
"""
- logs: List["SandboxLog"]
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ logs: list["SandboxLog"]
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
logs = []
for logs_item_data in self.logs:
logs_item = logs_item_data.to_dict()
logs.append(logs_item)
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -37,7 +37,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
from ..models.sandbox_log import SandboxLog
d = src_dict.copy()
@@ -56,7 +56,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return sandbox_logs
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/sandbox_metric.py b/packages/python-sdk/e2b/api/client/models/sandbox_metric.py
index 2f636976f..54cea611c 100644
--- a/packages/python-sdk/e2b/api/client/models/sandbox_metric.py
+++ b/packages/python-sdk/e2b/api/client/models/sandbox_metric.py
@@ -1,5 +1,5 @@
import datetime
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -25,9 +25,9 @@ class SandboxMetric:
mem_total_mi_b: int
mem_used_mi_b: int
timestamp: datetime.datetime
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
cpu_count = self.cpu_count
cpu_used_pct = self.cpu_used_pct
@@ -38,7 +38,7 @@ def to_dict(self) -> Dict[str, Any]:
timestamp = self.timestamp.isoformat()
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -53,7 +53,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
cpu_count = d.pop("cpuCount")
@@ -77,7 +77,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return sandbox_metric
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/sandbox_state.py b/packages/python-sdk/e2b/api/client/models/sandbox_state.py
new file mode 100644
index 000000000..3aba3e923
--- /dev/null
+++ b/packages/python-sdk/e2b/api/client/models/sandbox_state.py
@@ -0,0 +1,14 @@
+from typing import Literal, cast
+
+SandboxState = Literal["paused", "running"]
+
+SANDBOX_STATE_VALUES: set[SandboxState] = {
+ "paused",
+ "running",
+}
+
+
+def check_sandbox_state(value: str) -> SandboxState:
+ if value in SANDBOX_STATE_VALUES:
+ return cast(SandboxState, value)
+ raise TypeError(f"Unexpected value {value!r}. Expected one of {SANDBOX_STATE_VALUES!r}")
diff --git a/packages/python-sdk/e2b/api/client/models/team.py b/packages/python-sdk/e2b/api/client/models/team.py
index c957f9384..f17cedd14 100644
--- a/packages/python-sdk/e2b/api/client/models/team.py
+++ b/packages/python-sdk/e2b/api/client/models/team.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -20,9 +20,9 @@ class Team:
is_default: bool
name: str
team_id: str
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
api_key = self.api_key
is_default = self.is_default
@@ -31,7 +31,7 @@ def to_dict(self) -> Dict[str, Any]:
team_id = self.team_id
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -45,7 +45,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
api_key = d.pop("apiKey")
@@ -66,7 +66,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return team
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/team_user.py b/packages/python-sdk/e2b/api/client/models/team_user.py
index 9aa62dfb5..b52b82ab4 100644
--- a/packages/python-sdk/e2b/api/client/models/team_user.py
+++ b/packages/python-sdk/e2b/api/client/models/team_user.py
@@ -1,4 +1,5 @@
-from typing import Any, Dict, List, Type, TypeVar
+from typing import Any, TypeVar
+from uuid import UUID
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -11,19 +12,19 @@ class TeamUser:
"""
Attributes:
email (str): Email of the user
- id (str): Identifier of the user
+ id (UUID): Identifier of the user
"""
email: str
- id: str
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ id: UUID
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
email = self.email
- id = self.id
+ id = str(self.id)
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -35,11 +36,11 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
email = d.pop("email")
- id = d.pop("id")
+ id = UUID(d.pop("id"))
team_user = cls(
email=email,
@@ -50,7 +51,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return team_user
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/template.py b/packages/python-sdk/e2b/api/client/models/template.py
index 7e76153e9..4c65a62de 100644
--- a/packages/python-sdk/e2b/api/client/models/template.py
+++ b/packages/python-sdk/e2b/api/client/models/template.py
@@ -1,5 +1,5 @@
import datetime
-from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast
+from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -29,7 +29,7 @@ class Template:
spawn_count (int): Number of times the template was used
template_id (str): Identifier of the template
updated_at (datetime.datetime): Time when the template was last updated
- aliases (Union[Unset, List[str]]): Aliases of the template
+ aliases (Union[Unset, list[str]]): Aliases of the template
"""
build_count: int
@@ -43,10 +43,10 @@ class Template:
spawn_count: int
template_id: str
updated_at: datetime.datetime
- aliases: Union[Unset, List[str]] = UNSET
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ aliases: Union[Unset, list[str]] = UNSET
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
from ..models.team_user import TeamUser
build_count = self.build_count
@@ -57,7 +57,7 @@ def to_dict(self) -> Dict[str, Any]:
created_at = self.created_at.isoformat()
- created_by: Union[Dict[str, Any], None]
+ created_by: Union[None, dict[str, Any]]
if isinstance(self.created_by, TeamUser):
created_by = self.created_by.to_dict()
else:
@@ -75,11 +75,11 @@ def to_dict(self) -> Dict[str, Any]:
updated_at = self.updated_at.isoformat()
- aliases: Union[Unset, List[str]] = UNSET
+ aliases: Union[Unset, list[str]] = UNSET
if not isinstance(self.aliases, Unset):
aliases = self.aliases
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -102,7 +102,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
from ..models.team_user import TeamUser
d = src_dict.copy()
@@ -141,7 +141,7 @@ def _parse_created_by(data: object) -> Union["TeamUser", None]:
updated_at = isoparse(d.pop("updatedAt"))
- aliases = cast(List[str], d.pop("aliases", UNSET))
+ aliases = cast(list[str], d.pop("aliases", UNSET))
template = cls(
build_count=build_count,
@@ -162,7 +162,7 @@ def _parse_created_by(data: object) -> Union["TeamUser", None]:
return template
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/template_build.py b/packages/python-sdk/e2b/api/client/models/template_build.py
index ed05e1114..167adc21b 100644
--- a/packages/python-sdk/e2b/api/client/models/template_build.py
+++ b/packages/python-sdk/e2b/api/client/models/template_build.py
@@ -1,9 +1,9 @@
-from typing import Any, Dict, List, Type, TypeVar, cast
+from typing import Any, TypeVar, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
-from ..models.template_build_status import TemplateBuildStatus
+from ..models.template_build_status import TemplateBuildStatus, check_template_build_status
T = TypeVar("T", bound="TemplateBuild")
@@ -13,27 +13,27 @@ class TemplateBuild:
"""
Attributes:
build_id (str): Identifier of the build
- logs (List[str]): Build logs
+ logs (list[str]): Build logs
status (TemplateBuildStatus): Status of the template
template_id (str): Identifier of the template
"""
build_id: str
- logs: List[str]
+ logs: list[str]
status: TemplateBuildStatus
template_id: str
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
build_id = self.build_id
logs = self.logs
- status = self.status.value
+ status: str = self.status
template_id = self.template_id
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -47,13 +47,13 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
build_id = d.pop("buildID")
- logs = cast(List[str], d.pop("logs"))
+ logs = cast(list[str], d.pop("logs"))
- status = TemplateBuildStatus(d.pop("status"))
+ status = check_template_build_status(d.pop("status"))
template_id = d.pop("templateID")
@@ -68,7 +68,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return template_build
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/template_build_request.py b/packages/python-sdk/e2b/api/client/models/template_build_request.py
index 08ac58490..18f1fb8f3 100644
--- a/packages/python-sdk/e2b/api/client/models/template_build_request.py
+++ b/packages/python-sdk/e2b/api/client/models/template_build_request.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -26,9 +26,9 @@ class TemplateBuildRequest:
memory_mb: Union[Unset, int] = UNSET
start_cmd: Union[Unset, str] = UNSET
team_id: Union[Unset, str] = UNSET
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
dockerfile = self.dockerfile
alias = self.alias
@@ -41,7 +41,7 @@ def to_dict(self) -> Dict[str, Any]:
team_id = self.team_id
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
@@ -62,7 +62,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
dockerfile = d.pop("dockerfile")
@@ -89,7 +89,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return template_build_request
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/models/template_build_status.py b/packages/python-sdk/e2b/api/client/models/template_build_status.py
index 9ecd81941..57a070cf5 100644
--- a/packages/python-sdk/e2b/api/client/models/template_build_status.py
+++ b/packages/python-sdk/e2b/api/client/models/template_build_status.py
@@ -1,10 +1,15 @@
-from enum import Enum
+from typing import Literal, cast
+TemplateBuildStatus = Literal["building", "error", "ready"]
-class TemplateBuildStatus(str, Enum):
- BUILDING = "building"
- ERROR = "error"
- READY = "ready"
+TEMPLATE_BUILD_STATUS_VALUES: set[TemplateBuildStatus] = {
+ "building",
+ "error",
+ "ready",
+}
- def __str__(self) -> str:
- return str(self.value)
+
+def check_template_build_status(value: str) -> TemplateBuildStatus:
+ if value in TEMPLATE_BUILD_STATUS_VALUES:
+ return cast(TemplateBuildStatus, value)
+ raise TypeError(f"Unexpected value {value!r}. Expected one of {TEMPLATE_BUILD_STATUS_VALUES!r}")
diff --git a/packages/python-sdk/e2b/api/client/models/template_update_request.py b/packages/python-sdk/e2b/api/client/models/template_update_request.py
index 66e235c41..17df72644 100644
--- a/packages/python-sdk/e2b/api/client/models/template_update_request.py
+++ b/packages/python-sdk/e2b/api/client/models/template_update_request.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Type, TypeVar, Union
+from typing import Any, TypeVar, Union
from attrs import define as _attrs_define
from attrs import field as _attrs_field
@@ -16,12 +16,12 @@ class TemplateUpdateRequest:
"""
public: Union[Unset, bool] = UNSET
- additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
- def to_dict(self) -> Dict[str, Any]:
+ def to_dict(self) -> dict[str, Any]:
public = self.public
- field_dict: Dict[str, Any] = {}
+ field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if public is not UNSET:
@@ -30,7 +30,7 @@ def to_dict(self) -> Dict[str, Any]:
return field_dict
@classmethod
- def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = src_dict.copy()
public = d.pop("public", UNSET)
@@ -42,7 +42,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
return template_update_request
@property
- def additional_keys(self) -> List[str]:
+ def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
diff --git a/packages/python-sdk/e2b/api/client/types.py b/packages/python-sdk/e2b/api/client/types.py
index 21fac106f..b9ed58b8a 100644
--- a/packages/python-sdk/e2b/api/client/types.py
+++ b/packages/python-sdk/e2b/api/client/types.py
@@ -1,7 +1,8 @@
"""Contains some shared types for properties"""
+from collections.abc import MutableMapping
from http import HTTPStatus
-from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar
+from typing import BinaryIO, Generic, Literal, Optional, TypeVar
from attrs import define
@@ -13,7 +14,7 @@ def __bool__(self) -> Literal[False]:
UNSET: Unset = Unset()
-FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]]
+FileJsonType = tuple[Optional[str], BinaryIO, Optional[str]]
@define
@@ -42,4 +43,4 @@ class Response(Generic[T]):
parsed: Optional[T]
-__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"]
+__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"]
diff --git a/packages/python-sdk/e2b/sandbox/sandbox_api.py b/packages/python-sdk/e2b/sandbox/sandbox_api.py
index 5030e174c..9a3e9858c 100644
--- a/packages/python-sdk/e2b/sandbox/sandbox_api.py
+++ b/packages/python-sdk/e2b/sandbox/sandbox_api.py
@@ -5,6 +5,7 @@
from httpx import Limits
+from e2b.api.client.models.sandbox_state import SandboxState
@dataclass
class SandboxInfo:
@@ -20,6 +21,8 @@ class SandboxInfo:
"""Saved sandbox metadata."""
started_at: datetime
"""Sandbox start time."""
+ state: SandboxState
+ """Sandbox state."""
@dataclass
diff --git a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py
index ee212f7db..069a2c2c2 100644
--- a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py
+++ b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py
@@ -1,5 +1,5 @@
import urllib.parse
-from typing import Dict, List, Optional
+from typing import Dict, List, Optional, Generator
from e2b.api import AsyncApiClient, SandboxCreateResponse, handle_api_exception
from e2b.api.client.api.sandboxes import (
@@ -16,32 +16,48 @@
PostSandboxesSandboxIDTimeoutBody,
ResumedSandbox,
)
+from e2b.api.client.models import SandboxState
+from e2b.api.client.types import UNSET, Unset
from e2b.connection_config import ConnectionConfig
from e2b.exceptions import TemplateException, NotFoundException
from e2b.sandbox.sandbox_api import SandboxApiBase, SandboxInfo, SandboxMetrics
from packaging.version import Version
+class ListSandboxesResponse:
+ def __init__(self, sandboxes: List[SandboxInfo], has_more_items: bool, next_token: Optional[str], iterator: Generator[SandboxInfo, None, None]):
+ self.sandboxes = sandboxes
+ self.has_more_items = has_more_items
+ self.next_token = next_token
+ self.iterator = iterator
+
+
class SandboxApi(SandboxApiBase):
@classmethod
async def list(
cls,
api_key: Optional[str] = None,
filters: Optional[Dict[str, str]] = None,
+ state: Optional[List[SandboxState]] = None,
domain: Optional[str] = None,
debug: Optional[bool] = None,
request_timeout: Optional[float] = None,
- ) -> List[SandboxInfo]:
+ limit: Optional[int] = None,
+ next_token: Optional[str] = None,
+ ):
"""
- List all running sandboxes.
+ List sandboxes with pagination.
:param api_key: API key to use for authentication, defaults to `E2B_API_KEY` environment variable
:param filters: Filter the list of sandboxes by metadata, e.g. `{"key": "value"}`, if there are multiple filters they are combined with AND.
+ :param state: Filter the list of sandboxes by state, e.g. `['paused', 'running']`
:param domain: Domain to use for the request, only relevant for self-hosted environments
:param debug: Enable debug mode, all requested are then sent to localhost
:param request_timeout: Timeout for the request in **seconds**
+ :param limit: Maximum number of sandboxes to return
+ :param next_token: Token for pagination
- :return: List of running sandboxes
+ :returns: ListSandboxesResponse containing sandboxes list, pagination info and iterator
"""
config = ConnectionConfig(
api_key=api_key,
@@ -50,7 +66,7 @@ async def list(
request_timeout=request_timeout,
)
- query = None
+ query = UNSET
if filters:
filters = {
urllib.parse.quote(k): urllib.parse.quote(v) for k, v in filters.items()
@@ -61,29 +77,90 @@ async def list(
res = await get_sandboxes.asyncio_detailed(
client=api_client,
query=query,
+ state=state or UNSET,
+ limit=limit,
+ next_token=next_token,
)
- if res.status_code >= 300:
- raise handle_api_exception(res)
+ if res.status_code >= 300:
+ raise handle_api_exception(res)
- if res.parsed is None:
- return []
+ if res.parsed is None:
+ return ListSandboxesResponse(
+ sandboxes=[],
+ has_more_items=False,
+ next_token=None,
+ iterator=cls._list_iterator(filters=filters, state=state, api_key=api_key, domain=domain, debug=debug, request_timeout=request_timeout)
+ )
- return [
- SandboxInfo(
- sandbox_id=SandboxApi._get_sandbox_id(
- sandbox.sandbox_id,
- sandbox.client_id,
- ),
- template_id=sandbox.template_id,
- name=sandbox.alias if isinstance(sandbox.alias, str) else None,
- metadata=(
- sandbox.metadata if isinstance(sandbox.metadata, dict) else {}
- ),
- started_at=sandbox.started_at,
+ token = res.headers.get("x-next-token")
+ has_more_items = bool(token)
+
+ sandboxes = [
+ SandboxInfo(
+ sandbox_id=SandboxApi._get_sandbox_id(
+ sandbox.sandbox_id,
+ sandbox.client_id,
+ ),
+ template_id=sandbox.template_id,
+ name=sandbox.alias if isinstance(sandbox.alias, str) else None,
+ metadata=(
+ sandbox.metadata if isinstance(sandbox.metadata, dict) else {}
+ ),
+ started_at=sandbox.started_at,
+ state=sandbox.state,
+ )
+ for sandbox in res.parsed
+ ]
+
+ return ListSandboxesResponse(
+ sandboxes=sandboxes,
+ has_more_items=has_more_items,
+ next_token=token,
+ iterator=cls._list_iterator(
+ limit=limit,
+ next_token=token,
+ filters=filters,
+ state=state,
+ api_key=api_key,
+ domain=domain,
+ debug=debug,
+ request_timeout=request_timeout
+ )
+ )
+
+ @classmethod
+ async def _list_iterator(
+ cls,
+ filters: Optional[Dict[str, str]] = None,
+ state: Optional[List[SandboxState]] = None,
+ api_key: Optional[str] = None,
+ domain: Optional[str] = None,
+ debug: Optional[bool] = None,
+ request_timeout: Optional[float] = None,
+ limit: Optional[int] = None,
+ next_token: Optional[str] = None,
+ ):
+ next_page = True
+ token = next_token
+
+ while next_page:
+ result = await cls.list(
+ filters=filters,
+ state=state,
+ api_key=api_key,
+ domain=domain,
+ debug=debug,
+ request_timeout=request_timeout,
+ limit=limit,
+ next_token=token,
)
- for sandbox in res.parsed
- ]
+
+ next_page = result.has_more_items
+ token = result.next_token
+
+ for sandbox in result.sandboxes:
+ yield sandbox
@classmethod
async def _cls_kill(
diff --git a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py
index bc49b4efe..846d474c6 100644
--- a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py
+++ b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py
@@ -1,5 +1,5 @@
import urllib.parse
-from typing import Dict, List, Optional
+from typing import Dict, List, Optional, Generator
from e2b.api import ApiClient, SandboxCreateResponse, handle_api_exception
from e2b.api.client.api.sandboxes import (
@@ -16,6 +16,8 @@
PostSandboxesSandboxIDTimeoutBody,
ResumedSandbox,
)
+from e2b.api.client.models import SandboxState
+from e2b.api.client.types import UNSET, Unset
from e2b.connection_config import ConnectionConfig
from e2b.exceptions import NotFoundException, TemplateException
from e2b.sandbox.sandbox_api import SandboxApiBase, SandboxInfo, SandboxMetrics
@@ -23,26 +25,40 @@
from packaging.version import Version
+class ListSandboxesResponse:
+ def __init__(self, sandboxes: List[SandboxInfo], has_more_items: bool, next_token: Optional[str], iterator: Generator[SandboxInfo, None, None]):
+ self.sandboxes = sandboxes
+ self.has_more_items = has_more_items
+ self.next_token = next_token
+ self.iterator = iterator
+
+
class SandboxApi(SandboxApiBase):
@classmethod
def list(
cls,
api_key: Optional[str] = None,
filters: Optional[Dict[str, str]] = None,
+ state: Optional[List[SandboxState]] = None,
domain: Optional[str] = None,
debug: Optional[bool] = None,
request_timeout: Optional[float] = None,
- ) -> List[SandboxInfo]:
+ limit: Optional[int] = None,
+ next_token: Optional[str] = None,
+ ):
"""
- List all running sandboxes.
+ List sandboxes with pagination.
:param api_key: API key to use for authentication, defaults to `E2B_API_KEY` environment variable
:param filters: Filter the list of sandboxes by metadata, e.g. `{"key": "value"}`, if there are multiple filters they are combined with AND.
+ :param state: Filter the list of sandboxes by state, e.g. `['paused', 'running']`
:param domain: Domain to use for the request, only relevant for self-hosted environments
:param debug: Enable debug mode, all requested are then sent to localhost
:param request_timeout: Timeout for the request in **seconds**
+ :param limit: Maximum number of sandboxes to return
+ :param next_token: Token for pagination
- :return: List of running sandboxes
+ :returns: ListSandboxesResponse containing sandboxes list, pagination info and iterator
"""
config = ConnectionConfig(
api_key=api_key,
@@ -51,8 +67,7 @@ def list(
request_timeout=request_timeout,
)
- # Convert filters to the format expected by the API
- query = None
+ query = UNSET
if filters:
filters = {
urllib.parse.quote(k): urllib.parse.quote(v) for k, v in filters.items()
@@ -62,15 +77,29 @@ def list(
with ApiClient(
config, transport=HTTPTransport(limits=SandboxApiBase._limits)
) as api_client:
- res = get_sandboxes.sync_detailed(client=api_client, query=query)
+ res = get_sandboxes.sync_detailed(
+ client=api_client,
+ query=query,
+ state=state or UNSET,
+ limit=limit,
+ next_token=next_token,
+ )
if res.status_code >= 300:
raise handle_api_exception(res)
if res.parsed is None:
- return []
+ return ListSandboxesResponse(
+ sandboxes=[],
+ has_more_items=False,
+ next_token=None,
+ iterator=cls._list_iterator(filters=filters, state=state, api_key=api_key, domain=domain, debug=debug, request_timeout=request_timeout)
+ )
- return [
+ token = res.headers.get("x-next-token")
+ has_more_items = bool(token)
+
+ sandboxes = [
SandboxInfo(
sandbox_id=SandboxApi._get_sandbox_id(
sandbox.sandbox_id,
@@ -82,10 +111,59 @@ def list(
sandbox.metadata if isinstance(sandbox.metadata, dict) else {}
),
started_at=sandbox.started_at,
+ state=sandbox.state,
)
for sandbox in res.parsed
]
+ return ListSandboxesResponse(
+ sandboxes=sandboxes,
+ has_more_items=has_more_items,
+ next_token=token,
+ iterator=cls._list_iterator(
+ limit=limit,
+ next_token=token,
+ filters=filters,
+ state=state,
+ api_key=api_key,
+ domain=domain,
+ debug=debug,
+ request_timeout=request_timeout
+ )
+ )
+
+ @classmethod
+ def _list_iterator(
+ cls,
+ filters: Optional[Dict[str, str]] = None,
+ state: Optional[List[SandboxState]] = None,
+ api_key: Optional[str] = None,
+ domain: Optional[str] = None,
+ debug: Optional[bool] = None,
+ request_timeout: Optional[float] = None,
+ limit: Optional[int] = None,
+ next_token: Optional[str] = None,
+ ) -> Generator[SandboxInfo, None, None]:
+ next_page = True
+ token = next_token
+
+ while next_page:
+ result = cls.list(
+ filters=filters,
+ state=state,
+ api_key=api_key,
+ domain=domain,
+ debug=debug,
+ request_timeout=request_timeout,
+ limit=limit,
+ next_token=token,
+ )
+
+ next_page = result.has_more_items
+ token = result.next_token
+
+ for sandbox in result.sandboxes:
+ yield sandbox
@classmethod
def _cls_kill(
cls,
diff --git a/packages/python-sdk/openapi-generator.yml b/packages/python-sdk/openapi-generator.yml
new file mode 100644
index 000000000..120eae0a7
--- /dev/null
+++ b/packages/python-sdk/openapi-generator.yml
@@ -0,0 +1 @@
+literal_enums: true
diff --git a/packages/python-sdk/tests/async/api_async/test_sbx_kill.py b/packages/python-sdk/tests/async/api_async/test_sbx_kill.py
index 5e9a9d80a..9194cee29 100644
--- a/packages/python-sdk/tests/async/api_async/test_sbx_kill.py
+++ b/packages/python-sdk/tests/async/api_async/test_sbx_kill.py
@@ -8,7 +8,7 @@ async def test_kill_existing_sandbox(async_sandbox: AsyncSandbox):
assert await AsyncSandbox.kill(async_sandbox.sandbox_id) == True
list = await AsyncSandbox.list()
- assert async_sandbox.sandbox_id not in [s.sandbox_id for s in list]
+ assert async_sandbox.sandbox_id not in [s.sandbox_id for s in list.sandboxes]
@pytest.mark.skip_debug()
diff --git a/packages/python-sdk/tests/async/api_async/test_sbx_list.py b/packages/python-sdk/tests/async/api_async/test_sbx_list.py
index 4e58e736b..71df0b93a 100644
--- a/packages/python-sdk/tests/async/api_async/test_sbx_list.py
+++ b/packages/python-sdk/tests/async/api_async/test_sbx_list.py
@@ -9,8 +9,8 @@
@pytest.mark.skip_debug()
async def test_list_sandboxes(async_sandbox: AsyncSandbox):
sandboxes = await AsyncSandbox.list()
- assert len(sandboxes) > 0
- assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes]
+ assert len(sandboxes.sandboxes) > 0
+ assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
@pytest.mark.skip_debug()
@@ -20,7 +20,100 @@ async def test_list_sandboxes_with_filter(async_sandbox: AsyncSandbox):
try:
# There's an extra sandbox created by the test runner
sandboxes = await AsyncSandbox.list(filters={"unique_id": unique_id})
- assert len(sandboxes) == 1
- assert sandboxes[0].metadata["unique_id"] == unique_id
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].metadata["unique_id"] == unique_id
finally:
await sbx.kill()
+
+@pytest.mark.skip_debug()
+async def test_list_paused_sandboxes(async_sandbox: AsyncSandbox):
+ paused_sandbox = await async_sandbox.pause()
+ paused_sandbox_id = paused_sandbox.split("-")[0] + "-" + "00000000"
+ sandboxes = await AsyncSandbox.list(state=["paused"])
+ assert len(sandboxes.sandboxes) > 0
+ assert paused_sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+@pytest.mark.skip_debug()
+async def test_list_running_sandboxes(async_sandbox: AsyncSandbox):
+ sandboxes = await AsyncSandbox.list(state=["running"])
+ assert len(sandboxes.sandboxes) > 0
+ assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+@pytest.mark.skip_debug()
+async def test_list_sandboxes_with_limit(async_sandbox: AsyncSandbox):
+ sandboxes = await AsyncSandbox.list(limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+@pytest.mark.skip_debug()
+async def test_paginate_running_sandboxes(async_sandbox: AsyncSandbox):
+ extra_sbx = await AsyncSandbox.create()
+
+ # Check first page
+ sandboxes = await AsyncSandbox.list(state=["running"], limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].state == "running"
+ assert sandboxes.has_more_items is True
+ assert sandboxes.next_token is not None
+ assert extra_sbx.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+ # Check second page
+ sandboxes2 = await AsyncSandbox.list(state=["running"], next_token=sandboxes.next_token, limit=1)
+ assert len(sandboxes2.sandboxes) == 1
+ assert sandboxes2.sandboxes[0].state == "running"
+ assert sandboxes2.has_more_items is False
+ assert sandboxes2.next_token is None
+ assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes2.sandboxes]
+
+ await extra_sbx.kill()
+
+@pytest.mark.skip_debug()
+async def test_paginate_paused_sandboxes(async_sandbox: AsyncSandbox):
+ # Pause the current sandbox
+ await async_sandbox.pause()
+
+ # Create and pause a new sandbox
+ extra_sbx = await AsyncSandbox.create()
+ await extra_sbx.pause()
+
+ # Check first page
+ sandboxes = await AsyncSandbox.list(state=["paused"], limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].state == "paused"
+ assert sandboxes.has_more_items is True
+ assert sandboxes.next_token is not None
+ assert extra_sbx.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+ # Check second page
+ sandboxes2 = await AsyncSandbox.list(state=["paused"], next_token=sandboxes.next_token, limit=1)
+ assert len(sandboxes2.sandboxes) == 1
+ assert sandboxes2.sandboxes[0].state == "paused"
+ assert sandboxes2.has_more_items is False
+ assert sandboxes2.next_token is None
+ assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes2.sandboxes]
+
+ await extra_sbx.kill()
+
+@pytest.mark.skip_debug()
+async def test_paginate_paused_and_running_sandboxes(async_sandbox: AsyncSandbox):
+ # Create and pause a new sandbox
+ extra_sbx = await AsyncSandbox.create()
+ await extra_sbx.pause()
+
+ # Check first page
+ sandboxes = await AsyncSandbox.list(state=["paused", "running"], limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].state == "paused"
+ assert sandboxes.has_more_items is True
+ assert sandboxes.next_token is not None
+ assert extra_sbx.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+ # Check second page
+ sandboxes2 = await AsyncSandbox.list(state=["paused", "running"], next_token=sandboxes.next_token, limit=1)
+ assert len(sandboxes2.sandboxes) == 1
+ assert sandboxes2.sandboxes[0].state == "running"
+ assert sandboxes2.has_more_items is False
+ assert sandboxes2.next_token is None
+ assert async_sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes2.sandboxes]
+
+ await extra_sbx.kill()
diff --git a/packages/python-sdk/tests/async/sandbox_async/test_create.py b/packages/python-sdk/tests/async/sandbox_async/test_create.py
index 2574d5d17..5a77e7291 100644
--- a/packages/python-sdk/tests/async/sandbox_async/test_create.py
+++ b/packages/python-sdk/tests/async/sandbox_async/test_create.py
@@ -22,7 +22,7 @@ async def test_metadata(template):
try:
sbxs = await AsyncSandbox.list()
- for sbx_info in sbxs:
+ for sbx_info in sbxs.sandboxes:
if sbx.sandbox_id == sbx_info.sandbox_id:
assert sbx_info.metadata is not None
assert sbx_info.metadata["test-key"] == "test-value"
diff --git a/packages/python-sdk/tests/async/sandbox_async/test_kill.py b/packages/python-sdk/tests/async/sandbox_async/test_kill.py
index ea44b2fb7..2c52958eb 100644
--- a/packages/python-sdk/tests/async/sandbox_async/test_kill.py
+++ b/packages/python-sdk/tests/async/sandbox_async/test_kill.py
@@ -8,4 +8,4 @@ async def test_kill(async_sandbox: AsyncSandbox):
await async_sandbox.kill()
list = await AsyncSandbox.list()
- assert async_sandbox.sandbox_id not in [s.sandbox_id for s in list]
+ assert async_sandbox.sandbox_id not in [s.sandbox_id for s in list.sandboxes]
diff --git a/packages/python-sdk/tests/sync/api_sync/test_sbx_kill.py b/packages/python-sdk/tests/sync/api_sync/test_sbx_kill.py
index 0958e657c..1db8f3229 100644
--- a/packages/python-sdk/tests/sync/api_sync/test_sbx_kill.py
+++ b/packages/python-sdk/tests/sync/api_sync/test_sbx_kill.py
@@ -8,7 +8,7 @@ def test_kill_existing_sandbox(sandbox: Sandbox):
assert Sandbox.kill(sandbox.sandbox_id) == True
list = Sandbox.list()
- assert sandbox.sandbox_id not in [s.sandbox_id for s in list]
+ assert sandbox.sandbox_id not in [s.sandbox_id for s in list.sandboxes]
@pytest.mark.skip_debug()
diff --git a/packages/python-sdk/tests/sync/api_sync/test_sbx_list.py b/packages/python-sdk/tests/sync/api_sync/test_sbx_list.py
index cc2541a5f..2503cb9f3 100644
--- a/packages/python-sdk/tests/sync/api_sync/test_sbx_list.py
+++ b/packages/python-sdk/tests/sync/api_sync/test_sbx_list.py
@@ -9,14 +9,107 @@
@pytest.mark.skip_debug()
def test_list_sandboxes(sandbox: Sandbox):
sandboxes = Sandbox.list()
- assert len(sandboxes) > 0
- assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes]
+ assert len(sandboxes.sandboxes) > 0
+ assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
@pytest.mark.skip_debug()
def test_list_sandboxes_with_filter(sandbox: Sandbox):
unique_id = "".join(random.choices(string.ascii_letters, k=5))
- Sandbox(metadata={"unique_id": unique_id})
- sandboxes = Sandbox.list(filters={"unique_id": unique_id})
- assert len(sandboxes) == 1
- assert sandboxes[0].metadata["unique_id"] == unique_id
+ extra_sbx = Sandbox(metadata={"unique_id": unique_id})
+ try:
+ # There's an extra sandbox created by the test runner
+ sandboxes = Sandbox.list(filters={"unique_id": unique_id})
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].metadata["unique_id"] == unique_id
+ finally:
+ extra_sbx.kill()
+
+@pytest.mark.skip_debug()
+def test_list_paused_sandboxes(sandbox: Sandbox):
+ paused_sandbox = sandbox.pause()
+ paused_sandbox_id = paused_sandbox.split("-")[0] + "-" + "00000000"
+ sandboxes = Sandbox.list(state=["paused"])
+ assert len(sandboxes.sandboxes) > 0
+ assert paused_sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+@pytest.mark.skip_debug()
+def test_list_running_sandboxes(sandbox: Sandbox):
+ sandboxes = Sandbox.list(state=["running"])
+ assert len(sandboxes.sandboxes) > 0
+ assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+@pytest.mark.skip_debug()
+def test_list_sandboxes_with_limit(sandbox: Sandbox):
+ sandboxes = Sandbox.list(limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+@pytest.mark.skip_debug()
+def test_paginate_running_sandboxes(sandbox: Sandbox):
+ extra_sbx = Sandbox()
+ # Check first page
+ try:
+ sandboxes = Sandbox.list(state=["running"], limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].state == "running"
+ assert sandboxes.has_more_items is True
+ assert sandboxes.next_token is not None
+ assert extra_sbx.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+ # Check second page
+ sandboxes2 = Sandbox.list(state=["running"], next_token=sandboxes.next_token, limit=1)
+ assert len(sandboxes2.sandboxes) == 1
+ assert sandboxes2.sandboxes[0].state == "running"
+ assert sandboxes2.has_more_items is False
+ assert sandboxes2.next_token is None
+ assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes2.sandboxes]
+ finally:
+ extra_sbx.kill()
+
+@pytest.mark.skip_debug()
+def test_paginate_paused_sandboxes(sandbox: Sandbox):
+ # Pause the current sandbox
+ sandbox.pause()
+
+ # Create and pause a new sandbox
+ extra_sbx = Sandbox()
+ extra_sbx.pause()
+
+ # Check first page
+ sandboxes = Sandbox.list(state=["paused"], limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].state == "paused"
+ assert sandboxes.has_more_items is True
+ assert sandboxes.next_token is not None
+ assert extra_sbx.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+ # Check second page
+ sandboxes2 = Sandbox.list(state=["paused"], next_token=sandboxes.next_token, limit=1)
+ assert len(sandboxes2.sandboxes) == 1
+ assert sandboxes2.sandboxes[0].state == "paused"
+ assert sandboxes2.has_more_items is False
+ assert sandboxes2.next_token is None
+ assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes2.sandboxes]
+
+@pytest.mark.skip_debug()
+def test_paginate_paused_and_running_sandboxes(sandbox: Sandbox):
+ # Create and pause a new sandbox
+ extra_sbx = Sandbox()
+ extra_sbx.pause()
+
+ # Check first page
+ sandboxes = Sandbox.list(state=["paused", "running"], limit=1)
+ assert len(sandboxes.sandboxes) == 1
+ assert sandboxes.sandboxes[0].state == "paused"
+ assert sandboxes.has_more_items is True
+ assert sandboxes.next_token is not None
+ assert extra_sbx.sandbox_id in [sbx.sandbox_id for sbx in sandboxes.sandboxes]
+
+ # Check second page
+ sandboxes2 = Sandbox.list(state=["paused", "running"], next_token=sandboxes.next_token, limit=1)
+ assert len(sandboxes2.sandboxes) == 1
+ assert sandboxes2.sandboxes[0].state == "running"
+ assert sandboxes2.has_more_items is False
+ assert sandboxes2.next_token is None
+ assert sandbox.sandbox_id in [sbx.sandbox_id for sbx in sandboxes2.sandboxes]
diff --git a/packages/python-sdk/tests/sync/sandbox_sync/test_create.py b/packages/python-sdk/tests/sync/sandbox_sync/test_create.py
index 56450138f..beb599e90 100644
--- a/packages/python-sdk/tests/sync/sandbox_sync/test_create.py
+++ b/packages/python-sdk/tests/sync/sandbox_sync/test_create.py
@@ -20,7 +20,7 @@ def test_metadata(template):
try:
sbxs = Sandbox.list()
- for sbx_info in sbxs:
+ for sbx_info in sbxs.sandboxes:
if sbx.sandbox_id == sbx_info.sandbox_id:
assert sbx_info.metadata is not None
assert sbx_info.metadata["test-key"] == "test-value"
diff --git a/packages/python-sdk/tests/sync/sandbox_sync/test_kill.py b/packages/python-sdk/tests/sync/sandbox_sync/test_kill.py
index f6ab6e538..26a7e37ec 100644
--- a/packages/python-sdk/tests/sync/sandbox_sync/test_kill.py
+++ b/packages/python-sdk/tests/sync/sandbox_sync/test_kill.py
@@ -8,4 +8,4 @@ def test_kill(sandbox: Sandbox):
sandbox.kill()
list = Sandbox.list()
- assert sandbox.sandbox_id not in [s.sandbox_id for s in list]
+ assert sandbox.sandbox_id not in [s.sandbox_id for s in list.sandboxes]
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4cf51b902..9f4229d21 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -379,8 +379,8 @@ importers:
specifier: ^16.14.20
version: 16.14.20
openapi-typescript:
- specifier: ^6.7.6
- version: 6.7.6
+ specifier: ^7.6.1
+ version: 7.6.1(typescript@5.5.3)
playwright:
specifier: ^1.48.0
version: 1.48.0
@@ -478,10 +478,6 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
- '@babel/code-frame@7.24.7':
- resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
- engines: {node: '>=6.9.0'}
-
'@babel/code-frame@7.25.7':
resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
engines: {node: '>=6.9.0'}
@@ -574,10 +570,6 @@ packages:
resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.24.7':
- resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-validator-identifier@7.25.7':
resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
engines: {node: '>=6.9.0'}
@@ -598,10 +590,6 @@ packages:
resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
engines: {node: '>=6.9.0'}
- '@babel/highlight@7.24.7':
- resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
- engines: {node: '>=6.9.0'}
-
'@babel/highlight@7.25.7':
resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
engines: {node: '>=6.9.0'}
@@ -1213,10 +1201,6 @@ packages:
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- '@fastify/busboy@2.0.0':
- resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==}
- engines: {node: '>=14'}
-
'@floating-ui/core@1.5.0':
resolution: {integrity: sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==}
@@ -2421,6 +2405,16 @@ packages:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ '@redocly/ajv@8.11.2':
+ resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
+
+ '@redocly/config@0.20.3':
+ resolution: {integrity: sha512-Nyyv1Bj7GgYwj/l46O0nkH1GTKWbO3Ixe7KFcn021aZipkZd+z8Vlu1BwkhqtVgivcKaClaExtWU/lDHkjBzag==}
+
+ '@redocly/openapi-core@1.28.5':
+ resolution: {integrity: sha512-eAuL+x1oBbodJksPm4UpFU57A6z1n1rx9JNpD87CObwtbRf5EzW29Ofd0t057bPGcHc8cYZtZzJ69dcRQ9xGdg==}
+ engines: {node: '>=18.17.0', npm: '>=9.5.0'}
+
'@rollup/plugin-commonjs@26.0.1':
resolution: {integrity: sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==}
engines: {node: '>=16.0.0 || 14 >= 14.17'}
@@ -3380,6 +3374,10 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
+ agent-base@7.1.3:
+ resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+ engines: {node: '>= 14'}
+
agentkeepalive@4.5.0:
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
engines: {node: '>= 8.0.0'}
@@ -3761,6 +3759,9 @@ packages:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+ change-case@5.4.4:
+ resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
+
changeset@0.2.6:
resolution: {integrity: sha512-d21ym9zLPOKMVhIa8ulJo5IV3QR2NNdK6BWuwg48qJA0XSQaMeDjo1UGThcTn7YDmU08j3UpKyFNvb3zplk8mw==}
@@ -3900,6 +3901,9 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
+ colorette@1.4.0:
+ resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
+
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@@ -5025,6 +5029,10 @@ packages:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
human-id@1.0.2:
resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==}
@@ -5094,6 +5102,10 @@ packages:
indento@1.1.13:
resolution: {integrity: sha512-YZWk3mreBEM7sBPddsiQnW9Z8SGg/gNpFfscJq00HCDS7pxcQWWWMSVKJU7YkTRyDu1Zv2s8zaK8gQWKmCXHlg==}
+ index-to-position@0.1.2:
+ resolution: {integrity: sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==}
+ engines: {node: '>=18'}
+
individual@3.0.0:
resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==}
@@ -5410,6 +5422,10 @@ packages:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
+ js-levenshtein@1.1.6:
+ resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
+ engines: {node: '>=0.10.0'}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -5965,6 +5981,10 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ minimatch@5.1.6:
+ resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+ engines: {node: '>=10'}
+
minimatch@8.0.4:
resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -6335,9 +6355,11 @@ packages:
openapi-typescript-helpers@0.0.8:
resolution: {integrity: sha512-1eNjQtbfNi5Z/kFhagDIaIRj6qqDzhjNJKz8cmMW0CVdGwT6e1GLbAfgI0d28VTJa1A8jz82jm/4dG8qNoNS8g==}
- openapi-typescript@6.7.6:
- resolution: {integrity: sha512-c/hfooPx+RBIOPM09GSxABOZhYPblDoyaGhqBkD/59vtpN21jEuWKDlM0KYTvqJVlSYjKs0tBcIdeXKChlSPtw==}
+ openapi-typescript@7.6.1:
+ resolution: {integrity: sha512-F7RXEeo/heF3O9lOXo2bNjCOtfp7u+D6W3a3VNEH2xE6v+fxLtn5nq0uvUcA1F5aT+CMhNeC5Uqtg5tlXFX/ag==}
hasBin: true
+ peerDependencies:
+ typescript: ^5.x
opentelemetry-instrumentation-fetch-node@1.2.3:
resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==}
@@ -6447,6 +6469,10 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
+ parse-json@8.1.0:
+ resolution: {integrity: sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==}
+ engines: {node: '>=18'}
+
parse-ms@3.0.0:
resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==}
engines: {node: '>=12'}
@@ -6533,9 +6559,6 @@ packages:
picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
- picocolors@1.1.0:
- resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
-
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -6579,6 +6602,10 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ pluralize@8.0.0:
+ resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
+ engines: {node: '>=4'}
+
postcss-import@15.1.0:
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'}
@@ -7774,10 +7801,6 @@ packages:
undici-types@6.19.8:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
- undici@5.28.4:
- resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
- engines: {node: '>=14.0'}
-
unescape-js@1.1.4:
resolution: {integrity: sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g==}
@@ -7891,6 +7914,9 @@ packages:
resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==}
engines: {node: '>=14.16'}
+ uri-js-replace@1.0.1:
+ resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
+
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -8229,6 +8255,9 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ yaml-ast-parser@0.0.43:
+ resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
+
yaml@2.3.2:
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
engines: {node: '>= 14'}
@@ -8401,15 +8430,10 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
- '@babel/code-frame@7.24.7':
- dependencies:
- '@babel/highlight': 7.24.7
- picocolors: 1.1.0
-
'@babel/code-frame@7.25.7':
dependencies:
'@babel/highlight': 7.25.7
- picocolors: 1.1.0
+ picocolors: 1.1.1
'@babel/compat-data@7.24.9': {}
@@ -8418,7 +8442,7 @@ snapshots:
'@babel/core@7.24.9':
dependencies:
'@ampproject/remapping': 2.3.0
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.25.7
'@babel/generator': 7.24.10
'@babel/helper-compilation-targets': 7.24.8
'@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9)
@@ -8428,7 +8452,7 @@ snapshots:
'@babel/traverse': 7.24.8
'@babel/types': 7.24.9
convert-source-map: 2.0.0
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -8519,7 +8543,7 @@ snapshots:
'@babel/helper-module-imports': 7.24.7
'@babel/helper-simple-access': 7.24.7
'@babel/helper-split-export-declaration': 7.24.7
- '@babel/helper-validator-identifier': 7.24.7
+ '@babel/helper-validator-identifier': 7.25.7
transitivePeerDependencies:
- supports-color
@@ -8557,8 +8581,6 @@ snapshots:
'@babel/helper-string-parser@7.25.7': {}
- '@babel/helper-validator-identifier@7.24.7': {}
-
'@babel/helper-validator-identifier@7.25.7': {}
'@babel/helper-validator-option@7.24.8': {}
@@ -8575,19 +8597,12 @@ snapshots:
'@babel/template': 7.25.7
'@babel/types': 7.25.8
- '@babel/highlight@7.24.7':
- dependencies:
- '@babel/helper-validator-identifier': 7.24.7
- chalk: 2.4.2
- js-tokens: 4.0.0
- picocolors: 1.1.0
-
'@babel/highlight@7.25.7':
dependencies:
'@babel/helper-validator-identifier': 7.25.7
chalk: 2.4.2
js-tokens: 4.0.0
- picocolors: 1.1.0
+ picocolors: 1.1.1
'@babel/parser@7.24.8':
dependencies:
@@ -8613,7 +8628,7 @@ snapshots:
'@babel/template@7.24.7':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.25.7
'@babel/parser': 7.24.8
'@babel/types': 7.24.9
@@ -8625,7 +8640,7 @@ snapshots:
'@babel/traverse@7.24.8':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.25.7
'@babel/generator': 7.24.10
'@babel/helper-environment-visitor': 7.24.7
'@babel/helper-function-name': 7.24.7
@@ -8633,7 +8648,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.24.7
'@babel/parser': 7.24.8
'@babel/types': 7.24.9
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -8645,7 +8660,7 @@ snapshots:
'@babel/parser': 7.25.8
'@babel/template': 7.25.7
'@babel/types': 7.25.8
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -8653,7 +8668,7 @@ snapshots:
'@babel/types@7.24.9':
dependencies:
'@babel/helper-string-parser': 7.24.8
- '@babel/helper-validator-identifier': 7.24.7
+ '@babel/helper-validator-identifier': 7.25.7
to-fast-properties: 2.0.0
'@babel/types@7.25.8':
@@ -9110,7 +9125,7 @@ snapshots:
'@eslint/eslintrc@2.1.2':
dependencies:
ajv: 6.12.6
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
espree: 9.6.1
globals: 13.21.0
ignore: 5.2.4
@@ -9124,7 +9139,7 @@ snapshots:
'@eslint/eslintrc@2.1.4':
dependencies:
ajv: 6.12.6
- debug: 4.4.0
+ debug: 4.4.0(supports-color@9.4.0)
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.2
@@ -9139,8 +9154,6 @@ snapshots:
'@eslint/js@8.57.1': {}
- '@fastify/busboy@2.0.0': {}
-
'@floating-ui/core@1.5.0':
dependencies:
'@floating-ui/utils': 0.1.4
@@ -9171,7 +9184,7 @@ snapshots:
'@humanwhocodes/config-array@0.11.11':
dependencies:
'@humanwhocodes/object-schema': 1.2.1
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -9179,7 +9192,7 @@ snapshots:
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
- debug: 4.4.0
+ debug: 4.4.0(supports-color@9.4.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -10569,6 +10582,29 @@ snapshots:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
+ '@redocly/ajv@8.11.2':
+ dependencies:
+ fast-deep-equal: 3.1.3
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+ uri-js-replace: 1.0.1
+
+ '@redocly/config@0.20.3': {}
+
+ '@redocly/openapi-core@1.28.5(supports-color@9.4.0)':
+ dependencies:
+ '@redocly/ajv': 8.11.2
+ '@redocly/config': 0.20.3
+ colorette: 1.4.0
+ https-proxy-agent: 7.0.6(supports-color@9.4.0)
+ js-levenshtein: 1.1.6
+ js-yaml: 4.1.0
+ minimatch: 5.1.6
+ pluralize: 8.0.0
+ yaml-ast-parser: 0.0.43
+ transitivePeerDependencies:
+ - supports-color
+
'@rollup/plugin-commonjs@26.0.1(rollup@3.29.4)':
dependencies:
'@rollup/pluginutils': 5.0.4(rollup@3.29.4)
@@ -11067,7 +11103,7 @@ snapshots:
'@testing-library/dom@10.4.0':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.25.7
'@babel/runtime': 7.22.15
'@types/aria-query': 5.0.4
aria-query: 5.3.0
@@ -11422,7 +11458,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 6.7.2(typescript@5.5.3)
'@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.5.3)
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
eslint: 8.49.0
ts-api-utils: 1.0.3(typescript@5.5.3)
optionalDependencies:
@@ -11454,7 +11490,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 6.21.0
'@typescript-eslint/visitor-keys': 6.21.0
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.3
@@ -11469,7 +11505,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 6.7.2
'@typescript-eslint/visitor-keys': 6.7.2
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
@@ -11483,7 +11519,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 6.8.0
'@typescript-eslint/visitor-keys': 6.8.0
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
@@ -11761,10 +11797,12 @@ snapshots:
agent-base@6.0.2:
dependencies:
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
transitivePeerDependencies:
- supports-color
+ agent-base@7.1.3: {}
+
agentkeepalive@4.5.0:
dependencies:
humanize-ms: 1.2.1
@@ -12204,6 +12242,8 @@ snapshots:
chalk@5.3.0: {}
+ change-case@5.4.4: {}
+
changeset@0.2.6:
dependencies:
udc: 1.0.1
@@ -12344,6 +12384,8 @@ snapshots:
color-convert: 2.0.1
color-string: 1.9.1
+ colorette@1.4.0: {}
+
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
@@ -12537,9 +12579,11 @@ snapshots:
dependencies:
ms: 2.1.3
- debug@4.4.0:
+ debug@4.4.0(supports-color@9.4.0):
dependencies:
ms: 2.1.3
+ optionalDependencies:
+ supports-color: 9.4.0
decamelize-keys@1.1.1:
dependencies:
@@ -13136,7 +13180,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.4.0
+ debug: 4.4.0(supports-color@9.4.0)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -13501,7 +13545,7 @@ snapshots:
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.6
- minimatch: 9.0.3
+ minimatch: 9.0.5
minipass: 7.0.3
path-scurry: 1.10.1
@@ -13726,7 +13770,7 @@ snapshots:
dependencies:
'@tootallnate/once': 2.0.0
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
transitivePeerDependencies:
- supports-color
@@ -13738,7 +13782,14 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6(supports-color@9.4.0):
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.0(supports-color@9.4.0)
transitivePeerDependencies:
- supports-color
@@ -13767,7 +13818,7 @@ snapshots:
ignore-walk@6.0.3:
dependencies:
- minimatch: 9.0.3
+ minimatch: 9.0.5
ignore@5.2.4: {}
@@ -13803,6 +13854,8 @@ snapshots:
indento@1.1.13: {}
+ index-to-position@0.1.2: {}
+
individual@3.0.0: {}
inflight@1.0.6:
@@ -14067,7 +14120,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- '@types/node': 20.6.3
+ '@types/node': 18.18.6
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -14081,6 +14134,8 @@ snapshots:
joycon@3.1.1: {}
+ js-levenshtein@1.1.6: {}
+
js-tokens@4.0.0: {}
js-yaml@3.14.1:
@@ -14863,7 +14918,7 @@ snapshots:
micromark@3.2.0:
dependencies:
'@types/debug': 4.1.8
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
decode-named-character-reference: 1.0.2
micromark-core-commonmark: 1.1.0
micromark-factory-space: 1.1.0
@@ -14911,6 +14966,10 @@ snapshots:
dependencies:
brace-expansion: 1.1.11
+ minimatch@5.1.6:
+ dependencies:
+ brace-expansion: 2.0.1
+
minimatch@8.0.4:
dependencies:
brace-expansion: 2.0.1
@@ -15367,13 +15426,14 @@ snapshots:
openapi-typescript-helpers@0.0.8: {}
- openapi-typescript@6.7.6:
+ openapi-typescript@7.6.1(typescript@5.5.3):
dependencies:
+ '@redocly/openapi-core': 1.28.5(supports-color@9.4.0)
ansi-colors: 4.1.3
- fast-glob: 3.3.2
- js-yaml: 4.1.0
+ change-case: 5.4.4
+ parse-json: 8.1.0
supports-color: 9.4.0
- undici: 5.28.4
+ typescript: 5.5.3
yargs-parser: 21.1.1
opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0):
@@ -15519,11 +15579,17 @@ snapshots:
parse-json@5.2.0:
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.25.7
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
+ parse-json@8.1.0:
+ dependencies:
+ '@babel/code-frame': 7.25.7
+ index-to-position: 0.1.2
+ type-fest: 4.26.1
+
parse-ms@3.0.0: {}
parse-ms@4.0.0: {}
@@ -15594,8 +15660,6 @@ snapshots:
picocolors@1.0.1: {}
- picocolors@1.1.0: {}
-
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -15626,6 +15690,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.2
+ pluralize@8.0.0: {}
+
postcss-import@15.1.0(postcss@8.4.31):
dependencies:
postcss: 8.4.31
@@ -15686,7 +15752,7 @@ snapshots:
postcss@8.4.47:
dependencies:
nanoid: 3.3.7
- picocolors: 1.1.0
+ picocolors: 1.1.1
source-map-js: 1.2.1
postgres-array@2.0.0: {}
@@ -16036,7 +16102,7 @@ snapshots:
require-in-the-middle@7.3.0:
dependencies:
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
module-details-from-path: 1.0.3
resolve: 1.22.8
transitivePeerDependencies:
@@ -16325,7 +16391,7 @@ snapshots:
socks-proxy-agent@7.0.0:
dependencies:
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.4.0(supports-color@9.4.0)
socks: 2.7.1
transitivePeerDependencies:
- supports-color
@@ -16811,7 +16877,7 @@ snapshots:
tuf-js@1.1.7:
dependencies:
'@tufjs/models': 1.0.4
- debug: 4.3.7
+ debug: 4.4.0(supports-color@9.4.0)
make-fetch-happen: 11.1.1
transitivePeerDependencies:
- supports-color
@@ -16911,10 +16977,6 @@ snapshots:
undici-types@6.19.8: {}
- undici@5.28.4:
- dependencies:
- '@fastify/busboy': 2.0.0
-
unescape-js@1.1.4:
dependencies:
string.fromcodepoint: 0.2.1
@@ -17029,19 +17091,19 @@ snapshots:
dependencies:
browserslist: 4.21.10
escalade: 3.1.1
- picocolors: 1.0.1
+ picocolors: 1.1.1
update-browserslist-db@1.1.0(browserslist@4.23.2):
dependencies:
browserslist: 4.23.2
escalade: 3.1.2
- picocolors: 1.1.0
+ picocolors: 1.1.1
update-browserslist-db@1.1.0(browserslist@4.24.0):
dependencies:
browserslist: 4.24.0
escalade: 3.1.2
- picocolors: 1.1.0
+ picocolors: 1.1.1
update-browserslist-db@1.1.2(browserslist@4.24.4):
dependencies:
@@ -17083,6 +17145,8 @@ snapshots:
semver-diff: 4.0.0
xdg-basedir: 5.1.0
+ uri-js-replace@1.0.1: {}
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.0
@@ -17457,6 +17521,8 @@ snapshots:
yallist@4.0.0: {}
+ yaml-ast-parser@0.0.43: {}
+
yaml@2.3.2: {}
yaml@2.5.1: {}
diff --git a/spec/openapi.yml b/spec/openapi.yml
index ca440f363..3a9e5556e 100644
--- a/spec/openapi.yml
+++ b/spec/openapi.yml
@@ -138,6 +138,13 @@ components:
type: string
description: Metadata of the sandbox
+ SandboxState:
+ type: string
+ description: State of the sandbox
+ enum:
+ - running
+ - paused
+
EnvVars:
additionalProperties:
type: string
@@ -220,7 +227,7 @@ components:
type: string
description: Version of the envd running in the sandbox
- RunningSandbox:
+ ListedSandbox:
required:
- templateID
- sandboxID
@@ -229,6 +236,7 @@ components:
- cpuCount
- memoryMB
- endAt
+ - state
properties:
templateID:
type: string
@@ -256,6 +264,49 @@ components:
$ref: "#/components/schemas/MemoryMB"
metadata:
$ref: "#/components/schemas/SandboxMetadata"
+ state:
+ $ref: "#/components/schemas/SandboxState"
+
+ RunningSandboxWithMetrics:
+ required:
+ - templateID
+ - sandboxID
+ - clientID
+ - startedAt
+ - cpuCount
+ - memoryMB
+ - endAt
+ properties:
+ templateID:
+ type: string
+ description: Identifier of the template from which is the sandbox created
+ alias:
+ type: string
+ description: Alias of the template
+ sandboxID:
+ type: string
+ description: Identifier of the sandbox
+ clientID:
+ type: string
+ description: Identifier of the client
+ startedAt:
+ type: string
+ format: date-time
+ description: Time when the sandbox was started
+ endAt:
+ type: string
+ format: date-time
+ description: Time when the sandbox will expire
+ cpuCount:
+ $ref: "#/components/schemas/CPUCount"
+ memoryMB:
+ $ref: "#/components/schemas/MemoryMB"
+ metadata:
+ $ref: "#/components/schemas/SandboxMetadata"
+ metrics:
+ type: array
+ items:
+ $ref: "#/components/schemas/SandboxMetric"
NewSandbox:
required:
@@ -270,6 +321,10 @@ components:
minimum: 0
default: 15
description: Time to live for the sandbox in seconds.
+ autoPause:
+ type: boolean
+ default: false
+ description: Automatically pauses the sandbox after the timeout
metadata:
$ref: "#/components/schemas/SandboxMetadata"
envVars:
@@ -283,6 +338,10 @@ components:
minimum: 0
default: 15
description: Time to live for the sandbox in seconds.
+ autoPause:
+ type: boolean
+ default: false
+ description: Automatically pauses the sandbox after the timeout
Template:
required:
@@ -395,6 +454,8 @@ components:
enum:
- ready
- draining
+ - connecting
+ - unhealthy
NodeStatusChange:
required:
@@ -410,6 +471,8 @@ components:
- sandboxCount
- allocatedCPU
- allocatedMemoryMiB
+ - createFails
+ - sandboxStartingCount
properties:
nodeID:
type: string
@@ -428,6 +491,14 @@ components:
type: integer
format: int32
description: Amount of allocated memory in MiB
+ createFails:
+ type: integer
+ format: uint64
+ description: Number of sandbox create fails
+ sandboxStartingCount:
+ type: integer
+ format: int
+ description: Number of starting Sandboxes
NodeDetail:
required:
@@ -435,6 +506,7 @@ components:
- status
- sandboxes
- cachedBuilds
+ - createFails
properties:
nodeID:
type: string
@@ -445,12 +517,16 @@ components:
type: array
description: List of sandboxes running on the node
items:
- $ref: "#/components/schemas/RunningSandbox"
+ $ref: "#/components/schemas/ListedSandbox"
cachedBuilds:
- type: array
- description: List of cached builds id on the node
- items:
- type: string
+ type: array
+ description: List of cached builds id on the node
+ items:
+ type: string
+ createFails:
+ type: integer
+ format: uint64
+ description: Number of sandbox create fails
Error:
@@ -504,7 +580,7 @@ paths:
/sandboxes:
get:
- description: List all running sandboxes
+ description: List all sandboxes
tags: [sandboxes]
security:
- ApiKeyAuth: []
@@ -515,6 +591,32 @@ paths:
required: false
schema:
type: string
+ - name: state
+ in: query
+ description: Filter sandboxes by one or more states
+ required: false
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/SandboxState"
+ style: form
+ explode: false
+ - name: nextToken
+ in: query
+ description: Cursor to start the list from
+ required: false
+ schema:
+ type: string
+ - name: limit
+ in: query
+ description: Maximum number of items to return per page
+ required: false
+ schema:
+ type: integer
+ format: int32
+ minimum: 1
+ default: 1000
+ maximum: 1000
responses:
"200":
description: Successfully returned all running sandboxes
@@ -524,7 +626,7 @@ paths:
type: array
items:
allOf:
- - $ref: "#/components/schemas/RunningSandbox"
+ - $ref: "#/components/schemas/ListedSandbox"
"401":
$ref: "#/components/responses/401"
"400":
@@ -556,6 +658,36 @@ paths:
"500":
$ref: "#/components/responses/500"
+ /sandboxes/metrics:
+ get:
+ description: List all running sandboxes with metrics
+ tags: [sandboxes]
+ security:
+ - ApiKeyAuth: []
+ parameters:
+ - name: query
+ in: query
+ description: A query used to filter the sandboxes (e.g. "user=abc&app=prod"). Query and each key and values must be URL encoded.
+ required: false
+ schema:
+ type: string
+ responses:
+ "200":
+ description: Successfully returned all running sandboxes with metrics
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ allOf:
+ - $ref: "#/components/schemas/RunningSandboxWithMetrics"
+ "401":
+ $ref: "#/components/responses/401"
+ "400":
+ $ref: "#/components/responses/400"
+ "500":
+ $ref: "#/components/responses/500"
+
/sandboxes/{sandboxID}/logs:
get:
description: Get sandbox logs
@@ -632,7 +764,7 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/RunningSandbox"
+ $ref: "#/components/schemas/ListedSandbox"
"404":
$ref: "#/components/responses/404"
"401":
@@ -990,4 +1122,4 @@ paths:
"404":
$ref: "#/components/responses/404"
"500":
- $ref: "#/components/responses/500"
\ No newline at end of file
+ $ref: "#/components/responses/500"