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"