From 6c2513fd5a738c40e6dfa862b5c6ee37d5d97ebe Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Thu, 24 Sep 2026 03:00:16 -0700 Subject: [PATCH 1/9] feat(sandboxes): add JavaScript session and execution facade --- __tests__/sandboxes.test.ts | 180 ++ docs/README.md | 2 + docs/sandboxes.md | 95 + openapi/openapi.yaml | 2316 ++++++++++++++--- orval.config.mjs | 18 + src/generated-runtime/client.js | 318 +++ .../model/createSandboxSessionRequest.js | 1 + .../createSandboxSessionRequestMemoryMb.js | 13 + .../createSandboxSessionRequestPreset.js | 13 + .../model/createSandboxTemplateRequest.js | 1 + .../createSandboxTemplateRequestMemoryMb.js | 13 + .../createSandboxTemplateRequestPreset.js | 13 + .../model/cursorParameter.js | 10 + src/generated-runtime/model/index.js | 34 + .../model/listSandboxSessionsParams.js | 1 + .../model/listSandboxesParams.js | 1 + src/generated-runtime/model/sandboxAccess.js | 10 + .../model/sandboxAccessRequest.js | 10 + .../model/sandboxCommandRequest.js | 1 + .../model/sandboxCommandRequestEnvironment.js | 10 + .../model/sandboxCommandResult.js | 10 + .../model/sandboxExecutionRequest.js | 1 + .../sandboxExecutionRequestEnvironment.js | 10 + .../model/sandboxExecutionRequestMemoryMb.js | 13 + .../model/sandboxExecutionRequestPreset.js | 13 + .../model/sandboxExecutionResult.js | 10 + .../model/sandboxFileReadRequest.js | 10 + .../model/sandboxFileResult.js | 10 + .../model/sandboxFileWriteRequest.js | 10 + .../model/sandboxPagination.js | 10 + src/generated-runtime/model/sandboxPreset.js | 1 + .../model/sandboxPresetList.js | 1 + .../model/sandboxPresetMemoryMb.js | 13 + src/generated-runtime/model/sandboxSession.js | 1 + .../model/sandboxSessionDesiredState.js | 14 + .../model/sandboxSessionPage.js | 1 + .../model/sandboxSessionState.js | 19 + .../model/sandboxSubjectGrantRequest.js | 10 + .../model/sandboxTemplate.js | 1 + .../model/sandboxTemplatePage.js | 1 + .../model/sandboxTemplateStatus.js | 14 + src/generated/client.ts | 882 +++++++ .../model/createSandboxSessionRequest.ts | 29 + .../createSandboxSessionRequestMemoryMb.ts | 17 + .../createSandboxSessionRequestPreset.ts | 17 + .../model/createSandboxTemplateRequest.ts | 18 + .../createSandboxTemplateRequestMemoryMb.ts | 17 + .../createSandboxTemplateRequestPreset.ts | 17 + src/generated/model/cursorParameter.ts | 17 + src/generated/model/index.ts | 34 + .../model/listSandboxSessionsParams.ts | 27 + src/generated/model/listSandboxesParams.ts | 27 + src/generated/model/sandboxAccess.ts | 15 + src/generated/model/sandboxAccessRequest.ts | 22 + src/generated/model/sandboxCommandRequest.ts | 24 + .../model/sandboxCommandRequestEnvironment.ts | 11 + src/generated/model/sandboxCommandResult.ts | 18 + .../model/sandboxExecutionRequest.ts | 31 + .../sandboxExecutionRequestEnvironment.ts | 11 + .../model/sandboxExecutionRequestMemoryMb.ts | 17 + .../model/sandboxExecutionRequestPreset.ts | 17 + src/generated/model/sandboxExecutionResult.ts | 22 + src/generated/model/sandboxFileReadRequest.ts | 17 + src/generated/model/sandboxFileResult.ts | 13 + .../model/sandboxFileWriteRequest.ts | 19 + src/generated/model/sandboxPagination.ts | 15 + src/generated/model/sandboxPreset.ts | 18 + src/generated/model/sandboxPresetList.ts | 14 + src/generated/model/sandboxPresetMemoryMb.ts | 17 + src/generated/model/sandboxSession.ts | 24 + .../model/sandboxSessionDesiredState.ts | 18 + src/generated/model/sandboxSessionPage.ts | 16 + src/generated/model/sandboxSessionState.ts | 23 + .../model/sandboxSubjectGrantRequest.ts | 13 + src/generated/model/sandboxTemplate.ts | 21 + src/generated/model/sandboxTemplatePage.ts | 16 + src/generated/model/sandboxTemplateStatus.ts | 18 + src/generated/openapi.d.ts | 1222 ++++++++- src/index.d.ts | 101 + src/index.js | 2 + src/sandbox-request.ts | 62 + src/sandbox-session.ts | 152 ++ src/sandbox-wire.ts | 128 + src/sandboxes.ts | 107 + 84 files changed, 6101 insertions(+), 458 deletions(-) create mode 100644 __tests__/sandboxes.test.ts create mode 100644 docs/sandboxes.md create mode 100644 src/generated-runtime/model/createSandboxSessionRequest.js create mode 100644 src/generated-runtime/model/createSandboxSessionRequestMemoryMb.js create mode 100644 src/generated-runtime/model/createSandboxSessionRequestPreset.js create mode 100644 src/generated-runtime/model/createSandboxTemplateRequest.js create mode 100644 src/generated-runtime/model/createSandboxTemplateRequestMemoryMb.js create mode 100644 src/generated-runtime/model/createSandboxTemplateRequestPreset.js create mode 100644 src/generated-runtime/model/cursorParameter.js create mode 100644 src/generated-runtime/model/listSandboxSessionsParams.js create mode 100644 src/generated-runtime/model/listSandboxesParams.js create mode 100644 src/generated-runtime/model/sandboxAccess.js create mode 100644 src/generated-runtime/model/sandboxAccessRequest.js create mode 100644 src/generated-runtime/model/sandboxCommandRequest.js create mode 100644 src/generated-runtime/model/sandboxCommandRequestEnvironment.js create mode 100644 src/generated-runtime/model/sandboxCommandResult.js create mode 100644 src/generated-runtime/model/sandboxExecutionRequest.js create mode 100644 src/generated-runtime/model/sandboxExecutionRequestEnvironment.js create mode 100644 src/generated-runtime/model/sandboxExecutionRequestMemoryMb.js create mode 100644 src/generated-runtime/model/sandboxExecutionRequestPreset.js create mode 100644 src/generated-runtime/model/sandboxExecutionResult.js create mode 100644 src/generated-runtime/model/sandboxFileReadRequest.js create mode 100644 src/generated-runtime/model/sandboxFileResult.js create mode 100644 src/generated-runtime/model/sandboxFileWriteRequest.js create mode 100644 src/generated-runtime/model/sandboxPagination.js create mode 100644 src/generated-runtime/model/sandboxPreset.js create mode 100644 src/generated-runtime/model/sandboxPresetList.js create mode 100644 src/generated-runtime/model/sandboxPresetMemoryMb.js create mode 100644 src/generated-runtime/model/sandboxSession.js create mode 100644 src/generated-runtime/model/sandboxSessionDesiredState.js create mode 100644 src/generated-runtime/model/sandboxSessionPage.js create mode 100644 src/generated-runtime/model/sandboxSessionState.js create mode 100644 src/generated-runtime/model/sandboxSubjectGrantRequest.js create mode 100644 src/generated-runtime/model/sandboxTemplate.js create mode 100644 src/generated-runtime/model/sandboxTemplatePage.js create mode 100644 src/generated-runtime/model/sandboxTemplateStatus.js create mode 100644 src/generated/model/createSandboxSessionRequest.ts create mode 100644 src/generated/model/createSandboxSessionRequestMemoryMb.ts create mode 100644 src/generated/model/createSandboxSessionRequestPreset.ts create mode 100644 src/generated/model/createSandboxTemplateRequest.ts create mode 100644 src/generated/model/createSandboxTemplateRequestMemoryMb.ts create mode 100644 src/generated/model/createSandboxTemplateRequestPreset.ts create mode 100644 src/generated/model/cursorParameter.ts create mode 100644 src/generated/model/listSandboxSessionsParams.ts create mode 100644 src/generated/model/listSandboxesParams.ts create mode 100644 src/generated/model/sandboxAccess.ts create mode 100644 src/generated/model/sandboxAccessRequest.ts create mode 100644 src/generated/model/sandboxCommandRequest.ts create mode 100644 src/generated/model/sandboxCommandRequestEnvironment.ts create mode 100644 src/generated/model/sandboxCommandResult.ts create mode 100644 src/generated/model/sandboxExecutionRequest.ts create mode 100644 src/generated/model/sandboxExecutionRequestEnvironment.ts create mode 100644 src/generated/model/sandboxExecutionRequestMemoryMb.ts create mode 100644 src/generated/model/sandboxExecutionRequestPreset.ts create mode 100644 src/generated/model/sandboxExecutionResult.ts create mode 100644 src/generated/model/sandboxFileReadRequest.ts create mode 100644 src/generated/model/sandboxFileResult.ts create mode 100644 src/generated/model/sandboxFileWriteRequest.ts create mode 100644 src/generated/model/sandboxPagination.ts create mode 100644 src/generated/model/sandboxPreset.ts create mode 100644 src/generated/model/sandboxPresetList.ts create mode 100644 src/generated/model/sandboxPresetMemoryMb.ts create mode 100644 src/generated/model/sandboxSession.ts create mode 100644 src/generated/model/sandboxSessionDesiredState.ts create mode 100644 src/generated/model/sandboxSessionPage.ts create mode 100644 src/generated/model/sandboxSessionState.ts create mode 100644 src/generated/model/sandboxSubjectGrantRequest.ts create mode 100644 src/generated/model/sandboxTemplate.ts create mode 100644 src/generated/model/sandboxTemplatePage.ts create mode 100644 src/generated/model/sandboxTemplateStatus.ts create mode 100644 src/sandbox-request.ts create mode 100644 src/sandbox-session.ts create mode 100644 src/sandbox-wire.ts create mode 100644 src/sandboxes.ts diff --git a/__tests__/sandboxes.test.ts b/__tests__/sandboxes.test.ts new file mode 100644 index 00000000..d866c089 --- /dev/null +++ b/__tests__/sandboxes.test.ts @@ -0,0 +1,180 @@ +/** @jest-environment ./__tests__/node-environment.cjs */ +import { afterEach, describe, expect, jest, test } from '@jest/globals'; +import { VolcanoAuth } from '../src/index.js'; + +const projectId = '11111111-1111-4111-8111-111111111111'; +const sessionId = '22222222-2222-4222-8222-222222222222'; +const requestId = '33333333-3333-4333-8333-333333333333'; +const originalFetch = globalThis.fetch; +const snapshot = { + id: sessionId, + project_id: projectId, + sandbox_id: requestId, + state: 'running', + desired_state: 'running', + region: 'aws-us-east-1', + memory_mb: 1024, + created_at: '2026-09-24T00:00:00Z', + expires_at: '2026-09-24T01:00:00Z', +}; +function client() { + return new VolcanoAuth({ + apiUrl: 'https://api.test.com', + anonKey: 'ak-project', + accessToken: 'sk-service', + }); +} +function bodyJson(call: Parameters | undefined): unknown { + const body = call?.[1]?.body; + if (typeof body !== 'string') { + throw new TypeError('Expected JSON request body'); + } + return JSON.parse(body); +} +function requestCall(calls: Parameters[], index: number): Parameters { + const call = calls[index]; + if (call === undefined) { + throw new Error('Missing request'); + } + return call; +} +afterEach(() => { + globalThis.fetch = originalFetch; +}); + +describe('Sandboxes facade', () => { + test('preserves command output and explicitly supplied replay key', async () => { + const fetchMock = jest.fn().mockResolvedValue( + Response.json({ + stdout: 'out', + stderr: 'err', + exit_code: 7, + timed_out: false, + stdout_truncated: false, + stderr_truncated: false, + session_id: sessionId, + region: 'aws-us-east-1', + duration_ms: 10, + }), + ); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.exec(projectId, 'exit 7', { + preset: 'python3.12', + region: 'aws-us-east-1', + requestId, + }); + expect(result.error).toBeNull(); + expect(result.data).toMatchObject({ stdout: 'out', stderr: 'err', exitCode: 7 }); + const request = fetchMock.mock.calls[0]; + expect(request?.[0]).toBe(`https://api.test.com/projects/${projectId}/sandbox-executions`); + expect(new Headers(request?.[1]?.headers).get('Idempotency-Key')).toBe(requestId); + }); + test('returns a session that refreshes state and controls the same id', async () => { + const fetchMock = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce( + Response.json({ ...snapshot, state: 'suspended', desired_state: 'suspended' }), + ); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.create(projectId, { + preset: 'node22', + region: 'aws-us-east-1', + requestId, + }); + expect(result.error).toBeNull(); + const handle = result.data; + if (handle === null) { + throw result.error; + } + expect(handle.id).toBe(sessionId); + const suspended = await handle.suspend(); + expect(suspended.error).toBeNull(); + expect(handle.state).toBe('suspended'); + expect(fetchMock.mock.calls[1]?.[0]).toBe( + `https://api.test.com/sandbox-sessions/${sessionId}/suspend`, + ); + }); + test('does not replay a failed command automatically', async () => { + const fetchMock = jest + .fn() + .mockResolvedValue(Response.json({ error: 'outcome unknown' }, { status: 409 })); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.exec(projectId, 'increment', { + preset: 'python3.12', + region: 'aws-us-east-1', + requestId, + }); + expect(result.error).toMatchObject({ status: 409 }); + expect(result.data).toBeNull(); + expect(fetchMock).toHaveBeenCalledTimes(1); + }); +}); + +test('passes session lifetime controls and terminates on async disposal', async () => { + const fetchMock = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce(Response.json({ ...snapshot, state: 'terminating' })); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.create(projectId, { + preset: 'python3.12', + region: 'aws-us-east-1', + maxDurationSeconds: 300, + idleTimeoutSeconds: 60, + }); + expect(bodyJson(fetchMock.mock.calls[0])).toMatchObject({ + max_duration_seconds: 300, + idle_timeout_seconds: 60, + }); + if (result.data === null) { + throw result.error; + } + await result.data[Symbol.asyncDispose](); + const disposal = requestCall(fetchMock.mock.calls, 1); + expect(disposal[0]).toBe(`https://api.test.com/sandbox-sessions/${sessionId}`); + expect(disposal[1]?.method).toBe('DELETE'); +}); + +test('preserves binary files and returns a scoped HTTP credential', async () => { + const fetchMock = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce(new Response(null, { status: 204 })) + .mockResolvedValueOnce(Response.json({ data: 'AP+A' })) + .mockResolvedValueOnce( + Response.json({ + url: 'https://session.example', + token: 'scoped-token', + expires_at: snapshot.expires_at, + }), + ); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.get(sessionId); + if (result.data === null) { + throw result.error; + } + const bytes = new Uint8Array([0, 255, 128]); + const written = await result.data.files.write('/workspace/data', bytes); + expect(written.error).toBeNull(); + expect(bodyJson(fetchMock.mock.calls[1])).toEqual({ + path: '/workspace/data', + data: 'AP+A', + }); + const read = await result.data.files.read('/workspace/data'); + expect(read.data).toEqual(bytes); + const access = await result.data.access(8080); + expect(access.data).toEqual({ + url: 'https://session.example', + token: 'scoped-token', + expiresAt: snapshot.expires_at, + }); +}); + +test('rejects invalid selectors before issuing a request', async () => { + const fetchMock = jest.fn(); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.create(projectId, { region: 'aws-us-east-1' }); + expect(result.error).toBeInstanceOf(TypeError); + expect(fetchMock).not.toHaveBeenCalled(); +}); diff --git a/docs/README.md b/docs/README.md index 6decdf7d..89ca54f4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -92,3 +92,5 @@ Realtime support is included with the SDK and is available from ## License Apache License 2.0 - see [LICENSE](https://github.com/Kong/volcano-sdk-js/blob/main/LICENSE) for details. + +- [Sandboxes](sandboxes.md): isolated commands, sessions, files, and HTTP access. diff --git a/docs/sandboxes.md b/docs/sandboxes.md new file mode 100644 index 00000000..4d9cf722 --- /dev/null +++ b/docs/sandboxes.md @@ -0,0 +1,95 @@ +--- +title: Sandboxes +description: Run isolated commands, keep a session for multiple steps, and access its files and HTTP services. +--- + +Run a command in a temporary Sandbox. Volcano terminates the session after the command completes. + +```javascript +import { VolcanoClient } from '@volcano.dev/sdk'; + +const client = new VolcanoClient({ + apiUrl: process.env.VOLCANO_API_URL ?? 'https://api.volcano.dev', + anonKey: process.env.VOLCANO_ANON_KEY, + accessToken: process.env.VOLCANO_SERVICE_KEY, + timeout: 180_000, +}); +const projectId = process.env.VOLCANO_PROJECT_ID; +const { data, error } = await client.sandboxes.exec(projectId, 'python -c "print(42)"', { + preset: 'python3.12', + region: 'aws-us-east-1', +}); +if (error) throw error; +console.log(data.stdout, data.exitCode); +``` + +Keep service keys on your backend. Creating and managing sessions requires a platform user or service key. An authenticated project user can access only sessions explicitly granted to that user. Anonymous keys cannot access Sandboxes. Availability depends on your platform's Sandbox rollout. + +A nonzero command exit is returned as `data.exitCode`; it is not a platform error. All methods return `{ data, error }`. Check `error` before using `data`. + +## Reuse a session + +Creation returns the current state, which may be `starting`. Poll `refresh()` until it becomes `running`; fail on `unknown` or `terminated` and keep polling bounded. Commands require a running session. + +```javascript +const created = await client.sandboxes.create(projectId, { + preset: 'python3.12', + region: 'aws-us-east-1', + maxDurationSeconds: 300, +}); +if (created.error) throw created.error; +const session = created.data; +try { + const deadline = Date.now() + 120_000; + while (session.state !== 'running') { + if (Date.now() >= deadline || ['unknown', 'terminated'].includes(session.state)) { + throw new Error(`Session unavailable: ${session.state}`); + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + const refreshed = await session.refresh(); + if (refreshed.error) throw refreshed.error; + } + const written = await session.files.write( + '/workspace/input.txt', + new TextEncoder().encode('hello'), + ); + if (written.error) throw written.error; + const output = await session.exec('cat /workspace/input.txt'); + if (output.error) throw output.error; + console.log(output.data.stdout); +} finally { + const stopped = await session.terminate(); + if (stopped.error) throw stopped.error; +} +``` + +In a runtime with explicit resource management, use `await using session = created.data` after checking the creation result. Leaving the scope requests termination through `Symbol.asyncDispose`. Termination is durable and asynchronous; poll `refresh()` if you need confirmation that the session has stopped. + +`session.suspend()` and `session.resume()` request state transitions. Poll for `suspended` or `running` before the next operation. Local suspension preserves processes and files, but the container continues to reserve memory. + +## Read files and access HTTP + +`session.files.read(path)` returns `Uint8Array`. Writes accept `Uint8Array`; paths stay within the session workspace. Files are limited to 8 MiB. + +Start a background HTTP service inside the session, then obtain a short-lived credential for its port: + +```javascript +const started = await session.exec('python -m http.server 8080 >/workspace/http.log 2>&1 &'); +if (started.error) throw started.error; +const access = await session.access(8080); +if (access.error) throw access.error; +const response = await fetch(access.data.url, { + headers: { 'X-Volcano-Sandbox-Token': access.data.token }, +}); +console.log(await response.text()); +``` + +The token is bound to this session and port. Do not append it to URLs. See [Sandbox HTTP access](/platform/guides/sandboxes) for browser redemption and authorization. + +## Retry safely + +Pass a UUID `requestId` to creation or command execution. Retry the same intent with the same ID after a network failure. Do not reuse the ID for a different command. The SDK does not automatically replay failed commands. + +Pass `signal` to cancel waiting. Cancellation does not prove the remote command stopped. Choose a client `timeout` longer than the command timeout plus provisioning time; use sessions for long-running work. + +Use either `preset` or a saved template's `sandboxId`, never both. `sandboxes.presets()` lists available preset IDs, memory sizes, and regions. `sandboxes.get(sessionId)` reconnects to an existing session. Backend code can call `grant(sessionId, authUserId, expiresAt)` and `revoke(sessionId, authUserId)` to control user access. diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index f6009f77..cc3492d5 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -30,7 +30,10 @@ tags: work is metered on its own allowances: executions, and the operations each execution performs while it runs. - name: Frontends - description: Frontend deployment and management. Supports Next.js 15.x and 16.x with Node.js 22.x or 24.x inferred from package.json engines.node when the selected Node.js family satisfies the installed Next.js package engines.node constraint. + description: Frontend deployment and management. Supports Next.js 15.x and 16.x + with Node.js 22.x or 24.x inferred from package.json engines.node when the + selected Node.js family satisfies the installed Next.js package + engines.node constraint. - name: Tokens description: Service keys for function invocation and database admin access - name: Variables @@ -50,7 +53,8 @@ tags: - name: Anon Keys description: Project-specific public keys for frontend auth - name: Service Keys - description: Project-specific secret keys for admin operations (bypass RLS - backend only!) + description: Project-specific secret keys for admin operations (bypass RLS - + backend only!) - name: OAuth Configuration description: OAuth provider configuration (Google, GitHub, Microsoft, Apple) - name: OAuth Authentication @@ -199,7 +203,8 @@ paths: type: string responses: '303': - description: Redirect to the provider completion URL, signed application redirect, or Volcano import page + description: Redirect to the provider completion URL, signed application + redirect, or Volcano import page headers: Location: description: Validated redirect target @@ -396,7 +401,8 @@ paths: tags: - Project Imports summary: Check whether a provider project is ready to import - description: Produces a deterministic read-only readiness report for a proposed new Volcano project. + description: Produces a deterministic read-only readiness report for a proposed + new Volcano project. operationId: preflightProjectImport security: - UserToken: [] @@ -473,7 +479,9 @@ paths: tags: - Project Imports summary: Start a Vercel project import - description: Creates a Volcano project from an importable production preflight report. Retrying the same request with the same Idempotency-Key returns the existing run. + description: Creates a Volcano project from an importable production preflight + report. Retrying the same request with the same Idempotency-Key returns + the existing run. operationId: startProjectImport security: - UserToken: [] @@ -535,7 +543,8 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Preflight is stale, idempotency key was reused, or destination conflicts + description: Preflight is stale, idempotency key was reused, or destination + conflicts content: application/json: schema: @@ -1100,7 +1109,8 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Conflict (project name already exists or a resource deployment blocks a region change) + description: Conflict (project name already exists or a resource deployment + blocks a region change) content: application/json: schema: @@ -1460,7 +1470,8 @@ paths: expected_shared_variables: type: array uniqueItems: true - description: When present, replace only if the current complete shared list matches this list. + description: When present, replace only if the current complete shared list + matches this list. items: type: string maxLength: 256 @@ -1470,7 +1481,9 @@ paths: minLength: 64 maxLength: 64 pattern: ^[a-f0-9]{64}$ - description: SHA-256 of the sorted unique current shared names joined by a newline. Use instead of expected_shared_variables for a compact conditional replacement. + description: SHA-256 of the sorted unique current shared names joined by a + newline. Use instead of expected_shared_variables for a + compact conditional replacement. responses: '204': description: Shared list replaced and affected function synchronization started. @@ -2682,14 +2695,18 @@ paths: properties: name: type: string - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot + start or end with hyphen) maxLength: 63 pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ example: my-api-function code: type: string format: binary - description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz source archive. + description: ZIP or tar.gz archive containing function source code plus + dependency manifests/lockfiles. The API enforces + SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz + source archive. runtime: type: string enum: @@ -2961,7 +2978,8 @@ paths: description: Function response (passthrough from function runtime) headers: X-Volcano-Version: - description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) + description: Volcano API/runtime version that served this invocation + (`` in production, `-` in non-production) schema: type: string X-Volcano-Region: @@ -2985,7 +3003,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - CORS blocked, missing `functions.invoke`, or private function with anon key + description: Forbidden - CORS blocked, missing `functions.invoke`, or private + function with anon key content: application/json: schema: @@ -3018,10 +3037,12 @@ paths: schema: $ref: '#/components/schemas/Error' default: - description: Function response (passthrough; status code/body/headers come from the function) + description: Function response (passthrough; status code/body/headers come from + the function) headers: X-Volcano-Version: - description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) + description: Volcano API/runtime version that served this invocation + (`` in production, `-` in non-production) schema: type: string X-Volcano-Region: @@ -3209,7 +3230,8 @@ paths: type: string maxLength: 63 pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot + start or end with hyphen) example: my-function responses: '200': @@ -3231,7 +3253,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - CORS blocked or missing `functions.invoke` permission for anon key + description: Forbidden - CORS blocked or missing `functions.invoke` permission + for anon key content: application/json: schema: @@ -3401,7 +3424,8 @@ paths: required: false schema: type: string - description: Opaque stream cursor fallback when setting `Last-Event-ID` is not practical. + description: Opaque stream cursor fallback when setting `Last-Event-ID` is not + practical. requestBody: required: true content: @@ -3410,7 +3434,9 @@ paths: $ref: '#/components/schemas/LogStreamRequest' responses: '200': - description: Server-Sent Events stream. `log` events contain a JSON `LogSearchEvent`; `warning` events contain a JSON object with an `error` field. + description: Server-Sent Events stream. `log` events contain a JSON + `LogSearchEvent`; `warning` events contain a JSON object with an + `error` field. content: text/event-stream: schema: @@ -3425,7 +3451,8 @@ paths: event: log data: {"id":"LOG_EVENT_ID","timestamp":"2024-01-01T12:00:00Z","level":"info","message":"User logged in","resource":{"type":"function","id":"550e8400-e29b-41d4-a716-446655440000","name":"login"},"region":"us-east-1"} '400': - description: Bad request - invalid selector, stream cursor, or unsupported stream field + description: Bad request - invalid selector, stream cursor, or unsupported + stream field content: application/json: schema: @@ -3492,7 +3519,10 @@ paths: code_0: type: string format: binary - description: Function ZIP or tar.gz archive referenced by the first manifest entry's `file_field`; additional code_N file fields may be included. Each archive is subject to SOURCE_ARCHIVE_SIZE_LIMIT_MB. + description: Function ZIP or tar.gz archive referenced by the first manifest + entry's `file_field`; additional code_N file fields may be + included. Each archive is subject to + SOURCE_ARCHIVE_SIZE_LIMIT_MB. required: - functions responses: @@ -3503,7 +3533,8 @@ paths: schema: $ref: '#/components/schemas/BatchFunctionDeployResponse' '207': - description: Batch deployment partially accepted; successful functions started deployment and failed functions were compensated where possible + description: Batch deployment partially accepted; successful functions started + deployment and failed functions were compensated where possible content: application/json: schema: @@ -3585,7 +3616,8 @@ paths: tags: - Functions summary: Create a scheduler for a function - description: Creates regional scheduled invocation jobs. Requested regions must be a subset of the function's deployed regions. + description: Creates regional scheduled invocation jobs. Requested regions must + be a subset of the function's deployed regions. operationId: createFunctionScheduler security: - UserToken: [] @@ -3809,14 +3841,16 @@ paths: properties: name: type: string - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot + start or end with hyphen) maxLength: 63 pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ example: order-pipeline code: type: string format: binary - description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. + description: ZIP or tar.gz archive containing function source code plus + dependency manifests/lockfiles. runtime: type: string enum: @@ -3835,7 +3869,8 @@ paths: example: nodejs24.x handler: type: string - description: The name of the function to invoke. Defaults to "handler" if not specified. + description: The name of the function to invoke. Defaults to "handler" if not + specified. default: handler example: handler is_public: @@ -3863,7 +3898,8 @@ paths: JSON-encoded array of project variable names this function requires, on top of the ones detected in its source. A declared name the project does not define is rejected with 400; a detected name it does not define is ignored. Only used when `variable_scope` is `scoped`. Omitting this leaves an existing function's declared names unchanged. responses: '200': - description: Existing durable function updated; its deployment was started or queued + description: Existing durable function updated; its deployment was started or + queued content: application/json: schema: @@ -3890,7 +3926,8 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Name is held by a standard function, or a deletion is queued or running + description: Name is held by a standard function, or a deletion is queued or + running content: application/json: schema: @@ -4506,12 +4543,16 @@ paths: app_root: type: string maxLength: 1024 - description: Optional relative POSIX path from the uploaded archive root to the Next.js app to build, for example `apps/web`. + description: Optional relative POSIX path from the uploaded archive root to the + Next.js app to build, for example `apps/web`. example: apps/web archive: type: string format: binary - description: ZIP or tar.gz archive of the frontend project directory or monorepo workspace root. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz archive. + description: ZIP or tar.gz archive of the frontend project directory or monorepo + workspace root. The API enforces + SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz + archive. responses: '200': description: Existing frontend updated; its deployment was started or queued @@ -4538,7 +4579,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Frontend deployment limit exceeded for the current plan or project hard cap + description: Frontend deployment limit exceeded for the current plan or project + hard cap content: application/json: schema: @@ -4562,7 +4604,8 @@ paths: schema: $ref: '#/components/schemas/Error' '503': - description: Service unavailable - frontend workflow or archive limit configuration missing + description: Service unavailable - frontend workflow or archive limit + configuration missing content: application/json: schema: @@ -4847,7 +4890,8 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Conflict - custom domain already in use, still detaching, or frontend already has a custom domain + description: Conflict - custom domain already in use, still detaching, or + frontend already has a custom domain content: application/json: schema: @@ -4859,7 +4903,8 @@ paths: schema: $ref: '#/components/schemas/Error' '503': - description: Service unavailable - custom domain provisioning is temporarily unavailable + description: Service unavailable - custom domain provisioning is temporarily + unavailable content: application/json: schema: @@ -6100,14 +6145,16 @@ paths: type: string role_name: type: string - description: Volcano-managed per-database client login (also the pgproxy routing username) + description: Volcano-managed per-database client login (also the pgproxy routing + username) example: volcano_client_11111111-1111-1111-1111-111111111111 new_password: type: string description: New Volcano-managed client password. Always starts with `vpg_`. connection_string: type: string - description: Updated pgproxy connection string using Volcano-managed credentials. + description: Updated pgproxy connection string using Volcano-managed + credentials. '400': description: Database is not active content: @@ -6212,14 +6259,16 @@ paths: schema: type: string format: date-time - description: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). Defaults to 24 hours ago. + description: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). + Defaults to 24 hours ago. - name: to in: query required: false schema: type: string format: date-time - description: End time in RFC3339 format (e.g., "2024-01-02T00:00:00Z"). Defaults to now. + description: End time in RFC3339 format (e.g., "2024-01-02T00:00:00Z"). Defaults + to now. - name: granularity in: query required: false @@ -7920,7 +7969,8 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send + as `ending_before`. '400': description: Invalid or conflicting pagination parameters content: @@ -8115,7 +8165,8 @@ paths: '204': description: Identity unlinked '400': - description: Identity cannot be unlinked (primary, last, or would remove last sign-in method), or the identity id is malformed + description: Identity cannot be unlinked (primary, last, or would remove last + sign-in method), or the identity id is malformed content: application/json: schema: @@ -8186,7 +8237,9 @@ paths: schema: $ref: '#/components/schemas/AuthMethodSummary' '400': - description: This method cannot be set as primary (password stub, converted anonymous method, or unverified email), or the method id is malformed + description: This method cannot be set as primary (password stub, converted + anonymous method, or unverified email), or the method id is + malformed content: application/json: schema: @@ -8198,7 +8251,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: The promoted identity's email domain is not in the project's allowed_email_domains + description: The promoted identity's email domain is not in the project's + allowed_email_domains content: application/json: schema: @@ -8315,7 +8369,8 @@ paths: - name: status in: query required: false - description: Filter by effective status. `banned` returns only currently-banned users; an expired temporary ban lists as `active`. + description: Filter by effective status. `banned` returns only currently-banned + users; an expired temporary ban lists as `active`. schema: type: string enum: @@ -8503,7 +8558,8 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send + as `ending_before`. '400': description: Invalid or conflicting pagination parameters content: @@ -8827,7 +8883,8 @@ paths: tags: - Auth Configuration summary: Get default email templates - description: Returns the default email templates used when no custom template is configured. + description: Returns the default email templates used when no custom template is + configured. operationId: getDefaultEmailTemplates responses: '200': @@ -9294,7 +9351,8 @@ paths: schema: type: string '404': - description: Ticket is invalid or expired, its path does not match, or managed authentication is disabled + description: Ticket is invalid or expired, its path does not match, or managed + authentication is disabled content: text/plain: schema: @@ -9425,13 +9483,15 @@ paths: required: false schema: type: string - description: Device user code (from `POST /auth/device/authorize`) used with `action=device`. + description: Device user code (from `POST /auth/device/authorize`) used with + `action=device`. - name: anon_key in: query required: false schema: type: string - description: Project anon key used by built-in managed auth flows (required for login/signup/device actions). + description: Project anon key used by built-in managed auth flows (required for + login/signup/device actions). - name: state in: query required: false @@ -10563,7 +10623,8 @@ paths: schema: $ref: '#/components/schemas/Error' '502': - description: Provider transport failure, invalid JSON, or response body larger than 8 MiB + description: Provider transport failure, invalid JSON, or response body larger + than 8 MiB content: application/json: schema: @@ -10605,7 +10666,8 @@ paths: $ref: '#/components/schemas/AnonKey' total: type: integer - description: Total number of items matching the query (so the UI can render numbered pages). + description: Total number of items matching the query (so the UI can render + numbered pages). has_more: type: boolean description: Whether a next page exists. @@ -10614,7 +10676,8 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send + as `ending_before`. post: tags: - Anon Keys @@ -10770,7 +10833,8 @@ paths: tags: - Anon Keys summary: Set default anon key - description: Promotes the given key to the project's configured default. At most one key per project can be default. + description: Promotes the given key to the project's configured default. At most + one key per project can be default. operationId: setDefaultAnonKey security: - UserToken: [] @@ -11196,13 +11260,15 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send + as `ending_before`. /projects/{id}/storage/stats: get: tags: - Storage Admin summary: Get storage statistics for a project - description: Returns aggregate storage statistics including bucket count, object count, and total size. + description: Returns aggregate storage statistics including bucket count, object + count, and total size. operationId: getStorageStats security: - UserToken: [] @@ -11285,7 +11351,8 @@ paths: tags: - Realtime summary: Get realtime statistics for a project - description: Returns realtime usage statistics including connection counts and subscribed tables. + description: Returns realtime usage statistics including connection counts and + subscribed tables. operationId: getRealtimeStats security: - UserToken: [] @@ -11388,7 +11455,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + description: A non-service credential was supplied or the service key lacks + `locks.manage` content: application/json: schema: @@ -11460,7 +11528,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + description: A non-service credential was supplied or the service key lacks + `locks.manage` content: application/json: schema: @@ -11516,7 +11585,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + description: A non-service credential was supplied or the service key lacks + `locks.manage` content: application/json: schema: @@ -11580,7 +11650,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + description: A non-service credential was supplied or the service key lacks + `locks.manage` content: application/json: schema: @@ -11636,7 +11707,8 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + description: A non-service credential was supplied or the service key lacks + `locks.manage` content: application/json: schema: @@ -12094,7 +12166,8 @@ paths: tags: - System summary: Health check endpoint - description: Returns server health status. Used for load balancer and monitoring checks. + description: Returns server health status. Used for load balancer and monitoring + checks. operationId: healthCheck responses: '200': @@ -12104,6 +12177,740 @@ paths: schema: type: string example: OK + /sandboxes/presets: + get: + tags: + - Sandboxes + summary: List available sandbox presets + operationId: listSandboxPresets + security: [] + responses: + "200": + description: List available sandbox presets + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxPresetList" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /projects/{id}/sandboxes: + get: + tags: + - Sandboxes + summary: List sandbox templates + operationId: listSandboxes + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - $ref: "#/components/parameters/Limit" + - $ref: "#/components/parameters/Cursor" + responses: + "200": + description: List sandbox templates + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxTemplatePage" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + tags: + - Sandboxes + summary: Create a sandbox template from a verified preset + operationId: createSandbox + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: Idempotency-Key + in: header + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateSandboxTemplateRequest" + responses: + "201": + description: Create a sandbox template from a verified preset + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxTemplate" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /projects/{id}/sandboxes/{sandboxId}: + get: + tags: + - Sandboxes + summary: Get a sandbox template + operationId: getSandbox + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId + in: path + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Get a sandbox template + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxTemplate" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + patch: + tags: + - Sandboxes + summary: Rename a sandbox template + operationId: updateSandbox + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateSandboxTemplateRequest" + responses: + "200": + description: Rename a sandbox template + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxTemplate" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + delete: + tags: + - Sandboxes + summary: Retire a template and terminate its sessions + operationId: deleteSandbox + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId + in: path + required: true + schema: + type: string + format: uuid + responses: + "202": + description: Retire a template and terminate its sessions + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /projects/{id}/sandboxes/{sandboxId}/deployments: + get: + tags: + - Sandboxes + summary: List sandbox deployment history + operationId: listSandboxDeployments + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId + in: path + required: true + schema: + type: string + format: uuid + - $ref: "#/components/parameters/Limit" + - $ref: "#/components/parameters/Cursor" + responses: + "200": + description: List sandbox deployment history + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxDeploymentPage" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /projects/{id}/sandbox-sessions: + get: + tags: + - Sandboxes + summary: List project sandbox sessions + operationId: listSandboxSessions + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - $ref: "#/components/parameters/Limit" + - $ref: "#/components/parameters/Cursor" + responses: + "200": + description: List project sandbox sessions + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSessionPage" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + tags: + - Sandboxes + summary: Start a sandbox session + operationId: createSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: Idempotency-Key + in: header + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateSandboxSessionRequest" + responses: + "201": + description: Start a sandbox session + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSession" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /projects/{id}/sandbox-executions: + post: + tags: + - Sandboxes + summary: Execute once and return after confirmed termination + operationId: executeSandbox + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: Idempotency-Key + in: header + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxExecutionRequest" + responses: + "200": + description: Execute once and return after confirmed termination + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxExecutionResult" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}: + get: + tags: + - Sandboxes + summary: Get a sandbox session + operationId: getSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Get a sandbox session + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSession" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + delete: + tags: + - Sandboxes + summary: Request sandbox termination + operationId: terminateSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + "202": + description: Request sandbox termination + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSession" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/suspend: + post: + tags: + - Sandboxes + summary: Suspend a sandbox session + operationId: suspendSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + "202": + description: Suspend a sandbox session + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSession" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/resume: + post: + tags: + - Sandboxes + summary: Resume a sandbox session + operationId: resumeSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + "202": + description: Resume a sandbox session + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSession" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/exec: + post: + tags: + - Sandboxes + summary: Execute a command within a session + operationId: executeSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + - name: Idempotency-Key + in: header + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxCommandRequest" + responses: + "200": + description: Execute a command within a session + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxCommandResult" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/files/read: + post: + tags: + - Sandboxes + summary: Read a workspace file + operationId: readSandboxSessionFile + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxFileReadRequest" + responses: + "200": + description: Read a workspace file + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxFileResult" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/files/write: + post: + tags: + - Sandboxes + summary: Write a workspace file + operationId: writeSandboxSessionFile + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxFileWriteRequest" + responses: + "204": + description: Write a workspace file + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/grants/{subjectId}: + put: + tags: + - Sandboxes + summary: Authorize an authenticated project user for this session + operationId: grantSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + - name: subjectId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSubjectGrantRequest" + responses: + "204": + description: Authorize an authenticated project user for this session + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + delete: + tags: + - Sandboxes + summary: Revoke a project user session grant + operationId: revokeSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + - name: subjectId + in: path + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Revoke a project user session grant + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /sandbox-sessions/{sessionId}/access: + post: + tags: + - Sandboxes + summary: Issue a short-lived port-scoped access credential + operationId: createSandboxSessionAccess + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxAccessRequest" + responses: + "200": + description: Issue a short-lived port-scoped access credential + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxAccess" + default: + description: Request refused or unavailable. Errors include invalid input (400), + unauthenticated (401), forbidden (403), not found (404), conflicting + retry (409), capacity exhausted (429), and disabled or unavailable + (503). + content: + application/json: + schema: + $ref: "#/components/schemas/Error" components: securitySchemes: AnonKey: @@ -12165,7 +12972,8 @@ components: type: string pattern: ^[a-z0-9_]+$ maxLength: 64 - description: Branch name (unique within the parent database, lowercase letters, numbers, and underscores only) + description: Branch name (unique within the parent database, lowercase letters, + numbers, and underscores only) BucketName: name: bucketName in: path @@ -12207,7 +13015,8 @@ components: type: string pattern: ^[a-z0-9_]+$ maxLength: 64 - description: Database name (unique within project, lowercase letters, numbers, and underscores only) + description: Database name (unique within project, lowercase letters, numbers, + and underscores only) DeploymentId: name: deploymentId in: path @@ -12271,7 +13080,8 @@ components: name: X-Volcano-Lock-Token in: header required: true - description: Opaque UUID generated once by the caller and retained for the lease lifetime. + description: Opaque UUID generated once by the caller and retained for the lease + lifetime. schema: type: string format: uuid @@ -12485,7 +13295,8 @@ components: - auth.logout is_default: type: boolean - description: Whether this is the project's configured default anon key. Only one key per project can be default. + description: Whether this is the project's configured default anon key. Only one + key per project can be default. created_at: type: string format: date-time @@ -12605,7 +13416,8 @@ components: default: signup platform_token_ttl: type: integer - description: TTL in seconds for platform tokens minted via `/auth/platform/exchange` + description: TTL in seconds for platform tokens minted via + `/auth/platform/exchange` default: 2592000 allow_password_reset: type: boolean @@ -12629,7 +13441,8 @@ components: default: 86400 require_email_confirmation: type: boolean - description: Require users to confirm email before sign-in. Can only be true when email_enabled is true. + description: Require users to confirm email before sign-in. Can only be true + when email_enabled is true. default: false email_confirmation_timeout: type: integer @@ -12637,11 +13450,15 @@ components: default: 86400 auto_link_verified_oauth: type: boolean - description: Link a verified OAuth identity to an existing confirmed account with the same email instead of returning a conflict. Requires require_email_confirmation to be true. + description: Link a verified OAuth identity to an existing confirmed account + with the same email instead of returning a conflict. Requires + require_email_confirmation to be true. default: false email_enabled: type: boolean - description: Enable transactional email sending (confirmation, reset, change notifications). Must be true when require_email_confirmation is true. + description: Enable transactional email sending (confirmation, reset, change + notifications). Must be true when require_email_confirmation is + true. default: false email_from_address: type: string @@ -12656,7 +13473,8 @@ components: type: string smtp_password_configured: type: boolean - description: Whether an SMTP password is configured. The password itself is never returned. + description: Whether an SMTP password is configured. The password itself is + never returned. default: false smtp_use_tls: type: boolean @@ -12676,7 +13494,8 @@ components: description: Default redirect target after successful hosted auth. allowed_redirect_urls: type: array - description: Redirect allowlist used to validate post_auth_redirect_url and post_logout_redirect_url. + description: Redirect allowlist used to validate post_auth_redirect_url and + post_logout_redirect_url. items: type: string post_logout_redirect_url: @@ -12761,7 +13580,8 @@ components: type: integer format: int64 minimum: 0 - description: Users with a successful session creation or refresh in the trailing 30 days since activity collection was deployed. + description: Users with a successful session creation or refresh in the trailing + 30 days since activity collection was deployed. required: - total_users - active_users_30d @@ -12900,7 +13720,8 @@ components: allOf: - $ref: '#/components/schemas/AuthHostedPage' nullable: true - description: The saved page, or null when the project has not customized this page type yet. + description: The saved page, or null when the project has not customized this + page type yet. defaults: $ref: '#/components/schemas/AuthHostedPageDefaults' runtime: @@ -12924,7 +13745,8 @@ components: description: Whether ownership of this email has been verified is_primary: type: boolean - description: Whether the account's primary sign-in method resolves to this identity + description: Whether the account's primary sign-in method resolves to this + identity created_at: type: string format: date-time @@ -13017,7 +13839,8 @@ components: type: integer minimum: 15 maximum: 15 - description: Lowest minimum password length accepted by the auth configuration endpoint. + description: Lowest minimum password length accepted by the auth configuration + endpoint. max_length: type: integer minimum: 128 @@ -13034,10 +13857,12 @@ components: description: Whether passwords must contain an ASCII digit (0-9). require_special_chars: type: boolean - description: Whether passwords must contain one of the backend-supported special characters. + description: Whether passwords must contain one of the backend-supported special + characters. compromised_passwords_rejected: type: boolean - description: Whether common and known-compromised passwords are rejected by the backend. + description: Whether common and known-compromised passwords are rejected by the + backend. required: - effective_min_length - min_configurable_length @@ -13266,7 +14091,9 @@ components: $ref: '#/components/schemas/Function' failed: type: array - description: Functions that failed before their workflow started. Successful functions are left running; failed new functions are deleted and failed updates are rolled back where possible. + description: Functions that failed before their workflow started. Successful + functions are left running; failed new functions are deleted and + failed updates are rolled back where possible. items: $ref: '#/components/schemas/BatchFunctionDeployFailure' required: @@ -13445,7 +14272,9 @@ components: additionalProperties: true regions: type: array - description: Optional single explicit deployed region. If omitted, the scheduler chooses one deployed region and invokes according to the cron expression. + description: Optional single explicit deployed region. If omitted, the scheduler + chooses one deployed region and invokes according to the cron + expression. maxItems: 1 items: type: string @@ -13466,11 +14295,13 @@ components: client_id: type: string example: 123456789.apps.googleusercontent.com - description: Required for non-device providers. Must not be provided for `provider=device`; server always auto-generates it. + description: Required for non-device providers. Must not be provided for + `provider=device`; server always auto-generates it. client_secret: type: string example: GOCSPX-abc123def456 - description: Required for non-device providers. Must not be provided for `provider=device`; server always auto-generates it. + description: Required for non-device providers. Must not be provided for + `provider=device`; server always auto-generates it. redirect_url: type: string description: Required for non-device providers. Not used for `provider=device`. @@ -13484,7 +14315,8 @@ components: - openid - email - profile - description: Optional for non-device providers, uses provider defaults if omitted. Not used for `provider=device`. + description: Optional for non-device providers, uses provider defaults if + omitted. Not used for `provider=device`. CreateProjectRequest: type: object description: Request to create a new project @@ -13615,7 +14447,10 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. + description: Include this name in the project's shared function variables. + Omission preserves existing membership; new variables default to + true for legacy clients. Send false explicitly to create a + non-shared variable. name: type: string minLength: 1 @@ -14252,7 +15087,8 @@ components: description: Total active compute time in seconds time_range: type: string - description: Time range of the metrics (e.g., "2024-01-01T00:00:00Z to 2024-01-02T00:00:00Z") + description: Time range of the metrics (e.g., "2024-01-01T00:00:00Z to + 2024-01-02T00:00:00Z") granularity: type: string enum: @@ -14367,7 +15203,8 @@ components: type: string code: type: string - description: Stable machine-readable error code when a specific recovery path is available. + description: Stable machine-readable error code when a specific recovery path is + available. required: - error ImportProvider: @@ -14550,7 +15387,11 @@ components: confirm_environment_variable_read: type: boolean default: false - description: Set to true only after the user confirms an immediately preceding disclosure that the Vercel Integration grant permits reads and writes, while this preflight reads production variable values only. When false or omitted, preflight reads variable metadata only and reports readable values as manual input. + description: Set to true only after the user confirms an immediately preceding + disclosure that the Vercel Integration grant permits reads and + writes, while this preflight reads production variable values only. + When false or omitted, preflight reads variable metadata only and + reports readable values as manual input. required: - connection_id - source_id @@ -14664,7 +15505,8 @@ components: scope: type: string minLength: 1 - description: URL-encoded source scope, including target, branch, and custom environment identifiers. + description: URL-encoded source scope, including target, branch, and custom + environment identifiers. required: - kind - name @@ -14866,7 +15708,8 @@ components: type: boolean is_empty: type: boolean - description: Whether the repository has no commits and can receive an initial source export. + description: Whether the repository has no commits and can receive an initial + source export. required: - id - full_name @@ -14891,14 +15734,17 @@ components: repo_id: type: integer format: int64 - description: Stable GitHub repository id (repository.id), the authoritative binding. + description: Stable GitHub repository id (repository.id), the authoritative + binding. repo_full_name: type: string root_directory: type: string production_branch: type: string - description: The branch a push must land on to deploy. Follows the repository's GitHub default branch unless the project set its own, which a default-branch rename on GitHub then leaves alone. + description: The branch a push must land on to deploy. Follows the repository's + GitHub default branch unless the project set its own, which a + default-branch rename on GitHub then leaves alone. updated_at: type: string format: date-time @@ -14911,7 +15757,9 @@ components: - updated_at ProjectGitDeploySettings: type: object - description: 'A project''s GitHub auto-deploy settings: what a push to the connected repo''s production branch deploys. All settings are default-off.' + description: 'A project''s GitHub auto-deploy settings: what a push to the + connected repo''s production branch deploys. All settings are + default-off.' properties: auto_deploy_enabled: type: boolean @@ -14921,10 +15769,12 @@ components: description: Whether the repo's functions are deployed on push. frontend_name: type: string - description: Name of the frontend to build and deploy on push. Omitted when no frontend is deployed. Resolved at deploy time; need not exist yet. + description: Name of the frontend to build and deploy on push. Omitted when no + frontend is deployed. Resolved at deploy time; need not exist yet. frontend_app_root: type: string - description: App root (subdirectory) the frontend builds from. Omitted for the repo root. + description: App root (subdirectory) the frontend builds from. Omitted for the + repo root. updated_at: type: string format: date-time @@ -14945,7 +15795,8 @@ components: description: Frontend to deploy on push. Omit or empty to deploy no frontend. frontend_app_root: type: string - description: App root the frontend builds from. Requires frontend_name; omit for the repo root. + description: App root the frontend builds from. Requires frontend_name; omit for + the repo root. required: - auto_deploy_enabled - deploy_functions @@ -15019,17 +15870,22 @@ components: type: string format: date-time nullable: true - description: When source export started, cleared if an incomplete transition is canceled. + description: When source export started, cleared if an incomplete transition is + canceled. exported_at: type: string format: date-time nullable: true - description: When the initial export push entered deployment, or when a transition was canceled after its commit was reserved. Once set, the one-time export is consumed. + description: When the initial export push entered deployment, or when a + transition was canceled after its commit was reserved. Once set, the + one-time export is consumed. handed_over_at: type: string format: date-time nullable: true - description: When a complete production-branch deployment first proved the repository could drive the project, null until then. Once set, the repository is the project's source of truth. + description: When a complete production-branch deployment first proved the + repository could drive the project, null until then. Once set, the + repository is the project's source of truth. required: - mode - transition_started_at @@ -15037,7 +15893,8 @@ components: - handed_over_at ProjectSourceExport: type: object - description: The initial production-branch commit, and everything export could not carry. + description: The initial production-branch commit, and everything export could + not carry. properties: repo_full_name: type: string @@ -15050,7 +15907,8 @@ components: type: integer skipped: type: array - description: Resources whose source could not be taken, with the reason. Most often a resource that has never deployed successfully. + description: Resources whose source could not be taken, with the reason. Most + often a resource that has never deployed successfully. items: $ref: '#/components/schemas/ProjectSourceExportSkip' omitted: @@ -15072,7 +15930,8 @@ components: production_branch: type: string minLength: 1 - description: The currently configured production branch the user confirmed for export. + description: The currently configured production branch the user confirmed for + export. required: - production_branch ProjectSourceExportSkip: @@ -15080,7 +15939,9 @@ components: properties: kind: type: string - description: 'The kind of resource, "function" or "frontend". Deliberately not an enum: the generated constants would collide with an existing resource-type enum and rename its members.' + description: 'The kind of resource, "function" or "frontend". Deliberately not + an enum: the generated constants would collide with an existing + resource-type enum and rename its members.' name: type: string reason: @@ -15094,7 +15955,9 @@ components: properties: kind: type: string - description: 'What was left out: migrations Volcano stores no copy of, variable values, a credential-shaped file, installed dependencies, or an archive entry a repository cannot carry.' + description: 'What was left out: migrations Volcano stores no copy of, variable + values, a credential-shaped file, installed dependencies, or an + archive entry a repository cannot carry.' resource: type: string description: The resource it came from, empty when project-wide. @@ -15111,7 +15974,8 @@ components: type: string minLength: 1 maxLength: 255 - description: The branch a push must land on to deploy. Validated as a Git branch name only — it does not have to exist yet. + description: The branch a push must land on to deploy. Validated as a Git branch + name only — it does not have to exist yet. required: - production_branch ConnectProjectGitRequest: @@ -15127,18 +15991,38 @@ components: repository_id: type: integer format: int64 - description: Stable GitHub repository id (repository.id), the preferred selector. Either repository_id or repo_full_name is required; when both are given they must identify the same live repository. + description: Stable GitHub repository id (repository.id), the preferred + selector. Either repository_id or repo_full_name is required; when + both are given they must identify the same live repository. repo_full_name: type: string deprecated: true - description: Deprecated selector kept for a compatibility window; prefer repository_id. Either repository_id or repo_full_name is required. + description: Deprecated selector kept for a compatibility window; prefer + repository_id. Either repository_id or repo_full_name is required. root_directory: type: string - description: Monorepo subdirectory the project builds from. Omit for the repo root. + description: Monorepo subdirectory the project builds from. Omit for the repo + root. production_branch: type: string maxLength: 255 - description: 'The branch a push must land on to deploy, with three cases, because this is a full replace and a read-modify-write client sends back whatever it read. Omit it to follow the repository''s GitHub default branch, which also discards a branch set earlier. Send back the branch the project already deploys from, when that is the repository''s default, and nothing changes either way — a project pinned to that branch stays pinned. Sending the default branch when the project deploys from something else returns it to following the default, including on a rebind, where a pin describes a branch chosen for the repository being left. Any other branch becomes the project''s own choice, exempt from later default-branch renames. Validated as a Git branch name only: it does not have to exist yet, so a project can be pointed at a branch about to be pushed. Changing repository and naming a branch other than the new repository''s default in one request is refused with 400, because the branch named is almost always the previous repository''s, echoed back — connect first, then set the branch.' + description: 'The branch a push must land on to deploy, with three cases, + because this is a full replace and a read-modify-write client sends + back whatever it read. Omit it to follow the repository''s GitHub + default branch, which also discards a branch set earlier. Send back + the branch the project already deploys from, when that is the + repository''s default, and nothing changes either way — a project + pinned to that branch stays pinned. Sending the default branch when + the project deploys from something else returns it to following the + default, including on a rebind, where a pin describes a branch + chosen for the repository being left. Any other branch becomes the + project''s own choice, exempt from later default-branch renames. + Validated as a Git branch name only: it does not have to exist yet, + so a project can be pointed at a branch about to be pushed. Changing + repository and naming a branch other than the new repository''s + default in one request is refused with 400, because the branch named + is almost always the previous repository''s, echoed back — connect + first, then set the branch.' required: - connection_id - installation_id @@ -15162,7 +16046,8 @@ components: app_root: type: string maxLength: 1024 - description: Optional relative POSIX path from the uploaded archive root to the Next.js app that is deployed. + description: Optional relative POSIX path from the uploaded archive root to the + Next.js app that is deployed. status: type: string description: | @@ -15343,7 +16228,8 @@ components: description: What initiated this deployment. initiated_by: type: string - description: Platform user that triggered a request-initiated deployment; absent for git and system deployments. + description: Platform user that triggered a request-initiated deployment; absent + for git and system deployments. artifact_bucket: type: string artifact_key: @@ -15359,10 +16245,12 @@ components: codebuild_duration_seconds: type: integer format: int64 - description: Total CodeBuild build duration recorded for this deployment, in seconds. + description: Total CodeBuild build duration recorded for this deployment, in + seconds. codebuild_build_count: type: integer - description: Number of completed CodeBuild builds included in codebuild_duration_seconds. + description: Number of completed CodeBuild builds included in + codebuild_duration_seconds. codebuild_duration_recorded_at: type: string format: date-time @@ -15432,7 +16320,8 @@ components: page_views: type: integer format: int64 - description: Navigable-document responses served on this day (text/html or Sec-Fetch-Dest=document) — strict subset of `requests`. + description: Navigable-document responses served on this day (text/html or + Sec-Fetch-Dest=document) — strict subset of `requests`. required: - day - requests @@ -15459,14 +16348,16 @@ components: - requests FrontendUsageHistoryResponse: type: object - description: Zero-filled daily series of request + error counts for a single frontend, oldest first. + description: Zero-filled daily series of request + error counts for a single + frontend, oldest first. properties: frontend_id: type: string format: uuid days: type: integer - description: Number of daily entries returned (always equal to the `days` query param after clamping). + description: Number of daily entries returned (always equal to the `days` query + param after clamping). daily: type: array items: @@ -15525,15 +16416,21 @@ components: type: object nullable: true additionalProperties: true - description: Optional OpenAPI 3.0 or 3.1 document describing an HTTP-mode function. + description: Optional OpenAPI 3.0 or 3.1 document describing an HTTP-mode + function. has_openapi_spec: type: boolean - description: Whether OpenAPI metadata is configured; list responses omit the document itself. + description: Whether OpenAPI metadata is configured; list responses omit the + document itself. aws_function_arn: type: string invoke_url: type: string - description: 'Canonical geo-routed HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development, so a client testing for an empty string never matches.' + description: 'Canonical geo-routed HTTPS endpoint for invoking this function. + Use it as-is: it does not share a domain with the API, so a host + derived from the API URL will not reach the function. Omitted when + the deployment serves no public invocation domain, as in local + development, so a client testing for an empty string never matches.' deployed_regions: type: array items: @@ -15839,7 +16736,8 @@ components: description: What initiated this deployment. initiated_by: type: string - description: Platform user that triggered a request-initiated deployment; absent for git and system deployments. + description: Platform user that triggered a request-initiated deployment; absent + for git and system deployments. artifact_bucket: type: string artifact_key: @@ -15849,10 +16747,12 @@ components: codebuild_duration_seconds: type: integer format: int64 - description: Total CodeBuild build duration recorded for this deployment, in seconds. + description: Total CodeBuild build duration recorded for this deployment, in + seconds. codebuild_build_count: type: integer - description: Number of completed CodeBuild builds included in codebuild_duration_seconds. + description: Number of completed CodeBuild builds included in + codebuild_duration_seconds. codebuild_duration_recorded_at: type: string format: date-time @@ -15956,7 +16856,10 @@ components: q: type: string maxLength: 512 - description: Optional activity query. Supports quoted text, implicit AND, AND/OR/NOT, parentheses, and fields such as `level`, `region`, `invocation.id`, `resource.id`, `resource.name`, `function`, `frontend`, `database`, and `body`. + description: Optional activity query. Supports quoted text, implicit AND, + AND/OR/NOT, parentheses, and fields such as `level`, `region`, + `invocation.id`, `resource.id`, `resource.name`, `function`, + `frontend`, `database`, and `body`. start_time: type: string format: date-time @@ -15996,7 +16899,10 @@ components: q: type: string maxLength: 512 - description: Optional log query. Supports quoted text, implicit AND, AND/OR/NOT, parentheses, and fields such as `level`, `region`, `invocation.id`, `resource.id`, `resource.name`, `function`, `frontend`, `database`, and `body`. + description: Optional log query. Supports quoted text, implicit AND, AND/OR/NOT, + parentheses, and fields such as `level`, `region`, `invocation.id`, + `resource.id`, `resource.name`, `function`, `frontend`, `database`, + and `body`. start_time: type: string format: date-time @@ -16018,7 +16924,8 @@ components: - resource LogStreamRequest: type: object - description: Stream request for live project logs. Pagination cursors and fixed end times are not supported. + description: Stream request for live project logs. Pagination cursors and fixed + end times are not supported. additionalProperties: false properties: resource: @@ -16026,7 +16933,8 @@ components: q: type: string maxLength: 512 - description: Optional log query using the same syntax as search and activity requests. + description: Optional log query using the same syntax as search and activity + requests. start_time: type: string format: date-time @@ -16036,18 +16944,21 @@ components: minimum: 1 maximum: 1000 default: 100 - description: Maximum number of records to deliver on connect or reconnect before following new events. + description: Maximum number of records to deliver on connect or reconnect before + following new events. required: - resource LogSearchEvent: allOf: - $ref: '#/components/schemas/LogEvent' - type: object - description: Runtime log event returned by project log search. Search results always include a stable event ID and owning resource. + description: Runtime log event returned by project log search. Search results + always include a stable event ID and owning resource. properties: id: type: string - description: Opaque stable log event ID for pagination, deduplication, and display. + description: Opaque stable log event ID for pagination, deduplication, and + display. resource: $ref: '#/components/schemas/LogResource' required: @@ -16070,7 +16981,8 @@ components: description: Whether there are more log events available. next_cursor: type: string - description: Opaque cursor for the next page. Send this value as `cursor` on the next request. + description: Opaque cursor for the next page. Send this value as `cursor` on the + next request. required: - data - limit @@ -16110,13 +17022,16 @@ components: language: type: string example: nodejs - description: Runtime language family used by the CLI to choose defaults from source files. + description: Runtime language family used by the CLI to choose defaults from + source files. default: type: boolean description: Whether this runtime is the CLI default for its language. durable_capable: type: boolean - description: Whether a durable function can be authored on this runtime. Only runtimes with a durable authoring API report true, and a durable deploy naming any other runtime is rejected. + description: Whether a durable function can be authored on this runtime. Only + runtimes with a durable authoring API report true, and a durable + deploy naming any other runtime is rejected. deployment: $ref: '#/components/schemas/FunctionRuntimeDeployment' FunctionRuntimesResponse: @@ -16182,7 +17097,8 @@ components: run_count: type: integer format: int64 - description: Total number of times this scheduler has executed. 0 for a scheduler that has never run. + description: Total number of times this scheduler has executed. 0 for a + scheduler that has never run. created_at: type: string format: date-time @@ -16206,10 +17122,12 @@ components: type: boolean next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - page @@ -16267,7 +17185,8 @@ components: properties: id: type: string - description: Opaque stable log event ID for pagination, deduplication, and display. + description: Opaque stable log event ID for pagination, deduplication, and + display. timestamp: type: string format: date-time @@ -16275,7 +17194,9 @@ components: level: $ref: '#/components/schemas/LiveLogLevel' body: - description: Application log value. JSON arguments retain their JSON type. Strings containing a serialized JSON object or array are normalized to that object or array; all other strings remain strings. + description: Application log value. JSON arguments retain their JSON type. + Strings containing a serialized JSON object or array are normalized + to that object or array; all other strings remain strings. nullable: true oneOf: - type: string @@ -16295,7 +17216,8 @@ components: $ref: '#/components/schemas/LogDeployment' invocation_id: type: string - description: Function invocation ID associated with this log event, when available. + description: Function invocation ID associated with this log event, when + available. required: - timestamp - body @@ -16460,10 +17382,12 @@ components: description: URL path to next page (only present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - page @@ -16519,13 +17443,16 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more is true) + description: URL path to next page (offset pagination only; present if has_more + is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - page @@ -16584,10 +17511,12 @@ components: description: URL path to next page (only present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - page @@ -16698,13 +17627,16 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more is true) + description: URL path to next page (offset pagination only; present if has_more + is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - limit @@ -16731,13 +17663,16 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more is true) + description: URL path to next page (offset pagination only; present if has_more + is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. status: type: string enum: @@ -16780,13 +17715,16 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more is true) + description: URL path to next page (offset pagination only; present if has_more + is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - page @@ -16795,7 +17733,8 @@ components: - has_more PaginatedStorageBuckets: type: object - description: Cursor-paginated storage buckets (returned only when cursor pagination is requested). + description: Cursor-paginated storage buckets (returned only when cursor + pagination is requested). properties: data: type: array @@ -16815,7 +17754,8 @@ components: description: Opaque cursor for the next page (present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (present when a previous page + exists). Send as `ending_before`. required: - data - limit @@ -16844,10 +17784,12 @@ components: description: URL path to next page (only present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if + has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; + present when a previous page exists). Send as `ending_before`. required: - data - page @@ -16920,11 +17862,13 @@ components: git_connection: allOf: - $ref: '#/components/schemas/ProjectGitConnectionSummary' - description: Present for connected projects when `git_connection` is requested through the list endpoint's `include` parameter. + description: Present for connected projects when `git_connection` is requested + through the list endpoint's `include` parameter. health: allOf: - $ref: '#/components/schemas/ProjectHealthSummary' - description: Present when `health` is requested through the list endpoint's `include` parameter. + description: Present when `health` is requested through the list endpoint's + `include` parameter. created_at: type: string format: date-time @@ -16968,13 +17912,16 @@ components: shared_variables: type: array uniqueItems: true - description: Replace the complete shared function-variable list with existing names, without changing variable values. Omission keeps membership unchanged; an empty list clears it. + description: Replace the complete shared function-variable list with existing + names, without changing variable values. Omission keeps membership + unchanged; an empty list clears it. items: type: string pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ variables: type: array - description: Fully synced when declared - variables absent from this list are deleted. + description: Fully synced when declared - variables absent from this list are + deleted. items: $ref: '#/components/schemas/ProjectConfigVariable' buckets: @@ -17033,10 +17980,12 @@ components: properties: section: type: string - description: Manifest section the entry belongs to (e.g. variables, buckets, auth.providers.oauth). + description: Manifest section the entry belongs to (e.g. variables, buckets, + auth.providers.oauth). name: type: string - description: Resource name or key within the section. Empty for singleton sections. + description: Resource name or key within the section. Empty for singleton + sections. action: type: string enum: @@ -17050,7 +17999,8 @@ components: description: Error detail when action is `error`. notice: type: string - description: Optional operational note (e.g. disabling realtime drops active connections). + description: Optional operational note (e.g. disabling realtime drops active + connections). required: - section - action @@ -17170,7 +18120,8 @@ components: properties: require_confirmation: type: boolean - description: Require users to confirm email before sign-in. Requires email sending to be enabled. + description: Require users to confirm email before sign-in. Requires email + sending to be enabled. confirmation_timeout: type: integer description: Email confirmation token expiry in seconds @@ -17223,7 +18174,8 @@ components: $ref: '#/components/schemas/ProjectConfigAuthEmailPasswordProvider' oauth: type: array - description: Fully synced when declared - providers absent from this list are deleted. + description: Fully synced when declared - providers absent from this list are + deleted. items: $ref: '#/components/schemas/ProjectConfigOAuthProvider' ProjectConfigAuthRateLimits: @@ -17622,7 +18574,8 @@ components: type: string format: password writeOnly: true - description: Write-only; omitted from config export. Not used for `provider=device`. + description: Write-only; omitted from config export. Not used for + `provider=device`. redirect_url: type: string description: Not used for `provider=device`. @@ -17732,7 +18685,10 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. + description: Include this name in the project's shared function variables. + Omission preserves existing membership; new variables default to + true for legacy clients. Send false explicitly to create a + non-shared variable. name: type: string minLength: 1 @@ -18304,7 +19260,11 @@ components: description: Canonical function ID used for invocation routing invoke_url: type: string - description: 'Canonical HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development; invoke through POST /functions/{functionId}/invoke instead.' + description: 'Canonical HTTPS endpoint for invoking this function. Use it as-is: + it does not share a domain with the API, so a host derived from the + API URL will not reach the function. Omitted when the deployment + serves no public invocation domain, as in local development; invoke + through POST /functions/{functionId}/invoke instead.' cache_ttl_seconds: type: integer minimum: 1 @@ -18325,7 +19285,8 @@ components: default: cron cron_expression: type: string - description: Standard 5-field cron expression evaluated in UTC. Seconds fields, descriptors, and Quartz syntax are not supported. + description: Standard 5-field cron expression evaluated in UTC. Seconds fields, + descriptors, and Quartz syntax are not supported. example: '*/5 * * * *' ServiceKey: type: object @@ -18510,7 +19471,8 @@ components: - mime_type StorageObjectWithBucket: type: object - description: Storage object with bucket name included (for admin listing across buckets) + description: Storage object with bucket name included (for admin listing across + buckets) properties: id: type: string @@ -18680,11 +19642,13 @@ components: html_body: type: string maxLength: 262144 - description: Optional HTML body override. Rendered as an html/template. Max 256 KiB. + description: Optional HTML body override. Rendered as an html/template. Max 256 + KiB. text_body: type: string maxLength: 262144 - description: Optional plain-text body override. Rendered as a text/template. Max 256 KiB. + description: Optional plain-text body override. Rendered as a text/template. Max + 256 KiB. TestEmailResponse: type: object required: @@ -18694,7 +19658,9 @@ components: type: boolean UpdateAuthConfigRequest: type: object - description: 'All fields optional - only include fields you want to update. Validation rule: require_email_confirmation=true requires email_enabled=true.' + description: 'All fields optional - only include fields you want to update. + Validation rule: require_email_confirmation=true requires + email_enabled=true.' properties: access_token_lifetime: type: integer @@ -18772,16 +19738,20 @@ components: type: integer require_email_confirmation: type: boolean - description: Require users to confirm email before sign-in. Can only be true when email_enabled is true. + description: Require users to confirm email before sign-in. Can only be true + when email_enabled is true. email_confirmation_timeout: type: integer description: Email confirmation token expiry in seconds. auto_link_verified_oauth: type: boolean - description: Link a verified OAuth identity to an existing confirmed account with the same email instead of returning a conflict. Requires require_email_confirmation to be true. + description: Link a verified OAuth identity to an existing confirmed account + with the same email instead of returning a conflict. Requires + require_email_confirmation to be true. email_enabled: type: boolean - description: Enable transactional email sending. Cannot be false while require_email_confirmation is true. + description: Enable transactional email sending. Cannot be false while + require_email_confirmation is true. email_from_address: type: string email_from_name: @@ -18797,7 +19767,9 @@ components: format: password writeOnly: true minLength: 1 - description: Replacement SMTP password. Omit this field to preserve the configured password. The value is encrypted at rest and never returned. + description: Replacement SMTP password. Omit this field to preserve the + configured password. The value is encrypted at rest and never + returned. smtp_use_tls: type: boolean email_confirmation_subject: @@ -18923,10 +19895,12 @@ components: properties: client_id: type: string - description: Supported for non-device providers. Not supported for `provider=device`. + description: Supported for non-device providers. Not supported for + `provider=device`. client_secret: type: string - description: Supported for non-device providers. Not supported for `provider=device`. + description: Supported for non-device providers. Not supported for + `provider=device`. redirect_url: type: string format: uri @@ -18992,7 +19966,10 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. + description: Include this name in the project's shared function variables. + Omission preserves existing membership; new variables default to + true for legacy clients. Send false explicitly to create a + non-shared variable. value: type: string required: @@ -19084,7 +20061,10 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. + description: Include this name in the project's shared function variables. + Omission preserves existing membership; new variables default to + true for legacy clients. Send false explicitly to create a + non-shared variable. id: type: string format: uuid @@ -19120,7 +20100,8 @@ components: - api - system - unknown - description: What initiated the latest variable propagation sync, when one has run. + description: What initiated the latest variable propagation sync, when one has + run. created_at: type: string format: date-time @@ -19320,7 +20301,8 @@ components: - status DeploymentProgress: type: object - description: Normalized live progress derived from the deployment workflow and build phases. + description: Normalized live progress derived from the deployment workflow and + build phases. properties: current_phase: type: string @@ -19350,310 +20332,830 @@ components: $ref: '#/components/schemas/DeploymentPhase' updated_at: type: string - format: date-time + format: date-time + required: + - started_at + - elapsed_seconds + - phases + - updated_at + LogDeploymentRequestSelector: + type: object + description: Deployment log selector for deployable resources. + additionalProperties: false + properties: + ids: + type: array + maxItems: 25 + description: Optional deployment identifiers. Omit or send an empty array to + include every deployment for the selected resources. + items: + type: string + format: uuid + LogFunctionRequestResource: + type: object + description: Edge Function log resource selector. + additionalProperties: false + properties: + type: + type: string + enum: + - function + description: Resource type to read logs for. + ids: + type: array + maxItems: 25 + description: Optional function identifiers. Omit or send an empty array to + include every function in the project. + items: + type: string + format: uuid + deployments: + $ref: '#/components/schemas/LogDeploymentRequestSelector' + required: + - type + LogFrontendRequestResource: + type: object + description: Frontend log resource selector. + additionalProperties: false + properties: + type: + type: string + enum: + - frontend + description: Resource type to read logs for. + ids: + type: array + maxItems: 25 + description: Optional frontend identifiers. Omit or send an empty array to + include every frontend in the project. + items: + type: string + format: uuid + deployments: + $ref: '#/components/schemas/LogDeploymentRequestSelector' + required: + - type + LogDatabaseRequestResource: + type: object + description: Database runtime log resource selector. Deployment logs are not + supported for databases. + additionalProperties: false + properties: + type: + type: string + enum: + - database + description: Resource type to read logs for. + ids: + type: array + maxItems: 25 + description: Optional database identifiers. Omit or send an empty array to + include every database in the project. + items: + type: string + format: uuid + required: + - type + LogRequestResource: + description: Resource selectors for project log reads. + oneOf: + - $ref: '#/components/schemas/LogFunctionRequestResource' + - $ref: '#/components/schemas/LogFrontendRequestResource' + - $ref: '#/components/schemas/LogDatabaseRequestResource' + discriminator: + propertyName: type + mapping: + function: '#/components/schemas/LogFunctionRequestResource' + frontend: '#/components/schemas/LogFrontendRequestResource' + database: '#/components/schemas/LogDatabaseRequestResource' + LogResource: + type: object + description: Resource that owns a historical log event. + properties: + type: + type: string + enum: + - function + - frontend + - database + description: Resource type that owns the log event. + id: + type: string + format: uuid + description: Resource ID that owns the log event. + name: + type: string + description: Resource name associated with the log event, when available. + required: + - type + - id + LogDeployment: + type: object + description: Deployment context associated with a historical deployment log event. + properties: + id: + type: string + format: uuid + description: Deployment ID associated with the log event. + stage: + type: string + enum: + - compile + - publish + description: Deployment stage that produced the log event, when available. + required: + - id + DatabaseQueryFilter: + type: object + description: One WHERE condition. Conditions are combined with AND. + properties: + column: + type: string + example: status + operator: + type: string + enum: + - eq + - neq + - gt + - gte + - lt + - lte + - like + - ilike + - is + - in + example: eq + description: | + Filter operators: + - eq: equals (=) + - neq: not equals (<>) + - gt: greater than (>) + - gte: greater than or equal (>=) + - lt: less than (<) + - lte: less than or equal (<=) + - like: pattern match (LIKE) + - ilike: case-insensitive pattern match (ILIKE) + - is: IS NULL / IS NOT NULL + - in: IN array + value: + oneOf: + - type: string + nullable: true + - type: number + - type: boolean + - type: array + description: Array of values, for the `in` operator + items: + oneOf: + - type: string + - type: number + - type: boolean + example: published + required: + - column + - operator + - value + DatabaseQueryOrder: + type: object + description: One ORDER BY clause. + properties: + column: + type: string + example: created_at + ascending: + type: boolean + default: true + example: false + nulls_first: + type: boolean + default: false + required: + - column + FunctionRuntimeDeployment: + type: object + required: + - file_extensions + - entrypoint + - handler + - dependency_manifests + properties: + file_extensions: + type: array + description: Source file extensions the CLI can use to detect this runtime. + items: + type: string + example: + - .js + - .mjs + entrypoint: + type: string + description: Archive path the CLI should use for a single-file function source. + example: index.js + handler: + type: string + description: Handler symbol the CLI should submit when deploying this runtime. + example: handler + dependency_manifests: + type: array + description: Dependency manifest files the CLI should include for hosted builds. + items: + type: string + example: + - package.json + - package-lock.json + AuthHostedPageDefaults: + type: object + required: + - html + - css + description: | + The starting point for an unsaved page: the theme shell we render for the + built-in page plus its stylesheet. Valid input to the update endpoint — + it carries no script, meta, or link tags. + properties: + html: + type: string + description: Body shell markup containing the runtime render root. + css: + type: string + description: The built-in stylesheet, themed by the customer. + AuthHostedPageRuntime: + type: object + required: + - root_id + - script + - mock_prelude + description: | + The server-owned behavior of a hosted page. Clients compose previews from + this instead of reimplementing the page, so a preview cannot drift from + what is actually served. + properties: + root_id: + type: string + description: Element id the runtime script renders into. Markup carrying it opts + into the theme-shell contract. + script: + type: string + description: The runtime script injected into the rendered page. + mock_prelude: + type: string + description: Preview harness that supplies request params and stubs the + hosted-auth API. Never served on a real page. + AuthPageAppearanceDefaults: + type: object required: - - started_at - - elapsed_seconds - - phases - - updated_at - LogDeploymentRequestSelector: + - theme + - layout + properties: + theme: + $ref: '#/components/schemas/AuthPageTheme' + layout: + $ref: '#/components/schemas/AuthPageLayout' + AuthPageAppearanceOptions: type: object - description: Deployment log selector for deployable resources. - additionalProperties: false + required: + - pages + - fonts + - scales + - densities + - radii + - layouts properties: - ids: + pages: type: array - maxItems: 25 - description: Optional deployment identifiers. Omit or send an empty array to include every deployment for the selected resources. items: - type: string - format: uuid - LogFunctionRequestResource: + $ref: '#/components/schemas/HostedAuthPageType' + fonts: + type: array + items: + $ref: '#/components/schemas/AuthPageFont' + scales: + type: array + items: + $ref: '#/components/schemas/AuthPageScale' + densities: + type: array + items: + $ref: '#/components/schemas/AuthPageDensity' + radii: + type: array + items: + $ref: '#/components/schemas/AuthPageRadius' + layouts: + type: array + items: + $ref: '#/components/schemas/AuthPageLayout' + SandboxPreset: type: object - description: Edge Function log resource selector. additionalProperties: false properties: - type: + id: + type: string + runtime: + type: string + version: type: string + memory_mb: + type: integer enum: - - function - description: Resource type to read logs for. - ids: + - 1024 + - 2048 + regions: type: array - maxItems: 25 - description: Optional function identifiers. Omit or send an empty array to include every function in the project. items: type: string - format: uuid - deployments: - $ref: '#/components/schemas/LogDeploymentRequestSelector' required: - - type - LogFrontendRequestResource: + - id + - runtime + - version + - memory_mb + - regions + SandboxTemplate: type: object - description: Frontend log resource selector. additionalProperties: false properties: - type: + id: + type: string + format: uuid + project_id: + type: string + format: uuid + name: + type: string + pattern: ^[a-z][a-z0-9-]{0,62}$ + preset: + type: string + memory_mb: + type: integer + status: type: string enum: - - frontend - description: Resource type to read logs for. - ids: - type: array - maxItems: 25 - description: Optional frontend identifiers. Omit or send an empty array to include every frontend in the project. - items: - type: string - format: uuid - deployments: - $ref: '#/components/schemas/LogDeploymentRequestSelector' + - ready + - unavailable + - deleting + created_at: + type: string + format: date-time required: - - type - LogDatabaseRequestResource: + - id + - project_id + - name + - status + - created_at + CreateSandboxTemplateRequest: type: object - description: Database runtime log resource selector. Deployment logs are not supported for databases. additionalProperties: false properties: - type: + name: + type: string + pattern: ^[a-z][a-z0-9-]{0,62}$ + preset: type: string enum: - - database - description: Resource type to read logs for. - ids: - type: array - maxItems: 25 - description: Optional database identifiers. Omit or send an empty array to include every database in the project. - items: - type: string - format: uuid + - python3.12 + - node22 + memory_mb: + type: integer + enum: + - 1024 + - 2048 + default: 1024 required: - - type - LogRequestResource: - description: Resource selectors for project log reads. - oneOf: - - $ref: '#/components/schemas/LogFunctionRequestResource' - - $ref: '#/components/schemas/LogFrontendRequestResource' - - $ref: '#/components/schemas/LogDatabaseRequestResource' - discriminator: - propertyName: type - mapping: - function: '#/components/schemas/LogFunctionRequestResource' - frontend: '#/components/schemas/LogFrontendRequestResource' - database: '#/components/schemas/LogDatabaseRequestResource' - LogResource: + - name + - preset + UpdateSandboxTemplateRequest: type: object - description: Resource that owns a historical log event. + additionalProperties: false properties: - type: - type: string - enum: - - function - - frontend - - database - description: Resource type that owns the log event. - id: - type: string - format: uuid - description: Resource ID that owns the log event. name: type: string - description: Resource name associated with the log event, when available. + pattern: ^[a-z][a-z0-9-]{0,62}$ required: - - type - - id - LogDeployment: + - name + SandboxSession: type: object - description: Deployment context associated with a historical deployment log event. + additionalProperties: false properties: id: type: string format: uuid - description: Deployment ID associated with the log event. - stage: + project_id: + type: string + format: uuid + sandbox_id: + type: string + format: uuid + state: type: string enum: - - compile - - publish - description: Deployment stage that produced the log event, when available. + - starting + - running + - suspending + - suspended + - resuming + - terminating + - terminated + - unknown + desired_state: + type: string + enum: + - running + - suspended + - terminated + region: + type: string + memory_mb: + type: integer + created_at: + type: string + format: date-time + started_at: + type: string + format: date-time + expires_at: + type: string + format: date-time required: - id - DatabaseQueryFilter: + - project_id + - sandbox_id + - state + - desired_state + - region + - memory_mb + - created_at + - expires_at + CreateSandboxSessionRequest: type: object - description: One WHERE condition. Conditions are combined with AND. + additionalProperties: false properties: - column: + preset: type: string - example: status - operator: + enum: + - python3.12 + - node22 + sandbox_id: type: string + format: uuid + memory_mb: + type: integer enum: - - eq - - neq - - gt - - gte - - lt - - lte - - like - - ilike - - is - - in - example: eq - description: | - Filter operators: - - eq: equals (=) - - neq: not equals (<>) - - gt: greater than (>) - - gte: greater than or equal (>=) - - lt: less than (<) - - lte: less than or equal (<=) - - like: pattern match (LIKE) - - ilike: case-insensitive pattern match (ILIKE) - - is: IS NULL / IS NOT NULL - - in: IN array - value: - oneOf: - - type: string - nullable: true - - type: number - - type: boolean - - type: array - description: Array of values, for the `in` operator - items: - oneOf: - - type: string - - type: number - - type: boolean - example: published + - 1024 + - 2048 + region: + type: string + pattern: ^aws-[a-z0-9-]+$ + max_duration_seconds: + type: integer + minimum: 30 + maximum: 28800 + default: 3600 + idle_timeout_seconds: + type: integer + minimum: 0 + maximum: 28800 + default: 0 + required: + - region + oneOf: + - required: + - preset + not: + required: + - sandbox_id + - required: + - sandbox_id + not: + required: + - preset + SandboxCommandRequest: + type: object + additionalProperties: false + properties: + command: + type: string + minLength: 1 + maxLength: 65536 + timeout_seconds: + type: integer + minimum: 1 + maximum: 3600 + default: 60 + environment: + type: object + additionalProperties: + type: string + maxProperties: 64 + required: + - command + SandboxExecutionRequest: + type: object + additionalProperties: false + properties: + preset: + type: string + enum: + - python3.12 + - node22 + sandbox_id: + type: string + format: uuid + memory_mb: + type: integer + enum: + - 1024 + - 2048 + region: + type: string + pattern: ^aws-[a-z0-9-]+$ + command: + type: string + minLength: 1 + maxLength: 65536 + timeout_seconds: + type: integer + minimum: 1 + maximum: 60 + default: 60 + environment: + type: object + additionalProperties: + type: string + maxProperties: 64 required: - - column - - operator - - value - DatabaseQueryOrder: + - region + - command + oneOf: + - required: + - preset + not: + required: + - sandbox_id + - required: + - sandbox_id + not: + required: + - preset + SandboxCommandResult: type: object - description: One ORDER BY clause. + additionalProperties: false properties: - column: + stdout: type: string - example: created_at - ascending: + stderr: + type: string + exit_code: + type: integer + stdout_truncated: type: boolean - default: true - example: false - nulls_first: + stderr_truncated: + type: boolean + timed_out: type: boolean - default: false required: - - column - FunctionRuntimeDeployment: + - stdout + - stderr + - exit_code + - stdout_truncated + - stderr_truncated + - timed_out + SandboxExecutionResult: type: object + additionalProperties: false + properties: + stdout: + type: string + stderr: + type: string + exit_code: + type: integer + stdout_truncated: + type: boolean + stderr_truncated: + type: boolean + timed_out: + type: boolean + session_id: + type: string + format: uuid + region: + type: string + duration_ms: + type: integer + format: int64 + minimum: 0 required: - - file_extensions - - entrypoint - - handler - - dependency_manifests + - stdout + - stderr + - exit_code + - stdout_truncated + - stderr_truncated + - timed_out + - session_id + - region + - duration_ms + SandboxFileWriteRequest: + type: object + additionalProperties: false properties: - file_extensions: - type: array - description: Source file extensions the CLI can use to detect this runtime. - items: - type: string - example: - - .js - - .mjs - entrypoint: + path: type: string - description: Archive path the CLI should use for a single-file function source. - example: index.js - handler: + minLength: 1 + maxLength: 4096 + data: type: string - description: Handler symbol the CLI should submit when deploying this runtime. - example: handler - dependency_manifests: - type: array - description: Dependency manifest files the CLI should include for hosted builds. - items: - type: string - example: - - package.json - - package-lock.json - AuthHostedPageDefaults: + format: byte + maxLength: 11184812 + required: + - path + - data + SandboxFileReadRequest: type: object + additionalProperties: false + properties: + path: + type: string + minLength: 1 + maxLength: 4096 required: - - html - - css - description: | - The starting point for an unsaved page: the theme shell we render for the - built-in page plus its stylesheet. Valid input to the update endpoint — - it carries no script, meta, or link tags. + - path + SandboxFileResult: + type: object + additionalProperties: false properties: - html: + data: type: string - description: Body shell markup containing the runtime render root. - css: + format: byte + required: + - data + SandboxSubjectGrantRequest: + type: object + additionalProperties: false + properties: + expires_at: type: string - description: The built-in stylesheet, themed by the customer. - AuthHostedPageRuntime: + format: date-time + required: + - expires_at + SandboxAccessRequest: type: object + additionalProperties: false + properties: + port: + type: integer + minimum: 1 + maximum: 65532 + expires_in_seconds: + type: integer + minimum: 1 + maximum: 300 + default: 300 required: - - root_id - - script - - mock_prelude - description: | - The server-owned behavior of a hosted page. Clients compose previews from - this instead of reimplementing the page, so a preview cannot drift from - what is actually served. + - port + SandboxAccess: + type: object + additionalProperties: false properties: - root_id: + url: type: string - description: Element id the runtime script renders into. Markup carrying it opts into the theme-shell contract. - script: + format: uri + token: type: string - description: The runtime script injected into the rendered page. - mock_prelude: + expires_at: type: string - description: Preview harness that supplies request params and stubs the hosted-auth API. Never served on a real page. - AuthPageAppearanceDefaults: - type: object + format: date-time required: - - theme - - layout + - url + - token + - expires_at + SandboxDeployment: + type: object + additionalProperties: false properties: - theme: - $ref: '#/components/schemas/AuthPageTheme' - layout: - $ref: '#/components/schemas/AuthPageLayout' - AuthPageAppearanceOptions: + id: + type: string + format: uuid + status: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + required: + - id + - status + - created_at + - updated_at + SandboxPagination: type: object + additionalProperties: false + properties: + limit: + type: integer + has_more: + type: boolean + next_cursor: + type: string required: - - pages - - fonts - - scales - - densities - - radii - - layouts + - limit + - has_more + SandboxTemplatePage: + type: object + additionalProperties: false properties: - pages: - type: array - items: - $ref: '#/components/schemas/HostedAuthPageType' - fonts: + data: type: array items: - $ref: '#/components/schemas/AuthPageFont' - scales: + $ref: "#/components/schemas/SandboxTemplate" + pagination: + $ref: "#/components/schemas/SandboxPagination" + required: + - data + - pagination + SandboxSessionPage: + type: object + additionalProperties: false + properties: + data: type: array items: - $ref: '#/components/schemas/AuthPageScale' - densities: + $ref: "#/components/schemas/SandboxSession" + pagination: + $ref: "#/components/schemas/SandboxPagination" + required: + - data + - pagination + SandboxDeploymentPage: + type: object + additionalProperties: false + properties: + data: type: array items: - $ref: '#/components/schemas/AuthPageDensity' - radii: + $ref: "#/components/schemas/SandboxDeployment" + pagination: + $ref: "#/components/schemas/SandboxPagination" + required: + - data + - pagination + SandboxPresetList: + type: object + additionalProperties: false + properties: + data: type: array items: - $ref: '#/components/schemas/AuthPageRadius' - layouts: + $ref: "#/components/schemas/SandboxPreset" + required: + - data + SandboxCapacity: + type: object + additionalProperties: false + properties: + region: + type: string + allocated_memory_mb: + type: integer + format: int64 + minimum: 0 + required: + - region + - allocated_memory_mb + SandboxCapacityList: + type: object + additionalProperties: false + properties: + data: type: array items: - $ref: '#/components/schemas/AuthPageLayout' + $ref: "#/components/schemas/SandboxCapacity" + required: + - data + PublishSandboxPresetRequest: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + preset: + type: string + enum: + - python3.12 + - node22 + memory_mb: + type: integer + enum: + - 1024 + - 2048 + deployment_id: + type: string + format: uuid + required: + - id + - preset + - memory_mb + - deployment_id diff --git a/orval.config.mjs b/orval.config.mjs index 0f17cd6c..af8cc2b7 100644 --- a/orval.config.mjs +++ b/orval.config.mjs @@ -1,6 +1,24 @@ import { defineConfig, defineTransformer } from 'orval'; const sdkOperations = new Set([ + 'listSandboxPresets', + 'listSandboxes', + 'createSandbox', + 'getSandbox', + 'deleteSandbox', + 'listSandboxSessions', + 'createSandboxSession', + 'executeSandbox', + 'getSandboxSession', + 'terminateSandboxSession', + 'suspendSandboxSession', + 'resumeSandboxSession', + 'executeSandboxSession', + 'readSandboxSessionFile', + 'writeSandboxSessionFile', + 'grantSandboxSession', + 'revokeSandboxSession', + 'createSandboxSessionAccess', 'acquireProjectLock', 'authSignin', 'downloadStorageObject', diff --git a/src/generated-runtime/client.js b/src/generated-runtime/client.js index 64412559..406c6387 100644 --- a/src/generated-runtime/client.js +++ b/src/generated-runtime/client.js @@ -270,3 +270,321 @@ export const downloadStorageObject = async (bucketName, path, options) => { method: 'GET' }); }; +export const getListSandboxPresetsUrl = () => { + return `/sandboxes/presets`; +}; +/** + * @summary List available sandbox presets + */ +export const listSandboxPresets = async (options) => { + return volcanoFetch(getListSandboxPresetsUrl(), { + ...options, + method: 'GET' + }); +}; +export const getListSandboxesUrl = (id, params) => { + const normalizedParams = new URLSearchParams(); + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : String(value)); + } + }); + const stringifiedParams = normalizedParams.toString(); + return stringifiedParams.length > 0 ? `/projects/${id}/sandboxes?${stringifiedParams}` : `/projects/${id}/sandboxes`; +}; +/** + * @summary List sandbox templates + */ +export const listSandboxes = async (id, params, options) => { + return volcanoFetch(getListSandboxesUrl(id, params), { + ...options, + method: 'GET' + }); +}; +export const getCreateSandboxUrl = (id) => { + return `/projects/${id}/sandboxes`; +}; +/** + * @summary Create a sandbox template from a verified preset + */ +export const createSandbox = async (id, createSandboxTemplateRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getCreateSandboxUrl(id), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(createSandboxTemplateRequest) + }); +}; +export const getGetSandboxUrl = (id, sandboxId) => { + return `/projects/${id}/sandboxes/${sandboxId}`; +}; +/** + * @summary Get a sandbox template + */ +export const getSandbox = async (id, sandboxId, options) => { + return volcanoFetch(getGetSandboxUrl(id, sandboxId), { + ...options, + method: 'GET' + }); +}; +export const getDeleteSandboxUrl = (id, sandboxId) => { + return `/projects/${id}/sandboxes/${sandboxId}`; +}; +/** + * @summary Retire a template and terminate its sessions + */ +export const deleteSandbox = async (id, sandboxId, options) => { + return volcanoFetch(getDeleteSandboxUrl(id, sandboxId), { + ...options, + method: 'DELETE' + }); +}; +export const getListSandboxSessionsUrl = (id, params) => { + const normalizedParams = new URLSearchParams(); + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : String(value)); + } + }); + const stringifiedParams = normalizedParams.toString(); + return stringifiedParams.length > 0 ? `/projects/${id}/sandbox-sessions?${stringifiedParams}` : `/projects/${id}/sandbox-sessions`; +}; +/** + * @summary List project sandbox sessions + */ +export const listSandboxSessions = async (id, params, options) => { + return volcanoFetch(getListSandboxSessionsUrl(id, params), { + ...options, + method: 'GET' + }); +}; +export const getCreateSandboxSessionUrl = (id) => { + return `/projects/${id}/sandbox-sessions`; +}; +/** + * @summary Start a sandbox session + */ +export const createSandboxSession = async (id, createSandboxSessionRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getCreateSandboxSessionUrl(id), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(createSandboxSessionRequest) + }); +}; +export const getExecuteSandboxUrl = (id) => { + return `/projects/${id}/sandbox-executions`; +}; +/** + * @summary Execute once and return after confirmed termination + */ +export const executeSandbox = async (id, sandboxExecutionRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getExecuteSandboxUrl(id), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxExecutionRequest) + }); +}; +export const getGetSandboxSessionUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}`; +}; +/** + * @summary Get a sandbox session + */ +export const getSandboxSession = async (sessionId, options) => { + return volcanoFetch(getGetSandboxSessionUrl(sessionId), { + ...options, + method: 'GET' + }); +}; +export const getTerminateSandboxSessionUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}`; +}; +/** + * @summary Request sandbox termination + */ +export const terminateSandboxSession = async (sessionId, options) => { + return volcanoFetch(getTerminateSandboxSessionUrl(sessionId), { + ...options, + method: 'DELETE' + }); +}; +export const getSuspendSandboxSessionUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}/suspend`; +}; +/** + * @summary Suspend a sandbox session + */ +export const suspendSandboxSession = async (sessionId, options) => { + return volcanoFetch(getSuspendSandboxSessionUrl(sessionId), { + ...options, + method: 'POST' + }); +}; +export const getResumeSandboxSessionUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}/resume`; +}; +/** + * @summary Resume a sandbox session + */ +export const resumeSandboxSession = async (sessionId, options) => { + return volcanoFetch(getResumeSandboxSessionUrl(sessionId), { + ...options, + method: 'POST' + }); +}; +export const getExecuteSandboxSessionUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}/exec`; +}; +/** + * @summary Execute a command within a session + */ +export const executeSandboxSession = async (sessionId, sandboxCommandRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getExecuteSandboxSessionUrl(sessionId), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxCommandRequest) + }); +}; +export const getReadSandboxSessionFileUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}/files/read`; +}; +/** + * @summary Read a workspace file + */ +export const readSandboxSessionFile = async (sessionId, sandboxFileReadRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getReadSandboxSessionFileUrl(sessionId), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxFileReadRequest) + }); +}; +export const getWriteSandboxSessionFileUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}/files/write`; +}; +/** + * @summary Write a workspace file + */ +export const writeSandboxSessionFile = async (sessionId, sandboxFileWriteRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getWriteSandboxSessionFileUrl(sessionId), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxFileWriteRequest) + }); +}; +export const getGrantSandboxSessionUrl = (sessionId, subjectId) => { + return `/sandbox-sessions/${sessionId}/grants/${subjectId}`; +}; +/** + * @summary Authorize an authenticated project user for this session + */ +export const grantSandboxSession = async (sessionId, subjectId, sandboxSubjectGrantRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getGrantSandboxSessionUrl(sessionId, subjectId), { + ...options, + method: 'PUT', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxSubjectGrantRequest) + }); +}; +export const getRevokeSandboxSessionUrl = (sessionId, subjectId) => { + return `/sandbox-sessions/${sessionId}/grants/${subjectId}`; +}; +/** + * @summary Revoke a project user session grant + */ +export const revokeSandboxSession = async (sessionId, subjectId, options) => { + return volcanoFetch(getRevokeSandboxSessionUrl(sessionId, subjectId), { + ...options, + method: 'DELETE' + }); +}; +export const getCreateSandboxSessionAccessUrl = (sessionId) => { + return `/sandbox-sessions/${sessionId}/access`; +}; +/** + * @summary Issue a short-lived port-scoped access credential + */ +export const createSandboxSessionAccess = async (sessionId, sandboxAccessRequest, options) => { + const getHeaders = (h) => { + if (!h) + return {}; + if (h instanceof Headers) + return Object.fromEntries(h.entries()); + if (Array.isArray(h)) + return Object.fromEntries(h); + return h; + }; + return volcanoFetch(getCreateSandboxSessionAccessUrl(sessionId), { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxAccessRequest) + }); +}; diff --git a/src/generated-runtime/model/createSandboxSessionRequest.js b/src/generated-runtime/model/createSandboxSessionRequest.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/createSandboxSessionRequest.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/createSandboxSessionRequestMemoryMb.js b/src/generated-runtime/model/createSandboxSessionRequestMemoryMb.js new file mode 100644 index 00000000..11370b51 --- /dev/null +++ b/src/generated-runtime/model/createSandboxSessionRequestMemoryMb.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const CreateSandboxSessionRequestMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +}; diff --git a/src/generated-runtime/model/createSandboxSessionRequestPreset.js b/src/generated-runtime/model/createSandboxSessionRequestPreset.js new file mode 100644 index 00000000..07ebf6b1 --- /dev/null +++ b/src/generated-runtime/model/createSandboxSessionRequestPreset.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const CreateSandboxSessionRequestPreset = { + python312: 'python3.12', + node22: 'node22', +}; diff --git a/src/generated-runtime/model/createSandboxTemplateRequest.js b/src/generated-runtime/model/createSandboxTemplateRequest.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/createSandboxTemplateRequest.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/createSandboxTemplateRequestMemoryMb.js b/src/generated-runtime/model/createSandboxTemplateRequestMemoryMb.js new file mode 100644 index 00000000..16c013ac --- /dev/null +++ b/src/generated-runtime/model/createSandboxTemplateRequestMemoryMb.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const CreateSandboxTemplateRequestMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +}; diff --git a/src/generated-runtime/model/createSandboxTemplateRequestPreset.js b/src/generated-runtime/model/createSandboxTemplateRequestPreset.js new file mode 100644 index 00000000..25c45e55 --- /dev/null +++ b/src/generated-runtime/model/createSandboxTemplateRequestPreset.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const CreateSandboxTemplateRequestPreset = { + python312: 'python3.12', + node22: 'node22', +}; diff --git a/src/generated-runtime/model/cursorParameter.js b/src/generated-runtime/model/cursorParameter.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/cursorParameter.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/index.js b/src/generated-runtime/model/index.js index ad0e0bf2..a2b20407 100644 --- a/src/generated-runtime/model/index.js +++ b/src/generated-runtime/model/index.js @@ -16,8 +16,15 @@ export * from './authUserStatus'; export * from './authUserUserMetadata'; export * from './bandwidthCapExceededResponse'; export * from './completeUploadSessionResponse'; +export * from './createSandboxSessionRequest'; +export * from './createSandboxSessionRequestMemoryMb'; +export * from './createSandboxSessionRequestPreset'; +export * from './createSandboxTemplateRequest'; +export * from './createSandboxTemplateRequestMemoryMb'; +export * from './createSandboxTemplateRequestPreset'; export * from './createUploadSessionRequest'; export * from './createUploadSessionResponse'; +export * from './cursorParameter'; export * from './databaseQueryCapExceededResponse'; export * from './databaseQueryFilter'; export * from './databaseQueryFilterOperator'; @@ -32,12 +39,39 @@ export * from './error'; export * from './hostedAuthPageType'; export * from './limitParameter'; export * from './listDurableExecutionsParams'; +export * from './listSandboxesParams'; +export * from './listSandboxSessionsParams'; export * from './lockRequestIdParameter'; export * from './lockTokenParameter'; export * from './pageParameter'; export * from './paginatedDurableExecutions'; export * from './projectLockLease'; export * from './projectLockLeaseRequest'; +export * from './sandboxAccess'; +export * from './sandboxAccessRequest'; +export * from './sandboxCommandRequest'; +export * from './sandboxCommandRequestEnvironment'; +export * from './sandboxCommandResult'; +export * from './sandboxExecutionRequest'; +export * from './sandboxExecutionRequestEnvironment'; +export * from './sandboxExecutionRequestMemoryMb'; +export * from './sandboxExecutionRequestPreset'; +export * from './sandboxExecutionResult'; +export * from './sandboxFileReadRequest'; +export * from './sandboxFileResult'; +export * from './sandboxFileWriteRequest'; +export * from './sandboxPagination'; +export * from './sandboxPreset'; +export * from './sandboxPresetList'; +export * from './sandboxPresetMemoryMb'; +export * from './sandboxSession'; +export * from './sandboxSessionDesiredState'; +export * from './sandboxSessionPage'; +export * from './sandboxSessionState'; +export * from './sandboxSubjectGrantRequest'; +export * from './sandboxTemplate'; +export * from './sandboxTemplatePage'; +export * from './sandboxTemplateStatus'; export * from './storageObject'; export * from './storageObjectMetadata'; export * from './uploadSessionPart'; diff --git a/src/generated-runtime/model/listSandboxSessionsParams.js b/src/generated-runtime/model/listSandboxSessionsParams.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/listSandboxSessionsParams.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/listSandboxesParams.js b/src/generated-runtime/model/listSandboxesParams.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/listSandboxesParams.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxAccess.js b/src/generated-runtime/model/sandboxAccess.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxAccess.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxAccessRequest.js b/src/generated-runtime/model/sandboxAccessRequest.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxAccessRequest.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxCommandRequest.js b/src/generated-runtime/model/sandboxCommandRequest.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxCommandRequest.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxCommandRequestEnvironment.js b/src/generated-runtime/model/sandboxCommandRequestEnvironment.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxCommandRequestEnvironment.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxCommandResult.js b/src/generated-runtime/model/sandboxCommandResult.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxCommandResult.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxExecutionRequest.js b/src/generated-runtime/model/sandboxExecutionRequest.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxExecutionRequest.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxExecutionRequestEnvironment.js b/src/generated-runtime/model/sandboxExecutionRequestEnvironment.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxExecutionRequestEnvironment.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxExecutionRequestMemoryMb.js b/src/generated-runtime/model/sandboxExecutionRequestMemoryMb.js new file mode 100644 index 00000000..51e1a2e7 --- /dev/null +++ b/src/generated-runtime/model/sandboxExecutionRequestMemoryMb.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const SandboxExecutionRequestMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +}; diff --git a/src/generated-runtime/model/sandboxExecutionRequestPreset.js b/src/generated-runtime/model/sandboxExecutionRequestPreset.js new file mode 100644 index 00000000..c0d6ec95 --- /dev/null +++ b/src/generated-runtime/model/sandboxExecutionRequestPreset.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const SandboxExecutionRequestPreset = { + python312: 'python3.12', + node22: 'node22', +}; diff --git a/src/generated-runtime/model/sandboxExecutionResult.js b/src/generated-runtime/model/sandboxExecutionResult.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxExecutionResult.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxFileReadRequest.js b/src/generated-runtime/model/sandboxFileReadRequest.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxFileReadRequest.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxFileResult.js b/src/generated-runtime/model/sandboxFileResult.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxFileResult.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxFileWriteRequest.js b/src/generated-runtime/model/sandboxFileWriteRequest.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxFileWriteRequest.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxPagination.js b/src/generated-runtime/model/sandboxPagination.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxPagination.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxPreset.js b/src/generated-runtime/model/sandboxPreset.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxPreset.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxPresetList.js b/src/generated-runtime/model/sandboxPresetList.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxPresetList.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxPresetMemoryMb.js b/src/generated-runtime/model/sandboxPresetMemoryMb.js new file mode 100644 index 00000000..c53f1530 --- /dev/null +++ b/src/generated-runtime/model/sandboxPresetMemoryMb.js @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const SandboxPresetMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +}; diff --git a/src/generated-runtime/model/sandboxSession.js b/src/generated-runtime/model/sandboxSession.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxSession.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxSessionDesiredState.js b/src/generated-runtime/model/sandboxSessionDesiredState.js new file mode 100644 index 00000000..003f425f --- /dev/null +++ b/src/generated-runtime/model/sandboxSessionDesiredState.js @@ -0,0 +1,14 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const SandboxSessionDesiredState = { + running: 'running', + suspended: 'suspended', + terminated: 'terminated', +}; diff --git a/src/generated-runtime/model/sandboxSessionPage.js b/src/generated-runtime/model/sandboxSessionPage.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxSessionPage.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxSessionState.js b/src/generated-runtime/model/sandboxSessionState.js new file mode 100644 index 00000000..c8b9574c --- /dev/null +++ b/src/generated-runtime/model/sandboxSessionState.js @@ -0,0 +1,19 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const SandboxSessionState = { + starting: 'starting', + running: 'running', + suspending: 'suspending', + suspended: 'suspended', + resuming: 'resuming', + terminating: 'terminating', + terminated: 'terminated', + unknown: 'unknown', +}; diff --git a/src/generated-runtime/model/sandboxSubjectGrantRequest.js b/src/generated-runtime/model/sandboxSubjectGrantRequest.js new file mode 100644 index 00000000..5346660d --- /dev/null +++ b/src/generated-runtime/model/sandboxSubjectGrantRequest.js @@ -0,0 +1,10 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export {}; diff --git a/src/generated-runtime/model/sandboxTemplate.js b/src/generated-runtime/model/sandboxTemplate.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxTemplate.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxTemplatePage.js b/src/generated-runtime/model/sandboxTemplatePage.js new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/generated-runtime/model/sandboxTemplatePage.js @@ -0,0 +1 @@ +export {}; diff --git a/src/generated-runtime/model/sandboxTemplateStatus.js b/src/generated-runtime/model/sandboxTemplateStatus.js new file mode 100644 index 00000000..55f76c0e --- /dev/null +++ b/src/generated-runtime/model/sandboxTemplateStatus.js @@ -0,0 +1,14 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +export const SandboxTemplateStatus = { + ready: 'ready', + unavailable: 'unavailable', + deleting: 'deleting', +}; diff --git a/src/generated/client.ts b/src/generated/client.ts index dc0692dc..31ff6ba8 100644 --- a/src/generated/client.ts +++ b/src/generated/client.ts @@ -11,20 +11,46 @@ import type { AuthSigninBody, AuthTokenResponse, BandwidthCapExceededResponse, + CreateSandboxSessionRequest, + CreateSandboxTemplateRequest, DatabaseQueryCapExceededResponse, DatabaseQueryResult, DatabaseSelectRequest, DurableExecution, Error, ListDurableExecutionsParams, + ListSandboxSessionsParams, + ListSandboxesParams, PaginatedDurableExecutions, ProjectLockLease, ProjectLockLeaseRequest, + SandboxAccess, + SandboxAccessRequest, + SandboxCommandRequest, + SandboxCommandResult, + SandboxExecutionRequest, + SandboxExecutionResult, + SandboxFileReadRequest, + SandboxFileResult, + SandboxFileWriteRequest, + SandboxPresetList, + SandboxSession, + SandboxSessionPage, + SandboxSubjectGrantRequest, + SandboxTemplate, + SandboxTemplatePage, UploadSessionStatusResponse, UploadStorageObjectBodyOne } from './model'; import { volcanoFetch } from './volcano-fetch'; +export type HTTPStatusCode1xx = 100 | 101 | 102 | 103; +export type HTTPStatusCode2xx = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207; +export type HTTPStatusCode3xx = 300 | 301 | 302 | 303 | 304 | 305 | 307 | 308; +export type HTTPStatusCode4xx = 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 426 | 428 | 429 | 431 | 451; +export type HTTPStatusCode5xx = 500 | 501 | 502 | 503 | 504 | 505 | 507 | 511; +export type HTTPStatusCodes = HTTPStatusCode1xx | HTTPStatusCode2xx | HTTPStatusCode3xx | HTTPStatusCode4xx | HTTPStatusCode5xx; + export type startDurableExecutionFromApplicationResponse202 = { data: DurableExecution status: 202 @@ -760,3 +786,859 @@ export const downloadStorageObject = async (bucketName: string, } );} + + + +export type listSandboxPresetsResponse200 = { + data: SandboxPresetList + status: 200 +} + +export type listSandboxPresetsResponseDefault = { + data: Error + status: Exclude +} + +export type listSandboxPresetsResponseSuccess = (listSandboxPresetsResponse200) & { + headers: Headers; +}; +export type listSandboxPresetsResponseError = (listSandboxPresetsResponseDefault) & { + headers: Headers; +}; + +export type listSandboxPresetsResponse = (listSandboxPresetsResponseSuccess | listSandboxPresetsResponseError) + +export const getListSandboxPresetsUrl = () => { + + + + + return `/sandboxes/presets` +} + +/** + * @summary List available sandbox presets + */ +export const listSandboxPresets = async ( options?: Parameters[1]): Promise => { + + return volcanoFetch(getListSandboxPresetsUrl(), + { + ...options, + method: 'GET' + + + } +);} + + + +export type listSandboxesResponse200 = { + data: SandboxTemplatePage + status: 200 +} + +export type listSandboxesResponseDefault = { + data: Error + status: Exclude +} + +export type listSandboxesResponseSuccess = (listSandboxesResponse200) & { + headers: Headers; +}; +export type listSandboxesResponseError = (listSandboxesResponseDefault) & { + headers: Headers; +}; + +export type listSandboxesResponse = (listSandboxesResponseSuccess | listSandboxesResponseError) + +export const getListSandboxesUrl = (id: string, + params?: ListSandboxesParams,) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : String(value)) + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 ? `/projects/${id}/sandboxes?${stringifiedParams}` : `/projects/${id}/sandboxes` +} + +/** + * @summary List sandbox templates + */ +export const listSandboxes = async (id: string, + params?: ListSandboxesParams, options?: Parameters[1]): Promise => { + + return volcanoFetch(getListSandboxesUrl(id,params), + { + ...options, + method: 'GET' + + + } +);} + + + +export type createSandboxResponse201 = { + data: SandboxTemplate + status: 201 +} + +export type createSandboxResponseDefault = { + data: Error + status: Exclude +} + +export type createSandboxResponseSuccess = (createSandboxResponse201) & { + headers: Headers; +}; +export type createSandboxResponseError = (createSandboxResponseDefault) & { + headers: Headers; +}; + +export type createSandboxResponse = (createSandboxResponseSuccess | createSandboxResponseError) + +export const getCreateSandboxUrl = (id: string,) => { + + + + + return `/projects/${id}/sandboxes` +} + +/** + * @summary Create a sandbox template from a verified preset + */ +export const createSandbox = async (id: string, + createSandboxTemplateRequest: CreateSandboxTemplateRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getCreateSandboxUrl(id), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(createSandboxTemplateRequest) + } +);} + + + +export type getSandboxResponse200 = { + data: SandboxTemplate + status: 200 +} + +export type getSandboxResponseDefault = { + data: Error + status: Exclude +} + +export type getSandboxResponseSuccess = (getSandboxResponse200) & { + headers: Headers; +}; +export type getSandboxResponseError = (getSandboxResponseDefault) & { + headers: Headers; +}; + +export type getSandboxResponse = (getSandboxResponseSuccess | getSandboxResponseError) + +export const getGetSandboxUrl = (id: string, + sandboxId: string,) => { + + + + + return `/projects/${id}/sandboxes/${sandboxId}` +} + +/** + * @summary Get a sandbox template + */ +export const getSandbox = async (id: string, + sandboxId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getGetSandboxUrl(id,sandboxId), + { + ...options, + method: 'GET' + + + } +);} + + + +export type deleteSandboxResponse202 = { + data: void + status: 202 +} + +export type deleteSandboxResponseDefault = { + data: Error + status: Exclude +} + +export type deleteSandboxResponseSuccess = (deleteSandboxResponse202) & { + headers: Headers; +}; +export type deleteSandboxResponseError = (deleteSandboxResponseDefault) & { + headers: Headers; +}; + +export type deleteSandboxResponse = (deleteSandboxResponseSuccess | deleteSandboxResponseError) + +export const getDeleteSandboxUrl = (id: string, + sandboxId: string,) => { + + + + + return `/projects/${id}/sandboxes/${sandboxId}` +} + +/** + * @summary Retire a template and terminate its sessions + */ +export const deleteSandbox = async (id: string, + sandboxId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getDeleteSandboxUrl(id,sandboxId), + { + ...options, + method: 'DELETE' + + + } +);} + + + +export type listSandboxSessionsResponse200 = { + data: SandboxSessionPage + status: 200 +} + +export type listSandboxSessionsResponseDefault = { + data: Error + status: Exclude +} + +export type listSandboxSessionsResponseSuccess = (listSandboxSessionsResponse200) & { + headers: Headers; +}; +export type listSandboxSessionsResponseError = (listSandboxSessionsResponseDefault) & { + headers: Headers; +}; + +export type listSandboxSessionsResponse = (listSandboxSessionsResponseSuccess | listSandboxSessionsResponseError) + +export const getListSandboxSessionsUrl = (id: string, + params?: ListSandboxSessionsParams,) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : String(value)) + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 ? `/projects/${id}/sandbox-sessions?${stringifiedParams}` : `/projects/${id}/sandbox-sessions` +} + +/** + * @summary List project sandbox sessions + */ +export const listSandboxSessions = async (id: string, + params?: ListSandboxSessionsParams, options?: Parameters[1]): Promise => { + + return volcanoFetch(getListSandboxSessionsUrl(id,params), + { + ...options, + method: 'GET' + + + } +);} + + + +export type createSandboxSessionResponse201 = { + data: SandboxSession + status: 201 +} + +export type createSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type createSandboxSessionResponseSuccess = (createSandboxSessionResponse201) & { + headers: Headers; +}; +export type createSandboxSessionResponseError = (createSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type createSandboxSessionResponse = (createSandboxSessionResponseSuccess | createSandboxSessionResponseError) + +export const getCreateSandboxSessionUrl = (id: string,) => { + + + + + return `/projects/${id}/sandbox-sessions` +} + +/** + * @summary Start a sandbox session + */ +export const createSandboxSession = async (id: string, + createSandboxSessionRequest: CreateSandboxSessionRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getCreateSandboxSessionUrl(id), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(createSandboxSessionRequest) + } +);} + + + +export type executeSandboxResponse200 = { + data: SandboxExecutionResult + status: 200 +} + +export type executeSandboxResponseDefault = { + data: Error + status: Exclude +} + +export type executeSandboxResponseSuccess = (executeSandboxResponse200) & { + headers: Headers; +}; +export type executeSandboxResponseError = (executeSandboxResponseDefault) & { + headers: Headers; +}; + +export type executeSandboxResponse = (executeSandboxResponseSuccess | executeSandboxResponseError) + +export const getExecuteSandboxUrl = (id: string,) => { + + + + + return `/projects/${id}/sandbox-executions` +} + +/** + * @summary Execute once and return after confirmed termination + */ +export const executeSandbox = async (id: string, + sandboxExecutionRequest: SandboxExecutionRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getExecuteSandboxUrl(id), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxExecutionRequest) + } +);} + + + +export type getSandboxSessionResponse200 = { + data: SandboxSession + status: 200 +} + +export type getSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type getSandboxSessionResponseSuccess = (getSandboxSessionResponse200) & { + headers: Headers; +}; +export type getSandboxSessionResponseError = (getSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type getSandboxSessionResponse = (getSandboxSessionResponseSuccess | getSandboxSessionResponseError) + +export const getGetSandboxSessionUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}` +} + +/** + * @summary Get a sandbox session + */ +export const getSandboxSession = async (sessionId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getGetSandboxSessionUrl(sessionId), + { + ...options, + method: 'GET' + + + } +);} + + + +export type terminateSandboxSessionResponse202 = { + data: SandboxSession + status: 202 +} + +export type terminateSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type terminateSandboxSessionResponseSuccess = (terminateSandboxSessionResponse202) & { + headers: Headers; +}; +export type terminateSandboxSessionResponseError = (terminateSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type terminateSandboxSessionResponse = (terminateSandboxSessionResponseSuccess | terminateSandboxSessionResponseError) + +export const getTerminateSandboxSessionUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}` +} + +/** + * @summary Request sandbox termination + */ +export const terminateSandboxSession = async (sessionId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getTerminateSandboxSessionUrl(sessionId), + { + ...options, + method: 'DELETE' + + + } +);} + + + +export type suspendSandboxSessionResponse202 = { + data: SandboxSession + status: 202 +} + +export type suspendSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type suspendSandboxSessionResponseSuccess = (suspendSandboxSessionResponse202) & { + headers: Headers; +}; +export type suspendSandboxSessionResponseError = (suspendSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type suspendSandboxSessionResponse = (suspendSandboxSessionResponseSuccess | suspendSandboxSessionResponseError) + +export const getSuspendSandboxSessionUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/suspend` +} + +/** + * @summary Suspend a sandbox session + */ +export const suspendSandboxSession = async (sessionId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getSuspendSandboxSessionUrl(sessionId), + { + ...options, + method: 'POST' + + + } +);} + + + +export type resumeSandboxSessionResponse202 = { + data: SandboxSession + status: 202 +} + +export type resumeSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type resumeSandboxSessionResponseSuccess = (resumeSandboxSessionResponse202) & { + headers: Headers; +}; +export type resumeSandboxSessionResponseError = (resumeSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type resumeSandboxSessionResponse = (resumeSandboxSessionResponseSuccess | resumeSandboxSessionResponseError) + +export const getResumeSandboxSessionUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/resume` +} + +/** + * @summary Resume a sandbox session + */ +export const resumeSandboxSession = async (sessionId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getResumeSandboxSessionUrl(sessionId), + { + ...options, + method: 'POST' + + + } +);} + + + +export type executeSandboxSessionResponse200 = { + data: SandboxCommandResult + status: 200 +} + +export type executeSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type executeSandboxSessionResponseSuccess = (executeSandboxSessionResponse200) & { + headers: Headers; +}; +export type executeSandboxSessionResponseError = (executeSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type executeSandboxSessionResponse = (executeSandboxSessionResponseSuccess | executeSandboxSessionResponseError) + +export const getExecuteSandboxSessionUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/exec` +} + +/** + * @summary Execute a command within a session + */ +export const executeSandboxSession = async (sessionId: string, + sandboxCommandRequest: SandboxCommandRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getExecuteSandboxSessionUrl(sessionId), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxCommandRequest) + } +);} + + + +export type readSandboxSessionFileResponse200 = { + data: SandboxFileResult + status: 200 +} + +export type readSandboxSessionFileResponseDefault = { + data: Error + status: Exclude +} + +export type readSandboxSessionFileResponseSuccess = (readSandboxSessionFileResponse200) & { + headers: Headers; +}; +export type readSandboxSessionFileResponseError = (readSandboxSessionFileResponseDefault) & { + headers: Headers; +}; + +export type readSandboxSessionFileResponse = (readSandboxSessionFileResponseSuccess | readSandboxSessionFileResponseError) + +export const getReadSandboxSessionFileUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/files/read` +} + +/** + * @summary Read a workspace file + */ +export const readSandboxSessionFile = async (sessionId: string, + sandboxFileReadRequest: SandboxFileReadRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getReadSandboxSessionFileUrl(sessionId), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxFileReadRequest) + } +);} + + + +export type writeSandboxSessionFileResponse204 = { + data: void + status: 204 +} + +export type writeSandboxSessionFileResponseDefault = { + data: Error + status: Exclude +} + +export type writeSandboxSessionFileResponseSuccess = (writeSandboxSessionFileResponse204) & { + headers: Headers; +}; +export type writeSandboxSessionFileResponseError = (writeSandboxSessionFileResponseDefault) & { + headers: Headers; +}; + +export type writeSandboxSessionFileResponse = (writeSandboxSessionFileResponseSuccess | writeSandboxSessionFileResponseError) + +export const getWriteSandboxSessionFileUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/files/write` +} + +/** + * @summary Write a workspace file + */ +export const writeSandboxSessionFile = async (sessionId: string, + sandboxFileWriteRequest: SandboxFileWriteRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getWriteSandboxSessionFileUrl(sessionId), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxFileWriteRequest) + } +);} + + + +export type grantSandboxSessionResponse204 = { + data: void + status: 204 +} + +export type grantSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type grantSandboxSessionResponseSuccess = (grantSandboxSessionResponse204) & { + headers: Headers; +}; +export type grantSandboxSessionResponseError = (grantSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type grantSandboxSessionResponse = (grantSandboxSessionResponseSuccess | grantSandboxSessionResponseError) + +export const getGrantSandboxSessionUrl = (sessionId: string, + subjectId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/grants/${subjectId}` +} + +/** + * @summary Authorize an authenticated project user for this session + */ +export const grantSandboxSession = async (sessionId: string, + subjectId: string, + sandboxSubjectGrantRequest: SandboxSubjectGrantRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getGrantSandboxSessionUrl(sessionId,subjectId), + { + ...options, + method: 'PUT', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxSubjectGrantRequest) + } +);} + + + +export type revokeSandboxSessionResponse204 = { + data: void + status: 204 +} + +export type revokeSandboxSessionResponseDefault = { + data: Error + status: Exclude +} + +export type revokeSandboxSessionResponseSuccess = (revokeSandboxSessionResponse204) & { + headers: Headers; +}; +export type revokeSandboxSessionResponseError = (revokeSandboxSessionResponseDefault) & { + headers: Headers; +}; + +export type revokeSandboxSessionResponse = (revokeSandboxSessionResponseSuccess | revokeSandboxSessionResponseError) + +export const getRevokeSandboxSessionUrl = (sessionId: string, + subjectId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/grants/${subjectId}` +} + +/** + * @summary Revoke a project user session grant + */ +export const revokeSandboxSession = async (sessionId: string, + subjectId: string, options?: Parameters[1]): Promise => { + + return volcanoFetch(getRevokeSandboxSessionUrl(sessionId,subjectId), + { + ...options, + method: 'DELETE' + + + } +);} + + + +export type createSandboxSessionAccessResponse200 = { + data: SandboxAccess + status: 200 +} + +export type createSandboxSessionAccessResponseDefault = { + data: Error + status: Exclude +} + +export type createSandboxSessionAccessResponseSuccess = (createSandboxSessionAccessResponse200) & { + headers: Headers; +}; +export type createSandboxSessionAccessResponseError = (createSandboxSessionAccessResponseDefault) & { + headers: Headers; +}; + +export type createSandboxSessionAccessResponse = (createSandboxSessionAccessResponseSuccess | createSandboxSessionAccessResponseError) + +export const getCreateSandboxSessionAccessUrl = (sessionId: string,) => { + + + + + return `/sandbox-sessions/${sessionId}/access` +} + +/** + * @summary Issue a short-lived port-scoped access credential + */ +export const createSandboxSessionAccess = async (sessionId: string, + sandboxAccessRequest: SandboxAccessRequest, options?: Parameters[1]): Promise => { + + const getHeaders = (h?: NonNullable): Record => { + if (!h) return {}; + if (h instanceof Headers) return Object.fromEntries(h.entries()); + if (Array.isArray(h)) return Object.fromEntries(h); + return h; + }; +return volcanoFetch(getCreateSandboxSessionAccessUrl(sessionId), + { + ...options, + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) }, + body: JSON.stringify(sandboxAccessRequest) + } +);} diff --git a/src/generated/model/createSandboxSessionRequest.ts b/src/generated/model/createSandboxSessionRequest.ts new file mode 100644 index 00000000..f5ddd503 --- /dev/null +++ b/src/generated/model/createSandboxSessionRequest.ts @@ -0,0 +1,29 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { CreateSandboxSessionRequestMemoryMb } from './createSandboxSessionRequestMemoryMb'; +import type { CreateSandboxSessionRequestPreset } from './createSandboxSessionRequestPreset'; + +export type CreateSandboxSessionRequest = (unknown & { + preset?: CreateSandboxSessionRequestPreset; + sandbox_id?: string; + memory_mb?: CreateSandboxSessionRequestMemoryMb; + /** @pattern ^aws-[a-z0-9-]+$ */ + region: string; + /** + * @minimum 30 + * @maximum 28800 + */ + max_duration_seconds?: number; + /** + * @minimum 0 + * @maximum 28800 + */ + idle_timeout_seconds?: number; +}); diff --git a/src/generated/model/createSandboxSessionRequestMemoryMb.ts b/src/generated/model/createSandboxSessionRequestMemoryMb.ts new file mode 100644 index 00000000..22687915 --- /dev/null +++ b/src/generated/model/createSandboxSessionRequestMemoryMb.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type CreateSandboxSessionRequestMemoryMb = typeof CreateSandboxSessionRequestMemoryMb[keyof typeof CreateSandboxSessionRequestMemoryMb]; + + +export const CreateSandboxSessionRequestMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +} as const; diff --git a/src/generated/model/createSandboxSessionRequestPreset.ts b/src/generated/model/createSandboxSessionRequestPreset.ts new file mode 100644 index 00000000..0c7f4512 --- /dev/null +++ b/src/generated/model/createSandboxSessionRequestPreset.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type CreateSandboxSessionRequestPreset = typeof CreateSandboxSessionRequestPreset[keyof typeof CreateSandboxSessionRequestPreset]; + + +export const CreateSandboxSessionRequestPreset = { + python312: 'python3.12', + node22: 'node22', +} as const; diff --git a/src/generated/model/createSandboxTemplateRequest.ts b/src/generated/model/createSandboxTemplateRequest.ts new file mode 100644 index 00000000..37688c35 --- /dev/null +++ b/src/generated/model/createSandboxTemplateRequest.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { CreateSandboxTemplateRequestMemoryMb } from './createSandboxTemplateRequestMemoryMb'; +import type { CreateSandboxTemplateRequestPreset } from './createSandboxTemplateRequestPreset'; + +export interface CreateSandboxTemplateRequest { + /** @pattern ^[a-z][a-z0-9-]{0,62}$ */ + name: string; + preset: CreateSandboxTemplateRequestPreset; + memory_mb?: CreateSandboxTemplateRequestMemoryMb; +} diff --git a/src/generated/model/createSandboxTemplateRequestMemoryMb.ts b/src/generated/model/createSandboxTemplateRequestMemoryMb.ts new file mode 100644 index 00000000..2380cf78 --- /dev/null +++ b/src/generated/model/createSandboxTemplateRequestMemoryMb.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type CreateSandboxTemplateRequestMemoryMb = typeof CreateSandboxTemplateRequestMemoryMb[keyof typeof CreateSandboxTemplateRequestMemoryMb]; + + +export const CreateSandboxTemplateRequestMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +} as const; diff --git a/src/generated/model/createSandboxTemplateRequestPreset.ts b/src/generated/model/createSandboxTemplateRequestPreset.ts new file mode 100644 index 00000000..7c9835fc --- /dev/null +++ b/src/generated/model/createSandboxTemplateRequestPreset.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type CreateSandboxTemplateRequestPreset = typeof CreateSandboxTemplateRequestPreset[keyof typeof CreateSandboxTemplateRequestPreset]; + + +export const CreateSandboxTemplateRequestPreset = { + python312: 'python3.12', + node22: 'node22', +} as const; diff --git a/src/generated/model/cursorParameter.ts b/src/generated/model/cursorParameter.ts new file mode 100644 index 00000000..07d82948 --- /dev/null +++ b/src/generated/model/cursorParameter.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +/** + * Opaque keyset pagination cursor from a previous response's `next_cursor` + * — pages forward. Mutually exclusive with `page` and `ending_before`; + * combining them returns 400. When supplied, the request's `search` and + * `limit` must match the values bound to the cursor or the request returns 400. + */ +export type CursorParameter = string; diff --git a/src/generated/model/index.ts b/src/generated/model/index.ts index 64dc4293..297aa091 100644 --- a/src/generated/model/index.ts +++ b/src/generated/model/index.ts @@ -17,8 +17,15 @@ export * from './authUserStatus'; export * from './authUserUserMetadata'; export * from './bandwidthCapExceededResponse'; export * from './completeUploadSessionResponse'; +export * from './createSandboxSessionRequest'; +export * from './createSandboxSessionRequestMemoryMb'; +export * from './createSandboxSessionRequestPreset'; +export * from './createSandboxTemplateRequest'; +export * from './createSandboxTemplateRequestMemoryMb'; +export * from './createSandboxTemplateRequestPreset'; export * from './createUploadSessionRequest'; export * from './createUploadSessionResponse'; +export * from './cursorParameter'; export * from './databaseQueryCapExceededResponse'; export * from './databaseQueryFilter'; export * from './databaseQueryFilterOperator'; @@ -33,12 +40,39 @@ export * from './error'; export * from './hostedAuthPageType'; export * from './limitParameter'; export * from './listDurableExecutionsParams'; +export * from './listSandboxesParams'; +export * from './listSandboxSessionsParams'; export * from './lockRequestIdParameter'; export * from './lockTokenParameter'; export * from './pageParameter'; export * from './paginatedDurableExecutions'; export * from './projectLockLease'; export * from './projectLockLeaseRequest'; +export * from './sandboxAccess'; +export * from './sandboxAccessRequest'; +export * from './sandboxCommandRequest'; +export * from './sandboxCommandRequestEnvironment'; +export * from './sandboxCommandResult'; +export * from './sandboxExecutionRequest'; +export * from './sandboxExecutionRequestEnvironment'; +export * from './sandboxExecutionRequestMemoryMb'; +export * from './sandboxExecutionRequestPreset'; +export * from './sandboxExecutionResult'; +export * from './sandboxFileReadRequest'; +export * from './sandboxFileResult'; +export * from './sandboxFileWriteRequest'; +export * from './sandboxPagination'; +export * from './sandboxPreset'; +export * from './sandboxPresetList'; +export * from './sandboxPresetMemoryMb'; +export * from './sandboxSession'; +export * from './sandboxSessionDesiredState'; +export * from './sandboxSessionPage'; +export * from './sandboxSessionState'; +export * from './sandboxSubjectGrantRequest'; +export * from './sandboxTemplate'; +export * from './sandboxTemplatePage'; +export * from './sandboxTemplateStatus'; export * from './storageObject'; export * from './storageObjectMetadata'; export * from './uploadSessionPart'; diff --git a/src/generated/model/listSandboxSessionsParams.ts b/src/generated/model/listSandboxSessionsParams.ts new file mode 100644 index 00000000..5e898da8 --- /dev/null +++ b/src/generated/model/listSandboxSessionsParams.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { CursorParameter } from './cursorParameter'; +import type { LimitParameter } from './limitParameter'; + +export type ListSandboxSessionsParams = { +/** + * Number of items per page (max 100) + * @minimum 1 + * @maximum 100 + */ +limit?: LimitParameter; +/** + * Opaque keyset pagination cursor from a previous response's `next_cursor` + * — pages forward. Mutually exclusive with `page` and `ending_before`; + * combining them returns 400. When supplied, the request's `search` and + * `limit` must match the values bound to the cursor or the request returns 400. + */ +cursor?: CursorParameter; +}; diff --git a/src/generated/model/listSandboxesParams.ts b/src/generated/model/listSandboxesParams.ts new file mode 100644 index 00000000..9ede5b59 --- /dev/null +++ b/src/generated/model/listSandboxesParams.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { CursorParameter } from './cursorParameter'; +import type { LimitParameter } from './limitParameter'; + +export type ListSandboxesParams = { +/** + * Number of items per page (max 100) + * @minimum 1 + * @maximum 100 + */ +limit?: LimitParameter; +/** + * Opaque keyset pagination cursor from a previous response's `next_cursor` + * — pages forward. Mutually exclusive with `page` and `ending_before`; + * combining them returns 400. When supplied, the request's `search` and + * `limit` must match the values bound to the cursor or the request returns 400. + */ +cursor?: CursorParameter; +}; diff --git a/src/generated/model/sandboxAccess.ts b/src/generated/model/sandboxAccess.ts new file mode 100644 index 00000000..6baac5ca --- /dev/null +++ b/src/generated/model/sandboxAccess.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxAccess { + url: string; + token: string; + expires_at: string; +} diff --git a/src/generated/model/sandboxAccessRequest.ts b/src/generated/model/sandboxAccessRequest.ts new file mode 100644 index 00000000..354b0dc3 --- /dev/null +++ b/src/generated/model/sandboxAccessRequest.ts @@ -0,0 +1,22 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxAccessRequest { + /** + * @minimum 1 + * @maximum 65532 + */ + port: number; + /** + * @minimum 1 + * @maximum 300 + */ + expires_in_seconds?: number; +} diff --git a/src/generated/model/sandboxCommandRequest.ts b/src/generated/model/sandboxCommandRequest.ts new file mode 100644 index 00000000..feaac442 --- /dev/null +++ b/src/generated/model/sandboxCommandRequest.ts @@ -0,0 +1,24 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxCommandRequestEnvironment } from './sandboxCommandRequestEnvironment'; + +export interface SandboxCommandRequest { + /** + * @minLength 1 + * @maxLength 65536 + */ + command: string; + /** + * @minimum 1 + * @maximum 3600 + */ + timeout_seconds?: number; + environment?: SandboxCommandRequestEnvironment; +} diff --git a/src/generated/model/sandboxCommandRequestEnvironment.ts b/src/generated/model/sandboxCommandRequestEnvironment.ts new file mode 100644 index 00000000..f2c36698 --- /dev/null +++ b/src/generated/model/sandboxCommandRequestEnvironment.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxCommandRequestEnvironment = {[key: string]: string}; diff --git a/src/generated/model/sandboxCommandResult.ts b/src/generated/model/sandboxCommandResult.ts new file mode 100644 index 00000000..454ae2a4 --- /dev/null +++ b/src/generated/model/sandboxCommandResult.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxCommandResult { + stdout: string; + stderr: string; + exit_code: number; + stdout_truncated: boolean; + stderr_truncated: boolean; + timed_out: boolean; +} diff --git a/src/generated/model/sandboxExecutionRequest.ts b/src/generated/model/sandboxExecutionRequest.ts new file mode 100644 index 00000000..54f3b6d0 --- /dev/null +++ b/src/generated/model/sandboxExecutionRequest.ts @@ -0,0 +1,31 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxExecutionRequestEnvironment } from './sandboxExecutionRequestEnvironment'; +import type { SandboxExecutionRequestMemoryMb } from './sandboxExecutionRequestMemoryMb'; +import type { SandboxExecutionRequestPreset } from './sandboxExecutionRequestPreset'; + +export type SandboxExecutionRequest = (unknown & { + preset?: SandboxExecutionRequestPreset; + sandbox_id?: string; + memory_mb?: SandboxExecutionRequestMemoryMb; + /** @pattern ^aws-[a-z0-9-]+$ */ + region: string; + /** + * @minLength 1 + * @maxLength 65536 + */ + command: string; + /** + * @minimum 1 + * @maximum 60 + */ + timeout_seconds?: number; + environment?: SandboxExecutionRequestEnvironment; +}); diff --git a/src/generated/model/sandboxExecutionRequestEnvironment.ts b/src/generated/model/sandboxExecutionRequestEnvironment.ts new file mode 100644 index 00000000..e3e0eb75 --- /dev/null +++ b/src/generated/model/sandboxExecutionRequestEnvironment.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxExecutionRequestEnvironment = {[key: string]: string}; diff --git a/src/generated/model/sandboxExecutionRequestMemoryMb.ts b/src/generated/model/sandboxExecutionRequestMemoryMb.ts new file mode 100644 index 00000000..092a1aae --- /dev/null +++ b/src/generated/model/sandboxExecutionRequestMemoryMb.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxExecutionRequestMemoryMb = typeof SandboxExecutionRequestMemoryMb[keyof typeof SandboxExecutionRequestMemoryMb]; + + +export const SandboxExecutionRequestMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +} as const; diff --git a/src/generated/model/sandboxExecutionRequestPreset.ts b/src/generated/model/sandboxExecutionRequestPreset.ts new file mode 100644 index 00000000..3eecca3b --- /dev/null +++ b/src/generated/model/sandboxExecutionRequestPreset.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxExecutionRequestPreset = typeof SandboxExecutionRequestPreset[keyof typeof SandboxExecutionRequestPreset]; + + +export const SandboxExecutionRequestPreset = { + python312: 'python3.12', + node22: 'node22', +} as const; diff --git a/src/generated/model/sandboxExecutionResult.ts b/src/generated/model/sandboxExecutionResult.ts new file mode 100644 index 00000000..011025ae --- /dev/null +++ b/src/generated/model/sandboxExecutionResult.ts @@ -0,0 +1,22 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxExecutionResult { + stdout: string; + stderr: string; + exit_code: number; + stdout_truncated: boolean; + stderr_truncated: boolean; + timed_out: boolean; + session_id: string; + region: string; + /** @minimum 0 */ + duration_ms: number; +} diff --git a/src/generated/model/sandboxFileReadRequest.ts b/src/generated/model/sandboxFileReadRequest.ts new file mode 100644 index 00000000..20e645f6 --- /dev/null +++ b/src/generated/model/sandboxFileReadRequest.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxFileReadRequest { + /** + * @minLength 1 + * @maxLength 4096 + */ + path: string; +} diff --git a/src/generated/model/sandboxFileResult.ts b/src/generated/model/sandboxFileResult.ts new file mode 100644 index 00000000..55e4d795 --- /dev/null +++ b/src/generated/model/sandboxFileResult.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxFileResult { + data: string; +} diff --git a/src/generated/model/sandboxFileWriteRequest.ts b/src/generated/model/sandboxFileWriteRequest.ts new file mode 100644 index 00000000..d8692532 --- /dev/null +++ b/src/generated/model/sandboxFileWriteRequest.ts @@ -0,0 +1,19 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxFileWriteRequest { + /** + * @minLength 1 + * @maxLength 4096 + */ + path: string; + /** @maxLength 11184812 */ + data: string; +} diff --git a/src/generated/model/sandboxPagination.ts b/src/generated/model/sandboxPagination.ts new file mode 100644 index 00000000..3c255ba8 --- /dev/null +++ b/src/generated/model/sandboxPagination.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxPagination { + limit: number; + has_more: boolean; + next_cursor?: string; +} diff --git a/src/generated/model/sandboxPreset.ts b/src/generated/model/sandboxPreset.ts new file mode 100644 index 00000000..d6df0020 --- /dev/null +++ b/src/generated/model/sandboxPreset.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxPresetMemoryMb } from './sandboxPresetMemoryMb'; + +export interface SandboxPreset { + id: string; + runtime: string; + version: string; + memory_mb: SandboxPresetMemoryMb; + regions: string[]; +} diff --git a/src/generated/model/sandboxPresetList.ts b/src/generated/model/sandboxPresetList.ts new file mode 100644 index 00000000..5f8144e4 --- /dev/null +++ b/src/generated/model/sandboxPresetList.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxPreset } from './sandboxPreset'; + +export interface SandboxPresetList { + data: SandboxPreset[]; +} diff --git a/src/generated/model/sandboxPresetMemoryMb.ts b/src/generated/model/sandboxPresetMemoryMb.ts new file mode 100644 index 00000000..4fb4f20b --- /dev/null +++ b/src/generated/model/sandboxPresetMemoryMb.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxPresetMemoryMb = typeof SandboxPresetMemoryMb[keyof typeof SandboxPresetMemoryMb]; + + +export const SandboxPresetMemoryMb = { + NUMBER_1024: 1024, + NUMBER_2048: 2048, +} as const; diff --git a/src/generated/model/sandboxSession.ts b/src/generated/model/sandboxSession.ts new file mode 100644 index 00000000..67b82898 --- /dev/null +++ b/src/generated/model/sandboxSession.ts @@ -0,0 +1,24 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxSessionDesiredState } from './sandboxSessionDesiredState'; +import type { SandboxSessionState } from './sandboxSessionState'; + +export interface SandboxSession { + id: string; + project_id: string; + sandbox_id: string; + state: SandboxSessionState; + desired_state: SandboxSessionDesiredState; + region: string; + memory_mb: number; + created_at: string; + started_at?: string; + expires_at: string; +} diff --git a/src/generated/model/sandboxSessionDesiredState.ts b/src/generated/model/sandboxSessionDesiredState.ts new file mode 100644 index 00000000..e456b318 --- /dev/null +++ b/src/generated/model/sandboxSessionDesiredState.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxSessionDesiredState = typeof SandboxSessionDesiredState[keyof typeof SandboxSessionDesiredState]; + + +export const SandboxSessionDesiredState = { + running: 'running', + suspended: 'suspended', + terminated: 'terminated', +} as const; diff --git a/src/generated/model/sandboxSessionPage.ts b/src/generated/model/sandboxSessionPage.ts new file mode 100644 index 00000000..fc008063 --- /dev/null +++ b/src/generated/model/sandboxSessionPage.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxPagination } from './sandboxPagination'; +import type { SandboxSession } from './sandboxSession'; + +export interface SandboxSessionPage { + data: SandboxSession[]; + pagination: SandboxPagination; +} diff --git a/src/generated/model/sandboxSessionState.ts b/src/generated/model/sandboxSessionState.ts new file mode 100644 index 00000000..4e822f9d --- /dev/null +++ b/src/generated/model/sandboxSessionState.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxSessionState = typeof SandboxSessionState[keyof typeof SandboxSessionState]; + + +export const SandboxSessionState = { + starting: 'starting', + running: 'running', + suspending: 'suspending', + suspended: 'suspended', + resuming: 'resuming', + terminating: 'terminating', + terminated: 'terminated', + unknown: 'unknown', +} as const; diff --git a/src/generated/model/sandboxSubjectGrantRequest.ts b/src/generated/model/sandboxSubjectGrantRequest.ts new file mode 100644 index 00000000..5a902682 --- /dev/null +++ b/src/generated/model/sandboxSubjectGrantRequest.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export interface SandboxSubjectGrantRequest { + expires_at: string; +} diff --git a/src/generated/model/sandboxTemplate.ts b/src/generated/model/sandboxTemplate.ts new file mode 100644 index 00000000..ae0caceb --- /dev/null +++ b/src/generated/model/sandboxTemplate.ts @@ -0,0 +1,21 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxTemplateStatus } from './sandboxTemplateStatus'; + +export interface SandboxTemplate { + id: string; + project_id: string; + /** @pattern ^[a-z][a-z0-9-]{0,62}$ */ + name: string; + preset?: string; + memory_mb?: number; + status: SandboxTemplateStatus; + created_at: string; +} diff --git a/src/generated/model/sandboxTemplatePage.ts b/src/generated/model/sandboxTemplatePage.ts new file mode 100644 index 00000000..870e002f --- /dev/null +++ b/src/generated/model/sandboxTemplatePage.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ +import type { SandboxPagination } from './sandboxPagination'; +import type { SandboxTemplate } from './sandboxTemplate'; + +export interface SandboxTemplatePage { + data: SandboxTemplate[]; + pagination: SandboxPagination; +} diff --git a/src/generated/model/sandboxTemplateStatus.ts b/src/generated/model/sandboxTemplateStatus.ts new file mode 100644 index 00000000..c47986f9 --- /dev/null +++ b/src/generated/model/sandboxTemplateStatus.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval 🍺 + * Do not edit manually. + * Volcano Hosting API + * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). + * + * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + * OpenAPI spec version: 3.0.0 + */ + +export type SandboxTemplateStatus = typeof SandboxTemplateStatus[keyof typeof SandboxTemplateStatus]; + + +export const SandboxTemplateStatus = { + ready: 'ready', + unavailable: 'unavailable', + deleting: 'deleting', +} as const; diff --git a/src/generated/openapi.d.ts b/src/generated/openapi.d.ts index f4d96fef..b37d2e38 100644 --- a/src/generated/openapi.d.ts +++ b/src/generated/openapi.d.ts @@ -4527,6 +4527,250 @@ export interface paths { patch?: never; trace?: never; }; + "/sandboxes/presets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List available sandbox presets */ + get: operations["listSandboxPresets"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{id}/sandboxes": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List sandbox templates */ + get: operations["listSandboxes"]; + put?: never; + /** Create a sandbox template from a verified preset */ + post: operations["createSandbox"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{id}/sandboxes/{sandboxId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a sandbox template */ + get: operations["getSandbox"]; + put?: never; + post?: never; + /** Retire a template and terminate its sessions */ + delete: operations["deleteSandbox"]; + options?: never; + head?: never; + /** Rename a sandbox template */ + patch: operations["updateSandbox"]; + trace?: never; + }; + "/projects/{id}/sandboxes/{sandboxId}/deployments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List sandbox deployment history */ + get: operations["listSandboxDeployments"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{id}/sandbox-sessions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List project sandbox sessions */ + get: operations["listSandboxSessions"]; + put?: never; + /** Start a sandbox session */ + post: operations["createSandboxSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{id}/sandbox-executions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Execute once and return after confirmed termination */ + post: operations["executeSandbox"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a sandbox session */ + get: operations["getSandboxSession"]; + put?: never; + post?: never; + /** Request sandbox termination */ + delete: operations["terminateSandboxSession"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/suspend": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Suspend a sandbox session */ + post: operations["suspendSandboxSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/resume": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Resume a sandbox session */ + post: operations["resumeSandboxSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/exec": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Execute a command within a session */ + post: operations["executeSandboxSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/files/read": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Read a workspace file */ + post: operations["readSandboxSessionFile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/files/write": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Write a workspace file */ + post: operations["writeSandboxSessionFile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/grants/{subjectId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Authorize an authenticated project user for this session */ + put: operations["grantSandboxSession"]; + post?: never; + /** Revoke a project user session grant */ + delete: operations["revokeSandboxSession"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sandbox-sessions/{sessionId}/access": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Issue a short-lived port-scoped access credential */ + post: operations["createSandboxSessionAccess"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { @@ -8874,60 +9118,246 @@ export interface components { radii: components["schemas"]["AuthPageRadius"][]; layouts: components["schemas"]["AuthPageLayout"][]; }; - }; - responses: { - /** - * @description The platform user exceeded their billing-cycle bandwidth allowance (aggregate - * ingress + egress across owned projects). Enforcement is eventual: - * requests are rejected until the allowance increases or the next - * anniversary cycle begins. - */ - BandwidthCapExceeded: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; + SandboxPreset: { + id: string; + runtime: string; + version: string; + /** @enum {integer} */ + memory_mb: 1024 | 2048; + regions: string[]; }; - /** - * @description The query was rejected by a billing-cycle allowance: either the owning - * platform user's bandwidth allowance (aggregate ingress + egress across - * owned projects) or their database-request allowance. Enforcement is - * eventual: queries are rejected until the allowance increases or the - * next anniversary cycle begins. The error message identifies the resource. - */ - DatabaseQueryCapExceeded: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; + SandboxTemplate: { + /** Format: uuid */ + id: string; + /** Format: uuid */ + project_id: string; + name: string; + preset?: string; + memory_mb?: number; + /** @enum {string} */ + status: "ready" | "unavailable" | "deleting"; + /** Format: date-time */ + created_at: string; }; - /** - * @description The branch exists but cannot serve queries: it is still provisioning, - * being reset, expired, or its parent is being restored. Distinct from - * `404` so a caller waiting on a branch can tell it apart from a typo. - */ - DatabaseBranchQueryUnavailable: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; + CreateSandboxTemplateRequest: { + name: string; + /** @enum {string} */ + preset: "python3.12" | "node22"; + /** + * @default 1024 + * @enum {integer} + */ + memory_mb?: 1024 | 2048; }; - }; - parameters: { - /** - * @description Backup name, unique within the database, exactly as returned by the list - * endpoint. - * - * Deliberately looser than the names you can create: a backup made by a - * schedule is named for you, so reading or deleting one accepts any name a - * backup can have. - */ + UpdateSandboxTemplateRequest: { + name: string; + }; + SandboxSession: { + /** Format: uuid */ + id: string; + /** Format: uuid */ + project_id: string; + /** Format: uuid */ + sandbox_id: string; + /** @enum {string} */ + state: "starting" | "running" | "suspending" | "suspended" | "resuming" | "terminating" | "terminated" | "unknown"; + /** @enum {string} */ + desired_state: "running" | "suspended" | "terminated"; + region: string; + memory_mb: number; + /** Format: date-time */ + created_at: string; + /** Format: date-time */ + started_at?: string; + /** Format: date-time */ + expires_at: string; + }; + CreateSandboxSessionRequest: { + /** @enum {string} */ + preset?: "python3.12" | "node22"; + /** Format: uuid */ + sandbox_id?: string; + /** @enum {integer} */ + memory_mb?: 1024 | 2048; + region: string; + /** @default 3600 */ + max_duration_seconds?: number; + /** @default 0 */ + idle_timeout_seconds?: number; + } & (unknown | unknown); + SandboxCommandRequest: { + command: string; + /** @default 60 */ + timeout_seconds?: number; + environment?: { + [key: string]: string; + }; + }; + SandboxExecutionRequest: { + /** @enum {string} */ + preset?: "python3.12" | "node22"; + /** Format: uuid */ + sandbox_id?: string; + /** @enum {integer} */ + memory_mb?: 1024 | 2048; + region: string; + command: string; + /** @default 60 */ + timeout_seconds?: number; + environment?: { + [key: string]: string; + }; + } & (unknown | unknown); + SandboxCommandResult: { + stdout: string; + stderr: string; + exit_code: number; + stdout_truncated: boolean; + stderr_truncated: boolean; + timed_out: boolean; + }; + SandboxExecutionResult: { + stdout: string; + stderr: string; + exit_code: number; + stdout_truncated: boolean; + stderr_truncated: boolean; + timed_out: boolean; + /** Format: uuid */ + session_id: string; + region: string; + /** Format: int64 */ + duration_ms: number; + }; + SandboxFileWriteRequest: { + path: string; + /** Format: byte */ + data: string; + }; + SandboxFileReadRequest: { + path: string; + }; + SandboxFileResult: { + /** Format: byte */ + data: string; + }; + SandboxSubjectGrantRequest: { + /** Format: date-time */ + expires_at: string; + }; + SandboxAccessRequest: { + port: number; + /** @default 300 */ + expires_in_seconds?: number; + }; + SandboxAccess: { + /** Format: uri */ + url: string; + token: string; + /** Format: date-time */ + expires_at: string; + }; + SandboxDeployment: { + /** Format: uuid */ + id: string; + status: string; + /** Format: date-time */ + created_at: string; + /** Format: date-time */ + updated_at: string; + }; + SandboxPagination: { + limit: number; + has_more: boolean; + next_cursor?: string; + }; + SandboxTemplatePage: { + data: components["schemas"]["SandboxTemplate"][]; + pagination: components["schemas"]["SandboxPagination"]; + }; + SandboxSessionPage: { + data: components["schemas"]["SandboxSession"][]; + pagination: components["schemas"]["SandboxPagination"]; + }; + SandboxDeploymentPage: { + data: components["schemas"]["SandboxDeployment"][]; + pagination: components["schemas"]["SandboxPagination"]; + }; + SandboxPresetList: { + data: components["schemas"]["SandboxPreset"][]; + }; + SandboxCapacity: { + region: string; + /** Format: int64 */ + allocated_memory_mb: number; + }; + SandboxCapacityList: { + data: components["schemas"]["SandboxCapacity"][]; + }; + PublishSandboxPresetRequest: { + /** Format: uuid */ + id: string; + /** @enum {string} */ + preset: "python3.12" | "node22"; + /** @enum {integer} */ + memory_mb: 1024 | 2048; + /** Format: uuid */ + deployment_id: string; + }; + }; + responses: { + /** + * @description The platform user exceeded their billing-cycle bandwidth allowance (aggregate + * ingress + egress across owned projects). Enforcement is eventual: + * requests are rejected until the allowance increases or the next + * anniversary cycle begins. + */ + BandwidthCapExceeded: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** + * @description The query was rejected by a billing-cycle allowance: either the owning + * platform user's bandwidth allowance (aggregate ingress + egress across + * owned projects) or their database-request allowance. Enforcement is + * eventual: queries are rejected until the allowance increases or the + * next anniversary cycle begins. The error message identifies the resource. + */ + DatabaseQueryCapExceeded: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** + * @description The branch exists but cannot serve queries: it is still provisioning, + * being reset, expired, or its parent is being restored. Distinct from + * `404` so a caller waiting on a branch can tell it apart from a typo. + */ + DatabaseBranchQueryUnavailable: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + parameters: { + /** + * @description Backup name, unique within the database, exactly as returned by the list + * endpoint. + * + * Deliberately looser than the names you can create: a backup made by a + * schedule is named for you, so reading or deleting one accepts any name a + * backup can have. + */ BackupName: string; /** @description Branch name (unique within the parent database, lowercase letters, numbers, and underscores only) */ BranchName: string; @@ -22945,4 +23375,694 @@ export interface operations { }; }; }; + listSandboxPresets: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List available sandbox presets */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxPresetList"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listSandboxes: { + parameters: { + query?: { + /** @description Number of items per page (max 100) */ + limit?: components["parameters"]["Limit"]; + /** + * @description Opaque keyset pagination cursor from a previous response's `next_cursor` + * — pages forward. Mutually exclusive with `page` and `ending_before`; + * combining them returns 400. When supplied, the request's `search` and + * `limit` must match the values bound to the cursor or the request returns 400. + */ + cursor?: components["parameters"]["Cursor"]; + }; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List sandbox templates */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxTemplatePage"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + createSandbox: { + parameters: { + query?: never; + header: { + "Idempotency-Key": string; + }; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateSandboxTemplateRequest"]; + }; + }; + responses: { + /** @description Create a sandbox template from a verified preset */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxTemplate"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getSandbox: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + sandboxId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Get a sandbox template */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxTemplate"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteSandbox: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + sandboxId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Retire a template and terminate its sessions */ + 202: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + updateSandbox: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + sandboxId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateSandboxTemplateRequest"]; + }; + }; + responses: { + /** @description Rename a sandbox template */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxTemplate"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listSandboxDeployments: { + parameters: { + query?: { + /** @description Number of items per page (max 100) */ + limit?: components["parameters"]["Limit"]; + /** + * @description Opaque keyset pagination cursor from a previous response's `next_cursor` + * — pages forward. Mutually exclusive with `page` and `ending_before`; + * combining them returns 400. When supplied, the request's `search` and + * `limit` must match the values bound to the cursor or the request returns 400. + */ + cursor?: components["parameters"]["Cursor"]; + }; + header?: never; + path: { + id: string; + sandboxId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List sandbox deployment history */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxDeploymentPage"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listSandboxSessions: { + parameters: { + query?: { + /** @description Number of items per page (max 100) */ + limit?: components["parameters"]["Limit"]; + /** + * @description Opaque keyset pagination cursor from a previous response's `next_cursor` + * — pages forward. Mutually exclusive with `page` and `ending_before`; + * combining them returns 400. When supplied, the request's `search` and + * `limit` must match the values bound to the cursor or the request returns 400. + */ + cursor?: components["parameters"]["Cursor"]; + }; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List project sandbox sessions */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxSessionPage"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + createSandboxSession: { + parameters: { + query?: never; + header: { + "Idempotency-Key": string; + }; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateSandboxSessionRequest"]; + }; + }; + responses: { + /** @description Start a sandbox session */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxSession"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + executeSandbox: { + parameters: { + query?: never; + header: { + "Idempotency-Key": string; + }; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SandboxExecutionRequest"]; + }; + }; + responses: { + /** @description Execute once and return after confirmed termination */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxExecutionResult"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getSandboxSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Get a sandbox session */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxSession"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + terminateSandboxSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Request sandbox termination */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxSession"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + suspendSandboxSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Suspend a sandbox session */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxSession"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + resumeSandboxSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Resume a sandbox session */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxSession"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + executeSandboxSession: { + parameters: { + query?: never; + header: { + "Idempotency-Key": string; + }; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SandboxCommandRequest"]; + }; + }; + responses: { + /** @description Execute a command within a session */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxCommandResult"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + readSandboxSessionFile: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SandboxFileReadRequest"]; + }; + }; + responses: { + /** @description Read a workspace file */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxFileResult"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + writeSandboxSessionFile: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SandboxFileWriteRequest"]; + }; + }; + responses: { + /** @description Write a workspace file */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + grantSandboxSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + subjectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SandboxSubjectGrantRequest"]; + }; + }; + responses: { + /** @description Authorize an authenticated project user for this session */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + revokeSandboxSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + subjectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Revoke a project user session grant */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + createSandboxSessionAccess: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SandboxAccessRequest"]; + }; + }; + responses: { + /** @description Issue a short-lived port-scoped access credential */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SandboxAccess"]; + }; + }; + /** @description Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; } diff --git a/src/index.d.ts b/src/index.d.ts index 88fe6e95..0cbfaccb 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,3 +1,4 @@ +/// import type { FilterValue } from './database-filters'; export type { FilterValue } from './database-filters'; @@ -1090,6 +1091,7 @@ export class VolcanoAuth { /** Service-role-only project lease methods */ locks: ProjectLocks; + sandboxes: Sandboxes; /** Set current database name for query builder (required before querying) */ database(databaseName: string): VolcanoAuth; @@ -1165,3 +1167,102 @@ export { databaseConnectionString, type DatabaseConnectionStringOptions, } from './database-connection-string'; + +export type SandboxResult = { data: T; error: null } | { data: null; error: Error }; +export interface SandboxRequestOptions { + requestId?: string; + signal?: AbortSignal; +} +export interface SandboxCreateOptions extends SandboxRequestOptions { + preset?: 'python3.12' | 'node22'; + sandboxId?: string; + region: string; + memoryMB?: 1024 | 2048; + maxDurationSeconds?: number; + idleTimeoutSeconds?: number; +} +export interface SandboxExecOptions extends SandboxCreateOptions { + timeoutSeconds?: number; + environment?: Record; +} +export interface SandboxCommandOptions extends SandboxRequestOptions { + timeoutSeconds?: number; + environment?: Record; +} +export interface SandboxCommandResult { + stdout: string; + stderr: string; + exitCode: number; + timedOut: boolean; + stdoutTruncated: boolean; + stderrTruncated: boolean; +} +export interface SandboxExecutionResult extends SandboxCommandResult { + sessionId: string; + region: string; + durationMs: number; +} +export type SandboxState = + | 'starting' + | 'running' + | 'suspending' + | 'suspended' + | 'resuming' + | 'terminating' + | 'terminated' + | 'unknown'; +export interface SandboxAccess { + url: string; + token: string; + expiresAt: string; +} +export interface SandboxSession extends AsyncDisposable { + readonly id: string; + readonly projectId: string; + readonly region: string; + readonly state: SandboxState; + readonly expiresAt: string; + refresh(options?: SandboxRequestOptions): Promise>; + exec( + command: string, + options?: SandboxCommandOptions, + ): Promise>; + suspend(options?: SandboxRequestOptions): Promise>; + resume(options?: SandboxRequestOptions): Promise>; + terminate(options?: SandboxRequestOptions): Promise>; + access(port: number, options?: SandboxRequestOptions): Promise>; + files: { + read(path: string, options?: SandboxRequestOptions): Promise>; + write( + path: string, + data: Uint8Array, + options?: SandboxRequestOptions, + ): Promise>; + }; +} +export interface SandboxPreset { + id: string; + memoryMB: number; + regions: string[]; +} +export interface Sandboxes { + presets(options?: SandboxRequestOptions): Promise>; + exec( + projectId: string, + command: string, + options: SandboxExecOptions, + ): Promise>; + create(projectId: string, options: SandboxCreateOptions): Promise>; + get(sessionId: string, options?: SandboxRequestOptions): Promise>; + grant( + sessionId: string, + authUserId: string, + expiresAt: string, + options?: SandboxRequestOptions, + ): Promise>; + revoke( + sessionId: string, + authUserId: string, + options?: SandboxRequestOptions, + ): Promise>; +} diff --git a/src/index.js b/src/index.js index 1895c007..5b2981f6 100644 --- a/src/index.js +++ b/src/index.js @@ -112,6 +112,7 @@ import { } from './generated-runtime/client.js'; import { isBrowser } from './next/request.ts'; import { ProjectLocksApi } from './project-locks.ts'; +import { SandboxesApi } from './sandboxes.ts'; import { StorageFileApi } from './storage-file.ts'; /** @@ -311,6 +312,7 @@ class VolcanoAuth { }; this.locks = new ProjectLocksApi(this); + this.sandboxes = new SandboxesApi(this); } // ======================================================================== diff --git a/src/sandbox-request.ts b/src/sandbox-request.ts new file mode 100644 index 00000000..4ae78de7 --- /dev/null +++ b/src/sandbox-request.ts @@ -0,0 +1,62 @@ +import type { executeSandbox } from './generated/client.ts'; +import type { SandboxCommandOptions, SandboxRequestOptions, SandboxResult } from './index.js'; + +type TransportOptions = NonNullable[2]>; +export interface SandboxClient { + _completeOAuthExchange(): Promise; + _generatedOptions(mode: 'session', headers?: Record): TransportOptions; +} +export function pathId(value: string): string { + if (!/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i.test(value)) { + throw new TypeError('A Sandbox resource ID must be a UUID'); + } + return encodeURIComponent(value); +} +export function requestOptions( + client: SandboxClient, + options: SandboxRequestOptions = {}, + mutating = false, +): TransportOptions { + const result = client._generatedOptions('session', mutationHeaders(options, mutating)); + if (options.signal !== undefined) { + result.signal = options.signal; + } + return result; +} +function mutationHeaders( + options: SandboxRequestOptions, + mutating: boolean, +): Record { + if (!mutating) { + return {}; + } + const id = options.requestId ?? crypto.randomUUID(); + pathId(id); + return { 'Idempotency-Key': id }; +} + +export function commandRequest( + command: string, + options: SandboxCommandOptions, +): import('./generated/model/sandboxCommandRequest.ts').SandboxCommandRequest { + return { + command, + ...(options.timeoutSeconds === undefined ? {} : { timeout_seconds: options.timeoutSeconds }), + ...(options.environment === undefined ? {} : { environment: options.environment }), + }; +} +export async function sandboxResult(operation: () => Promise): Promise> { + try { + return { data: await operation(), error: null }; + } catch (error) { + return { + data: null, + error: error instanceof Error ? error : new Error('Sandbox request failed'), + }; + } +} + +export async function responseData(request: Promise<{ data: unknown }>): Promise { + const response = await request; + return response.data; +} diff --git a/src/sandbox-session.ts b/src/sandbox-session.ts new file mode 100644 index 00000000..5dab9be0 --- /dev/null +++ b/src/sandbox-session.ts @@ -0,0 +1,152 @@ +import { + createSandboxSessionAccess, + executeSandboxSession, + getSandboxSession, + readSandboxSessionFile, + resumeSandboxSession, + suspendSandboxSession, + terminateSandboxSession, + writeSandboxSessionFile, +} from './generated/client.ts'; +import type { + SandboxCommandOptions, + SandboxRequestOptions, + SandboxSession, + SandboxState, +} from './index.js'; +import { + commandRequest, + pathId, + requestOptions, + responseData, + type SandboxClient, + sandboxResult, +} from './sandbox-request.ts'; +import { + accessResult, + commandResult, + decodeBytes, + encodeBytes, + record, + sessionState, + text, +} from './sandbox-wire.ts'; + +export class SandboxSessionHandle implements SandboxSession { + readonly id: string; + readonly projectId: string; + readonly region: string; + state: SandboxState; + expiresAt: string; + readonly files; + constructor( + private readonly client: SandboxClient, + value: unknown, + ) { + const data = record(value); + this.id = text(data['id']); + this.projectId = text(data['project_id']); + this.region = text(data['region']); + this.state = sessionState(data['state']); + this.expiresAt = text(data['expires_at']); + this.files = { read: this.read.bind(this), write: this.write.bind(this) }; + } + private update(value: unknown): this { + const data = record(value); + if (data['id'] !== this.id) { + throw new TypeError('Sandbox session identity changed'); + } + this.state = sessionState(data['state']); + this.expiresAt = text(data['expires_at']); + return this; + } + refresh(options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult(async () => + this.update( + await responseData( + getSandboxSession(pathId(this.id), requestOptions(this.client, options)), + ), + ), + ); + } + exec(command: string, options: SandboxCommandOptions = {}): ReturnType { + return sandboxResult(async () => + commandResult( + await responseData( + executeSandboxSession( + pathId(this.id), + commandRequest(command, options), + requestOptions(this.client, options, true), + ), + ), + ), + ); + } + suspend(options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult(async () => + this.update( + await responseData( + suspendSandboxSession(pathId(this.id), requestOptions(this.client, options)), + ), + ), + ); + } + resume(options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult(async () => + this.update( + await responseData( + resumeSandboxSession(pathId(this.id), requestOptions(this.client, options)), + ), + ), + ); + } + terminate(options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult(async () => + this.update( + await responseData( + terminateSandboxSession(pathId(this.id), requestOptions(this.client, options)), + ), + ), + ); + } + access(port: number, options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult(async () => + accessResult( + await responseData( + createSandboxSessionAccess( + pathId(this.id), + { port }, + requestOptions(this.client, options), + ), + ), + ), + ); + } + async [Symbol.asyncDispose](): Promise { + if (this.state === 'terminated') { + return; + } + const result = await this.terminate(); + if (result.error !== null) { + throw result.error; + } + } + private read(path: string, options: SandboxRequestOptions = {}) { + return sandboxResult(async () => + decodeBytes( + await responseData( + readSandboxSessionFile(pathId(this.id), { path }, requestOptions(this.client, options)), + ), + ), + ); + } + private write(path: string, data: Uint8Array, options: SandboxRequestOptions = {}) { + return sandboxResult(async () => { + await writeSandboxSessionFile( + pathId(this.id), + { path, data: encodeBytes(data) }, + requestOptions(this.client, options), + ); + }); + } +} diff --git a/src/sandbox-wire.ts b/src/sandbox-wire.ts new file mode 100644 index 00000000..27144bdc --- /dev/null +++ b/src/sandbox-wire.ts @@ -0,0 +1,128 @@ +import type { + SandboxAccess, + SandboxCommandResult, + SandboxCreateOptions, + SandboxExecutionResult, + SandboxPreset, + SandboxState, +} from './index.js'; + +export function record(value: unknown): Record { + if (!isRecord(value)) { + throw new TypeError('Invalid Sandbox response'); + } + return value; +} +export function text(value: unknown): string { + if (typeof value !== 'string') { + throw new TypeError('Invalid Sandbox text field'); + } + return value; +} +function number(value: unknown): number { + if (typeof value !== 'number' || !Number.isFinite(value)) { + throw new TypeError('Invalid Sandbox numeric field'); + } + return value; +} +function flag(value: unknown): boolean { + if (typeof value !== 'boolean') { + throw new TypeError('Invalid Sandbox flag'); + } + return value; +} +export function commandResult(value: unknown): SandboxCommandResult { + const data = record(value); + return { + stdout: text(data['stdout']), + stderr: text(data['stderr']), + exitCode: number(data['exit_code']), + timedOut: flag(data['timed_out']), + stdoutTruncated: flag(data['stdout_truncated']), + stderrTruncated: flag(data['stderr_truncated']), + }; +} +export function executionResult(value: unknown): SandboxExecutionResult { + const data = record(value); + return { + ...commandResult(data), + sessionId: text(data['session_id']), + region: text(data['region']), + durationMs: number(data['duration_ms']), + }; +} +const states: readonly SandboxState[] = [ + 'starting', + 'running', + 'suspending', + 'suspended', + 'resuming', + 'terminating', + 'terminated', + 'unknown', +]; +export function sessionState(value: unknown): SandboxState { + const result = states.find((state) => state === value); + if (result === undefined) { + throw new TypeError('Invalid Sandbox state'); + } + return result; +} +export function createRequest( + options: SandboxCreateOptions, +): import('./generated/model/createSandboxSessionRequest.ts').CreateSandboxSessionRequest { + if ((options.preset === undefined) === (options.sandboxId === undefined)) { + throw new TypeError('Choose exactly one preset or sandboxId'); + } + return { + region: options.region, + ...(options.preset === undefined ? {} : { preset: options.preset }), + ...(options.sandboxId === undefined ? {} : { sandbox_id: options.sandboxId }), + ...(options.memoryMB === undefined ? {} : { memory_mb: options.memoryMB }), + }; +} +export function accessResult(value: unknown): SandboxAccess { + const data = record(value); + return { + url: text(data['url']), + token: text(data['token']), + expiresAt: text(data['expires_at']), + }; +} +export function presetsResult(value: unknown): SandboxPreset[] { + const data = record(value); + if (!Array.isArray(data['data'])) { + throw new TypeError('Invalid Sandbox preset catalog'); + } + return data['data'].map((value: unknown) => { + const preset = record(value); + if (!Array.isArray(preset['regions'])) { + throw new TypeError('Invalid Sandbox regions'); + } + return { + id: text(preset['id']), + memoryMB: number(preset['memory_mb']), + regions: preset['regions'].map((region: unknown) => text(region)), + }; + }); +} +export function decodeBytes(value: unknown): Uint8Array { + return Uint8Array.from( + atob(text(record(value)['data'])), + (character) => character.codePointAt(0) ?? 0, + ); +} +export function encodeBytes(data: Uint8Array): string { + if (data.byteLength > 8 * 1024 * 1024) { + throw new RangeError('Sandbox files are limited to 8 MiB'); + } + let binary = ''; + for (const byte of data) { + binary += String.fromCodePoint(byte); + } + return btoa(binary); +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null; +} diff --git a/src/sandboxes.ts b/src/sandboxes.ts new file mode 100644 index 00000000..7a2cdd1f --- /dev/null +++ b/src/sandboxes.ts @@ -0,0 +1,107 @@ +import { + createSandboxSession, + executeSandbox, + getSandboxSession, + grantSandboxSession, + listSandboxPresets, + revokeSandboxSession, +} from './generated/client.ts'; +import type { + SandboxCreateOptions, + Sandboxes, + SandboxExecOptions, + SandboxRequestOptions, +} from './index.js'; +import { + commandRequest, + pathId, + requestOptions, + responseData, + type SandboxClient, + sandboxResult, +} from './sandbox-request.ts'; +import { SandboxSessionHandle } from './sandbox-session.ts'; +import { createRequest, executionResult, presetsResult } from './sandbox-wire.ts'; + +export class SandboxesApi implements Sandboxes { + constructor(private readonly client: SandboxClient) {} + presets(options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult(async () => + presetsResult(await responseData(listSandboxPresets(requestOptions(this.client, options)))), + ); + } + exec( + projectId: string, + command: string, + options: SandboxExecOptions, + ): ReturnType { + return sandboxResult(async () => { + await this.client._completeOAuthExchange(); + const response = await executeSandbox( + pathId(projectId), + { ...createRequest(options), ...commandRequest(command, options) }, + requestOptions(this.client, options, true), + ); + return executionResult(response.data); + }); + } + create(projectId: string, options: SandboxCreateOptions): ReturnType { + return sandboxResult(async () => { + await this.client._completeOAuthExchange(); + const body = { + ...createRequest(options), + ...(options.maxDurationSeconds === undefined + ? {} + : { max_duration_seconds: options.maxDurationSeconds }), + ...(options.idleTimeoutSeconds === undefined + ? {} + : { idle_timeout_seconds: options.idleTimeoutSeconds }), + }; + const response = await createSandboxSession( + pathId(projectId), + body, + requestOptions(this.client, options, true), + ); + return new SandboxSessionHandle(this.client, response.data); + }); + } + get(sessionId: string, options: SandboxRequestOptions = {}): ReturnType { + return sandboxResult( + async () => + new SandboxSessionHandle( + this.client, + await responseData( + getSandboxSession(pathId(sessionId), requestOptions(this.client, options)), + ), + ), + ); + } + grant( + sessionId: string, + authUserId: string, + expiresAt: string, + options: SandboxRequestOptions = {}, + ): ReturnType { + return sandboxResult(async () => { + await grantSandboxSession( + pathId(sessionId), + pathId(authUserId), + { expires_at: expiresAt }, + requestOptions(this.client, options), + ); + }); + } + revoke( + sessionId: string, + authUserId: string, + options: SandboxRequestOptions = {}, + ): ReturnType { + return sandboxResult(async () => { + await revokeSandboxSession( + pathId(sessionId), + pathId(authUserId), + requestOptions(this.client, options), + ); + }); + } +} From 030dfc7c8796e4593fa0993a8502f137a5b4f474 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Thu, 24 Sep 2026 03:09:20 -0700 Subject: [PATCH 2/9] fix(sandboxes): exclude session lifetime options from execution --- openapi/openapi.yaml | 735 ++++++++++----------------------- package.json | 2 +- src/index.d.ts | 6 +- test/types/sandbox-contract.ts | 26 ++ 4 files changed, 241 insertions(+), 528 deletions(-) create mode 100644 test/types/sandbox-contract.ts diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index cc3492d5..6fd6bd5e 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -30,10 +30,7 @@ tags: work is metered on its own allowances: executions, and the operations each execution performs while it runs. - name: Frontends - description: Frontend deployment and management. Supports Next.js 15.x and 16.x - with Node.js 22.x or 24.x inferred from package.json engines.node when the - selected Node.js family satisfies the installed Next.js package - engines.node constraint. + description: Frontend deployment and management. Supports Next.js 15.x and 16.x with Node.js 22.x or 24.x inferred from package.json engines.node when the selected Node.js family satisfies the installed Next.js package engines.node constraint. - name: Tokens description: Service keys for function invocation and database admin access - name: Variables @@ -53,8 +50,7 @@ tags: - name: Anon Keys description: Project-specific public keys for frontend auth - name: Service Keys - description: Project-specific secret keys for admin operations (bypass RLS - - backend only!) + description: Project-specific secret keys for admin operations (bypass RLS - backend only!) - name: OAuth Configuration description: OAuth provider configuration (Google, GitHub, Microsoft, Apple) - name: OAuth Authentication @@ -203,8 +199,7 @@ paths: type: string responses: '303': - description: Redirect to the provider completion URL, signed application - redirect, or Volcano import page + description: Redirect to the provider completion URL, signed application redirect, or Volcano import page headers: Location: description: Validated redirect target @@ -401,8 +396,7 @@ paths: tags: - Project Imports summary: Check whether a provider project is ready to import - description: Produces a deterministic read-only readiness report for a proposed - new Volcano project. + description: Produces a deterministic read-only readiness report for a proposed new Volcano project. operationId: preflightProjectImport security: - UserToken: [] @@ -479,9 +473,7 @@ paths: tags: - Project Imports summary: Start a Vercel project import - description: Creates a Volcano project from an importable production preflight - report. Retrying the same request with the same Idempotency-Key returns - the existing run. + description: Creates a Volcano project from an importable production preflight report. Retrying the same request with the same Idempotency-Key returns the existing run. operationId: startProjectImport security: - UserToken: [] @@ -543,8 +535,7 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Preflight is stale, idempotency key was reused, or destination - conflicts + description: Preflight is stale, idempotency key was reused, or destination conflicts content: application/json: schema: @@ -1109,8 +1100,7 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Conflict (project name already exists or a resource deployment - blocks a region change) + description: Conflict (project name already exists or a resource deployment blocks a region change) content: application/json: schema: @@ -1470,8 +1460,7 @@ paths: expected_shared_variables: type: array uniqueItems: true - description: When present, replace only if the current complete shared list - matches this list. + description: When present, replace only if the current complete shared list matches this list. items: type: string maxLength: 256 @@ -1481,9 +1470,7 @@ paths: minLength: 64 maxLength: 64 pattern: ^[a-f0-9]{64}$ - description: SHA-256 of the sorted unique current shared names joined by a - newline. Use instead of expected_shared_variables for a - compact conditional replacement. + description: SHA-256 of the sorted unique current shared names joined by a newline. Use instead of expected_shared_variables for a compact conditional replacement. responses: '204': description: Shared list replaced and affected function synchronization started. @@ -2695,18 +2682,14 @@ paths: properties: name: type: string - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot - start or end with hyphen) + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) maxLength: 63 pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ example: my-api-function code: type: string format: binary - description: ZIP or tar.gz archive containing function source code plus - dependency manifests/lockfiles. The API enforces - SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz - source archive. + description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz source archive. runtime: type: string enum: @@ -2978,8 +2961,7 @@ paths: description: Function response (passthrough from function runtime) headers: X-Volcano-Version: - description: Volcano API/runtime version that served this invocation - (`` in production, `-` in non-production) + description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) schema: type: string X-Volcano-Region: @@ -3003,8 +2985,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - CORS blocked, missing `functions.invoke`, or private - function with anon key + description: Forbidden - CORS blocked, missing `functions.invoke`, or private function with anon key content: application/json: schema: @@ -3037,12 +3018,10 @@ paths: schema: $ref: '#/components/schemas/Error' default: - description: Function response (passthrough; status code/body/headers come from - the function) + description: Function response (passthrough; status code/body/headers come from the function) headers: X-Volcano-Version: - description: Volcano API/runtime version that served this invocation - (`` in production, `-` in non-production) + description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) schema: type: string X-Volcano-Region: @@ -3230,8 +3209,7 @@ paths: type: string maxLength: 63 pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot - start or end with hyphen) + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) example: my-function responses: '200': @@ -3253,8 +3231,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - CORS blocked or missing `functions.invoke` permission - for anon key + description: Forbidden - CORS blocked or missing `functions.invoke` permission for anon key content: application/json: schema: @@ -3424,8 +3401,7 @@ paths: required: false schema: type: string - description: Opaque stream cursor fallback when setting `Last-Event-ID` is not - practical. + description: Opaque stream cursor fallback when setting `Last-Event-ID` is not practical. requestBody: required: true content: @@ -3434,9 +3410,7 @@ paths: $ref: '#/components/schemas/LogStreamRequest' responses: '200': - description: Server-Sent Events stream. `log` events contain a JSON - `LogSearchEvent`; `warning` events contain a JSON object with an - `error` field. + description: Server-Sent Events stream. `log` events contain a JSON `LogSearchEvent`; `warning` events contain a JSON object with an `error` field. content: text/event-stream: schema: @@ -3451,8 +3425,7 @@ paths: event: log data: {"id":"LOG_EVENT_ID","timestamp":"2024-01-01T12:00:00Z","level":"info","message":"User logged in","resource":{"type":"function","id":"550e8400-e29b-41d4-a716-446655440000","name":"login"},"region":"us-east-1"} '400': - description: Bad request - invalid selector, stream cursor, or unsupported - stream field + description: Bad request - invalid selector, stream cursor, or unsupported stream field content: application/json: schema: @@ -3519,10 +3492,7 @@ paths: code_0: type: string format: binary - description: Function ZIP or tar.gz archive referenced by the first manifest - entry's `file_field`; additional code_N file fields may be - included. Each archive is subject to - SOURCE_ARCHIVE_SIZE_LIMIT_MB. + description: Function ZIP or tar.gz archive referenced by the first manifest entry's `file_field`; additional code_N file fields may be included. Each archive is subject to SOURCE_ARCHIVE_SIZE_LIMIT_MB. required: - functions responses: @@ -3533,8 +3503,7 @@ paths: schema: $ref: '#/components/schemas/BatchFunctionDeployResponse' '207': - description: Batch deployment partially accepted; successful functions started - deployment and failed functions were compensated where possible + description: Batch deployment partially accepted; successful functions started deployment and failed functions were compensated where possible content: application/json: schema: @@ -3616,8 +3585,7 @@ paths: tags: - Functions summary: Create a scheduler for a function - description: Creates regional scheduled invocation jobs. Requested regions must - be a subset of the function's deployed regions. + description: Creates regional scheduled invocation jobs. Requested regions must be a subset of the function's deployed regions. operationId: createFunctionScheduler security: - UserToken: [] @@ -3841,16 +3809,14 @@ paths: properties: name: type: string - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot - start or end with hyphen) + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) maxLength: 63 pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ example: order-pipeline code: type: string format: binary - description: ZIP or tar.gz archive containing function source code plus - dependency manifests/lockfiles. + description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. runtime: type: string enum: @@ -3869,8 +3835,7 @@ paths: example: nodejs24.x handler: type: string - description: The name of the function to invoke. Defaults to "handler" if not - specified. + description: The name of the function to invoke. Defaults to "handler" if not specified. default: handler example: handler is_public: @@ -3898,8 +3863,7 @@ paths: JSON-encoded array of project variable names this function requires, on top of the ones detected in its source. A declared name the project does not define is rejected with 400; a detected name it does not define is ignored. Only used when `variable_scope` is `scoped`. Omitting this leaves an existing function's declared names unchanged. responses: '200': - description: Existing durable function updated; its deployment was started or - queued + description: Existing durable function updated; its deployment was started or queued content: application/json: schema: @@ -3926,8 +3890,7 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Name is held by a standard function, or a deletion is queued or - running + description: Name is held by a standard function, or a deletion is queued or running content: application/json: schema: @@ -4543,16 +4506,12 @@ paths: app_root: type: string maxLength: 1024 - description: Optional relative POSIX path from the uploaded archive root to the - Next.js app to build, for example `apps/web`. + description: Optional relative POSIX path from the uploaded archive root to the Next.js app to build, for example `apps/web`. example: apps/web archive: type: string format: binary - description: ZIP or tar.gz archive of the frontend project directory or monorepo - workspace root. The API enforces - SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz - archive. + description: ZIP or tar.gz archive of the frontend project directory or monorepo workspace root. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz archive. responses: '200': description: Existing frontend updated; its deployment was started or queued @@ -4579,8 +4538,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Frontend deployment limit exceeded for the current plan or project - hard cap + description: Frontend deployment limit exceeded for the current plan or project hard cap content: application/json: schema: @@ -4604,8 +4562,7 @@ paths: schema: $ref: '#/components/schemas/Error' '503': - description: Service unavailable - frontend workflow or archive limit - configuration missing + description: Service unavailable - frontend workflow or archive limit configuration missing content: application/json: schema: @@ -4890,8 +4847,7 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Conflict - custom domain already in use, still detaching, or - frontend already has a custom domain + description: Conflict - custom domain already in use, still detaching, or frontend already has a custom domain content: application/json: schema: @@ -4903,8 +4859,7 @@ paths: schema: $ref: '#/components/schemas/Error' '503': - description: Service unavailable - custom domain provisioning is temporarily - unavailable + description: Service unavailable - custom domain provisioning is temporarily unavailable content: application/json: schema: @@ -6145,16 +6100,14 @@ paths: type: string role_name: type: string - description: Volcano-managed per-database client login (also the pgproxy routing - username) + description: Volcano-managed per-database client login (also the pgproxy routing username) example: volcano_client_11111111-1111-1111-1111-111111111111 new_password: type: string description: New Volcano-managed client password. Always starts with `vpg_`. connection_string: type: string - description: Updated pgproxy connection string using Volcano-managed - credentials. + description: Updated pgproxy connection string using Volcano-managed credentials. '400': description: Database is not active content: @@ -6259,16 +6212,14 @@ paths: schema: type: string format: date-time - description: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). - Defaults to 24 hours ago. + description: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). Defaults to 24 hours ago. - name: to in: query required: false schema: type: string format: date-time - description: End time in RFC3339 format (e.g., "2024-01-02T00:00:00Z"). Defaults - to now. + description: End time in RFC3339 format (e.g., "2024-01-02T00:00:00Z"). Defaults to now. - name: granularity in: query required: false @@ -7969,8 +7920,7 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send - as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. '400': description: Invalid or conflicting pagination parameters content: @@ -8165,8 +8115,7 @@ paths: '204': description: Identity unlinked '400': - description: Identity cannot be unlinked (primary, last, or would remove last - sign-in method), or the identity id is malformed + description: Identity cannot be unlinked (primary, last, or would remove last sign-in method), or the identity id is malformed content: application/json: schema: @@ -8237,9 +8186,7 @@ paths: schema: $ref: '#/components/schemas/AuthMethodSummary' '400': - description: This method cannot be set as primary (password stub, converted - anonymous method, or unverified email), or the method id is - malformed + description: This method cannot be set as primary (password stub, converted anonymous method, or unverified email), or the method id is malformed content: application/json: schema: @@ -8251,8 +8198,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: The promoted identity's email domain is not in the project's - allowed_email_domains + description: The promoted identity's email domain is not in the project's allowed_email_domains content: application/json: schema: @@ -8369,8 +8315,7 @@ paths: - name: status in: query required: false - description: Filter by effective status. `banned` returns only currently-banned - users; an expired temporary ban lists as `active`. + description: Filter by effective status. `banned` returns only currently-banned users; an expired temporary ban lists as `active`. schema: type: string enum: @@ -8558,8 +8503,7 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send - as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. '400': description: Invalid or conflicting pagination parameters content: @@ -8883,8 +8827,7 @@ paths: tags: - Auth Configuration summary: Get default email templates - description: Returns the default email templates used when no custom template is - configured. + description: Returns the default email templates used when no custom template is configured. operationId: getDefaultEmailTemplates responses: '200': @@ -9351,8 +9294,7 @@ paths: schema: type: string '404': - description: Ticket is invalid or expired, its path does not match, or managed - authentication is disabled + description: Ticket is invalid or expired, its path does not match, or managed authentication is disabled content: text/plain: schema: @@ -9483,15 +9425,13 @@ paths: required: false schema: type: string - description: Device user code (from `POST /auth/device/authorize`) used with - `action=device`. + description: Device user code (from `POST /auth/device/authorize`) used with `action=device`. - name: anon_key in: query required: false schema: type: string - description: Project anon key used by built-in managed auth flows (required for - login/signup/device actions). + description: Project anon key used by built-in managed auth flows (required for login/signup/device actions). - name: state in: query required: false @@ -10623,8 +10563,7 @@ paths: schema: $ref: '#/components/schemas/Error' '502': - description: Provider transport failure, invalid JSON, or response body larger - than 8 MiB + description: Provider transport failure, invalid JSON, or response body larger than 8 MiB content: application/json: schema: @@ -10666,8 +10605,7 @@ paths: $ref: '#/components/schemas/AnonKey' total: type: integer - description: Total number of items matching the query (so the UI can render - numbered pages). + description: Total number of items matching the query (so the UI can render numbered pages). has_more: type: boolean description: Whether a next page exists. @@ -10676,8 +10614,7 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send - as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. post: tags: - Anon Keys @@ -10833,8 +10770,7 @@ paths: tags: - Anon Keys summary: Set default anon key - description: Promotes the given key to the project's configured default. At most - one key per project can be default. + description: Promotes the given key to the project's configured default. At most one key per project can be default. operationId: setDefaultAnonKey security: - UserToken: [] @@ -11260,15 +11196,13 @@ paths: description: Opaque cursor for the next page (cursor pagination only) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only). Send - as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. /projects/{id}/storage/stats: get: tags: - Storage Admin summary: Get storage statistics for a project - description: Returns aggregate storage statistics including bucket count, object - count, and total size. + description: Returns aggregate storage statistics including bucket count, object count, and total size. operationId: getStorageStats security: - UserToken: [] @@ -11351,8 +11285,7 @@ paths: tags: - Realtime summary: Get realtime statistics for a project - description: Returns realtime usage statistics including connection counts and - subscribed tables. + description: Returns realtime usage statistics including connection counts and subscribed tables. operationId: getRealtimeStats security: - UserToken: [] @@ -11455,8 +11388,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks - `locks.manage` + description: A non-service credential was supplied or the service key lacks `locks.manage` content: application/json: schema: @@ -11528,8 +11460,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks - `locks.manage` + description: A non-service credential was supplied or the service key lacks `locks.manage` content: application/json: schema: @@ -11585,8 +11516,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks - `locks.manage` + description: A non-service credential was supplied or the service key lacks `locks.manage` content: application/json: schema: @@ -11650,8 +11580,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks - `locks.manage` + description: A non-service credential was supplied or the service key lacks `locks.manage` content: application/json: schema: @@ -11707,8 +11636,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: A non-service credential was supplied or the service key lacks - `locks.manage` + description: A non-service credential was supplied or the service key lacks `locks.manage` content: application/json: schema: @@ -12166,8 +12094,7 @@ paths: tags: - System summary: Health check endpoint - description: Returns server health status. Used for load balancer and monitoring - checks. + description: Returns server health status. Used for load balancer and monitoring checks. operationId: healthCheck responses: '200': @@ -12192,10 +12119,7 @@ paths: schema: $ref: "#/components/schemas/SandboxPresetList" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12226,10 +12150,7 @@ paths: schema: $ref: "#/components/schemas/SandboxTemplatePage" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12269,10 +12190,7 @@ paths: schema: $ref: "#/components/schemas/SandboxTemplate" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12307,10 +12225,7 @@ paths: schema: $ref: "#/components/schemas/SandboxTemplate" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12350,10 +12265,7 @@ paths: schema: $ref: "#/components/schemas/SandboxTemplate" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12383,10 +12295,7 @@ paths: "202": description: Retire a template and terminate its sessions default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12423,10 +12332,7 @@ paths: schema: $ref: "#/components/schemas/SandboxDeploymentPage" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12457,10 +12363,7 @@ paths: schema: $ref: "#/components/schemas/SandboxSessionPage" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12500,10 +12403,7 @@ paths: schema: $ref: "#/components/schemas/SandboxSession" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12544,10 +12444,7 @@ paths: schema: $ref: "#/components/schemas/SandboxExecutionResult" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12577,10 +12474,7 @@ paths: schema: $ref: "#/components/schemas/SandboxSession" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12608,10 +12502,7 @@ paths: schema: $ref: "#/components/schemas/SandboxSession" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12640,10 +12531,7 @@ paths: schema: $ref: "#/components/schemas/SandboxSession" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12672,10 +12560,7 @@ paths: schema: $ref: "#/components/schemas/SandboxSession" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12717,10 +12602,7 @@ paths: schema: $ref: "#/components/schemas/SandboxCommandResult" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12756,10 +12638,7 @@ paths: schema: $ref: "#/components/schemas/SandboxFileResult" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12791,10 +12670,7 @@ paths: "204": description: Write a workspace file default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12831,10 +12707,7 @@ paths: "204": description: Authorize an authenticated project user for this session default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12864,10 +12737,7 @@ paths: "204": description: Revoke a project user session grant default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12903,10 +12773,7 @@ paths: schema: $ref: "#/components/schemas/SandboxAccess" default: - description: Request refused or unavailable. Errors include invalid input (400), - unauthenticated (401), forbidden (403), not found (404), conflicting - retry (409), capacity exhausted (429), and disabled or unavailable - (503). + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: @@ -12972,8 +12839,7 @@ components: type: string pattern: ^[a-z0-9_]+$ maxLength: 64 - description: Branch name (unique within the parent database, lowercase letters, - numbers, and underscores only) + description: Branch name (unique within the parent database, lowercase letters, numbers, and underscores only) BucketName: name: bucketName in: path @@ -13015,8 +12881,7 @@ components: type: string pattern: ^[a-z0-9_]+$ maxLength: 64 - description: Database name (unique within project, lowercase letters, numbers, - and underscores only) + description: Database name (unique within project, lowercase letters, numbers, and underscores only) DeploymentId: name: deploymentId in: path @@ -13080,8 +12945,7 @@ components: name: X-Volcano-Lock-Token in: header required: true - description: Opaque UUID generated once by the caller and retained for the lease - lifetime. + description: Opaque UUID generated once by the caller and retained for the lease lifetime. schema: type: string format: uuid @@ -13295,8 +13159,7 @@ components: - auth.logout is_default: type: boolean - description: Whether this is the project's configured default anon key. Only one - key per project can be default. + description: Whether this is the project's configured default anon key. Only one key per project can be default. created_at: type: string format: date-time @@ -13416,8 +13279,7 @@ components: default: signup platform_token_ttl: type: integer - description: TTL in seconds for platform tokens minted via - `/auth/platform/exchange` + description: TTL in seconds for platform tokens minted via `/auth/platform/exchange` default: 2592000 allow_password_reset: type: boolean @@ -13441,8 +13303,7 @@ components: default: 86400 require_email_confirmation: type: boolean - description: Require users to confirm email before sign-in. Can only be true - when email_enabled is true. + description: Require users to confirm email before sign-in. Can only be true when email_enabled is true. default: false email_confirmation_timeout: type: integer @@ -13450,15 +13311,11 @@ components: default: 86400 auto_link_verified_oauth: type: boolean - description: Link a verified OAuth identity to an existing confirmed account - with the same email instead of returning a conflict. Requires - require_email_confirmation to be true. + description: Link a verified OAuth identity to an existing confirmed account with the same email instead of returning a conflict. Requires require_email_confirmation to be true. default: false email_enabled: type: boolean - description: Enable transactional email sending (confirmation, reset, change - notifications). Must be true when require_email_confirmation is - true. + description: Enable transactional email sending (confirmation, reset, change notifications). Must be true when require_email_confirmation is true. default: false email_from_address: type: string @@ -13473,8 +13330,7 @@ components: type: string smtp_password_configured: type: boolean - description: Whether an SMTP password is configured. The password itself is - never returned. + description: Whether an SMTP password is configured. The password itself is never returned. default: false smtp_use_tls: type: boolean @@ -13494,8 +13350,7 @@ components: description: Default redirect target after successful hosted auth. allowed_redirect_urls: type: array - description: Redirect allowlist used to validate post_auth_redirect_url and - post_logout_redirect_url. + description: Redirect allowlist used to validate post_auth_redirect_url and post_logout_redirect_url. items: type: string post_logout_redirect_url: @@ -13580,8 +13435,7 @@ components: type: integer format: int64 minimum: 0 - description: Users with a successful session creation or refresh in the trailing - 30 days since activity collection was deployed. + description: Users with a successful session creation or refresh in the trailing 30 days since activity collection was deployed. required: - total_users - active_users_30d @@ -13720,8 +13574,7 @@ components: allOf: - $ref: '#/components/schemas/AuthHostedPage' nullable: true - description: The saved page, or null when the project has not customized this - page type yet. + description: The saved page, or null when the project has not customized this page type yet. defaults: $ref: '#/components/schemas/AuthHostedPageDefaults' runtime: @@ -13745,8 +13598,7 @@ components: description: Whether ownership of this email has been verified is_primary: type: boolean - description: Whether the account's primary sign-in method resolves to this - identity + description: Whether the account's primary sign-in method resolves to this identity created_at: type: string format: date-time @@ -13839,8 +13691,7 @@ components: type: integer minimum: 15 maximum: 15 - description: Lowest minimum password length accepted by the auth configuration - endpoint. + description: Lowest minimum password length accepted by the auth configuration endpoint. max_length: type: integer minimum: 128 @@ -13857,12 +13708,10 @@ components: description: Whether passwords must contain an ASCII digit (0-9). require_special_chars: type: boolean - description: Whether passwords must contain one of the backend-supported special - characters. + description: Whether passwords must contain one of the backend-supported special characters. compromised_passwords_rejected: type: boolean - description: Whether common and known-compromised passwords are rejected by the - backend. + description: Whether common and known-compromised passwords are rejected by the backend. required: - effective_min_length - min_configurable_length @@ -14091,9 +13940,7 @@ components: $ref: '#/components/schemas/Function' failed: type: array - description: Functions that failed before their workflow started. Successful - functions are left running; failed new functions are deleted and - failed updates are rolled back where possible. + description: Functions that failed before their workflow started. Successful functions are left running; failed new functions are deleted and failed updates are rolled back where possible. items: $ref: '#/components/schemas/BatchFunctionDeployFailure' required: @@ -14272,9 +14119,7 @@ components: additionalProperties: true regions: type: array - description: Optional single explicit deployed region. If omitted, the scheduler - chooses one deployed region and invokes according to the cron - expression. + description: Optional single explicit deployed region. If omitted, the scheduler chooses one deployed region and invokes according to the cron expression. maxItems: 1 items: type: string @@ -14295,13 +14140,11 @@ components: client_id: type: string example: 123456789.apps.googleusercontent.com - description: Required for non-device providers. Must not be provided for - `provider=device`; server always auto-generates it. + description: Required for non-device providers. Must not be provided for `provider=device`; server always auto-generates it. client_secret: type: string example: GOCSPX-abc123def456 - description: Required for non-device providers. Must not be provided for - `provider=device`; server always auto-generates it. + description: Required for non-device providers. Must not be provided for `provider=device`; server always auto-generates it. redirect_url: type: string description: Required for non-device providers. Not used for `provider=device`. @@ -14315,8 +14158,7 @@ components: - openid - email - profile - description: Optional for non-device providers, uses provider defaults if - omitted. Not used for `provider=device`. + description: Optional for non-device providers, uses provider defaults if omitted. Not used for `provider=device`. CreateProjectRequest: type: object description: Request to create a new project @@ -14447,10 +14289,7 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. - Omission preserves existing membership; new variables default to - true for legacy clients. Send false explicitly to create a - non-shared variable. + description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. name: type: string minLength: 1 @@ -15087,8 +14926,7 @@ components: description: Total active compute time in seconds time_range: type: string - description: Time range of the metrics (e.g., "2024-01-01T00:00:00Z to - 2024-01-02T00:00:00Z") + description: Time range of the metrics (e.g., "2024-01-01T00:00:00Z to 2024-01-02T00:00:00Z") granularity: type: string enum: @@ -15203,8 +15041,7 @@ components: type: string code: type: string - description: Stable machine-readable error code when a specific recovery path is - available. + description: Stable machine-readable error code when a specific recovery path is available. required: - error ImportProvider: @@ -15387,11 +15224,7 @@ components: confirm_environment_variable_read: type: boolean default: false - description: Set to true only after the user confirms an immediately preceding - disclosure that the Vercel Integration grant permits reads and - writes, while this preflight reads production variable values only. - When false or omitted, preflight reads variable metadata only and - reports readable values as manual input. + description: Set to true only after the user confirms an immediately preceding disclosure that the Vercel Integration grant permits reads and writes, while this preflight reads production variable values only. When false or omitted, preflight reads variable metadata only and reports readable values as manual input. required: - connection_id - source_id @@ -15505,8 +15338,7 @@ components: scope: type: string minLength: 1 - description: URL-encoded source scope, including target, branch, and custom - environment identifiers. + description: URL-encoded source scope, including target, branch, and custom environment identifiers. required: - kind - name @@ -15708,8 +15540,7 @@ components: type: boolean is_empty: type: boolean - description: Whether the repository has no commits and can receive an initial - source export. + description: Whether the repository has no commits and can receive an initial source export. required: - id - full_name @@ -15734,17 +15565,14 @@ components: repo_id: type: integer format: int64 - description: Stable GitHub repository id (repository.id), the authoritative - binding. + description: Stable GitHub repository id (repository.id), the authoritative binding. repo_full_name: type: string root_directory: type: string production_branch: type: string - description: The branch a push must land on to deploy. Follows the repository's - GitHub default branch unless the project set its own, which a - default-branch rename on GitHub then leaves alone. + description: The branch a push must land on to deploy. Follows the repository's GitHub default branch unless the project set its own, which a default-branch rename on GitHub then leaves alone. updated_at: type: string format: date-time @@ -15757,9 +15585,7 @@ components: - updated_at ProjectGitDeploySettings: type: object - description: 'A project''s GitHub auto-deploy settings: what a push to the - connected repo''s production branch deploys. All settings are - default-off.' + description: 'A project''s GitHub auto-deploy settings: what a push to the connected repo''s production branch deploys. All settings are default-off.' properties: auto_deploy_enabled: type: boolean @@ -15769,12 +15595,10 @@ components: description: Whether the repo's functions are deployed on push. frontend_name: type: string - description: Name of the frontend to build and deploy on push. Omitted when no - frontend is deployed. Resolved at deploy time; need not exist yet. + description: Name of the frontend to build and deploy on push. Omitted when no frontend is deployed. Resolved at deploy time; need not exist yet. frontend_app_root: type: string - description: App root (subdirectory) the frontend builds from. Omitted for the - repo root. + description: App root (subdirectory) the frontend builds from. Omitted for the repo root. updated_at: type: string format: date-time @@ -15795,8 +15619,7 @@ components: description: Frontend to deploy on push. Omit or empty to deploy no frontend. frontend_app_root: type: string - description: App root the frontend builds from. Requires frontend_name; omit for - the repo root. + description: App root the frontend builds from. Requires frontend_name; omit for the repo root. required: - auto_deploy_enabled - deploy_functions @@ -15870,22 +15693,17 @@ components: type: string format: date-time nullable: true - description: When source export started, cleared if an incomplete transition is - canceled. + description: When source export started, cleared if an incomplete transition is canceled. exported_at: type: string format: date-time nullable: true - description: When the initial export push entered deployment, or when a - transition was canceled after its commit was reserved. Once set, the - one-time export is consumed. + description: When the initial export push entered deployment, or when a transition was canceled after its commit was reserved. Once set, the one-time export is consumed. handed_over_at: type: string format: date-time nullable: true - description: When a complete production-branch deployment first proved the - repository could drive the project, null until then. Once set, the - repository is the project's source of truth. + description: When a complete production-branch deployment first proved the repository could drive the project, null until then. Once set, the repository is the project's source of truth. required: - mode - transition_started_at @@ -15893,8 +15711,7 @@ components: - handed_over_at ProjectSourceExport: type: object - description: The initial production-branch commit, and everything export could - not carry. + description: The initial production-branch commit, and everything export could not carry. properties: repo_full_name: type: string @@ -15907,8 +15724,7 @@ components: type: integer skipped: type: array - description: Resources whose source could not be taken, with the reason. Most - often a resource that has never deployed successfully. + description: Resources whose source could not be taken, with the reason. Most often a resource that has never deployed successfully. items: $ref: '#/components/schemas/ProjectSourceExportSkip' omitted: @@ -15930,8 +15746,7 @@ components: production_branch: type: string minLength: 1 - description: The currently configured production branch the user confirmed for - export. + description: The currently configured production branch the user confirmed for export. required: - production_branch ProjectSourceExportSkip: @@ -15939,9 +15754,7 @@ components: properties: kind: type: string - description: 'The kind of resource, "function" or "frontend". Deliberately not - an enum: the generated constants would collide with an existing - resource-type enum and rename its members.' + description: 'The kind of resource, "function" or "frontend". Deliberately not an enum: the generated constants would collide with an existing resource-type enum and rename its members.' name: type: string reason: @@ -15955,9 +15768,7 @@ components: properties: kind: type: string - description: 'What was left out: migrations Volcano stores no copy of, variable - values, a credential-shaped file, installed dependencies, or an - archive entry a repository cannot carry.' + description: 'What was left out: migrations Volcano stores no copy of, variable values, a credential-shaped file, installed dependencies, or an archive entry a repository cannot carry.' resource: type: string description: The resource it came from, empty when project-wide. @@ -15974,8 +15785,7 @@ components: type: string minLength: 1 maxLength: 255 - description: The branch a push must land on to deploy. Validated as a Git branch - name only — it does not have to exist yet. + description: The branch a push must land on to deploy. Validated as a Git branch name only — it does not have to exist yet. required: - production_branch ConnectProjectGitRequest: @@ -15991,38 +15801,18 @@ components: repository_id: type: integer format: int64 - description: Stable GitHub repository id (repository.id), the preferred - selector. Either repository_id or repo_full_name is required; when - both are given they must identify the same live repository. + description: Stable GitHub repository id (repository.id), the preferred selector. Either repository_id or repo_full_name is required; when both are given they must identify the same live repository. repo_full_name: type: string deprecated: true - description: Deprecated selector kept for a compatibility window; prefer - repository_id. Either repository_id or repo_full_name is required. + description: Deprecated selector kept for a compatibility window; prefer repository_id. Either repository_id or repo_full_name is required. root_directory: type: string - description: Monorepo subdirectory the project builds from. Omit for the repo - root. + description: Monorepo subdirectory the project builds from. Omit for the repo root. production_branch: type: string maxLength: 255 - description: 'The branch a push must land on to deploy, with three cases, - because this is a full replace and a read-modify-write client sends - back whatever it read. Omit it to follow the repository''s GitHub - default branch, which also discards a branch set earlier. Send back - the branch the project already deploys from, when that is the - repository''s default, and nothing changes either way — a project - pinned to that branch stays pinned. Sending the default branch when - the project deploys from something else returns it to following the - default, including on a rebind, where a pin describes a branch - chosen for the repository being left. Any other branch becomes the - project''s own choice, exempt from later default-branch renames. - Validated as a Git branch name only: it does not have to exist yet, - so a project can be pointed at a branch about to be pushed. Changing - repository and naming a branch other than the new repository''s - default in one request is refused with 400, because the branch named - is almost always the previous repository''s, echoed back — connect - first, then set the branch.' + description: 'The branch a push must land on to deploy, with three cases, because this is a full replace and a read-modify-write client sends back whatever it read. Omit it to follow the repository''s GitHub default branch, which also discards a branch set earlier. Send back the branch the project already deploys from, when that is the repository''s default, and nothing changes either way — a project pinned to that branch stays pinned. Sending the default branch when the project deploys from something else returns it to following the default, including on a rebind, where a pin describes a branch chosen for the repository being left. Any other branch becomes the project''s own choice, exempt from later default-branch renames. Validated as a Git branch name only: it does not have to exist yet, so a project can be pointed at a branch about to be pushed. Changing repository and naming a branch other than the new repository''s default in one request is refused with 400, because the branch named is almost always the previous repository''s, echoed back — connect first, then set the branch.' required: - connection_id - installation_id @@ -16046,8 +15836,7 @@ components: app_root: type: string maxLength: 1024 - description: Optional relative POSIX path from the uploaded archive root to the - Next.js app that is deployed. + description: Optional relative POSIX path from the uploaded archive root to the Next.js app that is deployed. status: type: string description: | @@ -16228,8 +16017,7 @@ components: description: What initiated this deployment. initiated_by: type: string - description: Platform user that triggered a request-initiated deployment; absent - for git and system deployments. + description: Platform user that triggered a request-initiated deployment; absent for git and system deployments. artifact_bucket: type: string artifact_key: @@ -16245,12 +16033,10 @@ components: codebuild_duration_seconds: type: integer format: int64 - description: Total CodeBuild build duration recorded for this deployment, in - seconds. + description: Total CodeBuild build duration recorded for this deployment, in seconds. codebuild_build_count: type: integer - description: Number of completed CodeBuild builds included in - codebuild_duration_seconds. + description: Number of completed CodeBuild builds included in codebuild_duration_seconds. codebuild_duration_recorded_at: type: string format: date-time @@ -16320,8 +16106,7 @@ components: page_views: type: integer format: int64 - description: Navigable-document responses served on this day (text/html or - Sec-Fetch-Dest=document) — strict subset of `requests`. + description: Navigable-document responses served on this day (text/html or Sec-Fetch-Dest=document) — strict subset of `requests`. required: - day - requests @@ -16348,16 +16133,14 @@ components: - requests FrontendUsageHistoryResponse: type: object - description: Zero-filled daily series of request + error counts for a single - frontend, oldest first. + description: Zero-filled daily series of request + error counts for a single frontend, oldest first. properties: frontend_id: type: string format: uuid days: type: integer - description: Number of daily entries returned (always equal to the `days` query - param after clamping). + description: Number of daily entries returned (always equal to the `days` query param after clamping). daily: type: array items: @@ -16416,21 +16199,15 @@ components: type: object nullable: true additionalProperties: true - description: Optional OpenAPI 3.0 or 3.1 document describing an HTTP-mode - function. + description: Optional OpenAPI 3.0 or 3.1 document describing an HTTP-mode function. has_openapi_spec: type: boolean - description: Whether OpenAPI metadata is configured; list responses omit the - document itself. + description: Whether OpenAPI metadata is configured; list responses omit the document itself. aws_function_arn: type: string invoke_url: type: string - description: 'Canonical geo-routed HTTPS endpoint for invoking this function. - Use it as-is: it does not share a domain with the API, so a host - derived from the API URL will not reach the function. Omitted when - the deployment serves no public invocation domain, as in local - development, so a client testing for an empty string never matches.' + description: 'Canonical geo-routed HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development, so a client testing for an empty string never matches.' deployed_regions: type: array items: @@ -16736,8 +16513,7 @@ components: description: What initiated this deployment. initiated_by: type: string - description: Platform user that triggered a request-initiated deployment; absent - for git and system deployments. + description: Platform user that triggered a request-initiated deployment; absent for git and system deployments. artifact_bucket: type: string artifact_key: @@ -16747,12 +16523,10 @@ components: codebuild_duration_seconds: type: integer format: int64 - description: Total CodeBuild build duration recorded for this deployment, in - seconds. + description: Total CodeBuild build duration recorded for this deployment, in seconds. codebuild_build_count: type: integer - description: Number of completed CodeBuild builds included in - codebuild_duration_seconds. + description: Number of completed CodeBuild builds included in codebuild_duration_seconds. codebuild_duration_recorded_at: type: string format: date-time @@ -16856,10 +16630,7 @@ components: q: type: string maxLength: 512 - description: Optional activity query. Supports quoted text, implicit AND, - AND/OR/NOT, parentheses, and fields such as `level`, `region`, - `invocation.id`, `resource.id`, `resource.name`, `function`, - `frontend`, `database`, and `body`. + description: Optional activity query. Supports quoted text, implicit AND, AND/OR/NOT, parentheses, and fields such as `level`, `region`, `invocation.id`, `resource.id`, `resource.name`, `function`, `frontend`, `database`, and `body`. start_time: type: string format: date-time @@ -16899,10 +16670,7 @@ components: q: type: string maxLength: 512 - description: Optional log query. Supports quoted text, implicit AND, AND/OR/NOT, - parentheses, and fields such as `level`, `region`, `invocation.id`, - `resource.id`, `resource.name`, `function`, `frontend`, `database`, - and `body`. + description: Optional log query. Supports quoted text, implicit AND, AND/OR/NOT, parentheses, and fields such as `level`, `region`, `invocation.id`, `resource.id`, `resource.name`, `function`, `frontend`, `database`, and `body`. start_time: type: string format: date-time @@ -16924,8 +16692,7 @@ components: - resource LogStreamRequest: type: object - description: Stream request for live project logs. Pagination cursors and fixed - end times are not supported. + description: Stream request for live project logs. Pagination cursors and fixed end times are not supported. additionalProperties: false properties: resource: @@ -16933,8 +16700,7 @@ components: q: type: string maxLength: 512 - description: Optional log query using the same syntax as search and activity - requests. + description: Optional log query using the same syntax as search and activity requests. start_time: type: string format: date-time @@ -16944,21 +16710,18 @@ components: minimum: 1 maximum: 1000 default: 100 - description: Maximum number of records to deliver on connect or reconnect before - following new events. + description: Maximum number of records to deliver on connect or reconnect before following new events. required: - resource LogSearchEvent: allOf: - $ref: '#/components/schemas/LogEvent' - type: object - description: Runtime log event returned by project log search. Search results - always include a stable event ID and owning resource. + description: Runtime log event returned by project log search. Search results always include a stable event ID and owning resource. properties: id: type: string - description: Opaque stable log event ID for pagination, deduplication, and - display. + description: Opaque stable log event ID for pagination, deduplication, and display. resource: $ref: '#/components/schemas/LogResource' required: @@ -16981,8 +16744,7 @@ components: description: Whether there are more log events available. next_cursor: type: string - description: Opaque cursor for the next page. Send this value as `cursor` on the - next request. + description: Opaque cursor for the next page. Send this value as `cursor` on the next request. required: - data - limit @@ -17022,16 +16784,13 @@ components: language: type: string example: nodejs - description: Runtime language family used by the CLI to choose defaults from - source files. + description: Runtime language family used by the CLI to choose defaults from source files. default: type: boolean description: Whether this runtime is the CLI default for its language. durable_capable: type: boolean - description: Whether a durable function can be authored on this runtime. Only - runtimes with a durable authoring API report true, and a durable - deploy naming any other runtime is rejected. + description: Whether a durable function can be authored on this runtime. Only runtimes with a durable authoring API report true, and a durable deploy naming any other runtime is rejected. deployment: $ref: '#/components/schemas/FunctionRuntimeDeployment' FunctionRuntimesResponse: @@ -17097,8 +16856,7 @@ components: run_count: type: integer format: int64 - description: Total number of times this scheduler has executed. 0 for a - scheduler that has never run. + description: Total number of times this scheduler has executed. 0 for a scheduler that has never run. created_at: type: string format: date-time @@ -17122,12 +16880,10 @@ components: type: boolean next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - page @@ -17185,8 +16941,7 @@ components: properties: id: type: string - description: Opaque stable log event ID for pagination, deduplication, and - display. + description: Opaque stable log event ID for pagination, deduplication, and display. timestamp: type: string format: date-time @@ -17194,9 +16949,7 @@ components: level: $ref: '#/components/schemas/LiveLogLevel' body: - description: Application log value. JSON arguments retain their JSON type. - Strings containing a serialized JSON object or array are normalized - to that object or array; all other strings remain strings. + description: Application log value. JSON arguments retain their JSON type. Strings containing a serialized JSON object or array are normalized to that object or array; all other strings remain strings. nullable: true oneOf: - type: string @@ -17216,8 +16969,7 @@ components: $ref: '#/components/schemas/LogDeployment' invocation_id: type: string - description: Function invocation ID associated with this log event, when - available. + description: Function invocation ID associated with this log event, when available. required: - timestamp - body @@ -17382,12 +17134,10 @@ components: description: URL path to next page (only present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - page @@ -17443,16 +17193,13 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more - is true) + description: URL path to next page (offset pagination only; present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - page @@ -17511,12 +17258,10 @@ components: description: URL path to next page (only present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - page @@ -17627,16 +17372,13 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more - is true) + description: URL path to next page (offset pagination only; present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - limit @@ -17663,16 +17405,13 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more - is true) + description: URL path to next page (offset pagination only; present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. status: type: string enum: @@ -17715,16 +17454,13 @@ components: description: Whether there are more pages available next: type: string - description: URL path to next page (offset pagination only; present if has_more - is true) + description: URL path to next page (offset pagination only; present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - page @@ -17733,8 +17469,7 @@ components: - has_more PaginatedStorageBuckets: type: object - description: Cursor-paginated storage buckets (returned only when cursor - pagination is requested). + description: Cursor-paginated storage buckets (returned only when cursor pagination is requested). properties: data: type: array @@ -17754,8 +17489,7 @@ components: description: Opaque cursor for the next page (present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (present when a previous page - exists). Send as `ending_before`. + description: Opaque cursor for the previous page (present when a previous page exists). Send as `ending_before`. required: - data - limit @@ -17784,12 +17518,10 @@ components: description: URL path to next page (only present if has_more is true) next_cursor: type: string - description: Opaque cursor for the next page (cursor pagination only; present if - has_more is true) + description: Opaque cursor for the next page (cursor pagination only; present if has_more is true) prev_cursor: type: string - description: Opaque cursor for the previous page (cursor pagination only; - present when a previous page exists). Send as `ending_before`. + description: Opaque cursor for the previous page (cursor pagination only; present when a previous page exists). Send as `ending_before`. required: - data - page @@ -17862,13 +17594,11 @@ components: git_connection: allOf: - $ref: '#/components/schemas/ProjectGitConnectionSummary' - description: Present for connected projects when `git_connection` is requested - through the list endpoint's `include` parameter. + description: Present for connected projects when `git_connection` is requested through the list endpoint's `include` parameter. health: allOf: - $ref: '#/components/schemas/ProjectHealthSummary' - description: Present when `health` is requested through the list endpoint's - `include` parameter. + description: Present when `health` is requested through the list endpoint's `include` parameter. created_at: type: string format: date-time @@ -17912,16 +17642,13 @@ components: shared_variables: type: array uniqueItems: true - description: Replace the complete shared function-variable list with existing - names, without changing variable values. Omission keeps membership - unchanged; an empty list clears it. + description: Replace the complete shared function-variable list with existing names, without changing variable values. Omission keeps membership unchanged; an empty list clears it. items: type: string pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ variables: type: array - description: Fully synced when declared - variables absent from this list are - deleted. + description: Fully synced when declared - variables absent from this list are deleted. items: $ref: '#/components/schemas/ProjectConfigVariable' buckets: @@ -17980,12 +17707,10 @@ components: properties: section: type: string - description: Manifest section the entry belongs to (e.g. variables, buckets, - auth.providers.oauth). + description: Manifest section the entry belongs to (e.g. variables, buckets, auth.providers.oauth). name: type: string - description: Resource name or key within the section. Empty for singleton - sections. + description: Resource name or key within the section. Empty for singleton sections. action: type: string enum: @@ -17999,8 +17724,7 @@ components: description: Error detail when action is `error`. notice: type: string - description: Optional operational note (e.g. disabling realtime drops active - connections). + description: Optional operational note (e.g. disabling realtime drops active connections). required: - section - action @@ -18120,8 +17844,7 @@ components: properties: require_confirmation: type: boolean - description: Require users to confirm email before sign-in. Requires email - sending to be enabled. + description: Require users to confirm email before sign-in. Requires email sending to be enabled. confirmation_timeout: type: integer description: Email confirmation token expiry in seconds @@ -18174,8 +17897,7 @@ components: $ref: '#/components/schemas/ProjectConfigAuthEmailPasswordProvider' oauth: type: array - description: Fully synced when declared - providers absent from this list are - deleted. + description: Fully synced when declared - providers absent from this list are deleted. items: $ref: '#/components/schemas/ProjectConfigOAuthProvider' ProjectConfigAuthRateLimits: @@ -18574,8 +18296,7 @@ components: type: string format: password writeOnly: true - description: Write-only; omitted from config export. Not used for - `provider=device`. + description: Write-only; omitted from config export. Not used for `provider=device`. redirect_url: type: string description: Not used for `provider=device`. @@ -18685,10 +18406,7 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. - Omission preserves existing membership; new variables default to - true for legacy clients. Send false explicitly to create a - non-shared variable. + description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. name: type: string minLength: 1 @@ -19260,11 +18978,7 @@ components: description: Canonical function ID used for invocation routing invoke_url: type: string - description: 'Canonical HTTPS endpoint for invoking this function. Use it as-is: - it does not share a domain with the API, so a host derived from the - API URL will not reach the function. Omitted when the deployment - serves no public invocation domain, as in local development; invoke - through POST /functions/{functionId}/invoke instead.' + description: 'Canonical HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development; invoke through POST /functions/{functionId}/invoke instead.' cache_ttl_seconds: type: integer minimum: 1 @@ -19285,8 +18999,7 @@ components: default: cron cron_expression: type: string - description: Standard 5-field cron expression evaluated in UTC. Seconds fields, - descriptors, and Quartz syntax are not supported. + description: Standard 5-field cron expression evaluated in UTC. Seconds fields, descriptors, and Quartz syntax are not supported. example: '*/5 * * * *' ServiceKey: type: object @@ -19471,8 +19184,7 @@ components: - mime_type StorageObjectWithBucket: type: object - description: Storage object with bucket name included (for admin listing across - buckets) + description: Storage object with bucket name included (for admin listing across buckets) properties: id: type: string @@ -19642,13 +19354,11 @@ components: html_body: type: string maxLength: 262144 - description: Optional HTML body override. Rendered as an html/template. Max 256 - KiB. + description: Optional HTML body override. Rendered as an html/template. Max 256 KiB. text_body: type: string maxLength: 262144 - description: Optional plain-text body override. Rendered as a text/template. Max - 256 KiB. + description: Optional plain-text body override. Rendered as a text/template. Max 256 KiB. TestEmailResponse: type: object required: @@ -19658,9 +19368,7 @@ components: type: boolean UpdateAuthConfigRequest: type: object - description: 'All fields optional - only include fields you want to update. - Validation rule: require_email_confirmation=true requires - email_enabled=true.' + description: 'All fields optional - only include fields you want to update. Validation rule: require_email_confirmation=true requires email_enabled=true.' properties: access_token_lifetime: type: integer @@ -19738,20 +19446,16 @@ components: type: integer require_email_confirmation: type: boolean - description: Require users to confirm email before sign-in. Can only be true - when email_enabled is true. + description: Require users to confirm email before sign-in. Can only be true when email_enabled is true. email_confirmation_timeout: type: integer description: Email confirmation token expiry in seconds. auto_link_verified_oauth: type: boolean - description: Link a verified OAuth identity to an existing confirmed account - with the same email instead of returning a conflict. Requires - require_email_confirmation to be true. + description: Link a verified OAuth identity to an existing confirmed account with the same email instead of returning a conflict. Requires require_email_confirmation to be true. email_enabled: type: boolean - description: Enable transactional email sending. Cannot be false while - require_email_confirmation is true. + description: Enable transactional email sending. Cannot be false while require_email_confirmation is true. email_from_address: type: string email_from_name: @@ -19767,9 +19471,7 @@ components: format: password writeOnly: true minLength: 1 - description: Replacement SMTP password. Omit this field to preserve the - configured password. The value is encrypted at rest and never - returned. + description: Replacement SMTP password. Omit this field to preserve the configured password. The value is encrypted at rest and never returned. smtp_use_tls: type: boolean email_confirmation_subject: @@ -19895,12 +19597,10 @@ components: properties: client_id: type: string - description: Supported for non-device providers. Not supported for - `provider=device`. + description: Supported for non-device providers. Not supported for `provider=device`. client_secret: type: string - description: Supported for non-device providers. Not supported for - `provider=device`. + description: Supported for non-device providers. Not supported for `provider=device`. redirect_url: type: string format: uri @@ -19966,10 +19666,7 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. - Omission preserves existing membership; new variables default to - true for legacy clients. Send false explicitly to create a - non-shared variable. + description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. value: type: string required: @@ -20061,10 +19758,7 @@ components: properties: shared: type: boolean - description: Include this name in the project's shared function variables. - Omission preserves existing membership; new variables default to - true for legacy clients. Send false explicitly to create a - non-shared variable. + description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. id: type: string format: uuid @@ -20100,8 +19794,7 @@ components: - api - system - unknown - description: What initiated the latest variable propagation sync, when one has - run. + description: What initiated the latest variable propagation sync, when one has run. created_at: type: string format: date-time @@ -20301,8 +19994,7 @@ components: - status DeploymentProgress: type: object - description: Normalized live progress derived from the deployment workflow and - build phases. + description: Normalized live progress derived from the deployment workflow and build phases. properties: current_phase: type: string @@ -20346,8 +20038,7 @@ components: ids: type: array maxItems: 25 - description: Optional deployment identifiers. Omit or send an empty array to - include every deployment for the selected resources. + description: Optional deployment identifiers. Omit or send an empty array to include every deployment for the selected resources. items: type: string format: uuid @@ -20364,8 +20055,7 @@ components: ids: type: array maxItems: 25 - description: Optional function identifiers. Omit or send an empty array to - include every function in the project. + description: Optional function identifiers. Omit or send an empty array to include every function in the project. items: type: string format: uuid @@ -20386,8 +20076,7 @@ components: ids: type: array maxItems: 25 - description: Optional frontend identifiers. Omit or send an empty array to - include every frontend in the project. + description: Optional frontend identifiers. Omit or send an empty array to include every frontend in the project. items: type: string format: uuid @@ -20397,8 +20086,7 @@ components: - type LogDatabaseRequestResource: type: object - description: Database runtime log resource selector. Deployment logs are not - supported for databases. + description: Database runtime log resource selector. Deployment logs are not supported for databases. additionalProperties: false properties: type: @@ -20409,8 +20097,7 @@ components: ids: type: array maxItems: 25 - description: Optional database identifiers. Omit or send an empty array to - include every database in the project. + description: Optional database identifiers. Omit or send an empty array to include every database in the project. items: type: string format: uuid @@ -20593,15 +20280,13 @@ components: properties: root_id: type: string - description: Element id the runtime script renders into. Markup carrying it opts - into the theme-shell contract. + description: Element id the runtime script renders into. Markup carrying it opts into the theme-shell contract. script: type: string description: The runtime script injected into the rendered page. mock_prelude: type: string - description: Preview harness that supplies request params and stubs the - hosted-auth API. Never served on a real page. + description: Preview harness that supplies request params and stubs the hosted-auth API. Never served on a real page. AuthPageAppearanceDefaults: type: object required: diff --git a/package.json b/package.json index 7afb9870..9bdada90 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "lint": "pnpm build && eslint . --max-warnings=0 && pnpm format:check", "lint:fix": "pnpm build && eslint . --fix && pnpm format", "test": "pnpm build && jest", - "test:types": "tsc --noEmit -p tsconfig.json && tsc --ignoreConfig --noEmit --allowImportingTsExtensions --strictNullChecks --skipLibCheck false --moduleResolution bundler --module esnext --target es2022 src/index.d.ts src/durable.ts src/durable-runtime-peer.d.ts test/types/auth-contract.ts test/types/database-contract.ts test/types/durable-contract.ts test/types/openapi-contract.ts", + "test:types": "tsc --noEmit -p tsconfig.json && tsc --ignoreConfig --noEmit --allowImportingTsExtensions --strictNullChecks --skipLibCheck false --moduleResolution bundler --module esnext --target es2022 src/index.d.ts src/durable.ts src/durable-runtime-peer.d.ts test/types/auth-contract.ts test/types/database-contract.ts test/types/durable-contract.ts test/types/openapi-contract.ts test/types/sandbox-contract.ts", "test:package": "pnpm pack --out reports/volcano-sdk.tgz && publint reports/volcano-sdk.tgz --strict && attw reports/volcano-sdk.tgz", "test:watch": "pnpm build && jest --watch", "test:coverage": "pnpm build && jest --coverage", diff --git a/src/index.d.ts b/src/index.d.ts index 0cbfaccb..2f582d27 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1173,15 +1173,17 @@ export interface SandboxRequestOptions { requestId?: string; signal?: AbortSignal; } -export interface SandboxCreateOptions extends SandboxRequestOptions { +export interface SandboxSelectionOptions extends SandboxRequestOptions { preset?: 'python3.12' | 'node22'; sandboxId?: string; region: string; memoryMB?: 1024 | 2048; +} +export interface SandboxCreateOptions extends SandboxSelectionOptions { maxDurationSeconds?: number; idleTimeoutSeconds?: number; } -export interface SandboxExecOptions extends SandboxCreateOptions { +export interface SandboxExecOptions extends SandboxSelectionOptions { timeoutSeconds?: number; environment?: Record; } diff --git a/test/types/sandbox-contract.ts b/test/types/sandbox-contract.ts new file mode 100644 index 00000000..7095bb75 --- /dev/null +++ b/test/types/sandbox-contract.ts @@ -0,0 +1,26 @@ +import type { SandboxCreateOptions, SandboxExecOptions, SandboxSession } from '../../src/index.js'; + +const sessionOptions: SandboxCreateOptions = { + preset: 'python3.12', + region: 'aws-us-east-1', + maxDurationSeconds: 300, + idleTimeoutSeconds: 60, +}; +const executionOptions: SandboxExecOptions = { + preset: 'python3.12', + region: 'aws-us-east-1', + timeoutSeconds: 30, +}; +const invalidExecutionLifetime: SandboxExecOptions = { + region: 'aws-us-east-1', + // @ts-expect-error Session lifetime does not apply to one-shot execution. + maxDurationSeconds: 300, +}; +const invalidExecutionIdle: SandboxExecOptions = { + region: 'aws-us-east-1', + // @ts-expect-error Idle timeout does not apply to one-shot execution. + idleTimeoutSeconds: 60, +}; +declare const session: SandboxSession; +const disposable: AsyncDisposable = session; +void [sessionOptions, executionOptions, invalidExecutionLifetime, invalidExecutionIdle, disposable]; From ba9fc6d5e0c3f522d0464be7bed98801b5e74ea0 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Thu, 24 Sep 2026 03:29:38 -0700 Subject: [PATCH 3/9] test(sandboxes): cover lifecycle and malformed responses --- __tests__/sandboxes.test.ts | 205 ++++++++++++++++++++++++++++++++++++ src/sandbox-wire.ts | 5 +- 2 files changed, 207 insertions(+), 3 deletions(-) diff --git a/__tests__/sandboxes.test.ts b/__tests__/sandboxes.test.ts index d866c089..55d00258 100644 --- a/__tests__/sandboxes.test.ts +++ b/__tests__/sandboxes.test.ts @@ -1,6 +1,7 @@ /** @jest-environment ./__tests__/node-environment.cjs */ import { afterEach, describe, expect, jest, test } from '@jest/globals'; import { VolcanoAuth } from '../src/index.js'; +import { requestOptions, sandboxResult } from '../src/sandbox-request.ts'; const projectId = '11111111-1111-4111-8111-111111111111'; const sessionId = '22222222-2222-4222-8222-222222222222'; @@ -178,3 +179,207 @@ test('rejects invalid selectors before issuing a request', async () => { expect(result.error).toBeInstanceOf(TypeError); expect(fetchMock).not.toHaveBeenCalled(); }); + +test('lists preset sizes and regions', async () => { + globalThis.fetch = jest + .fn() + .mockResolvedValue( + Response.json({ data: [{ id: 'node22', memory_mb: 1024, regions: ['aws-us-east-1'] }] }), + ); + const observed1 = await client().sandboxes.presets(); + expect(observed1).toEqual({ + data: [{ id: 'node22', memoryMB: 1024, regions: ['aws-us-east-1'] }], + error: null, + }); +}); + +test.each([ + null, + { data: null }, + { data: [{ regions: null }] }, + { data: [{ id: 2, memory_mb: 1024, regions: [] }] }, + { data: [{ id: 'node22', memory_mb: '1024', regions: [] }] }, +])('rejects malformed preset responses: %j', async (value) => { + globalThis.fetch = jest.fn().mockResolvedValue(Response.json(value)); + const observed2 = await client().sandboxes.presets(); + expect(observed2.error).toBeInstanceOf(TypeError); +}); + +test('selects a template and forwards command limits and cancellation', async () => { + const fetchMock = jest.fn().mockResolvedValue(Response.json(snapshot)); + globalThis.fetch = fetchMock; + const signal = new AbortController().signal; + const result = await client().sandboxes.create(projectId, { + sandboxId: requestId, + region: 'aws-us-east-1', + memoryMB: 2048, + signal, + }); + expect(result.error).toBeNull(); + expect(bodyJson(fetchMock.mock.calls[0])).toEqual({ + sandbox_id: requestId, + region: 'aws-us-east-1', + memory_mb: 2048, + }); + expect(fetchMock.mock.calls[0]?.[1]?.signal?.aborted).toBe(false); + if (result.data === null) { + throw result.error; + } + fetchMock.mockResolvedValue( + Response.json({ + stdout: 'ok', + stderr: '', + exit_code: 0, + timed_out: false, + stdout_truncated: true, + stderr_truncated: true, + }), + ); + const observed3 = await result.data.exec('echo ok', { + timeoutSeconds: 5, + environment: { A: 'b' }, + }); + expect(observed3.data).toEqual({ + stdout: 'ok', + stderr: '', + exitCode: 0, + timedOut: false, + stdoutTruncated: true, + stderrTruncated: true, + }); + expect(bodyJson(fetchMock.mock.calls[1])).toEqual({ + command: 'echo ok', + timeout_seconds: 5, + environment: { A: 'b' }, + }); +}); + +test('refreshes and resumes sessions and refuses changed identity', async () => { + const fetchMock = jest.fn().mockResolvedValue(Response.json(snapshot)); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.get(sessionId); + if (result.data === null) { + throw result.error; + } + fetchMock.mockResolvedValueOnce(Response.json({ ...snapshot, state: 'suspended' })); + const observed4 = await result.data.refresh(); + expect(observed4.data).toMatchObject({ state: 'suspended' }); + fetchMock.mockResolvedValueOnce(Response.json(snapshot)); + const observed5 = await result.data.resume(); + expect(observed5.data).toMatchObject({ state: 'running' }); + expect(fetchMock.mock.calls[2]?.[0]).toBe( + `https://api.test.com/sandbox-sessions/${sessionId}/resume`, + ); + fetchMock.mockResolvedValueOnce(Response.json({ ...snapshot, id: requestId })); + const observed6 = await result.data.refresh(); + expect(observed6.error?.message).toBe('Sandbox session identity changed'); +}); + +test('grants and revokes access for one auth user', async () => { + const fetchMock = jest + .fn() + .mockImplementation(() => Promise.resolve(new Response(null, { status: 204 }))); + globalThis.fetch = fetchMock; + const api = client().sandboxes; + const observed7 = await api.grant(sessionId, requestId, snapshot.expires_at); + expect(observed7.error).toBeNull(); + expect(bodyJson(fetchMock.mock.calls[0])).toEqual({ expires_at: snapshot.expires_at }); + const observed8 = await api.revoke(sessionId, requestId); + expect(observed8.error).toBeNull(); + expect(fetchMock.mock.calls[1]?.[1]?.method).toBe('DELETE'); + expect(fetchMock.mock.calls[1]?.[0]).toContain(requestId); +}); + +test('rejects invalid IDs before sending credentials', async () => { + const fetchMock = jest.fn(); + globalThis.fetch = fetchMock; + const observed9 = await client().sandboxes.get('../other'); + expect(observed9.error).toBeInstanceOf(TypeError); + expect(fetchMock).not.toHaveBeenCalled(); +}); + +test.each(['future-state', null])('rejects invalid session state %j', async (state) => { + globalThis.fetch = jest + .fn() + .mockResolvedValue(Response.json({ ...snapshot, state })); + const observed10 = await client().sandboxes.get(sessionId); + expect(observed10.error).toBeInstanceOf(TypeError); +}); + +test('disposal of terminated sessions makes no request', async () => { + const fetchMock = jest + .fn() + .mockResolvedValue(Response.json({ ...snapshot, state: 'terminated' })); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.get(sessionId); + if (result.data === null) { + throw result.error; + } + await result.data[Symbol.asyncDispose](); + expect(fetchMock).toHaveBeenCalledTimes(1); +}); + +test('disposal propagates failures and large file writes fail locally', async () => { + const fetchMock = jest.fn().mockResolvedValue(Response.json(snapshot)); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.get(sessionId); + if (result.data === null) { + throw result.error; + } + const observed11 = await result.data.files.write( + '/workspace/large', + new Uint8Array(8 * 1024 * 1024 + 1), + ); + expect(observed11.error).toBeInstanceOf(RangeError); + expect(fetchMock).toHaveBeenCalledTimes(1); + fetchMock.mockResolvedValue(Response.json({ error: 'unavailable' }, { status: 503 })); + await expect(result.data[Symbol.asyncDispose]()).rejects.toMatchObject({ status: 503 }); +}); + +test('rejects malformed command flags', async () => { + globalThis.fetch = jest + .fn() + .mockResolvedValue(Response.json({ stdout: '', stderr: '', exit_code: 0, timed_out: 'false' })); + const observed12 = await client().sandboxes.exec(projectId, 'true', { + preset: 'node22', + region: 'aws-us-east-1', + }); + expect(observed12.error).toBeInstanceOf(TypeError); +}); + +test('normalizes non-Error failures without exposing arbitrary values', async () => { + const observed13 = await sandboxResult( + jest.fn<() => Promise>().mockRejectedValue('secret'), + ); + expect(observed13).toEqual({ data: null, error: new Error('Sandbox request failed') }); +}); +test('default request options contain no mutation headers', () => { + const generated = jest + .fn<(mode: 'session', headers?: Record) => object>() + .mockReturnValue({}); + requestOptions({ _completeOAuthExchange: () => Promise.resolve(), _generatedOptions: generated }); + expect(generated).toHaveBeenCalledWith('session', {}); +}); +test('session commands use default execution options', async () => { + const fetchMock = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce( + Response.json({ + stdout: '', + stderr: '', + exit_code: 0, + timed_out: false, + stdout_truncated: false, + stderr_truncated: false, + }), + ); + globalThis.fetch = fetchMock; + const result = await client().sandboxes.get(sessionId); + if (result.data === null) { + throw result.error; + } + const observed14 = await result.data.exec('true'); + expect(observed14.error).toBeNull(); + expect(bodyJson(fetchMock.mock.calls[1])).toEqual({ command: 'true' }); +}); diff --git a/src/sandbox-wire.ts b/src/sandbox-wire.ts index 27144bdc..6c979d83 100644 --- a/src/sandbox-wire.ts +++ b/src/sandbox-wire.ts @@ -107,9 +107,8 @@ export function presetsResult(value: unknown): SandboxPreset[] { }); } export function decodeBytes(value: unknown): Uint8Array { - return Uint8Array.from( - atob(text(record(value)['data'])), - (character) => character.codePointAt(0) ?? 0, + return Uint8Array.from(atob(text(record(value)['data'])), (character) => + Number(character.codePointAt(0)), ); } export function encodeBytes(data: Uint8Array): string { From c8285ad1c5bc4dd1af43a0a542438598dba674e3 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Thu, 24 Sep 2026 03:42:49 -0700 Subject: [PATCH 4/9] test(sandboxes): enforce wire validation and mutation coverage --- __tests__/sandbox-wire.test.ts | 133 +++++++++++++++++++++++++++++++++ __tests__/sandboxes.test.ts | 17 +++++ src/sandbox-wire.ts | 20 ++++- src/sandboxes.ts | 12 +-- 4 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 __tests__/sandbox-wire.test.ts diff --git a/__tests__/sandbox-wire.test.ts b/__tests__/sandbox-wire.test.ts new file mode 100644 index 00000000..3ee8a951 --- /dev/null +++ b/__tests__/sandbox-wire.test.ts @@ -0,0 +1,133 @@ +/** @jest-environment ./__tests__/node-environment.cjs */ +import { describe, expect, jest, test } from '@jest/globals'; +import { commandRequest, pathId, requestOptions } from '../src/sandbox-request.ts'; +import { + commandResult, + createRequest, + encodeBytes, + presetsResult, + record, + sessionRequest, + sessionState, + text, +} from '../src/sandbox-wire.ts'; + +const command = { + stdout: '', + stderr: '', + exit_code: 0, + timed_out: false, + stdout_truncated: false, + stderr_truncated: false, +}; + +describe('Sandbox wire validation', () => { + test.each([null, undefined, 0, 'text', [], true])('rejects non-record %j', (value) => { + expect(() => record(value)).toThrow('Invalid Sandbox response'); + }); + test('accepts records and text without coercion', () => { + const value = { field: 'value' }; + expect(record(value)).toBe(value); + expect(text('')).toBe(''); + expect(() => text(1)).toThrow('Invalid Sandbox text field'); + }); + test.each([Number.NaN, Number.POSITIVE_INFINITY, '0'])( + 'rejects invalid numeric fields %j', + (exit_code) => { + expect(() => commandResult({ ...command, exit_code })).toThrow( + 'Invalid Sandbox numeric field', + ); + }, + ); + test('rejects non-boolean flags', () => { + expect(() => commandResult({ ...command, timed_out: 'false' })).toThrow('Invalid Sandbox flag'); + }); + test.each([ + 'starting', + 'running', + 'suspending', + 'suspended', + 'resuming', + 'terminating', + 'terminated', + 'unknown', + ])('preserves session state %s', (state) => { + expect(sessionState(state)).toBe(state); + }); + test('rejects unknown session state', () => { + expect(() => sessionState('future')).toThrow('Invalid Sandbox state'); + }); + test.each([ + { region: 'aws-us-east-1' }, + { region: 'aws-us-east-1', preset: 'node22' as const, sandboxId: 'template' }, + ])('requires exactly one selector %j', (options) => { + expect(() => createRequest(options)).toThrow('Choose exactly one preset or sandboxId'); + }); + test.each([{}, { data: {} }, { data: [{ id: 'node22', memory_mb: 1024, regions: {} }] }])( + 'rejects malformed catalog %j', + (value) => { + const message = + 'data' in value && Array.isArray(value.data) + ? 'Invalid Sandbox regions' + : 'Invalid Sandbox preset catalog'; + expect(() => presetsResult(value)).toThrow(message); + }, + ); + test('accepts the file limit and rejects the next byte', () => { + const limit = 8 * 1024 * 1024; + expect(encodeBytes(new Uint8Array(limit))).toHaveLength(Math.ceil(limit / 3) * 4); + expect(() => encodeBytes(new Uint8Array(limit + 1))).toThrow( + 'Sandbox files are limited to 8 MiB', + ); + }); +}); + +test.each([ + 'prefix11111111-1111-4111-8111-111111111111', + '11111111-1111-4111-8111-111111111111suffix', +])('rejects a UUID embedded in %s', (id) => { + expect(() => pathId(id)).toThrow('A Sandbox resource ID must be a UUID'); +}); + +test('forwards the caller abort signal and leaves absent signals untouched', () => { + const generated = jest + .fn<(mode: 'session', headers?: Record) => object>() + .mockImplementation(() => ({})); + const client = { _completeOAuthExchange: () => Promise.resolve(), _generatedOptions: generated }; + const signal = new AbortController().signal; + expect(requestOptions(client, { signal }).signal).toBe(signal); + expect(requestOptions(client)).not.toHaveProperty('signal'); +}); + +test('omits absent optional wire properties', () => { + expect(createRequest({ preset: 'node22', region: 'aws-us-east-1' })).toStrictEqual({ + preset: 'node22', + region: 'aws-us-east-1', + }); + expect( + createRequest({ sandboxId: 'template', region: 'aws-us-east-1', memoryMB: 2048 }), + ).toStrictEqual({ sandbox_id: 'template', region: 'aws-us-east-1', memory_mb: 2048 }); + expect(commandRequest('true', {})).toStrictEqual({ command: 'true' }); + expect(commandRequest('true', { timeoutSeconds: 5, environment: { A: 'b' } })).toStrictEqual({ + command: 'true', + timeout_seconds: 5, + environment: { A: 'b' }, + }); + expect(sessionRequest({ preset: 'node22', region: 'aws-us-east-1' })).toStrictEqual({ + preset: 'node22', + region: 'aws-us-east-1', + }); + expect( + sessionRequest({ + preset: 'node22', + region: 'aws-us-east-1', + maxDurationSeconds: 300, + idleTimeoutSeconds: 60, + }), + ).toStrictEqual({ + preset: 'node22', + region: 'aws-us-east-1', + max_duration_seconds: 300, + idle_timeout_seconds: 60, + }); +}); diff --git a/__tests__/sandboxes.test.ts b/__tests__/sandboxes.test.ts index 55d00258..236555ae 100644 --- a/__tests__/sandboxes.test.ts +++ b/__tests__/sandboxes.test.ts @@ -69,6 +69,11 @@ describe('Sandboxes facade', () => { const request = fetchMock.mock.calls[0]; expect(request?.[0]).toBe(`https://api.test.com/projects/${projectId}/sandbox-executions`); expect(new Headers(request?.[1]?.headers).get('Idempotency-Key')).toBe(requestId); + expect(bodyJson(request)).toEqual({ + command: 'exit 7', + preset: 'python3.12', + region: 'aws-us-east-1', + }); }); test('returns a session that refreshes state and controls the same id', async () => { const fetchMock = jest @@ -89,6 +94,13 @@ describe('Sandboxes facade', () => { throw result.error; } expect(handle.id).toBe(sessionId); + expect(bodyJson(fetchMock.mock.calls[0])).toEqual({ + preset: 'node22', + region: 'aws-us-east-1', + }); + expect(new Headers(fetchMock.mock.calls[0]?.[1]?.headers).get('Idempotency-Key')).toBe( + requestId, + ); const suspended = await handle.suspend(); expect(suspended.error).toBeNull(); expect(handle.state).toBe('suspended'); @@ -164,7 +176,9 @@ test('preserves binary files and returns a scoped HTTP credential', async () => }); const read = await result.data.files.read('/workspace/data'); expect(read.data).toEqual(bytes); + expect(bodyJson(fetchMock.mock.calls[2])).toEqual({ path: '/workspace/data' }); const access = await result.data.access(8080); + expect(bodyJson(fetchMock.mock.calls[3])).toEqual({ port: 8080 }); expect(access.data).toEqual({ url: 'https://session.example', token: 'scoped-token', @@ -381,5 +395,8 @@ test('session commands use default execution options', async () => { } const observed14 = await result.data.exec('true'); expect(observed14.error).toBeNull(); + expect(new Headers(fetchMock.mock.calls[1]?.[1]?.headers).get('Idempotency-Key')).toMatch( + /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i, + ); expect(bodyJson(fetchMock.mock.calls[1])).toEqual({ command: 'true' }); }); diff --git a/src/sandbox-wire.ts b/src/sandbox-wire.ts index 6c979d83..50c10363 100644 --- a/src/sandbox-wire.ts +++ b/src/sandbox-wire.ts @@ -20,10 +20,10 @@ export function text(value: unknown): string { return value; } function number(value: unknown): number { - if (typeof value !== 'number' || !Number.isFinite(value)) { + if (!Number.isFinite(value)) { throw new TypeError('Invalid Sandbox numeric field'); } - return value; + return Number(value); } function flag(value: unknown): boolean { if (typeof value !== 'boolean') { @@ -123,5 +123,19 @@ export function encodeBytes(data: Uint8Array): string { } function isRecord(value: unknown): value is Record { - return typeof value === 'object' && value !== null; + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +export function sessionRequest( + options: SandboxCreateOptions, +): import('./generated/model/createSandboxSessionRequest.ts').CreateSandboxSessionRequest { + return { + ...createRequest(options), + ...(options.maxDurationSeconds === undefined + ? {} + : { max_duration_seconds: options.maxDurationSeconds }), + ...(options.idleTimeoutSeconds === undefined + ? {} + : { idle_timeout_seconds: options.idleTimeoutSeconds }), + }; } diff --git a/src/sandboxes.ts b/src/sandboxes.ts index 7a2cdd1f..76c8fb36 100644 --- a/src/sandboxes.ts +++ b/src/sandboxes.ts @@ -21,7 +21,7 @@ import { sandboxResult, } from './sandbox-request.ts'; import { SandboxSessionHandle } from './sandbox-session.ts'; -import { createRequest, executionResult, presetsResult } from './sandbox-wire.ts'; +import { createRequest, executionResult, presetsResult, sessionRequest } from './sandbox-wire.ts'; export class SandboxesApi implements Sandboxes { constructor(private readonly client: SandboxClient) {} @@ -48,15 +48,7 @@ export class SandboxesApi implements Sandboxes { create(projectId: string, options: SandboxCreateOptions): ReturnType { return sandboxResult(async () => { await this.client._completeOAuthExchange(); - const body = { - ...createRequest(options), - ...(options.maxDurationSeconds === undefined - ? {} - : { max_duration_seconds: options.maxDurationSeconds }), - ...(options.idleTimeoutSeconds === undefined - ? {} - : { idle_timeout_seconds: options.idleTimeoutSeconds }), - }; + const body = sessionRequest(options); const response = await createSandboxSession( pathId(projectId), body, From 1ff8f0e8bd63c54843a155b1429334a87f3aeee8 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Fri, 2 Oct 2026 14:08:16 -0700 Subject: [PATCH 5/9] fix(deps): patch URI and Markdown tooling advisories --- pnpm-lock.yaml | 20 +++++++++++--------- pnpm-workspace.yaml | 3 +++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7aeb3ffa..02cadac6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,8 @@ overrides: '@cucumber/messages>uuid': ^11.1.1 js-yaml@>=4.0.0 <4.3.2: ^4.3.2 typed-rest-client>qs: 6.16.0 + fast-uri@>=3.0.0 <3.1.8: ^3.1.8 + markdown-it@>=14.0.0 <14.3.1: ^14.3.1 importers: .: @@ -4871,10 +4873,10 @@ packages: integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==, } - fast-uri@3.1.6: + fast-uri@3.1.8: resolution: { - integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==, + integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==, } fast-wrap-ansi@0.2.2: @@ -5957,10 +5959,10 @@ packages: integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, } - markdown-it@14.3.0: + markdown-it@14.3.2: resolution: { - integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==, + integrity: sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==, } hasBin: true @@ -10135,14 +10137,14 @@ snapshots: ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.6 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.6 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -10866,7 +10868,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.6: {} + fast-uri@3.1.8: {} fast-wrap-ansi@0.2.2: dependencies: @@ -11650,7 +11652,7 @@ snapshots: dependencies: tmpl: 1.0.5 - markdown-it@14.3.0: + markdown-it@14.3.2: dependencies: argparse: 2.0.1 entities: 4.5.0 @@ -12502,7 +12504,7 @@ snapshots: dependencies: '@gerrit0/mini-shiki': 3.23.0 lunr: 2.3.9 - markdown-it: 14.3.0 + markdown-it: 14.3.2 minimatch: 10.2.6 typescript: 6.0.3 yaml: 2.9.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 49d4f8b6..5dd9e2cd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,6 +17,9 @@ overrides: 'js-yaml@>=4.0.0 <4.3.2': ^4.3.2 # Stryker's HTTP client resolves a qs release with DoS advisories. 'typed-rest-client>qs': 6.16.0 + # Patch URI parsing and Markdown denial-of-service advisories in tooling. + 'fast-uri@>=3.0.0 <3.1.8': ^3.1.8 + 'markdown-it@>=14.0.0 <14.3.1': ^14.3.1 allowBuilds: protobufjs: true From f4385356fa46927dcaaf36afb6622087f19caa47 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Mon, 5 Oct 2026 09:24:04 -0700 Subject: [PATCH 6/9] fix(sandboxes): align catalog auth and replay options --- __tests__/sandboxes.test.ts | 20 ++++++++++++---- docs/sandboxes.md | 4 ++-- src/sandbox-request.ts | 18 +++++++-------- src/sandbox-session.ts | 2 +- src/sandboxes.ts | 8 ++++--- src/sdk-public-types.ts | 8 ++++--- test/fixtures/published-api.d.ts | 8 ++++--- test/types/sandbox-contract.ts | 39 +++++++++++++++++++++++++++++++- 8 files changed, 81 insertions(+), 26 deletions(-) diff --git a/__tests__/sandboxes.test.ts b/__tests__/sandboxes.test.ts index a79b3f33..01f9c6b8 100644 --- a/__tests__/sandboxes.test.ts +++ b/__tests__/sandboxes.test.ts @@ -194,13 +194,25 @@ test('rejects invalid selectors before issuing a request', async () => { expect(fetchMock).not.toHaveBeenCalled(); }); -test('lists preset sizes and regions', async () => { - globalThis.fetch = jest +test('lists public presets with only anonymous credentials and cancellation', async () => { + const fetchMock = jest .fn() .mockResolvedValue( Response.json({ data: [{ id: 'node22', memory_mb: 1024, regions: ['aws-us-east-1'] }] }), ); - const observed1 = await client().sandboxes.presets(); + globalThis.fetch = fetchMock; + const signal = new AbortController().signal; + const anonymousClient = new VolcanoAuth({ + apiUrl: 'https://api.test.com', + anonKey: 'ak-project', + }); + const observed1 = await anonymousClient.sandboxes.presets({ signal }); + const request = requestCall(fetchMock.mock.calls, 0); + expect(request[0]).toBe('https://api.test.com/sandboxes/presets'); + expect(new Headers(request[1]?.headers).get('Authorization')).toBe('Bearer ak-project'); + expect(new Headers(request[1]?.headers).has('Idempotency-Key')).toBe(false); + expect(request[1]?.signal?.aborted).toBe(false); + expect(fetchMock).toHaveBeenCalledTimes(1); expect(observed1).toEqual({ data: [{ id: 'node22', memoryMB: 1024, regions: ['aws-us-east-1'] }], error: null, @@ -369,7 +381,7 @@ test('normalizes non-Error failures without exposing arbitrary values', async () }); test('default request options contain no mutation headers', () => { const generated = jest - .fn<(mode: 'session', headers?: Record) => object>() + .fn<(mode: 'anon' | 'session', headers?: Record) => object>() .mockReturnValue({}); requestOptions({ _completeOAuthExchange: () => Promise.resolve(), _generatedOptions: generated }); expect(generated).toHaveBeenCalledWith('session', {}); diff --git a/docs/sandboxes.md b/docs/sandboxes.md index 4d9cf722..68e33fb6 100644 --- a/docs/sandboxes.md +++ b/docs/sandboxes.md @@ -23,7 +23,7 @@ if (error) throw error; console.log(data.stdout, data.exitCode); ``` -Keep service keys on your backend. Creating and managing sessions requires a platform user or service key. An authenticated project user can access only sessions explicitly granted to that user. Anonymous keys cannot access Sandboxes. Availability depends on your platform's Sandbox rollout. +Keep service keys on your backend. Creating and managing sessions requires a platform user or service key. An authenticated project user can access only sessions explicitly granted to that user. Anonymous keys can list the public preset catalog with `sandboxes.presets()`, but cannot create or access sessions. Availability depends on your platform's Sandbox rollout. A nonzero command exit is returned as `data.exitCode`; it is not a platform error. All methods return `{ data, error }`. Check `error` before using `data`. @@ -88,7 +88,7 @@ The token is bound to this session and port. Do not append it to URLs. See [Sand ## Retry safely -Pass a UUID `requestId` to creation or command execution. Retry the same intent with the same ID after a network failure. Do not reuse the ID for a different command. The SDK does not automatically replay failed commands. +Pass a UUID `requestId` to `sandboxes.create()`, `sandboxes.exec()`, or `session.exec()`. Other operations accept cancellation through `signal` but do not accept `requestId`. Retry the same intent with the same ID after a network failure. Do not reuse the ID for a different command. The SDK does not automatically replay failed commands. Pass `signal` to cancel waiting. Cancellation does not prove the remote command stopped. Choose a client `timeout` longer than the command timeout plus provisioning time; use sessions for long-running work. diff --git a/src/sandbox-request.ts b/src/sandbox-request.ts index 2cdae7e6..096cfd33 100644 --- a/src/sandbox-request.ts +++ b/src/sandbox-request.ts @@ -1,10 +1,10 @@ import type { executeSandbox } from './generated/client.ts'; -import type { SandboxCommandOptions, SandboxRequestOptions, SandboxResult } from './index.ts'; +import type { SandboxCommandOptions, SandboxReplayOptions, SandboxResult } from './index.ts'; type TransportOptions = NonNullable[2]>; export interface SandboxClient { _completeOAuthExchange(): Promise; - _generatedOptions(mode: 'session', headers?: Record): TransportOptions; + _generatedOptions(mode: 'anon' | 'session', headers?: Record): TransportOptions; } export function pathId(value: string): string { if (!/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i.test(value)) { @@ -14,19 +14,19 @@ export function pathId(value: string): string { } export function requestOptions( client: SandboxClient, - options: SandboxRequestOptions = {}, - mutating = false, + options: SandboxReplayOptions = {}, + mode: 'anon' | 'session' | 'replayable' = 'session', ): TransportOptions { - const result = client._generatedOptions('session', mutationHeaders(options, mutating)); + const result = client._generatedOptions( + mode === 'anon' ? 'anon' : 'session', + mutationHeaders(options, mode === 'replayable'), + ); if (options.signal !== undefined) { result.signal = options.signal; } return result; } -function mutationHeaders( - options: SandboxRequestOptions, - mutating: boolean, -): Record { +function mutationHeaders(options: SandboxReplayOptions, mutating: boolean): Record { if (!mutating) { return {}; } diff --git a/src/sandbox-session.ts b/src/sandbox-session.ts index 16ab8826..595c28d2 100644 --- a/src/sandbox-session.ts +++ b/src/sandbox-session.ts @@ -76,7 +76,7 @@ export class SandboxSessionHandle implements SandboxSession { executeSandboxSession( pathId(this.id), commandRequest(command, options), - requestOptions(this.client, options, true), + requestOptions(this.client, options, 'replayable'), ), ), ), diff --git a/src/sandboxes.ts b/src/sandboxes.ts index 1abe36ee..758b02a6 100644 --- a/src/sandboxes.ts +++ b/src/sandboxes.ts @@ -27,7 +27,9 @@ export class SandboxesApi implements Sandboxes { constructor(private readonly client: SandboxClient) {} presets(options: SandboxRequestOptions = {}): ReturnType { return sandboxResult(async () => - presetsResult(await responseData(listSandboxPresets(requestOptions(this.client, options)))), + presetsResult( + await responseData(listSandboxPresets(requestOptions(this.client, options, 'anon'))), + ), ); } exec( @@ -40,7 +42,7 @@ export class SandboxesApi implements Sandboxes { const response = await executeSandbox( pathId(projectId), { ...createRequest(options), ...commandRequest(command, options) }, - requestOptions(this.client, options, true), + requestOptions(this.client, options, 'replayable'), ); return executionResult(response.data); }); @@ -52,7 +54,7 @@ export class SandboxesApi implements Sandboxes { const response = await createSandboxSession( pathId(projectId), body, - requestOptions(this.client, options, true), + requestOptions(this.client, options, 'replayable'), ); return new SandboxSessionHandle(this.client, response.data); }); diff --git a/src/sdk-public-types.ts b/src/sdk-public-types.ts index 83be2863..22b0e8a5 100644 --- a/src/sdk-public-types.ts +++ b/src/sdk-public-types.ts @@ -1067,10 +1067,12 @@ export interface RealtimeModule { export type SandboxResult = { data: T; error: null } | { data: null; error: Error }; export interface SandboxRequestOptions { - requestId?: string; signal?: AbortSignal; } -export interface SandboxSelectionOptions extends SandboxRequestOptions { +export interface SandboxReplayOptions extends SandboxRequestOptions { + requestId?: string; +} +export interface SandboxSelectionOptions extends SandboxReplayOptions { preset?: 'python3.12' | 'node22'; sandboxId?: string; region: string; @@ -1084,7 +1086,7 @@ export interface SandboxExecOptions extends SandboxSelectionOptions { timeoutSeconds?: number; environment?: Record; } -export interface SandboxCommandOptions extends SandboxRequestOptions { +export interface SandboxCommandOptions extends SandboxReplayOptions { timeoutSeconds?: number; environment?: Record; } diff --git a/test/fixtures/published-api.d.ts b/test/fixtures/published-api.d.ts index 6d098ff2..210145d1 100644 --- a/test/fixtures/published-api.d.ts +++ b/test/fixtures/published-api.d.ts @@ -714,10 +714,12 @@ export { export type SandboxResult = { data: T; error: null } | { data: null; error: Error }; export interface SandboxRequestOptions { - requestId?: string; signal?: AbortSignal; } -export interface SandboxSelectionOptions extends SandboxRequestOptions { +export interface SandboxReplayOptions extends SandboxRequestOptions { + requestId?: string; +} +export interface SandboxSelectionOptions extends SandboxReplayOptions { preset?: 'python3.12' | 'node22'; sandboxId?: string; region: string; @@ -731,7 +733,7 @@ export interface SandboxExecOptions extends SandboxSelectionOptions { timeoutSeconds?: number; environment?: Record; } -export interface SandboxCommandOptions extends SandboxRequestOptions { +export interface SandboxCommandOptions extends SandboxReplayOptions { timeoutSeconds?: number; environment?: Record; } diff --git a/test/types/sandbox-contract.ts b/test/types/sandbox-contract.ts index fe174ccf..ad9d1dae 100644 --- a/test/types/sandbox-contract.ts +++ b/test/types/sandbox-contract.ts @@ -1,4 +1,9 @@ -import type { SandboxCreateOptions, SandboxExecOptions, SandboxSession } from '../../src/index.ts'; +import type { + SandboxCreateOptions, + Sandboxes, + SandboxExecOptions, + SandboxSession, +} from '../../src/index.ts'; export const sessionOptions: SandboxCreateOptions = { preset: 'python3.12', @@ -23,3 +28,35 @@ export const invalidExecutionIdle: SandboxExecOptions = { }; declare const session: SandboxSession; export const disposable: AsyncDisposable = session; + +declare const sandboxes: Sandboxes; +const requestId = '33333333-3333-4333-8333-333333333333'; +export const replayableRequests = [ + sandboxes.create('project', { ...sessionOptions, requestId }), + sandboxes.exec('project', 'true', { ...executionOptions, requestId }), + session.exec('true', { requestId }), +]; +export const nonReplayableRequests = [ + // @ts-expect-error The public catalog does not support idempotency keys. + sandboxes.presets({ requestId }), + // @ts-expect-error Session lookup does not support idempotency keys. + sandboxes.get('session', { requestId }), + // @ts-expect-error Refresh does not support idempotency keys. + session.refresh({ requestId }), + // @ts-expect-error Suspend does not support idempotency keys. + session.suspend({ requestId }), + // @ts-expect-error Resume does not support idempotency keys. + session.resume({ requestId }), + // @ts-expect-error Termination does not support idempotency keys. + session.terminate({ requestId }), + // @ts-expect-error Access credentials do not support idempotency keys. + session.access(8080, { requestId }), + // @ts-expect-error File reads do not support idempotency keys. + session.files.read('/workspace/file', { requestId }), + // @ts-expect-error File writes do not support idempotency keys. + session.files.write('/workspace/file', new Uint8Array(), { requestId }), + // @ts-expect-error Grants do not support idempotency keys. + sandboxes.grant('session', 'user', '2026-10-06T00:00:00Z', { requestId }), + // @ts-expect-error Revocation does not support idempotency keys. + sandboxes.revoke('session', 'user', { requestId }), +]; From 13d3ec70da4486d7d95521b7a22b4dee0b04a859 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Mon, 5 Oct 2026 09:38:45 -0700 Subject: [PATCH 7/9] refactor(sandboxes): remove redundant request auth default --- src/sandbox-request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sandbox-request.ts b/src/sandbox-request.ts index 096cfd33..b48eaf55 100644 --- a/src/sandbox-request.ts +++ b/src/sandbox-request.ts @@ -15,7 +15,7 @@ export function pathId(value: string): string { export function requestOptions( client: SandboxClient, options: SandboxReplayOptions = {}, - mode: 'anon' | 'session' | 'replayable' = 'session', + mode?: 'anon' | 'session' | 'replayable', ): TransportOptions { const result = client._generatedOptions( mode === 'anon' ? 'anon' : 'session', From 6c48e5e048938fa21698cd9d85c269290eb06742 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Mon, 5 Oct 2026 10:47:44 -0700 Subject: [PATCH 8/9] fix(sandboxes): honor execution budgets and safe public contracts --- __tests__/database-query.test.ts | 10 +- __tests__/sandbox-review.test.ts | 121 ++++++++++++++++++ __tests__/sandbox-wire.test.ts | 31 ++++- __tests__/sandboxes.test.ts | 19 ++- docs/sandboxes.md | 7 +- openapi/openapi.yaml | 12 +- package.json | 2 +- scripts/quality-policy.test.mts | 3 +- src/auth-fetch-retry.ts | 3 +- src/generated-runtime/generated/client.js | 96 ++++++++++---- .../createSandboxSessionRequestPreset.js | 13 -- .../createSandboxTemplateRequestPreset.js | 13 -- .../generated/model/index.js | 3 - .../model/sandboxExecutionRequestPreset.js | 13 -- src/generated-runtime/volcano-fetch.js | 4 +- src/generated/client.ts | 96 +++++++++++--- .../model/createSandboxSessionRequest.ts | 4 +- .../createSandboxSessionRequestPreset.ts | 17 --- .../model/createSandboxTemplateRequest.ts | 4 +- .../createSandboxTemplateRequestPreset.ts | 17 --- src/generated/model/index.ts | 3 - .../model/sandboxExecutionRequest.ts | 4 +- .../model/sandboxExecutionRequestPreset.ts | 17 --- src/generated/openapi.d.ts | 12 +- src/sandbox-request.ts | 19 ++- src/sandbox-session.ts | 11 +- src/sandbox-wire.ts | 13 +- src/sandboxes.ts | 3 +- src/sdk-public-types.ts | 8 +- src/volcano-auth.ts | 8 +- src/volcano-fetch.ts | 12 +- test/fixtures/published-api.d.ts | 7 +- test/types/package-sandbox.ts | 8 ++ tsconfig.consumer-sandbox.json | 5 + 34 files changed, 428 insertions(+), 190 deletions(-) create mode 100644 __tests__/sandbox-review.test.ts delete mode 100644 src/generated-runtime/generated/model/createSandboxSessionRequestPreset.js delete mode 100644 src/generated-runtime/generated/model/createSandboxTemplateRequestPreset.js delete mode 100644 src/generated-runtime/generated/model/sandboxExecutionRequestPreset.js delete mode 100644 src/generated/model/createSandboxSessionRequestPreset.ts delete mode 100644 src/generated/model/createSandboxTemplateRequestPreset.ts delete mode 100644 src/generated/model/sandboxExecutionRequestPreset.ts create mode 100644 test/types/package-sandbox.ts create mode 100644 tsconfig.consumer-sandbox.json diff --git a/__tests__/database-query.test.ts b/__tests__/database-query.test.ts index 841ca1ef..f88b601f 100644 --- a/__tests__/database-query.test.ts +++ b/__tests__/database-query.test.ts @@ -36,7 +36,14 @@ test.each([undefined, 'application/vnd.volcano+json'])( }, ); const generatedFetch = jest - .fn<(path: string, options: RequestInit, mode: 'anon' | 'session') => Promise>() + .fn< + ( + path: string, + options: RequestInit, + mode: 'anon' | 'session', + timeoutMs?: number, + ) => Promise + >() .mockResolvedValue(fetchResponse); const client = { _generatedFetch: generatedFetch }; const date = new Date('2026-09-23T12:00:00.000Z'); @@ -65,6 +72,7 @@ test.each([undefined, 'application/vnd.volcano+json'])( headers: expect.any(Headers), }), 'session', + undefined, ); const call = generatedFetch.mock.calls[0]; expect(new Headers(call?.[1].headers).get('Content-Type')).toBe( diff --git a/__tests__/sandbox-review.test.ts b/__tests__/sandbox-review.test.ts new file mode 100644 index 00000000..7ad17576 --- /dev/null +++ b/__tests__/sandbox-review.test.ts @@ -0,0 +1,121 @@ +/** @jest-environment ./__tests__/node-environment.cjs */ +import { inspect } from 'node:util'; +import { afterEach, expect, jest, test } from '@jest/globals'; +import { VolcanoAuth } from '../src/index.ts'; + +const id = '11111111-1111-4111-8111-111111111111'; +const snapshot = { id, project_id: id, region: 'us-east-1', state: 'running', expires_at: '' }; +const command = { + stdout: '', + stderr: '', + exit_code: 0, + timed_out: false, + stdout_truncated: false, + stderr_truncated: false, + session_id: id, + region: 'us-east-1', + duration_ms: 0, +}; +const originalFetch = globalThis.fetch; +afterEach(() => { + globalThis.fetch = originalFetch; + jest.restoreAllMocks(); +}); + +test.each([ + [60_000, undefined, 180_000], + [60_000, 600, 720_000], + [900_000, 600, 900_000], +])( + 'command requests budget provisioning time: %s/%s', + async (timeout, timeoutSeconds, expected) => { + const client = new VolcanoAuth({ + apiUrl: 'https://api.test.com', + anonKey: 'anon', + accessToken: 'service', + timeout, + }); + const fetchMock = jest.fn().mockResolvedValueOnce(Response.json(snapshot)); + globalThis.fetch = fetchMock; + const session = await client.sandboxes.get(id); + if (session.data === null) { + throw session.error; + } + const timer = jest.spyOn(globalThis, 'setTimeout'); + const options = timeoutSeconds === undefined ? {} : { timeoutSeconds }; + fetchMock.mockImplementation(() => Promise.resolve(Response.json(command))); + const result = await session.data.exec('true', options); + expect(result.error).toBeNull(); + expect(timer).toHaveBeenLastCalledWith(expect.any(Function), expected); + const executed = await client.sandboxes.exec(id, 'true', { + ...options, + preset: 'node22', + region: 'us-east-1', + }); + expect(executed.error).toBeNull(); + expect(timer).toHaveBeenLastCalledWith(expect.any(Function), expected); + }, +); + +test.each(['terminating', 'terminated'])('disposal skips %s sessions', async (state) => { + const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' }); + const fetchMock = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce(Response.json({ ...snapshot, state })); + globalThis.fetch = fetchMock; + const session = await client.sandboxes.get(id); + if (session.data === null) { + throw session.error; + } + const terminated = await session.data.terminate(); + expect(terminated.error).toBeNull(); + await session.data[Symbol.asyncDispose](); + expect(fetchMock).toHaveBeenCalledTimes(2); +}); + +test('disposal accepts an already removed session', async () => { + const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' }); + globalThis.fetch = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce(Response.json({ error: 'not found' }, { status: 404 })); + const session = await client.sandboxes.get(id); + if (session.data === null) { + throw session.error; + } + await expect(session.data[Symbol.asyncDispose]()).resolves.toBeUndefined(); +}); + +test('scoped access credentials remain usable but are absent from routine logs', async () => { + const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' }); + globalThis.fetch = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockResolvedValueOnce( + Response.json({ url: 'https://session.test', token: 'private-access-token', expires_at: '' }), + ); + const session = await client.sandboxes.get(id); + if (session.data === null) { + throw session.error; + } + const access = await session.data.access(8080); + expect(access.data?.token).toBe('private-access-token'); + expect(inspect(access)).not.toContain('private-access-token'); + expect(JSON.stringify(access)).not.toContain('private-access-token'); + expect({ ...access.data }).not.toHaveProperty('token'); +}); + +test('disposal preserves network errors', async () => { + const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' }); + const failure = new Error('connection failed'); + globalThis.fetch = jest + .fn() + .mockResolvedValueOnce(Response.json(snapshot)) + .mockRejectedValueOnce(failure); + const session = await client.sandboxes.get(id); + if (session.data === null) { + throw session.error; + } + await expect(session.data[Symbol.asyncDispose]()).rejects.toBe(failure); +}); diff --git a/__tests__/sandbox-wire.test.ts b/__tests__/sandbox-wire.test.ts index 3ee8a951..fe0b62bf 100644 --- a/__tests__/sandbox-wire.test.ts +++ b/__tests__/sandbox-wire.test.ts @@ -91,9 +91,20 @@ test.each([ test('forwards the caller abort signal and leaves absent signals untouched', () => { const generated = jest - .fn<(mode: 'session', headers?: Record) => object>() + .fn< + ( + mode: 'session', + headers?: Record, + responseType?: 'blob', + timeoutMs?: number, + ) => object + >() .mockImplementation(() => ({})); - const client = { _completeOAuthExchange: () => Promise.resolve(), _generatedOptions: generated }; + const client = { + timeout: 60_000, + _completeOAuthExchange: () => Promise.resolve(), + _generatedOptions: generated, + }; const signal = new AbortController().signal; expect(requestOptions(client, { signal }).signal).toBe(signal); expect(requestOptions(client)).not.toHaveProperty('signal'); @@ -131,3 +142,19 @@ test('omits absent optional wire properties', () => { idle_timeout_seconds: 60, }); }); + +test.each([0, 1, 1023, 1025, 2047, 2049, Number.NaN])( + 'rejects unsupported memory %s before transport', + (memoryMB) => { + expect(() => createRequest({ preset: 'node22', region: 'us-east-1', memoryMB })).toThrow( + 'Sandbox memory must be 1024 or 2048 MB', + ); + }, +); +test.each([1024, 2048])('accepts catalog memory %s', (memoryMB) => { + expect(createRequest({ preset: 'catalog-preset', region: 'us-east-1', memoryMB })).toEqual({ + preset: 'catalog-preset', + region: 'us-east-1', + memory_mb: memoryMB, + }); +}); diff --git a/__tests__/sandboxes.test.ts b/__tests__/sandboxes.test.ts index 01f9c6b8..b272cef1 100644 --- a/__tests__/sandboxes.test.ts +++ b/__tests__/sandboxes.test.ts @@ -179,9 +179,9 @@ test('preserves binary files and returns a scoped HTTP credential', async () => expect(bodyJson(fetchMock.mock.calls[2])).toEqual({ path: '/workspace/data' }); const access = await result.data.access(8080); expect(bodyJson(fetchMock.mock.calls[3])).toEqual({ port: 8080 }); + expect(access.data?.token).toBe('scoped-token'); expect(access.data).toEqual({ url: 'https://session.example', - token: 'scoped-token', expiresAt: snapshot.expires_at, }); }); @@ -381,10 +381,21 @@ test('normalizes non-Error failures without exposing arbitrary values', async () }); test('default request options contain no mutation headers', () => { const generated = jest - .fn<(mode: 'anon' | 'session', headers?: Record) => object>() + .fn< + ( + mode: 'anon' | 'session', + headers?: Record, + responseType?: 'blob', + timeoutMs?: number, + ) => object + >() .mockReturnValue({}); - requestOptions({ _completeOAuthExchange: () => Promise.resolve(), _generatedOptions: generated }); - expect(generated).toHaveBeenCalledWith('session', {}); + requestOptions({ + timeout: 60_000, + _completeOAuthExchange: () => Promise.resolve(), + _generatedOptions: generated, + }); + expect(generated).toHaveBeenCalledWith('session', {}, undefined, undefined); }); test('session commands use default execution options', async () => { const fetchMock = jest diff --git a/docs/sandboxes.md b/docs/sandboxes.md index 68e33fb6..e0a53e5c 100644 --- a/docs/sandboxes.md +++ b/docs/sandboxes.md @@ -12,7 +12,6 @@ const client = new VolcanoClient({ apiUrl: process.env.VOLCANO_API_URL ?? 'https://api.volcano.dev', anonKey: process.env.VOLCANO_ANON_KEY, accessToken: process.env.VOLCANO_SERVICE_KEY, - timeout: 180_000, }); const projectId = process.env.VOLCANO_PROJECT_ID; const { data, error } = await client.sandboxes.exec(projectId, 'python -c "print(42)"', { @@ -63,7 +62,7 @@ try { } ``` -In a runtime with explicit resource management, use `await using session = created.data` after checking the creation result. Leaving the scope requests termination through `Symbol.asyncDispose`. Termination is durable and asynchronous; poll `refresh()` if you need confirmation that the session has stopped. +In a runtime with explicit resource management, use `await using session = created.data` after checking the creation result. Leaving the scope requests termination through `Symbol.asyncDispose`. Disposal skips sessions already terminating or terminated and accepts a 404 for a session already removed. Termination is durable and asynchronous; poll `refresh()` if you need confirmation that the session has stopped. `session.suspend()` and `session.resume()` request state transitions. Poll for `suspended` or `running` before the next operation. Local suspension preserves processes and files, but the container continues to reserve memory. @@ -84,12 +83,12 @@ const response = await fetch(access.data.url, { console.log(await response.text()); ``` -The token is bound to this session and port. Do not append it to URLs. See [Sandbox HTTP access](/platform/guides/sandboxes) for browser redemption and authorization. +Read the scoped credential through `access.data.token`; it is omitted from JSON serialization and routine object logging. The token is bound to this session and port. Do not append it to URLs. See [Sandbox HTTP access](/platform/guides/sandboxes) for browser redemption and authorization. ## Retry safely Pass a UUID `requestId` to `sandboxes.create()`, `sandboxes.exec()`, or `session.exec()`. Other operations accept cancellation through `signal` but do not accept `requestId`. Retry the same intent with the same ID after a network failure. Do not reuse the ID for a different command. The SDK does not automatically replay failed commands. -Pass `signal` to cancel waiting. Cancellation does not prove the remote command stopped. Choose a client `timeout` longer than the command timeout plus provisioning time; use sessions for long-running work. +Pass `signal` to cancel waiting. Cancellation does not prove the remote command stopped. Execution waits for the command timeout plus 120 seconds for provisioning, or the configured client timeout if longer. One-shot commands allow up to 60 seconds; session commands allow up to 3600 seconds. Use either `preset` or a saved template's `sandboxId`, never both. `sandboxes.presets()` lists available preset IDs, memory sizes, and regions. `sandboxes.get(sessionId)` reconnects to an existing session. Backend code can call `grant(sessionId, authUserId, expiresAt)` and `revoke(sessionId, authUserId)` to control user access. diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 3860bafb..1a04c044 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -13194,9 +13194,7 @@ components: pattern: ^[a-z][a-z0-9-]{0,62}$ preset: type: string - enum: - - python3.12 - - node22 + description: Preset ID from the available Sandbox preset catalog. memory_mb: type: integer enum: @@ -13274,9 +13272,7 @@ components: properties: preset: type: string - enum: - - python3.12 - - node22 + description: Preset ID from the available Sandbox preset catalog. sandbox_id: type: string format: uuid @@ -13338,9 +13334,7 @@ components: properties: preset: type: string - enum: - - python3.12 - - node22 + description: Preset ID from the available Sandbox preset catalog. sandbox_id: type: string format: uuid diff --git a/package.json b/package.json index b8ecba0e..1568a6b8 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "test:coverage": "pnpm build && jest --coverage", "prepublishOnly": "npm run build", "test:quickstart": "pnpm test:package && pnpm test:types:package && node .quality-tools/test-package-quickstart.mjs reports/volcano-sdk.tgz", - "test:types:package": "pnpm build && tsc --noEmit -p tsconfig.consumer.json", + "test:types:package": "pnpm build && tsc --noEmit -p tsconfig.consumer.json && tsc --noEmit -p tsconfig.consumer-sandbox.json", "test:examples": "pnpm build && NEXT_PUBLIC_VOLCANO_API_URL=http://127.0.0.1:8787 NEXT_PUBLIC_VOLCANO_ANON_KEY=example-test-key NEXT_PUBLIC_VOLCANO_DATABASE_NAME=app pnpm --dir examples/nextjs-notes-app build", "audit": "pnpm build:tooling && node .quality-tools/audit-dependencies.mjs", "quality": "pnpm quality:policy && pnpm quality:checks && pnpm mutation:full", diff --git a/scripts/quality-policy.test.mts b/scripts/quality-policy.test.mts index cc31d959..058751a5 100644 --- a/scripts/quality-policy.test.mts +++ b/scripts/quality-policy.test.mts @@ -23,7 +23,8 @@ const requiredTasks = { 'pnpm build && pnpm pack --out reports/volcano-sdk.tgz && publint reports/volcano-sdk.tgz --strict && attw reports/volcano-sdk.tgz', 'test:quickstart': 'pnpm test:package && pnpm test:types:package && node .quality-tools/test-package-quickstart.mjs reports/volcano-sdk.tgz', - 'test:types:package': 'pnpm build && tsc --noEmit -p tsconfig.consumer.json', + 'test:types:package': + 'pnpm build && tsc --noEmit -p tsconfig.consumer.json && tsc --noEmit -p tsconfig.consumer-sandbox.json', 'test:examples': 'pnpm build && NEXT_PUBLIC_VOLCANO_API_URL=http://127.0.0.1:8787 NEXT_PUBLIC_VOLCANO_ANON_KEY=example-test-key NEXT_PUBLIC_VOLCANO_DATABASE_NAME=app pnpm --dir examples/nextjs-notes-app build', audit: 'pnpm build:tooling && node .quality-tools/audit-dependencies.mjs', diff --git a/src/auth-fetch-retry.ts b/src/auth-fetch-retry.ts index c213458d..b2dab62d 100644 --- a/src/auth-fetch-retry.ts +++ b/src/auth-fetch-retry.ts @@ -19,6 +19,7 @@ export async function fetchWithAuthRetry( client: AuthRetryClient, url: string, options: RequestInit = {}, + timeoutMs = client.timeout, ): Promise { await client._completeOAuthExchange(); const context = client._captureAuthContext(); @@ -29,7 +30,7 @@ export async function fetchWithAuthRetry( ...options, headers: authenticatedHeaders(options.headers, accessToken), }, - client.timeout, + timeoutMs, ); }; diff --git a/src/generated-runtime/generated/client.js b/src/generated-runtime/generated/client.js index 0f347a24..c6c14e93 100644 --- a/src/generated-runtime/generated/client.js +++ b/src/generated-runtime/generated/client.js @@ -337,9 +337,15 @@ export const createSandbox = async (id, createSandboxTemplateRequest, options) = return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getCreateSandboxUrl(id), { ...options, @@ -403,9 +409,15 @@ export const createSandboxSession = async (id, createSandboxSessionRequest, opti return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getCreateSandboxSessionUrl(id), { ...options, @@ -426,9 +438,15 @@ export const executeSandbox = async (id, sandboxExecutionRequest, options) => { return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getExecuteSandboxUrl(id), { ...options, @@ -497,9 +515,15 @@ export const executeSandboxSession = async (sessionId, sandboxCommandRequest, op return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getExecuteSandboxSessionUrl(sessionId), { ...options, @@ -520,9 +544,15 @@ export const readSandboxSessionFile = async (sessionId, sandboxFileReadRequest, return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getReadSandboxSessionFileUrl(sessionId), { ...options, @@ -543,9 +573,15 @@ export const writeSandboxSessionFile = async (sessionId, sandboxFileWriteRequest return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getWriteSandboxSessionFileUrl(sessionId), { ...options, @@ -566,9 +602,15 @@ export const grantSandboxSession = async (sessionId, subjectId, sandboxSubjectGr return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getGrantSandboxSessionUrl(sessionId, subjectId), { ...options, @@ -601,9 +643,15 @@ export const createSandboxSessionAccess = async (sessionId, sandboxAccessRequest return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) - return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries(Array.from(h, (entry) => Array.from(entry))); + } + const headers = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) + headers[name] = value; + } + return headers; }; return volcanoFetch(getCreateSandboxSessionAccessUrl(sessionId), { ...options, diff --git a/src/generated-runtime/generated/model/createSandboxSessionRequestPreset.js b/src/generated-runtime/generated/model/createSandboxSessionRequestPreset.js deleted file mode 100644 index 07ebf6b1..00000000 --- a/src/generated-runtime/generated/model/createSandboxSessionRequestPreset.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Generated by orval 🍺 - * Do not edit manually. - * Volcano Hosting API - * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). - * - * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. - * OpenAPI spec version: 3.0.0 - */ -export const CreateSandboxSessionRequestPreset = { - python312: 'python3.12', - node22: 'node22', -}; diff --git a/src/generated-runtime/generated/model/createSandboxTemplateRequestPreset.js b/src/generated-runtime/generated/model/createSandboxTemplateRequestPreset.js deleted file mode 100644 index 25c45e55..00000000 --- a/src/generated-runtime/generated/model/createSandboxTemplateRequestPreset.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Generated by orval 🍺 - * Do not edit manually. - * Volcano Hosting API - * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). - * - * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. - * OpenAPI spec version: 3.0.0 - */ -export const CreateSandboxTemplateRequestPreset = { - python312: 'python3.12', - node22: 'node22', -}; diff --git a/src/generated-runtime/generated/model/index.js b/src/generated-runtime/generated/model/index.js index a2b20407..027bda57 100644 --- a/src/generated-runtime/generated/model/index.js +++ b/src/generated-runtime/generated/model/index.js @@ -18,10 +18,8 @@ export * from './bandwidthCapExceededResponse'; export * from './completeUploadSessionResponse'; export * from './createSandboxSessionRequest'; export * from './createSandboxSessionRequestMemoryMb'; -export * from './createSandboxSessionRequestPreset'; export * from './createSandboxTemplateRequest'; export * from './createSandboxTemplateRequestMemoryMb'; -export * from './createSandboxTemplateRequestPreset'; export * from './createUploadSessionRequest'; export * from './createUploadSessionResponse'; export * from './cursorParameter'; @@ -55,7 +53,6 @@ export * from './sandboxCommandResult'; export * from './sandboxExecutionRequest'; export * from './sandboxExecutionRequestEnvironment'; export * from './sandboxExecutionRequestMemoryMb'; -export * from './sandboxExecutionRequestPreset'; export * from './sandboxExecutionResult'; export * from './sandboxFileReadRequest'; export * from './sandboxFileResult'; diff --git a/src/generated-runtime/generated/model/sandboxExecutionRequestPreset.js b/src/generated-runtime/generated/model/sandboxExecutionRequestPreset.js deleted file mode 100644 index c0d6ec95..00000000 --- a/src/generated-runtime/generated/model/sandboxExecutionRequestPreset.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Generated by orval 🍺 - * Do not edit manually. - * Volcano Hosting API - * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). - * - * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. - * OpenAPI spec version: 3.0.0 - */ -export const SandboxExecutionRequestPreset = { - python312: 'python3.12', - node22: 'node22', -}; diff --git a/src/generated-runtime/volcano-fetch.js b/src/generated-runtime/volcano-fetch.js index 369a465c..e1fad29c 100644 --- a/src/generated-runtime/volcano-fetch.js +++ b/src/generated-runtime/volcano-fetch.js @@ -49,11 +49,11 @@ function errorMessage(status, data) { return `Request failed with status ${String(status)}`; } export async function volcanoFetch(path, options) { - const { volcanoAuthorization, volcanoClient, volcanoResponseType, ...request } = options; + const { volcanoAuthorization, volcanoClient, volcanoResponseType, volcanoTimeoutMs, ...request } = options; if (volcanoClient === undefined || volcanoAuthorization === undefined) { throw new Error('Generated transport requires a Volcano client and authorization mode'); } - const response = await volcanoClient._generatedFetch(path, request, volcanoAuthorization); + const response = await volcanoClient._generatedFetch(path, request, volcanoAuthorization, volcanoTimeoutMs); const data = await responseData(response, volcanoResponseType); if (!response.ok) { throw responseError(response, data); diff --git a/src/generated/client.ts b/src/generated/client.ts index ddf570f1..15cb404c 100644 --- a/src/generated/client.ts +++ b/src/generated/client.ts @@ -952,8 +952,16 @@ export const createSandbox = async (id: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getCreateSandboxUrl(id), { @@ -1144,8 +1152,16 @@ export const createSandboxSession = async (id: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getCreateSandboxSessionUrl(id), { @@ -1194,8 +1210,16 @@ export const executeSandbox = async (id: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getExecuteSandboxUrl(id), { @@ -1416,8 +1440,16 @@ export const executeSandboxSession = async (sessionId: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getExecuteSandboxSessionUrl(sessionId), { @@ -1466,8 +1498,16 @@ export const readSandboxSessionFile = async (sessionId: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getReadSandboxSessionFileUrl(sessionId), { @@ -1516,8 +1556,16 @@ export const writeSandboxSessionFile = async (sessionId: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getWriteSandboxSessionFileUrl(sessionId), { @@ -1568,8 +1616,16 @@ export const grantSandboxSession = async (sessionId: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getGrantSandboxSessionUrl(sessionId,subjectId), { @@ -1663,8 +1719,16 @@ export const createSandboxSessionAccess = async (sessionId: string, const getHeaders = (h?: NonNullable): Record => { if (!h) return {}; if (h instanceof Headers) return Object.fromEntries(h.entries()); - if (Array.isArray(h)) return Object.fromEntries(h); - return h; + if (Symbol.iterator in h) { + return Object.fromEntries( + Array.from(h as Iterable>, (entry) => Array.from(entry) as [string, string]), + ); + } + const headers: Record = {}; + for (const [name, value] of Object.entries(h)) { + if (value !== undefined) headers[name] = value; + } + return headers; }; return volcanoFetch(getCreateSandboxSessionAccessUrl(sessionId), { diff --git a/src/generated/model/createSandboxSessionRequest.ts b/src/generated/model/createSandboxSessionRequest.ts index 3c37a8ca..4e8007e5 100644 --- a/src/generated/model/createSandboxSessionRequest.ts +++ b/src/generated/model/createSandboxSessionRequest.ts @@ -8,10 +8,10 @@ * OpenAPI spec version: 3.0.0 */ import type { CreateSandboxSessionRequestMemoryMb } from './createSandboxSessionRequestMemoryMb'; -import type { CreateSandboxSessionRequestPreset } from './createSandboxSessionRequestPreset'; export type CreateSandboxSessionRequest = (unknown & { - preset?: CreateSandboxSessionRequestPreset; + /** Preset ID from the available Sandbox preset catalog. */ + preset?: string; sandbox_id?: string; memory_mb?: CreateSandboxSessionRequestMemoryMb; /** diff --git a/src/generated/model/createSandboxSessionRequestPreset.ts b/src/generated/model/createSandboxSessionRequestPreset.ts deleted file mode 100644 index 0c7f4512..00000000 --- a/src/generated/model/createSandboxSessionRequestPreset.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Generated by orval 🍺 - * Do not edit manually. - * Volcano Hosting API - * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). - * - * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. - * OpenAPI spec version: 3.0.0 - */ - -export type CreateSandboxSessionRequestPreset = typeof CreateSandboxSessionRequestPreset[keyof typeof CreateSandboxSessionRequestPreset]; - - -export const CreateSandboxSessionRequestPreset = { - python312: 'python3.12', - node22: 'node22', -} as const; diff --git a/src/generated/model/createSandboxTemplateRequest.ts b/src/generated/model/createSandboxTemplateRequest.ts index 37688c35..9c228580 100644 --- a/src/generated/model/createSandboxTemplateRequest.ts +++ b/src/generated/model/createSandboxTemplateRequest.ts @@ -8,11 +8,11 @@ * OpenAPI spec version: 3.0.0 */ import type { CreateSandboxTemplateRequestMemoryMb } from './createSandboxTemplateRequestMemoryMb'; -import type { CreateSandboxTemplateRequestPreset } from './createSandboxTemplateRequestPreset'; export interface CreateSandboxTemplateRequest { /** @pattern ^[a-z][a-z0-9-]{0,62}$ */ name: string; - preset: CreateSandboxTemplateRequestPreset; + /** Preset ID from the available Sandbox preset catalog. */ + preset: string; memory_mb?: CreateSandboxTemplateRequestMemoryMb; } diff --git a/src/generated/model/createSandboxTemplateRequestPreset.ts b/src/generated/model/createSandboxTemplateRequestPreset.ts deleted file mode 100644 index 7c9835fc..00000000 --- a/src/generated/model/createSandboxTemplateRequestPreset.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Generated by orval 🍺 - * Do not edit manually. - * Volcano Hosting API - * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). - * - * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. - * OpenAPI spec version: 3.0.0 - */ - -export type CreateSandboxTemplateRequestPreset = typeof CreateSandboxTemplateRequestPreset[keyof typeof CreateSandboxTemplateRequestPreset]; - - -export const CreateSandboxTemplateRequestPreset = { - python312: 'python3.12', - node22: 'node22', -} as const; diff --git a/src/generated/model/index.ts b/src/generated/model/index.ts index 297aa091..6cd6d4bb 100644 --- a/src/generated/model/index.ts +++ b/src/generated/model/index.ts @@ -19,10 +19,8 @@ export * from './bandwidthCapExceededResponse'; export * from './completeUploadSessionResponse'; export * from './createSandboxSessionRequest'; export * from './createSandboxSessionRequestMemoryMb'; -export * from './createSandboxSessionRequestPreset'; export * from './createSandboxTemplateRequest'; export * from './createSandboxTemplateRequestMemoryMb'; -export * from './createSandboxTemplateRequestPreset'; export * from './createUploadSessionRequest'; export * from './createUploadSessionResponse'; export * from './cursorParameter'; @@ -56,7 +54,6 @@ export * from './sandboxCommandResult'; export * from './sandboxExecutionRequest'; export * from './sandboxExecutionRequestEnvironment'; export * from './sandboxExecutionRequestMemoryMb'; -export * from './sandboxExecutionRequestPreset'; export * from './sandboxExecutionResult'; export * from './sandboxFileReadRequest'; export * from './sandboxFileResult'; diff --git a/src/generated/model/sandboxExecutionRequest.ts b/src/generated/model/sandboxExecutionRequest.ts index c65b3f13..558b25c1 100644 --- a/src/generated/model/sandboxExecutionRequest.ts +++ b/src/generated/model/sandboxExecutionRequest.ts @@ -9,10 +9,10 @@ */ import type { SandboxExecutionRequestEnvironment } from './sandboxExecutionRequestEnvironment'; import type { SandboxExecutionRequestMemoryMb } from './sandboxExecutionRequestMemoryMb'; -import type { SandboxExecutionRequestPreset } from './sandboxExecutionRequestPreset'; export type SandboxExecutionRequest = (unknown & { - preset?: SandboxExecutionRequestPreset; + /** Preset ID from the available Sandbox preset catalog. */ + preset?: string; sandbox_id?: string; memory_mb?: SandboxExecutionRequestMemoryMb; /** diff --git a/src/generated/model/sandboxExecutionRequestPreset.ts b/src/generated/model/sandboxExecutionRequestPreset.ts deleted file mode 100644 index 3eecca3b..00000000 --- a/src/generated/model/sandboxExecutionRequestPreset.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Generated by orval 🍺 - * Do not edit manually. - * Volcano Hosting API - * Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). - * - * This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. - * OpenAPI spec version: 3.0.0 - */ - -export type SandboxExecutionRequestPreset = typeof SandboxExecutionRequestPreset[keyof typeof SandboxExecutionRequestPreset]; - - -export const SandboxExecutionRequestPreset = { - python312: 'python3.12', - node22: 'node22', -} as const; diff --git a/src/generated/openapi.d.ts b/src/generated/openapi.d.ts index f6d1896a..9958c91c 100644 --- a/src/generated/openapi.d.ts +++ b/src/generated/openapi.d.ts @@ -4798,8 +4798,8 @@ export interface components { }; CreateSandboxTemplateRequest: { name: string; - /** @enum {string} */ - preset: "python3.12" | "node22"; + /** @description Preset ID from the available Sandbox preset catalog. */ + preset: string; /** * @default 1024 * @enum {integer} @@ -4830,8 +4830,8 @@ export interface components { expires_at: string; }; CreateSandboxSessionRequest: { - /** @enum {string} */ - preset?: "python3.12" | "node22"; + /** @description Preset ID from the available Sandbox preset catalog. */ + preset?: string; /** Format: uuid */ sandbox_id?: string; /** @enum {integer} */ @@ -4852,8 +4852,8 @@ export interface components { }; }; SandboxExecutionRequest: { - /** @enum {string} */ - preset?: "python3.12" | "node22"; + /** @description Preset ID from the available Sandbox preset catalog. */ + preset?: string; /** Format: uuid */ sandbox_id?: string; /** @enum {integer} */ diff --git a/src/sandbox-request.ts b/src/sandbox-request.ts index b48eaf55..7172a31e 100644 --- a/src/sandbox-request.ts +++ b/src/sandbox-request.ts @@ -3,8 +3,14 @@ import type { SandboxCommandOptions, SandboxReplayOptions, SandboxResult } from type TransportOptions = NonNullable[2]>; export interface SandboxClient { + readonly timeout: number; _completeOAuthExchange(): Promise; - _generatedOptions(mode: 'anon' | 'session', headers?: Record): TransportOptions; + _generatedOptions( + mode: 'anon' | 'session', + headers?: Record, + responseType?: 'blob', + timeoutMs?: number, + ): TransportOptions; } export function pathId(value: string): string { if (!/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i.test(value)) { @@ -16,10 +22,13 @@ export function requestOptions( client: SandboxClient, options: SandboxReplayOptions = {}, mode?: 'anon' | 'session' | 'replayable', + timeoutMs?: number, ): TransportOptions { const result = client._generatedOptions( mode === 'anon' ? 'anon' : 'session', mutationHeaders(options, mode === 'replayable'), + undefined, + timeoutMs, ); if (options.signal !== undefined) { result.signal = options.signal; @@ -60,3 +69,11 @@ export async function responseData(request: Promise<{ data: unknown }>): Promise const response = await request; return response.data; } + +export function executionRequestOptions( + client: SandboxClient, + options: SandboxCommandOptions, +): TransportOptions { + const timeoutMs = Math.max(client.timeout, ((options.timeoutSeconds ?? 60) + 120) * 1000); + return requestOptions(client, options, 'replayable', timeoutMs); +} diff --git a/src/sandbox-session.ts b/src/sandbox-session.ts index 595c28d2..10b4c72e 100644 --- a/src/sandbox-session.ts +++ b/src/sandbox-session.ts @@ -16,6 +16,7 @@ import type { } from './index.ts'; import { commandRequest, + executionRequestOptions, pathId, requestOptions, responseData, @@ -76,7 +77,7 @@ export class SandboxSessionHandle implements SandboxSession { executeSandboxSession( pathId(this.id), commandRequest(command, options), - requestOptions(this.client, options, 'replayable'), + executionRequestOptions(this.client, options), ), ), ), @@ -123,11 +124,11 @@ export class SandboxSessionHandle implements SandboxSession { ); } async [Symbol.asyncDispose](): Promise { - if (this.state === 'terminated') { + if (this.state === 'terminated' || this.state === 'terminating') { return; } const result = await this.terminate(); - if (result.error !== null) { + if (result.error !== null && !isMissingSession(result.error)) { throw result.error; } } @@ -150,3 +151,7 @@ export class SandboxSessionHandle implements SandboxSession { }); } } + +function isMissingSession(error: Error): boolean { + return 'status' in error && error.status === 404; +} diff --git a/src/sandbox-wire.ts b/src/sandbox-wire.ts index 2b38687e..7b2a9e21 100644 --- a/src/sandbox-wire.ts +++ b/src/sandbox-wire.ts @@ -78,16 +78,18 @@ export function createRequest( region: options.region, ...(options.preset === undefined ? {} : { preset: options.preset }), ...(options.sandboxId === undefined ? {} : { sandbox_id: options.sandboxId }), - ...(options.memoryMB === undefined ? {} : { memory_mb: options.memoryMB }), + ...(options.memoryMB === undefined ? {} : { memory_mb: presetMemory(options.memoryMB) }), }; } export function accessResult(value: unknown): SandboxAccess { const data = record(value); - return { + const access = { url: text(data['url']), token: text(data['token']), expiresAt: text(data['expires_at']), }; + Object.defineProperty(access, 'token', { enumerable: false }); + return access; } export function presetsResult(value: unknown): SandboxPreset[] { const data = record(value); @@ -139,3 +141,10 @@ export function sessionRequest( : { idle_timeout_seconds: options.idleTimeoutSeconds }), }; } + +function presetMemory(value: number): 1024 | 2048 { + if (value !== 1024 && value !== 2048) { + throw new RangeError('Sandbox memory must be 1024 or 2048 MB'); + } + return value; +} diff --git a/src/sandboxes.ts b/src/sandboxes.ts index 758b02a6..15ecf5f5 100644 --- a/src/sandboxes.ts +++ b/src/sandboxes.ts @@ -14,6 +14,7 @@ import type { } from './index.ts'; import { commandRequest, + executionRequestOptions, pathId, requestOptions, responseData, @@ -42,7 +43,7 @@ export class SandboxesApi implements Sandboxes { const response = await executeSandbox( pathId(projectId), { ...createRequest(options), ...commandRequest(command, options) }, - requestOptions(this.client, options, 'replayable'), + executionRequestOptions(this.client, options), ); return executionResult(response.data); }); diff --git a/src/sdk-public-types.ts b/src/sdk-public-types.ts index 22b0e8a5..f2a0fd56 100644 --- a/src/sdk-public-types.ts +++ b/src/sdk-public-types.ts @@ -1,4 +1,4 @@ -/// +/// import type { FilterValue } from './database-filters'; import type { FunctionInvokeResponse } from './function-invoke.ts'; export type { FilterValue } from './database-filters'; @@ -22,6 +22,8 @@ export type PaginatedDurableExecutions = GeneratedComponents['schemas']['PaginatedDurableExecutions']; export interface VolcanoAuthConfig { + /** HTTP request timeout in milliseconds. Sandbox execution may extend this budget. */ + timeout?: number; /** * Your Volcano API base URL. * Defaults to 'https://api.volcano.dev' if not specified. @@ -1073,10 +1075,10 @@ export interface SandboxReplayOptions extends SandboxRequestOptions { requestId?: string; } export interface SandboxSelectionOptions extends SandboxReplayOptions { - preset?: 'python3.12' | 'node22'; + preset?: string; sandboxId?: string; region: string; - memoryMB?: 1024 | 2048; + memoryMB?: number; } export interface SandboxCreateOptions extends SandboxSelectionOptions { maxDurationSeconds?: number; diff --git a/src/volcano-auth.ts b/src/volcano-auth.ts index 3d530b9c..1932a737 100644 --- a/src/volcano-auth.ts +++ b/src/volcano-auth.ts @@ -512,17 +512,20 @@ class VolcanoAuth { volcanoAuthorization: 'anon' | 'session', headers?: Record, responseType?: 'blob', + timeoutMs?: number, ): { volcanoAuthorization: 'anon' | 'session'; volcanoClient: VolcanoAuth; headers?: Record; volcanoResponseType?: 'blob'; + volcanoTimeoutMs?: number; } { return { volcanoAuthorization, volcanoClient: this, ...(headers === undefined ? {} : { headers }), ...(responseType === undefined ? {} : { volcanoResponseType: responseType }), + ...(timeoutMs === undefined ? {} : { volcanoTimeoutMs: timeoutMs }), }; } @@ -531,6 +534,7 @@ class VolcanoAuth { path: string, options: RequestInit, authorization: 'anon' | 'session', + timeoutMs = this.timeout, ): Promise { const url = `${this.apiUrl}${path}`; if (authorization === 'anon') { @@ -544,11 +548,11 @@ class VolcanoAuth { ...options, headers, }, - this.timeout, + timeoutMs, ); } - return fetchWithAuthRetry(this, url, options); + return fetchWithAuthRetry(this, url, options, timeoutMs); } /** @internal */ diff --git a/src/volcano-fetch.ts b/src/volcano-fetch.ts index fad17c6b..37d1c624 100644 --- a/src/volcano-fetch.ts +++ b/src/volcano-fetch.ts @@ -5,6 +5,7 @@ interface GeneratedTransportClient { path: string, options: RequestInit, authorization: GeneratedAuthorization, + timeoutMs?: number, ): Promise; } @@ -12,6 +13,7 @@ export interface VolcanoRequestInit extends RequestInit { volcanoAuthorization?: GeneratedAuthorization; volcanoClient?: GeneratedTransportClient; volcanoResponseType?: 'blob'; + volcanoTimeoutMs?: number; } async function readBody(response: Response): Promise { @@ -75,11 +77,17 @@ function errorMessage(status: number, data: unknown): string { } export async function volcanoFetch(path: string, options: VolcanoRequestInit): Promise { - const { volcanoAuthorization, volcanoClient, volcanoResponseType, ...request } = options; + const { volcanoAuthorization, volcanoClient, volcanoResponseType, volcanoTimeoutMs, ...request } = + options; if (volcanoClient === undefined || volcanoAuthorization === undefined) { throw new Error('Generated transport requires a Volcano client and authorization mode'); } - const response = await volcanoClient._generatedFetch(path, request, volcanoAuthorization); + const response = await volcanoClient._generatedFetch( + path, + request, + volcanoAuthorization, + volcanoTimeoutMs, + ); const data = await responseData(response, volcanoResponseType); if (!response.ok) { diff --git a/test/fixtures/published-api.d.ts b/test/fixtures/published-api.d.ts index 210145d1..aa1559d0 100644 --- a/test/fixtures/published-api.d.ts +++ b/test/fixtures/published-api.d.ts @@ -1,4 +1,3 @@ -/// // Main 3ec5cf3 API with the nine approved return-type corrections and the additive Sandbox facade. import type { FilterValue } from '../../dist/database-filters'; export type { FilterValue } from '../../dist/database-filters'; @@ -13,6 +12,8 @@ export type DurableExecutionStatus = GeneratedComponents['schemas']['DurableExec export type PaginatedDurableExecutions = GeneratedComponents['schemas']['PaginatedDurableExecutions']; export interface VolcanoAuthConfig { + /** HTTP request timeout in milliseconds. Sandbox execution may extend this budget. */ + timeout?: number; apiUrl?: string; anonKey: string; accessToken?: string; @@ -720,10 +721,10 @@ export interface SandboxReplayOptions extends SandboxRequestOptions { requestId?: string; } export interface SandboxSelectionOptions extends SandboxReplayOptions { - preset?: 'python3.12' | 'node22'; + preset?: string; sandboxId?: string; region: string; - memoryMB?: 1024 | 2048; + memoryMB?: number; } export interface SandboxCreateOptions extends SandboxSelectionOptions { maxDurationSeconds?: number; diff --git a/test/types/package-sandbox.ts b/test/types/package-sandbox.ts new file mode 100644 index 00000000..6c99d80b --- /dev/null +++ b/test/types/package-sandbox.ts @@ -0,0 +1,8 @@ +import { type SandboxPreset, VolcanoClient } from '../../dist/index.js'; +const client = new VolcanoClient({ anonKey: 'anonymous-key' }); +declare const preset: SandboxPreset; +export const created = client.sandboxes.create('project-id', { + preset: preset.id, + memoryMB: preset.memoryMB, + region: 'us-east-1', +}); diff --git a/tsconfig.consumer-sandbox.json b/tsconfig.consumer-sandbox.json new file mode 100644 index 00000000..12159560 --- /dev/null +++ b/tsconfig.consumer-sandbox.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.consumer.json", + "compilerOptions": { "target": "ES2022", "lib": ["ES2022", "DOM"], "types": [] }, + "include": ["test/types/package-sandbox.ts"] +} From 77524af7c81c5e8edbcf8699fc9b8fb1e4fe9026 Mon Sep 17 00:00:00 2001 From: "shawn.wu" Date: Mon, 5 Oct 2026 10:58:58 -0700 Subject: [PATCH 9/9] docs(sandboxes): distinguish one-shot and session timeouts --- docs/sandboxes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sandboxes.md b/docs/sandboxes.md index e0a53e5c..3e7dc9ba 100644 --- a/docs/sandboxes.md +++ b/docs/sandboxes.md @@ -89,6 +89,6 @@ Read the scoped credential through `access.data.token`; it is omitted from JSON Pass a UUID `requestId` to `sandboxes.create()`, `sandboxes.exec()`, or `session.exec()`. Other operations accept cancellation through `signal` but do not accept `requestId`. Retry the same intent with the same ID after a network failure. Do not reuse the ID for a different command. The SDK does not automatically replay failed commands. -Pass `signal` to cancel waiting. Cancellation does not prove the remote command stopped. Execution waits for the command timeout plus 120 seconds for provisioning, or the configured client timeout if longer. One-shot commands allow up to 60 seconds; session commands allow up to 3600 seconds. +Pass `signal` to cancel waiting. Cancellation does not prove the remote command stopped. The HTTP timeout budget is the command timeout plus 120 seconds for provisioning, or the configured client timeout if longer. A one-shot command timeout returns HTTP 504; retrying the same request ID returns HTTP 409 and does not rerun it. A session command timeout returns command data with `timedOut: true`. One-shot commands allow up to 60 seconds; session commands allow up to 3600 seconds. Use either `preset` or a saved template's `sandboxId`, never both. `sandboxes.presets()` lists available preset IDs, memory sizes, and regions. `sandboxes.get(sessionId)` reconnects to an existing session. Backend code can call `grant(sessionId, authUserId, expiresAt)` and `revoke(sessionId, authUserId)` to control user access.