From 41e921f56e4a148f558cbb6257e4ab857e96f3e6 Mon Sep 17 00:00:00 2001 From: thesujai Date: Fri, 18 Sep 2026 17:45:02 +0530 Subject: [PATCH 1/3] feat: Runtime resolution of sandbox environment --- .changeset/sandbox-environments.md | 2 +- .../src/agent-session/TurnHandle.ts | 5 +- .../src/agent-session/TurnResourceResolver.ts | 14 +- .../trueforge-core/src/core/events/schema.ts | 4 + packages/trueforge-core/src/core/index.ts | 2 + .../src/core/runtime/AgentThread.ts | 3 +- .../src/core/sandbox/Sandbox.ts | 29 +++- .../core/sandbox/provider/DaytonaProvider.ts | 91 +++++++++++-- .../src/core/sandbox/provider/Provider.ts | 27 +++- .../src/core/sandbox/sandboxRef.ts | 9 +- .../tests/core/sandbox/sandboxRef.test.ts | 26 +++- packages/trueforge/src/apis/schedules.ts | 3 + packages/trueforge/src/apis/turns.ts | 40 +++++- packages/trueforge/src/app.ts | 2 + .../trueforge/src/runtime/sessionResources.ts | 5 + .../src/sandbox/daytonaEnvironment.ts | 128 ++++++++++++++++++ .../src/schemas/sandboxEnvironment.ts | 15 +- .../unit/apis/deletedSessionCrud.test.ts | 1 + .../unit/apis/sandboxFileDownload.test.ts | 2 + .../tests/unit/apis/schedules.test.ts | 1 + .../unit/apis/turnHarnessErrorStatus.test.ts | 2 + .../trueforge/tests/unit/apis/turns.test.ts | 7 + ...apDaytonaEnvironmentToCreateParams.test.ts | 55 ++++++++ 23 files changed, 430 insertions(+), 43 deletions(-) create mode 100644 packages/trueforge/src/sandbox/daytonaEnvironment.ts create mode 100644 packages/trueforge/tests/unit/sandbox/mapDaytonaEnvironmentToCreateParams.test.ts diff --git a/.changeset/sandbox-environments.md b/.changeset/sandbox-environments.md index 52f0ac82b..94d61dc2f 100644 --- a/.changeset/sandbox-environments.md +++ b/.changeset/sandbox-environments.md @@ -3,4 +3,4 @@ "@truefoundry/trueforge-core": minor --- -Add owned sandbox environments (CRUD + AgentSpec.environment) gated by tenant CREATE permission. +Add owned sandbox environments (CRUD + AgentSpec.environment) and apply Daytona create overrides / reattach gating at turn time. diff --git a/packages/trueforge-core/src/agent-session/TurnHandle.ts b/packages/trueforge-core/src/agent-session/TurnHandle.ts index f0bb2be03..fc5e5d658 100644 --- a/packages/trueforge-core/src/agent-session/TurnHandle.ts +++ b/packages/trueforge-core/src/agent-session/TurnHandle.ts @@ -529,7 +529,10 @@ export class TurnHandle> { case HarnessEventType.SANDBOX_CREATED: { await this.store.patchSandboxInfo({ ...scope, - sandbox_info: { sandbox_id: event.sandbox_id }, + sandbox_info: + event.environment === undefined + ? { sandbox_id: event.sandbox_id } + : { sandbox_id: event.sandbox_id, environment: event.environment }, }); await this.store.appendToEvents({ ...scope, diff --git a/packages/trueforge-core/src/agent-session/TurnResourceResolver.ts b/packages/trueforge-core/src/agent-session/TurnResourceResolver.ts index 9bcfed4a3..e4534da7f 100644 --- a/packages/trueforge-core/src/agent-session/TurnResourceResolver.ts +++ b/packages/trueforge-core/src/agent-session/TurnResourceResolver.ts @@ -16,13 +16,13 @@ import type { AgentSpec } from './schemas/agentSpec'; /** * Factory that creates a Sandbox handle for a run (reattach via - * existingSandboxId). `spec` carries the sandbox-relevant agent settings - * (e.g. config.sandbox.file_downloads) so implementations can shape the - * Sandbox per run without the factory type growing a field per option. + * existing.sandbox_id). `spec` carries the sandbox-relevant agent settings + * (e.g. config.sandbox.file_downloads / environment) so implementations can + * shape the Sandbox per run without the factory type growing a field per option. */ export type TurnSandboxFactory = (input: { spec: AgentSpec; - existingSandboxId?: string | undefined; + existing?: SandboxInfo | undefined; signal: AbortSignal; tracing: AgentTracing; }) => Promise; @@ -118,8 +118,8 @@ export class TurnResourceResolver< /** * Default: creates a sandbox iff a provider is configured AND the spec wants - * one; reattaches to the previous turn's VM via `existing.sandbox_id` when - * present. Overrides that resolve their own sandbox must assign it so + * one; reattaches to the previous turn's VM via `existing` when present. + * Overrides that resolve their own sandbox must assign it so * close() can release it (or override close() too). */ async resolveSandbox(input: { @@ -134,7 +134,7 @@ export class TurnResourceResolver< } this.#sandbox = await this.deps.sandboxProvider({ spec: input.spec, - existingSandboxId: input.existing?.sandbox_id, + existing: input.existing, signal: input.signal, tracing: input.tracing, }); diff --git a/packages/trueforge-core/src/core/events/schema.ts b/packages/trueforge-core/src/core/events/schema.ts index bdced8eda..ee35e3fb6 100644 --- a/packages/trueforge-core/src/core/events/schema.ts +++ b/packages/trueforge-core/src/core/events/schema.ts @@ -312,6 +312,10 @@ export const SandboxCreatedEventSchema = z id: EventIdSchema, created_at: z.string().describe('ISO 8601 event timestamp.'), sandbox_id: z.string().describe('Provider sandbox id.'), + environment: z + .string() + .optional() + .describe('Sandbox environment name when created with one; omit for provider defaults.'), // This is a run-level event, sandbox once created is reused by all threads, // and future turns as well. thread_id: z.string().nullable().describe('Always null — sandbox is session-scoped.'), diff --git a/packages/trueforge-core/src/core/index.ts b/packages/trueforge-core/src/core/index.ts index 76538942f..1c02812a3 100644 --- a/packages/trueforge-core/src/core/index.ts +++ b/packages/trueforge-core/src/core/index.ts @@ -157,12 +157,14 @@ export type { DaytonaSandboxProviderOptions } from './sandbox/provider/DaytonaPr export { absolutizeRelativeExecEnv } from './sandbox/provider/execEnv'; export { ensureExecSuccess, shellEscape } from './sandbox/provider/Provider'; export type { + DaytonaSandboxCreateParams, ExecErrorResult, ExecResult, ExecSuccessResult, SandboxBuild, SandboxBuildMetadata, SandboxBuildStatus, + SandboxCreateOptions, SandboxExecParams, SandboxInit, SandboxProvider, diff --git a/packages/trueforge-core/src/core/runtime/AgentThread.ts b/packages/trueforge-core/src/core/runtime/AgentThread.ts index 3e2c6785b..467383672 100644 --- a/packages/trueforge-core/src/core/runtime/AgentThread.ts +++ b/packages/trueforge-core/src/core/runtime/AgentThread.ts @@ -220,7 +220,8 @@ function buildSandboxCreatedEvent(info: SandboxInfo): SandboxCreatedEvent { type: EventType.SANDBOX_CREATED, id: newEventId(), created_at: new Date().toISOString(), - ...info, + sandbox_id: info.sandbox_id, + ...(info.environment === undefined ? {} : { environment: info.environment }), thread_id: null, }; } diff --git a/packages/trueforge-core/src/core/sandbox/Sandbox.ts b/packages/trueforge-core/src/core/sandbox/Sandbox.ts index 4d7e79e3b..f37106975 100644 --- a/packages/trueforge-core/src/core/sandbox/Sandbox.ts +++ b/packages/trueforge-core/src/core/sandbox/Sandbox.ts @@ -17,7 +17,7 @@ import type { AgentTracing } from '../tracing/AgentTracing'; import { extractErrorLogFields } from '../util/errorLogFields'; import { CodeModeDispatcher } from './codeMode/CodeModeDispatcher'; import { type CodeModeClientInstall, type CodeModeTransport } from './codeMode/CodeModeTransport'; -import { ensureExecSuccess, shellEscape, type SandboxProvider } from './provider/Provider'; +import { ensureExecSuccess, shellEscape, type SandboxCreateOptions, type SandboxProvider } from './provider/Provider'; import { SandboxNotAvailableError, validateNoPathTraversal } from './SandboxErrors'; import { formatSandboxId, rawSandboxId } from './sandboxRef'; // Import submodules, not the ./skills barrel, to avoid a cycle (the mounters import from Sandbox). @@ -33,6 +33,8 @@ function mcpClientLayout(remotePath: string): { pythonPath: string; binDir: stri export interface SandboxInfo { sandbox_id: string; + /** Sandbox environment name when created with one; omit for provider defaults. */ + environment?: string | undefined; } // Downloader/setup scripts can run longer than a normal exec. @@ -99,6 +101,10 @@ export interface SandboxOptions { /** Used to derive the Code Mode NATS wait as request + connect. */ mcpRequestTimeoutMs: number; mcpConnectTimeoutMs: number; + /** Environment name stamped onto SandboxInfo when set. */ + environment?: string | undefined; + /** Applied only on fresh create (reattach ignores). */ + createOptions?: SandboxCreateOptions | undefined; tracing: AgentTracing; logger: Logger; } @@ -210,6 +216,8 @@ export class Sandbox extends LocalToolMCP { private readonly logger: Logger; // Pre-resolved credential-store file content (null = clear / no git auth). private readonly resolvedGitCredentialsContent: string | null; + private readonly environmentName: string | undefined; + private readonly createOptions: SandboxCreateOptions | undefined; private codeModeDispatcher: CodeModeDispatcher | undefined; private codeModeTransport: CodeModeTransport | undefined; /** Cached from transport.getClientInstall after sandbox init (when Code Mode is configured). */ @@ -234,12 +242,21 @@ export class Sandbox extends LocalToolMCP { this.requestTimeoutSeconds = Math.ceil(mcpBoundTimeoutMs / 1000) + NATS_REQUEST_TIMEOUT_BUFFER_SECONDS; this.logger = options.logger.child({ module: 'Sandbox' }); this.resolvedGitCredentialsContent = options.resolvedGitCredentialsContent ?? null; + this.environmentName = options.environment; + this.createOptions = options.createOptions; if (this.existingSandboxId) { - this.existingSandboxInfo = { sandbox_id: this.existingSandboxId }; + this.existingSandboxInfo = this.toSandboxInfo(this.existingSandboxId); } } + private toSandboxInfo(sandboxId: string): SandboxInfo { + if (this.environmentName === undefined) { + return { sandbox_id: sandboxId }; + } + return { sandbox_id: sandboxId, environment: this.environmentName }; + } + /** Provider-facing id (unwraps `v1:type:raw`; legacy ids pass through). */ private providerSandboxId(sessionId: string): string { return rawSandboxId(sessionId); @@ -458,10 +475,10 @@ export class Sandbox extends LocalToolMCP { } // Provider returns a raw id; persist the fancy `v1:type:raw` session id. this.sandboxCreationPromise ??= this.provider - .createSandbox() - .then(({ sandboxId }) => ({ - sandbox_id: formatSandboxId({ providerType: this.provider.type, rawId: sandboxId }), - })) + .createSandbox(this.createOptions) + .then(({ sandboxId }) => + this.toSandboxInfo(formatSandboxId({ providerType: this.provider.type, rawId: sandboxId })), + ) .catch((e: unknown) => { this.sandboxCreationPromise = undefined; throw e; diff --git a/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts b/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts index 2332325c6..263e9274d 100644 --- a/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts +++ b/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts @@ -1,4 +1,10 @@ -import type { Sandbox, Snapshot } from '@daytona/sdk'; +import type { + CreateSandboxBaseParams, + CreateSandboxFromImageParams, + CreateSandboxFromSnapshotParams, + Sandbox, + Snapshot, +} from '@daytona/sdk'; import { Daytona, DaytonaError } from '@daytona/sdk'; import { context } from '@opentelemetry/api'; import { suppressTracing } from '@opentelemetry/core'; @@ -16,7 +22,15 @@ import { import type { CodeModeTransport } from '../codeMode/CodeModeTransport'; import { CodeModeNatsTransport } from '../codeMode/nats/CodeModeNatsTransport'; import { DEFAULT_PREVIEW_URL_EXPIRY_SECONDS, DEFAULT_SANDBOX_NATS_WS_PORT } from '../constants'; -import type { ExecResult, SandboxBuild, SandboxExecParams, SandboxFileInfo, SandboxProvider } from './Provider'; +import type { + DaytonaSandboxCreateParams, + ExecResult, + SandboxBuild, + SandboxCreateOptions, + SandboxExecParams, + SandboxFileInfo, + SandboxProvider, +} from './Provider'; const SANDBOX_NOT_FOUND_STATUS = 404; /** Another replica already registered this build name; its create is the one that counts. */ @@ -32,6 +46,30 @@ const IMAGE_BUILD_NAME_PREFIX = 'trueforge-build-'; /** Same default the Daytona SDK applies when `DaytonaConfig.apiUrl` is omitted. */ const DEFAULT_DAYTONA_API_URL = 'https://app.daytona.io/api'; +function applyDaytonaCreateOverrides( + target: CreateSandboxBaseParams, + source: DaytonaSandboxCreateParams | undefined, +): void { + if (!source) { + return; + } + if (source.secrets) { + target.secrets = source.secrets; + } + if (source.networkBlockAll != null) { + target.networkBlockAll = source.networkBlockAll; + } + if (source.networkAllowList) { + target.networkAllowList = source.networkAllowList; + } + if (source.domainAllowList) { + target.domainAllowList = source.domainAllowList; + } + if (source.outboundProxyUrl) { + target.outboundProxyUrl = source.outboundProxyUrl; + } +} + function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null; } @@ -140,7 +178,10 @@ export class DaytonaSandboxProvider implements SandboxProvider { this.logger = options.logger.child({ module: 'DaytonaProvider' }); } - private async getOrCreateSandbox(sandboxId?: string): Promise<{ sandbox: Sandbox; defaultTimeoutMs: number }> { + private async getOrCreateSandbox( + sandboxId?: string, + createParams?: DaytonaSandboxCreateParams, + ): Promise<{ sandbox: Sandbox; defaultTimeoutMs: number }> { if (sandboxId) { validateSandboxOwnedByTenant({ sandboxId, tenantName: this.tenantName }); const cached = DaytonaSandboxProvider.cachedSandboxes.get(sandboxId); @@ -151,19 +192,45 @@ export class DaytonaSandboxProvider implements SandboxProvider { const sandbox = sandboxId ? await this.restoreExistingSandbox(sandboxId) - : await this.daytona.create({ - name: `${this.tenantName}.${randomUUID()}`, - snapshot: this.buildRef, - autoStopInterval: this.autoStopIntervalInMinutes, - autoArchiveInterval: this.autoArchiveIntervalInMinutes, - autoDeleteInterval: this.autoDeleteIntervalInMinutes, - }); + : await this.createNewSandbox(createParams); const entry = { sandbox, defaultTimeoutMs: this.timeoutMs }; DaytonaSandboxProvider.cachedSandboxes.set(sandbox.name, entry); return entry; } + private async createNewSandbox(createParams?: DaytonaSandboxCreateParams): Promise { + if (createParams?.snapshot && createParams.image) { + throw new Error('Daytona create overrides must not set both snapshot and image'); + } + + const name = `${this.tenantName}.${randomUUID()}`; + const base: CreateSandboxBaseParams = { + name, + autoStopInterval: createParams?.autoStopInterval ?? this.autoStopIntervalInMinutes, + autoArchiveInterval: createParams?.autoArchiveInterval ?? this.autoArchiveIntervalInMinutes, + autoDeleteInterval: createParams?.autoDeleteInterval ?? this.autoDeleteIntervalInMinutes, + }; + applyDaytonaCreateOverrides(base, createParams); + + if (createParams?.image) { + const params: CreateSandboxFromImageParams = { ...base, image: createParams.image }; + if (createParams.resources) { + params.resources = createParams.resources; + } + return await this.daytona.create(params); + } + + if (createParams?.resources) { + throw new Error('Resources require a docker image environment (not supported on snapshot create)'); + } + + return await this.daytona.create({ + ...base, + snapshot: createParams?.snapshot ?? this.buildRef, + } satisfies CreateSandboxFromSnapshotParams); + } + // Returns true iff the caller should retry: either we restarted a stopped sandbox, or the cache entry is missing and the retry will rebuild it via the cold path. private static recoverSandboxIfStopped(sandboxId: string): Promise { const existing = DaytonaSandboxProvider.inFlightRecoveries.get(sandboxId); @@ -245,9 +312,9 @@ export class DaytonaSandboxProvider implements SandboxProvider { } } - async createSandbox(): Promise<{ sandboxId: string }> { + async createSandbox(options?: SandboxCreateOptions): Promise<{ sandboxId: string }> { return context.with(suppressTracing(context.active()), async () => { - const { sandbox } = await this.getOrCreateSandbox(); + const { sandbox } = await this.getOrCreateSandbox(undefined, options?.daytona); this.logger.debug(`Sandbox created: name=${sandbox.name}`); return { sandboxId: sandbox.name }; }); diff --git a/packages/trueforge-core/src/core/sandbox/provider/Provider.ts b/packages/trueforge-core/src/core/sandbox/provider/Provider.ts index 0a46e5477..e086ebec8 100644 --- a/packages/trueforge-core/src/core/sandbox/provider/Provider.ts +++ b/packages/trueforge-core/src/core/sandbox/provider/Provider.ts @@ -80,6 +80,31 @@ export interface SandboxBuild { metadata: SandboxBuildMetadata | null; } +import type { Resources } from '@daytona/sdk'; + +/** + * Daytona `create` overrides from a sandbox environment (or host defaults). + * Snapshot vs image are mutually exclusive; omit both to use the provider buildRef. + */ +export interface DaytonaSandboxCreateParams { + snapshot?: string | undefined; + image?: string | undefined; + resources?: Resources | undefined; + secrets?: Record | undefined; + networkBlockAll?: boolean | undefined; + networkAllowList?: string | undefined; + domainAllowList?: string | undefined; + outboundProxyUrl?: string | undefined; + autoStopInterval?: number | undefined; + autoArchiveInterval?: number | undefined; + autoDeleteInterval?: number | undefined; +} + +/** Optional create-time overrides; non-Daytona providers ignore `daytona`. */ +export interface SandboxCreateOptions { + daytona?: DaytonaSandboxCreateParams | undefined; +} + export interface SandboxProvider { /** Stable provider kind used in fancy sandbox ids and carry-forward (plain string). */ readonly type: string; @@ -91,7 +116,7 @@ export interface SandboxProvider { buildImage(): Promise; /** Current build status of the release image. Read-only: never kicks off a build. */ getImageBuildStatus(): Promise; - createSandbox(): Promise<{ sandboxId: string }>; + createSandbox(options?: SandboxCreateOptions): Promise<{ sandboxId: string }>; exec(params: SandboxExecParams): Promise; /** Provider-specific instructions appended to the agent system prompt. */ getAdditionalInstructions(): string | undefined; diff --git a/packages/trueforge-core/src/core/sandbox/sandboxRef.ts b/packages/trueforge-core/src/core/sandbox/sandboxRef.ts index d85127a1b..fcd6d0209 100644 --- a/packages/trueforge-core/src/core/sandbox/sandboxRef.ts +++ b/packages/trueforge-core/src/core/sandbox/sandboxRef.ts @@ -39,10 +39,14 @@ export function rawSandboxId(sandboxId: string): string { return parsed.kind === 'v1' ? parsed.parts.rawId : parsed.rawId; } -/** Carry-forward gate for turn admit / download. Type mismatch drops the id (create fresh). */ +/** Carry-forward gate for turn admit / download. Type or environment mismatch drops the id. */ export function existingSandboxIdForProvider(params: { existingSandboxId: string | undefined; currentProviderType: string; + /** Previous turn's environment name; omit/undefined = provider defaults. */ + existingEnvironment?: string | undefined; + /** Current agent-spec environment name; omit/undefined = provider defaults. */ + currentEnvironment?: string | undefined; }): string | undefined { if (params.existingSandboxId === undefined) { return undefined; @@ -51,5 +55,8 @@ export function existingSandboxIdForProvider(params: { if (parsed.kind === 'v1' && parsed.parts.providerType !== params.currentProviderType) { return undefined; } + if ((params.existingEnvironment ?? undefined) !== (params.currentEnvironment ?? undefined)) { + return undefined; + } return params.existingSandboxId; } diff --git a/packages/trueforge-core/tests/core/sandbox/sandboxRef.test.ts b/packages/trueforge-core/tests/core/sandbox/sandboxRef.test.ts index 0afc83820..d18d5cc41 100644 --- a/packages/trueforge-core/tests/core/sandbox/sandboxRef.test.ts +++ b/packages/trueforge-core/tests/core/sandbox/sandboxRef.test.ts @@ -24,7 +24,7 @@ describe('sandboxRef', () => { expect(rawSandboxId('tenant.uuid')).toBe('tenant.uuid'); }); - it('carries legacy and same-type v1 ids; drops cross-type v1 ids', () => { + it('carries legacy and same-type v1 ids; drops cross-type or environment-mismatched ids', () => { expect( existingSandboxIdForProvider({ existingSandboxId: undefined, currentProviderType: 'local' }), ).toBeUndefined(); @@ -43,5 +43,29 @@ describe('sandboxRef', () => { currentProviderType: 'local', }), ).toBeUndefined(); + expect( + existingSandboxIdForProvider({ + existingSandboxId: 'v1:daytona:abc', + currentProviderType: 'daytona', + existingEnvironment: 'env-a', + currentEnvironment: 'env-a', + }), + ).toBe('v1:daytona:abc'); + expect( + existingSandboxIdForProvider({ + existingSandboxId: 'v1:daytona:abc', + currentProviderType: 'daytona', + existingEnvironment: 'env-a', + currentEnvironment: 'env-b', + }), + ).toBeUndefined(); + expect( + existingSandboxIdForProvider({ + existingSandboxId: 'v1:daytona:abc', + currentProviderType: 'daytona', + existingEnvironment: 'env-a', + currentEnvironment: undefined, + }), + ).toBeUndefined(); }); }); diff --git a/packages/trueforge/src/apis/schedules.ts b/packages/trueforge/src/apis/schedules.ts index da3dcc62d..08d377401 100644 --- a/packages/trueforge/src/apis/schedules.ts +++ b/packages/trueforge/src/apis/schedules.ts @@ -27,6 +27,7 @@ import { import type { AgentRecord, IAgentStore } from '../db/agentStore'; import type { IMcpServerWithAuthStore } from '../db/mcpServerStore'; import type { IModelProviderStore } from '../db/modelProviderStore'; +import type { ISandboxEnvironmentStore } from '../db/sandboxEnvironmentStore'; import type { ISandboxProviderStore } from '../db/sandboxProviderStore'; import { manualRunName, @@ -72,6 +73,7 @@ export interface ScheduleTurnExecutionDeps { resolveModelProviderStore: (c: Context, runAsAgent?: AgentRecord) => IModelProviderStore; resolveMcpServerStore: (c: Context, runAsAgent?: AgentRecord) => IMcpServerWithAuthStore; resolveSandboxProviderStore: (c: Context) => ISandboxProviderStore; + resolveSandboxEnvironmentStore: (c: Context) => ISandboxEnvironmentStore; /** Persistence agent store (schedule agent binding is not caller-scoped). */ agentStore: IAgentStore; turnSkillsResolverStore: Pick; @@ -115,6 +117,7 @@ export async function startScheduleRunOnRequest(params: { modelProviderStore: deps.resolveModelProviderStore(c, prepared.agent), mcpServerStore: deps.resolveMcpServerStore(c, prepared.agent), sandboxProviderStore: deps.resolveSandboxProviderStore(c), + sandboxEnvironmentStore: deps.resolveSandboxEnvironmentStore(c), skillStore: deps.turnSkillsResolverStore, logger: deps.logger, }, diff --git a/packages/trueforge/src/apis/turns.ts b/packages/trueforge/src/apis/turns.ts index 5d933410f..33138dd3c 100644 --- a/packages/trueforge/src/apis/turns.ts +++ b/packages/trueforge/src/apis/turns.ts @@ -37,6 +37,7 @@ import configuration, { isTrueFoundryModeEnabled } from '../config'; import type { AgentRecord, IAgentStore } from '../db/agentStore'; import type { IMcpServerWithAuthStore } from '../db/mcpServerStore'; import type { IModelProviderStore } from '../db/modelProviderStore'; +import type { ISandboxEnvironmentStore } from '../db/sandboxEnvironmentStore'; import type { ISandboxProviderStore } from '../db/sandboxProviderStore'; import type { ISkillStore } from '../db/skillStore'; import { @@ -61,6 +62,7 @@ import { withGatewayMetadataHeaders, X_TFY_METADATA, } from '../runtime/sessionResources'; +import { resolveSandboxEnvironmentForTurn } from '../sandbox/daytonaEnvironment'; import { checkSnapshotStatus } from '../sandbox/providerUtils'; import { MAX_SESSION_TITLE_LENGTH } from '../schemas/session'; import { newId } from '../utils/id'; @@ -120,6 +122,7 @@ export interface TurnsRouterDeps { /** Resumable live turn-event transport: create-turn writes, subscribe polls. */ eventSubscriptions: EventSubscriptionRegistry; resolveSandboxProviderStore: (c: Context) => ISandboxProviderStore; + resolveSandboxEnvironmentStore: (c: Context) => ISandboxEnvironmentStore; logger: Logger; resolveRequestContext: ResolveRequestContext; authorizer: Authorizer; @@ -135,6 +138,7 @@ export type BeginTurnExecutionDeps = Pick; }; @@ -146,6 +150,7 @@ function createTurnResolver(deps: { mcpServerStore: IMcpServerWithAuthStore; skillStore: Pick; sandboxProviderStore: ISandboxProviderStore; + sandboxEnvironmentStore: ISandboxEnvironmentStore; agentStore: IAgentStore; modelProviderStore: IModelProviderStore; webSearchProvider: IWebSearchProvider | undefined; @@ -160,6 +165,7 @@ function createTurnResolver(deps: { mcpServerStore, skillStore, sandboxProviderStore, + sandboxEnvironmentStore, agentStore, modelProviderStore, webSearchProvider, @@ -219,7 +225,7 @@ function createTurnResolver(deps: { mcpRequestTimeoutMs: configuration.MCP_REQUEST_TIMEOUT_MS, mcpConnectTimeoutMs: configuration.MCP_CONNECT_TIMEOUT_MS, mcpMaxResponseBytes: configuration.MCP_TOOL_CALL_MAX_RESPONSE_BYTES, - sandboxProvider: async ({ spec, existingSandboxId, tracing }) => { + sandboxProvider: async ({ spec, existing, tracing }) => { const provider = await resolveSandboxProvider({ tenant_id, store: sandboxProviderStore, @@ -231,14 +237,32 @@ function createTurnResolver(deps: { message: 'no sandbox provider configured — PUT /settings/sandbox-providers', }); } + + const environmentName = spec.config.sandbox.environment; + const providerRecord = await sandboxProviderStore.getSandboxProvider(tenant_id); + const resolvedEnvironment = await resolveSandboxEnvironmentForTurn({ + tenant_id, + subject_id: userRef, + environmentName, + providerRecord, + sandboxEnvironmentStore, + }); + const carriedSandboxId = existingSandboxIdForProvider({ - existingSandboxId, + existingSandboxId: existing?.sandbox_id, currentProviderType: provider.type, + existingEnvironment: existing?.environment, + currentEnvironment: environmentName, }); - // A fresh Daytona sandbox is cloned from the release snapshot, so the build must be ready first. - // Restoring an existing sandbox goes through daytona.get and never touches the snapshot. - // Local fallback has no image build. - if (carriedSandboxId === undefined && provider.type !== 'local') { + + // Fresh Daytona creates that use the tenant release snapshot need the build ready. + // Custom snapshot / docker images skip that gate. Restoring an existing sandbox never + // touches the snapshot. Local fallback has no image build. + const needsTenantSnapshot = + carriedSandboxId === undefined && + provider.type !== 'local' && + (resolvedEnvironment === undefined || resolvedEnvironment.requiresTenantSnapshot); + if (needsTenantSnapshot) { const status = await checkSnapshotStatus({ store: sandboxProviderStore, tenant_id, logger }); if (status?.status !== 'ready') { throw new HTTPException(422, { @@ -263,6 +287,8 @@ function createTurnResolver(deps: { skills: mountSkills, fileDownloadEnabled: spec.config.sandbox.file_downloads, existingSandboxId: carriedSandboxId, + environment: resolvedEnvironment?.environmentName, + createOptions: resolvedEnvironment?.createOptions, tracing, }); }, @@ -409,6 +435,7 @@ export async function beginTurnExecution(params: { mcpServerStore: deps.mcpServerStore, skillStore: deps.skillStore, sandboxProviderStore: deps.sandboxProviderStore, + sandboxEnvironmentStore: deps.sandboxEnvironmentStore, agentStore: deps.agentStore, modelProviderStore: deps.modelProviderStore, webSearchProvider: resolveWebSearchProvider(), @@ -797,6 +824,7 @@ export function createTurnsRouter(deps: TurnsRouterDeps) { skillStore: deps.resolveSkillStore(c), agentStore: deps.resolveAgentStore(c), sandboxProviderStore: deps.resolveSandboxProviderStore(c), + sandboxEnvironmentStore: deps.resolveSandboxEnvironmentStore(c), }, }; diff --git a/packages/trueforge/src/app.ts b/packages/trueforge/src/app.ts index 080353fef..f50685da2 100644 --- a/packages/trueforge/src/app.ts +++ b/packages/trueforge/src/app.ts @@ -240,6 +240,7 @@ export function createServerApp(deps: ServerDeps) { resolveModelProviderStore: deps.resolveModelProviderStore, resolveMcpServerStore: deps.resolveMcpServerStore, resolveSandboxProviderStore: deps.resolveSandboxProviderStore, + resolveSandboxEnvironmentStore: deps.resolveSandboxEnvironmentStore, activeTurns: deps.activeTurns, turnSkillsResolverStore: deps.turnSkillsResolverStore, }; @@ -481,6 +482,7 @@ export function createServerApp(deps: ServerDeps) { resolveAgentStore: deps.resolveAgentStore, eventSubscriptions: deps.eventSubscriptions, resolveSandboxProviderStore: deps.resolveSandboxProviderStore, + resolveSandboxEnvironmentStore: deps.resolveSandboxEnvironmentStore, logger: deps.logger, resolveRequestContext, authorizer: deps.authorizer, diff --git a/packages/trueforge/src/runtime/sessionResources.ts b/packages/trueforge/src/runtime/sessionResources.ts index d34df8b6f..8e064e408 100644 --- a/packages/trueforge/src/runtime/sessionResources.ts +++ b/packages/trueforge/src/runtime/sessionResources.ts @@ -6,6 +6,7 @@ import { type AgentTracing, type ModelParams, type RemoteMcpHeaders, + type SandboxCreateOptions, type SandboxProvider, type Skill, type VercelAIProviderConfig, @@ -267,6 +268,8 @@ export function buildTurnSandbox(input: { skills?: readonly Skill[]; fileDownloadEnabled: boolean; existingSandboxId?: string | undefined; + environment?: string | undefined; + createOptions?: SandboxCreateOptions | undefined; tracing: AgentTracing; }): Sandbox { // Empty mounter still uploads requested-skills file so existing skills are cleaned up. @@ -274,6 +277,8 @@ export function buildTurnSandbox(input: { provider: input.provider, existingSandboxId: input.existingSandboxId, fileDownloadEnabled: input.fileDownloadEnabled, + environment: input.environment, + createOptions: input.createOptions, blockDestructiveToolsInCodeMode: true, mcpRequestTimeoutMs: configuration.MCP_REQUEST_TIMEOUT_MS, mcpConnectTimeoutMs: configuration.MCP_CONNECT_TIMEOUT_MS, diff --git a/packages/trueforge/src/sandbox/daytonaEnvironment.ts b/packages/trueforge/src/sandbox/daytonaEnvironment.ts new file mode 100644 index 000000000..2f5ad16a8 --- /dev/null +++ b/packages/trueforge/src/sandbox/daytonaEnvironment.ts @@ -0,0 +1,128 @@ +/** + * Turn-time Daytona sandbox-environment resolution: manifest → create overrides. + */ +import type { Resources } from '@daytona/sdk'; +import type { DaytonaSandboxCreateParams, SandboxCreateOptions } from '@truefoundry/trueforge-core/core'; +import { HTTPException } from 'hono/http-exception'; +import type { ISandboxEnvironmentStore } from '../db/sandboxEnvironmentStore'; +import type { SandboxProviderRecord } from '../db/sandboxProviderStore'; +import type { DaytonaSandboxEnvironmentManifest } from '../schemas/sandboxEnvironment'; + +function mapResources(resources: NonNullable): Resources { + return { + ...(resources.cpu ? { cpu: resources.cpu } : {}), + ...(resources.memory ? { memory: resources.memory } : {}), + ...(resources.disk ? { disk: resources.disk } : {}), + ...(resources.gpu != null ? { gpu: resources.gpu } : {}), + ...(resources.gpu_type ? { gpuType: resources.gpu_type } : {}), + }; +} + +/** + * Maps a Daytona sandbox-environment manifest onto Daytona create overrides. + * `trueforge-default` uses the tenant provider snapshot (`buildRef`). + */ +export function mapDaytonaEnvironmentToCreateParams({ + manifest, + buildRef, +}: { + manifest: DaytonaSandboxEnvironmentManifest; + buildRef: string; +}): DaytonaSandboxCreateParams { + const image = + manifest.image.type === 'trueforge-default' + ? { snapshot: buildRef } + : manifest.image.type === 'snapshot' + ? { snapshot: manifest.image.name } + : { image: manifest.image.ref }; + + return { + ...image, + ...(manifest.resources ? { resources: mapResources(manifest.resources) } : {}), + ...(manifest.secrets ? { secrets: manifest.secrets } : {}), + ...(manifest.networking?.network_block_all != null + ? { networkBlockAll: manifest.networking.network_block_all } + : {}), + ...(manifest.networking?.network_allow_list ? { networkAllowList: manifest.networking.network_allow_list } : {}), + ...(manifest.networking?.domain_allow_list ? { domainAllowList: manifest.networking.domain_allow_list } : {}), + ...(manifest.networking?.outbound_proxy_url ? { outboundProxyUrl: manifest.networking.outbound_proxy_url } : {}), + ...(manifest.lifecycle?.auto_stop_interval_in_minutes != null + ? { autoStopInterval: manifest.lifecycle.auto_stop_interval_in_minutes } + : {}), + ...(manifest.lifecycle?.auto_archive_interval_in_minutes != null + ? { autoArchiveInterval: manifest.lifecycle.auto_archive_interval_in_minutes } + : {}), + ...(manifest.lifecycle?.auto_delete_interval_in_minutes != null + ? { autoDeleteInterval: manifest.lifecycle.auto_delete_interval_in_minutes } + : {}), + }; +} + +/** + * Resolves a caller-owned sandbox environment for turn create overrides. + * Returns undefined when the agent does not reference an environment. + */ +export async function resolveSandboxEnvironmentForTurn({ + tenant_id, + subject_id, + environmentName, + providerRecord, + sandboxEnvironmentStore, +}: { + tenant_id: string; + subject_id: string; + environmentName: string | undefined; + providerRecord: SandboxProviderRecord | undefined; + sandboxEnvironmentStore: ISandboxEnvironmentStore; +}): Promise< + | { + environmentName: string; + createOptions: SandboxCreateOptions; + /** True when create clones the tenant release snapshot (`trueforge-default`). */ + requiresTenantSnapshot: boolean; + } + | undefined +> { + if (!environmentName) { + return undefined; + } + if (providerRecord?.manifest.type !== 'daytona') { + throw new HTTPException(422, { + message: 'sandbox.environment requires a configured Daytona sandbox provider', + }); + } + + const environment = await sandboxEnvironmentStore.getSandboxEnvironment({ + tenant_id, + name: environmentName, + }); + if (environment?.created_by_subject.subject_id !== subject_id) { + throw new HTTPException(422, { + message: `Unknown sandbox environment "${environmentName}" — not found or not owned by the caller`, + }); + } + if (environment.manifest.provider !== providerRecord.name) { + throw new HTTPException(422, { + message: `Sandbox environment "${environmentName}" provider does not match the configured sandbox provider`, + }); + } + + const buildRef = providerRecord.build_metadata?.['build_ref']; + const requiresTenantSnapshot = environment.manifest.image.type === 'trueforge-default'; + if (requiresTenantSnapshot && !buildRef) { + throw new HTTPException(422, { + message: 'trueforge-default image requires a ready sandbox provider build_ref', + }); + } + + return { + environmentName, + createOptions: { + daytona: mapDaytonaEnvironmentToCreateParams({ + manifest: environment.manifest, + buildRef: buildRef ?? '', + }), + }, + requiresTenantSnapshot, + }; +} diff --git a/packages/trueforge/src/schemas/sandboxEnvironment.ts b/packages/trueforge/src/schemas/sandboxEnvironment.ts index a73ec0502..1541c227a 100644 --- a/packages/trueforge/src/schemas/sandboxEnvironment.ts +++ b/packages/trueforge/src/schemas/sandboxEnvironment.ts @@ -10,14 +10,17 @@ import { z } from '@hono/zod-openapi'; import { CreatedBySubjectSchema, TokenPaginationSchema } from '@truefoundry/trueforge-core/agent-session'; import { NameSchema } from './common'; -// random things in sdk:) -const DAYTONA_GPU_TYPE_VALUES = Object.values(GpuType).filter(value => value !== GpuType.UNKNOWN_DEFAULT_OPEN_API) as [ - string, - ...string[], -]; +/** Allowed Daytona GPU types (excludes the SDK's OpenAPI unknown sentinel). */ +const DAYTONA_GPU_TYPES = [ + GpuType.H100, + GpuType.H200, + GpuType.RTX_PRO_6000, + GpuType.RTX_4090, + GpuType.RTX_5090, +] as const satisfies readonly GpuType[]; export const DaytonaGpuTypeSchema = z - .enum(DAYTONA_GPU_TYPE_VALUES) + .enum(DAYTONA_GPU_TYPES) .describe('Preferred Daytona GPU type.') .openapi('DaytonaGpuType'); diff --git a/packages/trueforge/tests/unit/apis/deletedSessionCrud.test.ts b/packages/trueforge/tests/unit/apis/deletedSessionCrud.test.ts index 579bbf659..6da037dca 100644 --- a/packages/trueforge/tests/unit/apis/deletedSessionCrud.test.ts +++ b/packages/trueforge/tests/unit/apis/deletedSessionCrud.test.ts @@ -73,6 +73,7 @@ describe('public CRUD after session deletion', () => { resolveAgentStore: () => agentStore, eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => sandboxProviderStore, + resolveSandboxEnvironmentStore: () => sandboxEnvironmentStore, logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), diff --git a/packages/trueforge/tests/unit/apis/sandboxFileDownload.test.ts b/packages/trueforge/tests/unit/apis/sandboxFileDownload.test.ts index 3ad136a6c..db6bea793 100644 --- a/packages/trueforge/tests/unit/apis/sandboxFileDownload.test.ts +++ b/packages/trueforge/tests/unit/apis/sandboxFileDownload.test.ts @@ -11,6 +11,7 @@ import { SqliteAgentStore } from '../../../src/db/sqlite/agent-store/SqliteAgent import { createSqliteDb } from '../../../src/db/sqlite/client'; import { SqliteMcpServerStore } from '../../../src/db/sqlite/mcp-server-store/SqliteMcpServerStore'; import { SqliteModelProviderStore } from '../../../src/db/sqlite/model-provider-store/SqliteModelProviderStore'; +import { SqliteSandboxEnvironmentStore } from '../../../src/db/sqlite/sandbox-environment-store/SqliteSandboxEnvironmentStore'; import { SqliteSandboxProviderStore } from '../../../src/db/sqlite/sandbox-provider-store/SqliteSandboxProviderStore'; import { SqliteSessionStore } from '../../../src/db/sqlite/session-store/SqliteSessionStore'; import { SqliteSkillStore } from '../../../src/db/sqlite/skill-store/SqliteSkillStore'; @@ -51,6 +52,7 @@ async function buildApp() { resolveAgentStore: () => new SqliteAgentStore(db), eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), diff --git a/packages/trueforge/tests/unit/apis/schedules.test.ts b/packages/trueforge/tests/unit/apis/schedules.test.ts index af4b1bf1f..a9fa29580 100644 --- a/packages/trueforge/tests/unit/apis/schedules.test.ts +++ b/packages/trueforge/tests/unit/apis/schedules.test.ts @@ -72,6 +72,7 @@ function stubTurnExecutionDeps(agentStore: SqliteAgentStore, scheduleStore: Sqli resolveMcpServerStore: () => ({}) as never, turnSkillsResolverStore: { resolveTurnSkills: async () => [] }, resolveSandboxProviderStore: () => ({}) as never, + resolveSandboxEnvironmentStore: () => ({}) as never, }; } diff --git a/packages/trueforge/tests/unit/apis/turnHarnessErrorStatus.test.ts b/packages/trueforge/tests/unit/apis/turnHarnessErrorStatus.test.ts index eb270a7ca..53a4328d8 100644 --- a/packages/trueforge/tests/unit/apis/turnHarnessErrorStatus.test.ts +++ b/packages/trueforge/tests/unit/apis/turnHarnessErrorStatus.test.ts @@ -11,6 +11,7 @@ import { SqliteAgentStore } from '../../../src/db/sqlite/agent-store/SqliteAgent import { createSqliteDb } from '../../../src/db/sqlite/client'; import { SqliteMcpServerStore } from '../../../src/db/sqlite/mcp-server-store/SqliteMcpServerStore'; import { SqliteModelProviderStore } from '../../../src/db/sqlite/model-provider-store/SqliteModelProviderStore'; +import { SqliteSandboxEnvironmentStore } from '../../../src/db/sqlite/sandbox-environment-store/SqliteSandboxEnvironmentStore'; import { SqliteSandboxProviderStore } from '../../../src/db/sqlite/sandbox-provider-store/SqliteSandboxProviderStore'; import { SqliteSessionStore } from '../../../src/db/sqlite/session-store/SqliteSessionStore'; import { SqliteSkillStore } from '../../../src/db/sqlite/skill-store/SqliteSkillStore'; @@ -79,6 +80,7 @@ async function postTurnRejectingWith(error: AgentHarnessError): Promise new SqliteAgentStore(db), eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), diff --git a/packages/trueforge/tests/unit/apis/turns.test.ts b/packages/trueforge/tests/unit/apis/turns.test.ts index 1d893d6e3..c838f6606 100644 --- a/packages/trueforge/tests/unit/apis/turns.test.ts +++ b/packages/trueforge/tests/unit/apis/turns.test.ts @@ -16,6 +16,7 @@ import { SqliteAgentStore } from '../../../src/db/sqlite/agent-store/SqliteAgent import { createSqliteDb } from '../../../src/db/sqlite/client'; import { SqliteMcpServerStore } from '../../../src/db/sqlite/mcp-server-store/SqliteMcpServerStore'; import { SqliteModelProviderStore } from '../../../src/db/sqlite/model-provider-store/SqliteModelProviderStore'; +import { SqliteSandboxEnvironmentStore } from '../../../src/db/sqlite/sandbox-environment-store/SqliteSandboxEnvironmentStore'; import { SqliteSandboxProviderStore } from '../../../src/db/sqlite/sandbox-provider-store/SqliteSandboxProviderStore'; import { SqliteSessionStore } from '../../../src/db/sqlite/session-store/SqliteSessionStore'; import { SqliteSkillStore } from '../../../src/db/sqlite/skill-store/SqliteSkillStore'; @@ -75,6 +76,7 @@ describe('turns', () => { resolveAgentStore: () => new SqliteAgentStore(db), eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), @@ -152,6 +154,7 @@ describe('turns', () => { resolveAgentStore: () => agentStore, eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: { @@ -233,6 +236,7 @@ describe('turns', () => { resolveAgentStore: () => new SqliteAgentStore(db), eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), @@ -339,6 +343,7 @@ describe('turns', () => { resolveSkillStore: () => new SqliteSkillStore(db), eventSubscriptions, resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger, resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), @@ -446,6 +451,7 @@ describe('turns', () => { resolveAgentStore: () => new SqliteAgentStore(db), eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger, resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer: new TrueForgeAuthorizer(), @@ -531,6 +537,7 @@ describe('turns', () => { resolveAgentStore: () => agentStore, eventSubscriptions: new EventSubscriptionRegistry(undefined), resolveSandboxProviderStore: () => new SqliteSandboxProviderStore(db), + resolveSandboxEnvironmentStore: () => new SqliteSandboxEnvironmentStore(db), logger: createLogger({ silent: true }), resolveRequestContext: () => STANDALONE_REQUEST_CONTEXT, authorizer, diff --git a/packages/trueforge/tests/unit/sandbox/mapDaytonaEnvironmentToCreateParams.test.ts b/packages/trueforge/tests/unit/sandbox/mapDaytonaEnvironmentToCreateParams.test.ts new file mode 100644 index 000000000..c100b165d --- /dev/null +++ b/packages/trueforge/tests/unit/sandbox/mapDaytonaEnvironmentToCreateParams.test.ts @@ -0,0 +1,55 @@ +import { mapDaytonaEnvironmentToCreateParams } from '../../../src/sandbox/daytonaEnvironment'; +import type { DaytonaSandboxEnvironmentManifest } from '../../../src/schemas/sandboxEnvironment'; + +function manifest( + overrides: Partial & Pick, +): DaytonaSandboxEnvironmentManifest { + return { + type: 'daytona', + provider: 'daytona', + ...overrides, + }; +} + +describe('mapDaytonaEnvironmentToCreateParams', () => { + it('maps trueforge-default to the tenant buildRef snapshot', () => { + expect( + mapDaytonaEnvironmentToCreateParams({ + manifest: manifest({ image: { type: 'trueforge-default' } }), + buildRef: 'trueforge-build-abc', + }), + ).toEqual({ snapshot: 'trueforge-build-abc' }); + }); + + it('maps snapshot and docker image kinds with shared overrides', () => { + expect( + mapDaytonaEnvironmentToCreateParams({ + manifest: manifest({ + image: { type: 'snapshot', name: 'my-snap' }, + secrets: { API_KEY: 'org-secret' }, + networking: { network_block_all: true }, + lifecycle: { auto_stop_interval_in_minutes: 30 }, + }), + buildRef: 'unused', + }), + ).toEqual({ + snapshot: 'my-snap', + secrets: { API_KEY: 'org-secret' }, + networkBlockAll: true, + autoStopInterval: 30, + }); + + expect( + mapDaytonaEnvironmentToCreateParams({ + manifest: manifest({ + image: { type: 'docker', ref: 'ghcr.io/acme/box:1' }, + resources: { cpu: 2, memory: 4, gpu: 1, gpu_type: 'H100' }, + }), + buildRef: 'unused', + }), + ).toEqual({ + image: 'ghcr.io/acme/box:1', + resources: { cpu: 2, memory: 4, gpu: 1, gpuType: 'H100' }, + }); + }); +}); From d0f2e79ce48310634f78e05baee6b110485cfb30 Mon Sep 17 00:00:00 2001 From: "trueforge-dev-bot[bot]" Date: Fri, 18 Sep 2026 12:19:56 +0000 Subject: [PATCH 2/3] Regenerate OpenAPI document and SDKs --- .github/fern/openapi/openapi.json | 726 +++++++++++++- docs/openapi.json | 726 +++++++++++++- packages/trueforge-sdk/package.json | 11 + packages/trueforge-sdk/reference.md | 355 +++++++ packages/trueforge-sdk/src/Client.ts | 6 + .../trueforge-sdk/src/api/resources/index.ts | 2 + .../sandboxEnvironments/client/Client.ts | 642 ++++++++++++ .../sandboxEnvironments/client/index.ts | 1 + .../CreateSandboxEnvironmentRequest.ts | 24 + .../ListSandboxEnvironmentsRequest.ts | 12 + .../UpdateSandboxEnvironmentRequest.ts | 22 + .../client/requests/index.ts | 3 + .../resources/sandboxEnvironments/exports.ts | 4 + .../resources/sandboxEnvironments/index.ts | 1 + .../src/api/types/DaytonaDockerImage.ts | 7 + .../src/api/types/DaytonaGpuType.ts | 11 + .../types/DaytonaSandboxEnvironmentImage.ts | 8 + .../DaytonaSandboxEnvironmentLifecycle.ts | 10 + .../DaytonaSandboxEnvironmentNetworking.ts | 12 + .../DaytonaSandboxEnvironmentResources.ts | 16 + ...ytonaSandboxEnvironmentResourcesGpuType.ts | 8 + .../src/api/types/DaytonaSnapshotImage.ts | 7 + .../api/types/DaytonaTrueforgeDefaultImage.ts | 5 + .../types/DeleteSandboxEnvironmentResponse.ts | 3 + .../types/GetSandboxEnvironmentResponse.ts | 7 + .../src/api/types/ListPermissionsData.ts | 2 +- .../types/ListSandboxEnvironmentsResponse.ts | 8 + .../src/api/types/SandboxConfig.ts | 2 + .../src/api/types/SandboxCreatedEvent.ts | 2 + .../src/api/types/SandboxEnvironment.ts | 17 + .../api/types/SandboxEnvironmentManifest.ts | 16 + packages/trueforge-sdk/src/api/types/index.ts | 14 + .../src/serialization/resources/index.ts | 2 + .../sandboxEnvironments/client/index.ts | 1 + .../CreateSandboxEnvironmentRequest.ts | 24 + .../UpdateSandboxEnvironmentRequest.ts | 21 + .../client/requests/index.ts | 2 + .../resources/sandboxEnvironments/index.ts | 1 + .../serialization/types/DaytonaDockerImage.ts | 20 + .../src/serialization/types/DaytonaGpuType.ts | 12 + .../types/DaytonaSandboxEnvironmentImage.ts | 17 + .../DaytonaSandboxEnvironmentLifecycle.ts | 31 + .../DaytonaSandboxEnvironmentNetworking.ts | 24 + .../DaytonaSandboxEnvironmentResources.ts | 27 + ...ytonaSandboxEnvironmentResourcesGpuType.ts | 15 + .../types/DaytonaSnapshotImage.ts | 20 + .../types/DaytonaTrueforgeDefaultImage.ts | 18 + .../types/DeleteSandboxEnvironmentResponse.ts | 14 + .../types/GetSandboxEnvironmentResponse.ts | 19 + .../types/ListSandboxEnvironmentsResponse.ts | 22 + .../src/serialization/types/SandboxConfig.ts | 2 + .../types/SandboxCreatedEvent.ts | 2 + .../serialization/types/SandboxEnvironment.ts | 33 + .../types/SandboxEnvironmentManifest.ts | 34 + .../src/serialization/types/index.ts | 14 + .../tests/wire/sandboxEnvironments.test.ts | 691 +++++++++++++ python/trueforge_sdk/reference.md | 415 ++++++++ .../src/trueforge_sdk/__init__.py | 59 +- .../src/trueforge_sdk/base_client.py | 19 + .../sandbox_environments/__init__.py | 4 + .../sandbox_environments/client.py | 542 ++++++++++ .../sandbox_environments/raw_client.py | 927 ++++++++++++++++++ .../src/trueforge_sdk/types/__init__.py | 42 + .../types/daytona_docker_image.py | 24 + .../trueforge_sdk/types/daytona_gpu_type.py | 50 + .../daytona_sandbox_environment_image.py | 9 + .../daytona_sandbox_environment_lifecycle.py | 32 + .../daytona_sandbox_environment_networking.py | 37 + .../daytona_sandbox_environment_resources.py | 43 + ..._sandbox_environment_resources_gpu_type.py | 7 + .../types/daytona_snapshot_image.py | 24 + .../types/daytona_trueforge_default_image.py | 19 + .../delete_sandbox_environment_response.py | 17 + .../types/get_sandbox_environment_response.py | 20 + .../types/list_permissions_data.py | 2 +- .../list_sandbox_environments_response.py | 22 + .../src/trueforge_sdk/types/sandbox_config.py | 5 + .../types/sandbox_created_event.py | 5 + .../types/sandbox_environment.py | 44 + .../types/sandbox_environment_manifest.py | 40 + 80 files changed, 6149 insertions(+), 15 deletions(-) create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/Client.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/index.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/ListSandboxEnvironmentsRequest.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/index.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/exports.ts create mode 100644 packages/trueforge-sdk/src/api/resources/sandboxEnvironments/index.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaDockerImage.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaGpuType.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentImage.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentLifecycle.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentNetworking.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResources.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResourcesGpuType.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaSnapshotImage.ts create mode 100644 packages/trueforge-sdk/src/api/types/DaytonaTrueforgeDefaultImage.ts create mode 100644 packages/trueforge-sdk/src/api/types/DeleteSandboxEnvironmentResponse.ts create mode 100644 packages/trueforge-sdk/src/api/types/GetSandboxEnvironmentResponse.ts create mode 100644 packages/trueforge-sdk/src/api/types/ListSandboxEnvironmentsResponse.ts create mode 100644 packages/trueforge-sdk/src/api/types/SandboxEnvironment.ts create mode 100644 packages/trueforge-sdk/src/api/types/SandboxEnvironmentManifest.ts create mode 100644 packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/index.ts create mode 100644 packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts create mode 100644 packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts create mode 100644 packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/index.ts create mode 100644 packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/index.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaDockerImage.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaGpuType.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentImage.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentLifecycle.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentNetworking.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResources.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResourcesGpuType.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaSnapshotImage.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DaytonaTrueforgeDefaultImage.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/DeleteSandboxEnvironmentResponse.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/GetSandboxEnvironmentResponse.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/ListSandboxEnvironmentsResponse.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/SandboxEnvironment.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/SandboxEnvironmentManifest.ts create mode 100644 packages/trueforge-sdk/tests/wire/sandboxEnvironments.test.ts create mode 100644 python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/__init__.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/client.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/raw_client.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_docker_image.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_gpu_type.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_image.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_lifecycle.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_networking.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources_gpu_type.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_snapshot_image.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/daytona_trueforge_default_image.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/delete_sandbox_environment_response.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/get_sandbox_environment_response.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/list_sandbox_environments_response.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment_manifest.py diff --git a/.github/fern/openapi/openapi.json b/.github/fern/openapi/openapi.json index b40934b3f..d283137b7 100644 --- a/.github/fern/openapi/openapi.json +++ b/.github/fern/openapi/openapi.json @@ -1053,6 +1053,27 @@ ], "type": "object" }, + "CreateSandboxEnvironmentRequest": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional human-readable description.", + "maxLength": 1024, + "type": "string" + }, + "manifest": { + "$ref": "#/components/schemas/SandboxEnvironmentManifest" + }, + "name": { + "$ref": "#/components/schemas/ResourceName" + } + }, + "required": [ + "name", + "manifest" + ], + "type": "object" + }, "CreateScheduleRequest": { "additionalProperties": false, "properties": { @@ -1226,6 +1247,147 @@ ], "type": "object" }, + "DaytonaDockerImage": { + "additionalProperties": false, + "properties": { + "ref": { + "description": "Container image reference passed to Daytona create-from-image.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Build/create from a container image reference.", + "enum": [ + "docker" + ], + "type": "string" + } + }, + "required": [ + "type", + "ref" + ], + "type": "object" + }, + "DaytonaGpuType": { + "description": "Preferred Daytona GPU type.", + "enum": [ + "H100", + "H200", + "RTX-PRO-6000", + "RTX-4090", + "RTX-5090" + ], + "type": "string" + }, + "DaytonaSandboxEnvironmentImage": { + "discriminator": { + "mapping": { + "docker": "#/components/schemas/DaytonaDockerImage", + "snapshot": "#/components/schemas/DaytonaSnapshotImage", + "trueforge-default": "#/components/schemas/DaytonaTrueforgeDefaultImage" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/DaytonaTrueforgeDefaultImage" + }, + { + "$ref": "#/components/schemas/DaytonaSnapshotImage" + }, + { + "$ref": "#/components/schemas/DaytonaDockerImage" + } + ] + }, + "DaytonaSandboxEnvironmentLifecycle": { + "additionalProperties": false, + "properties": { + "auto_archive_interval_in_minutes": { + "description": "Minutes before Daytona auto-archives the sandbox (0 disables).", + "minimum": 0, + "type": "integer" + }, + "auto_delete_interval_in_minutes": { + "description": "Minutes before Daytona auto-deletes the sandbox (0 disables).", + "minimum": 0, + "type": "integer" + }, + "auto_stop_interval_in_minutes": { + "description": "Minutes of idle time before Daytona auto-stops the sandbox (0 disables).", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "DaytonaSandboxEnvironmentNetworking": { + "additionalProperties": false, + "properties": { + "domain_allow_list": { + "description": "Comma-separated allowed domains.", + "minLength": 1, + "type": "string" + }, + "network_allow_list": { + "description": "Comma-separated allowed CIDR network addresses.", + "minLength": 1, + "type": "string" + }, + "network_block_all": { + "description": "Block all outbound network access.", + "type": "boolean" + }, + "outbound_proxy_url": { + "description": "Outbound HTTP(S) proxy URL.", + "minLength": 1, + "type": "string" + } + }, + "type": "object" + }, + "DaytonaSandboxEnvironmentResources": { + "additionalProperties": false, + "properties": { + "cpu": { + "description": "CPU allocation in cores.", + "exclusiveMinimum": 0, + "type": "number" + }, + "disk": { + "description": "Disk allocation in GiB.", + "exclusiveMinimum": 0, + "type": "number" + }, + "gpu": { + "description": "GPU allocation in Daytona GPU units.", + "minimum": 0, + "type": "number" + }, + "gpu_type": { + "anyOf": [ + { + "$ref": "#/components/schemas/DaytonaGpuType" + }, + { + "items": { + "$ref": "#/components/schemas/DaytonaGpuType" + }, + "minItems": 1, + "type": "array" + } + ], + "description": "Preferred GPU type, or an ordered fallback list." + }, + "memory": { + "description": "Memory allocation in GiB.", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "type": "object" + }, "DaytonaSandboxProviderAuth": { "additionalProperties": false, "description": "Daytona authentication credentials.", @@ -1241,10 +1403,52 @@ ], "type": "object" }, + "DaytonaSnapshotImage": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Daytona snapshot name.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Clone an existing Daytona snapshot by name.", + "enum": [ + "snapshot" + ], + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + "DaytonaTrueforgeDefaultImage": { + "additionalProperties": false, + "properties": { + "type": { + "description": "Use the tenant provider TrueForge release snapshot.", + "enum": [ + "trueforge-default" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, "DeleteAgentResponse": { "properties": {}, "type": "object" }, + "DeleteSandboxEnvironmentResponse": { + "properties": {}, + "type": "object" + }, "DeleteScheduleResponse": { "properties": {}, "type": "object" @@ -1528,6 +1732,17 @@ ], "type": "object" }, + "GetSandboxEnvironmentResponse": { + "properties": { + "data": { + "$ref": "#/components/schemas/SandboxEnvironment" + } + }, + "required": [ + "data" + ], + "type": "object" + }, "GetSandboxProviderCatalogResponse": { "properties": { "data": { @@ -2277,7 +2492,7 @@ }, "type": "array" }, - "description": "For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` → `CREATE`).", + "description": "For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` or `sandbox-environment` → `CREATE`).", "type": "object" }, "type": { @@ -2322,6 +2537,24 @@ ], "type": "object" }, + "ListSandboxEnvironmentsResponse": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/SandboxEnvironment" + }, + "type": "array" + }, + "pagination": { + "$ref": "#/components/schemas/TokenPagination" + } + }, + "required": [ + "data", + "pagination" + ], + "type": "object" + }, "ListScheduleRunsResponse": { "properties": { "data": { @@ -3658,6 +3891,11 @@ "description": "Give the agent a sandbox. Required for skills and Code Mode.", "type": "boolean" }, + "environment": { + "description": "Caller-owned sandbox environment name. Omit to use the tenant provider defaults.", + "minLength": 1, + "type": "string" + }, "file_downloads": { "default": true, "description": "Allow downloading agent-produced files via the turn download endpoint. Default: true.", @@ -3675,6 +3913,10 @@ "description": "ISO 8601 event timestamp.", "type": "string" }, + "environment": { + "description": "Sandbox environment name when created with one; omit for provider defaults.", + "type": "string" + }, "id": { "description": "Unique identifier for the event (monotonic ULID).", "type": "string" @@ -3707,6 +3949,97 @@ ], "type": "object" }, + "SandboxEnvironment": { + "additionalProperties": false, + "properties": { + "created_at": { + "description": "ISO-8601 create time.", + "format": "date-time", + "type": "string" + }, + "created_by_subject": { + "$ref": "#/components/schemas/CreatedBySubject" + }, + "description": { + "description": "Optional human-readable description.", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "Immutable server-generated environment identifier.", + "minLength": 1, + "type": "string" + }, + "manifest": { + "$ref": "#/components/schemas/SandboxEnvironmentManifest" + }, + "name": { + "$ref": "#/components/schemas/ResourceName" + }, + "updated_at": { + "description": "ISO-8601 last update time.", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "manifest", + "created_by_subject", + "created_at", + "updated_at" + ], + "type": "object" + }, + "SandboxEnvironmentManifest": { + "additionalProperties": false, + "properties": { + "image": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentImage" + }, + "lifecycle": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentLifecycle" + }, + "networking": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentNetworking" + }, + "provider": { + "description": "Must match the configured sandbox provider name/type.", + "enum": [ + "daytona" + ], + "type": "string" + }, + "resources": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentResources" + }, + "secrets": { + "additionalProperties": { + "minLength": 1, + "type": "string" + }, + "description": "Map of sandbox env var name to an existing Daytona organization secret name.", + "type": "object" + }, + "type": { + "description": "Daytona sandbox environment.", + "enum": [ + "daytona" + ], + "type": "string" + } + }, + "required": [ + "type", + "provider", + "image" + ], + "type": "object" + }, "SandboxProviderManifest": { "additionalProperties": false, "properties": { @@ -5581,13 +5914,33 @@ ], "type": "object" }, - "UpdateSandboxProviderRequest": { + "UpdateSandboxEnvironmentRequest": { "additionalProperties": false, "properties": { - "manifest": { - "$ref": "#/components/schemas/SandboxProviderManifest" - } - }, + "description": { + "description": "Optional human-readable description.", + "maxLength": 1024, + "type": [ + "string", + "null" + ] + }, + "manifest": { + "$ref": "#/components/schemas/SandboxEnvironmentManifest" + } + }, + "required": [ + "manifest" + ], + "type": "object" + }, + "UpdateSandboxProviderRequest": { + "additionalProperties": false, + "properties": { + "manifest": { + "$ref": "#/components/schemas/SandboxProviderManifest" + } + }, "required": [ "manifest" ], @@ -7537,6 +7890,367 @@ "x-fern-sdk-method-name": "list" } }, + "/api/v1/sandbox-environments": { + "get": { + "description": "List sandbox environments created by the authenticated subject, newest first.", + "parameters": [ + { + "description": "Page size. Defaults to 25", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 25, + "description": "Page size. Defaults to 25", + "maximum": 25, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Opaque token from a previous response `next_page_token`.", + "in": "query", + "name": "page_token", + "required": false, + "schema": { + "description": "Opaque token from a previous response `next_page_token`.", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListSandboxEnvironmentsResponse" + } + } + }, + "description": "Paginated caller-owned sandbox environments." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid query parameters or page token." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + } + }, + "summary": "List sandbox environments", + "tags": [ + "Sandboxes" + ], + "x-fern-pagination": { + "cursor": "$request.page_token", + "next_cursor": "$response.pagination.next_page_token", + "results": "$response.data" + }, + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "list" + }, + "post": { + "description": "Create a Daytona sandbox environment owned by the authenticated subject.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSandboxEnvironmentRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetSandboxEnvironmentResponse" + } + } + }, + "description": "Created sandbox environment." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid request body." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Name already exists in the tenant." + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Provider, snapshot, or secret validation failed." + } + }, + "summary": "Create a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "create" + } + }, + "/api/v1/sandbox-environments/{sandbox_environment_id}": { + "delete": { + "description": "Delete a caller-owned sandbox environment. Fails if any agent references it.", + "parameters": [ + { + "description": "Immutable sandbox environment identifier.", + "in": "path", + "name": "sandbox_environment_id", + "required": true, + "schema": { + "description": "Immutable sandbox environment identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteSandboxEnvironmentResponse" + } + } + }, + "description": "Deleted." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Not found or you do not have access." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Environment is referenced by one or more agents." + } + }, + "summary": "Delete a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "delete" + }, + "get": { + "description": "Get a sandbox environment owned by the authenticated subject.", + "parameters": [ + { + "description": "Immutable sandbox environment identifier.", + "in": "path", + "name": "sandbox_environment_id", + "required": true, + "schema": { + "description": "Immutable sandbox environment identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetSandboxEnvironmentResponse" + } + } + }, + "description": "The sandbox environment." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Not found or you do not have access." + } + }, + "summary": "Get a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "get" + }, + "put": { + "description": "Replace the manifest (and optionally description). Name is immutable.", + "parameters": [ + { + "description": "Immutable sandbox environment identifier.", + "in": "path", + "name": "sandbox_environment_id", + "required": true, + "schema": { + "description": "Immutable sandbox environment identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSandboxEnvironmentRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetSandboxEnvironmentResponse" + } + } + }, + "description": "Updated sandbox environment." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid request body." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Not found or you do not have access." + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Provider, snapshot, or secret validation failed." + } + }, + "summary": "Update a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "update" + } + }, "/api/v1/schedules": { "get": { "description": "List schedules for the tenant, newest first.", diff --git a/docs/openapi.json b/docs/openapi.json index b40934b3f..d283137b7 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1053,6 +1053,27 @@ ], "type": "object" }, + "CreateSandboxEnvironmentRequest": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional human-readable description.", + "maxLength": 1024, + "type": "string" + }, + "manifest": { + "$ref": "#/components/schemas/SandboxEnvironmentManifest" + }, + "name": { + "$ref": "#/components/schemas/ResourceName" + } + }, + "required": [ + "name", + "manifest" + ], + "type": "object" + }, "CreateScheduleRequest": { "additionalProperties": false, "properties": { @@ -1226,6 +1247,147 @@ ], "type": "object" }, + "DaytonaDockerImage": { + "additionalProperties": false, + "properties": { + "ref": { + "description": "Container image reference passed to Daytona create-from-image.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Build/create from a container image reference.", + "enum": [ + "docker" + ], + "type": "string" + } + }, + "required": [ + "type", + "ref" + ], + "type": "object" + }, + "DaytonaGpuType": { + "description": "Preferred Daytona GPU type.", + "enum": [ + "H100", + "H200", + "RTX-PRO-6000", + "RTX-4090", + "RTX-5090" + ], + "type": "string" + }, + "DaytonaSandboxEnvironmentImage": { + "discriminator": { + "mapping": { + "docker": "#/components/schemas/DaytonaDockerImage", + "snapshot": "#/components/schemas/DaytonaSnapshotImage", + "trueforge-default": "#/components/schemas/DaytonaTrueforgeDefaultImage" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/DaytonaTrueforgeDefaultImage" + }, + { + "$ref": "#/components/schemas/DaytonaSnapshotImage" + }, + { + "$ref": "#/components/schemas/DaytonaDockerImage" + } + ] + }, + "DaytonaSandboxEnvironmentLifecycle": { + "additionalProperties": false, + "properties": { + "auto_archive_interval_in_minutes": { + "description": "Minutes before Daytona auto-archives the sandbox (0 disables).", + "minimum": 0, + "type": "integer" + }, + "auto_delete_interval_in_minutes": { + "description": "Minutes before Daytona auto-deletes the sandbox (0 disables).", + "minimum": 0, + "type": "integer" + }, + "auto_stop_interval_in_minutes": { + "description": "Minutes of idle time before Daytona auto-stops the sandbox (0 disables).", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "DaytonaSandboxEnvironmentNetworking": { + "additionalProperties": false, + "properties": { + "domain_allow_list": { + "description": "Comma-separated allowed domains.", + "minLength": 1, + "type": "string" + }, + "network_allow_list": { + "description": "Comma-separated allowed CIDR network addresses.", + "minLength": 1, + "type": "string" + }, + "network_block_all": { + "description": "Block all outbound network access.", + "type": "boolean" + }, + "outbound_proxy_url": { + "description": "Outbound HTTP(S) proxy URL.", + "minLength": 1, + "type": "string" + } + }, + "type": "object" + }, + "DaytonaSandboxEnvironmentResources": { + "additionalProperties": false, + "properties": { + "cpu": { + "description": "CPU allocation in cores.", + "exclusiveMinimum": 0, + "type": "number" + }, + "disk": { + "description": "Disk allocation in GiB.", + "exclusiveMinimum": 0, + "type": "number" + }, + "gpu": { + "description": "GPU allocation in Daytona GPU units.", + "minimum": 0, + "type": "number" + }, + "gpu_type": { + "anyOf": [ + { + "$ref": "#/components/schemas/DaytonaGpuType" + }, + { + "items": { + "$ref": "#/components/schemas/DaytonaGpuType" + }, + "minItems": 1, + "type": "array" + } + ], + "description": "Preferred GPU type, or an ordered fallback list." + }, + "memory": { + "description": "Memory allocation in GiB.", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "type": "object" + }, "DaytonaSandboxProviderAuth": { "additionalProperties": false, "description": "Daytona authentication credentials.", @@ -1241,10 +1403,52 @@ ], "type": "object" }, + "DaytonaSnapshotImage": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Daytona snapshot name.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Clone an existing Daytona snapshot by name.", + "enum": [ + "snapshot" + ], + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + "DaytonaTrueforgeDefaultImage": { + "additionalProperties": false, + "properties": { + "type": { + "description": "Use the tenant provider TrueForge release snapshot.", + "enum": [ + "trueforge-default" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, "DeleteAgentResponse": { "properties": {}, "type": "object" }, + "DeleteSandboxEnvironmentResponse": { + "properties": {}, + "type": "object" + }, "DeleteScheduleResponse": { "properties": {}, "type": "object" @@ -1528,6 +1732,17 @@ ], "type": "object" }, + "GetSandboxEnvironmentResponse": { + "properties": { + "data": { + "$ref": "#/components/schemas/SandboxEnvironment" + } + }, + "required": [ + "data" + ], + "type": "object" + }, "GetSandboxProviderCatalogResponse": { "properties": { "data": { @@ -2277,7 +2492,7 @@ }, "type": "array" }, - "description": "For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` → `CREATE`).", + "description": "For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` or `sandbox-environment` → `CREATE`).", "type": "object" }, "type": { @@ -2322,6 +2537,24 @@ ], "type": "object" }, + "ListSandboxEnvironmentsResponse": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/SandboxEnvironment" + }, + "type": "array" + }, + "pagination": { + "$ref": "#/components/schemas/TokenPagination" + } + }, + "required": [ + "data", + "pagination" + ], + "type": "object" + }, "ListScheduleRunsResponse": { "properties": { "data": { @@ -3658,6 +3891,11 @@ "description": "Give the agent a sandbox. Required for skills and Code Mode.", "type": "boolean" }, + "environment": { + "description": "Caller-owned sandbox environment name. Omit to use the tenant provider defaults.", + "minLength": 1, + "type": "string" + }, "file_downloads": { "default": true, "description": "Allow downloading agent-produced files via the turn download endpoint. Default: true.", @@ -3675,6 +3913,10 @@ "description": "ISO 8601 event timestamp.", "type": "string" }, + "environment": { + "description": "Sandbox environment name when created with one; omit for provider defaults.", + "type": "string" + }, "id": { "description": "Unique identifier for the event (monotonic ULID).", "type": "string" @@ -3707,6 +3949,97 @@ ], "type": "object" }, + "SandboxEnvironment": { + "additionalProperties": false, + "properties": { + "created_at": { + "description": "ISO-8601 create time.", + "format": "date-time", + "type": "string" + }, + "created_by_subject": { + "$ref": "#/components/schemas/CreatedBySubject" + }, + "description": { + "description": "Optional human-readable description.", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "Immutable server-generated environment identifier.", + "minLength": 1, + "type": "string" + }, + "manifest": { + "$ref": "#/components/schemas/SandboxEnvironmentManifest" + }, + "name": { + "$ref": "#/components/schemas/ResourceName" + }, + "updated_at": { + "description": "ISO-8601 last update time.", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "manifest", + "created_by_subject", + "created_at", + "updated_at" + ], + "type": "object" + }, + "SandboxEnvironmentManifest": { + "additionalProperties": false, + "properties": { + "image": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentImage" + }, + "lifecycle": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentLifecycle" + }, + "networking": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentNetworking" + }, + "provider": { + "description": "Must match the configured sandbox provider name/type.", + "enum": [ + "daytona" + ], + "type": "string" + }, + "resources": { + "$ref": "#/components/schemas/DaytonaSandboxEnvironmentResources" + }, + "secrets": { + "additionalProperties": { + "minLength": 1, + "type": "string" + }, + "description": "Map of sandbox env var name to an existing Daytona organization secret name.", + "type": "object" + }, + "type": { + "description": "Daytona sandbox environment.", + "enum": [ + "daytona" + ], + "type": "string" + } + }, + "required": [ + "type", + "provider", + "image" + ], + "type": "object" + }, "SandboxProviderManifest": { "additionalProperties": false, "properties": { @@ -5581,13 +5914,33 @@ ], "type": "object" }, - "UpdateSandboxProviderRequest": { + "UpdateSandboxEnvironmentRequest": { "additionalProperties": false, "properties": { - "manifest": { - "$ref": "#/components/schemas/SandboxProviderManifest" - } - }, + "description": { + "description": "Optional human-readable description.", + "maxLength": 1024, + "type": [ + "string", + "null" + ] + }, + "manifest": { + "$ref": "#/components/schemas/SandboxEnvironmentManifest" + } + }, + "required": [ + "manifest" + ], + "type": "object" + }, + "UpdateSandboxProviderRequest": { + "additionalProperties": false, + "properties": { + "manifest": { + "$ref": "#/components/schemas/SandboxProviderManifest" + } + }, "required": [ "manifest" ], @@ -7537,6 +7890,367 @@ "x-fern-sdk-method-name": "list" } }, + "/api/v1/sandbox-environments": { + "get": { + "description": "List sandbox environments created by the authenticated subject, newest first.", + "parameters": [ + { + "description": "Page size. Defaults to 25", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 25, + "description": "Page size. Defaults to 25", + "maximum": 25, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Opaque token from a previous response `next_page_token`.", + "in": "query", + "name": "page_token", + "required": false, + "schema": { + "description": "Opaque token from a previous response `next_page_token`.", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListSandboxEnvironmentsResponse" + } + } + }, + "description": "Paginated caller-owned sandbox environments." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid query parameters or page token." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + } + }, + "summary": "List sandbox environments", + "tags": [ + "Sandboxes" + ], + "x-fern-pagination": { + "cursor": "$request.page_token", + "next_cursor": "$response.pagination.next_page_token", + "results": "$response.data" + }, + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "list" + }, + "post": { + "description": "Create a Daytona sandbox environment owned by the authenticated subject.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSandboxEnvironmentRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetSandboxEnvironmentResponse" + } + } + }, + "description": "Created sandbox environment." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid request body." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Name already exists in the tenant." + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Provider, snapshot, or secret validation failed." + } + }, + "summary": "Create a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "create" + } + }, + "/api/v1/sandbox-environments/{sandbox_environment_id}": { + "delete": { + "description": "Delete a caller-owned sandbox environment. Fails if any agent references it.", + "parameters": [ + { + "description": "Immutable sandbox environment identifier.", + "in": "path", + "name": "sandbox_environment_id", + "required": true, + "schema": { + "description": "Immutable sandbox environment identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteSandboxEnvironmentResponse" + } + } + }, + "description": "Deleted." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Not found or you do not have access." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Environment is referenced by one or more agents." + } + }, + "summary": "Delete a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "delete" + }, + "get": { + "description": "Get a sandbox environment owned by the authenticated subject.", + "parameters": [ + { + "description": "Immutable sandbox environment identifier.", + "in": "path", + "name": "sandbox_environment_id", + "required": true, + "schema": { + "description": "Immutable sandbox environment identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetSandboxEnvironmentResponse" + } + } + }, + "description": "The sandbox environment." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Not found or you do not have access." + } + }, + "summary": "Get a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "get" + }, + "put": { + "description": "Replace the manifest (and optionally description). Name is immutable.", + "parameters": [ + { + "description": "Immutable sandbox environment identifier.", + "in": "path", + "name": "sandbox_environment_id", + "required": true, + "schema": { + "description": "Immutable sandbox environment identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSandboxEnvironmentRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetSandboxEnvironmentResponse" + } + } + }, + "description": "Updated sandbox environment." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid request body." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Unauthenticated." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Not found or you do not have access." + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Provider, snapshot, or secret validation failed." + } + }, + "summary": "Update a sandbox environment", + "tags": [ + "Sandboxes" + ], + "x-fern-sdk-group-name": [ + "sandbox_environments" + ], + "x-fern-sdk-method-name": "update" + } + }, "/api/v1/schedules": { "get": { "description": "List schedules for the tenant, newest first.", diff --git a/packages/trueforge-sdk/package.json b/packages/trueforge-sdk/package.json index 8287ffb70..a8ea1ce6e 100644 --- a/packages/trueforge-sdk/package.json +++ b/packages/trueforge-sdk/package.json @@ -99,6 +99,17 @@ }, "default": "./dist/cjs/api/resources/models/exports.js" }, + "./sandboxEnvironments": { + "import": { + "types": "./dist/esm/api/resources/sandboxEnvironments/exports.d.mts", + "default": "./dist/esm/api/resources/sandboxEnvironments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/api/resources/sandboxEnvironments/exports.d.ts", + "default": "./dist/cjs/api/resources/sandboxEnvironments/exports.js" + }, + "default": "./dist/cjs/api/resources/sandboxEnvironments/exports.js" + }, "./schedules": { "import": { "types": "./dist/esm/api/resources/schedules/exports.d.mts", diff --git a/packages/trueforge-sdk/reference.md b/packages/trueforge-sdk/reference.md index 0936210ae..23868bf53 100644 --- a/packages/trueforge-sdk/reference.md +++ b/packages/trueforge-sdk/reference.md @@ -896,6 +896,361 @@ await client.models.list(); + + + + +## SandboxEnvironments +
client.sandboxEnvironments.list({ ...params }) -> core.Page<TrueForge.SandboxEnvironment, TrueForge.ListSandboxEnvironmentsResponse> +
+
+ +#### 📝 Description + +
+
+ +
+
+ +List sandbox environments created by the authenticated subject, newest first. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +const pageableResponse = await client.sandboxEnvironments.list(); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.sandboxEnvironments.list(); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `TrueForge.ListSandboxEnvironmentsRequest` + +
+
+ +
+
+ +**requestOptions:** `SandboxEnvironmentsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.sandboxEnvironments.create({ ...params }) -> TrueForge.GetSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a Daytona sandbox environment owned by the authenticated subject. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.sandboxEnvironments.create({ + manifest: { + image: { + ref: "ref", + type: "docker" + }, + provider: "daytona", + type: "daytona" + }, + name: "name" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `TrueForge.CreateSandboxEnvironmentRequest` + +
+
+ +
+
+ +**requestOptions:** `SandboxEnvironmentsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.sandboxEnvironments.get(sandbox_environment_id) -> TrueForge.GetSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Get a sandbox environment owned by the authenticated subject. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.sandboxEnvironments.get("sandbox_environment_id"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**sandbox_environment_id:** `string` — Immutable sandbox environment identifier. + +
+
+ +
+
+ +**requestOptions:** `SandboxEnvironmentsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.sandboxEnvironments.update(sandbox_environment_id, { ...params }) -> TrueForge.GetSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Replace the manifest (and optionally description). Name is immutable. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.sandboxEnvironments.update("sandbox_environment_id", { + manifest: { + image: { + ref: "ref", + type: "docker" + }, + provider: "daytona", + type: "daytona" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**sandbox_environment_id:** `string` — Immutable sandbox environment identifier. + +
+
+ +
+
+ +**request:** `TrueForge.UpdateSandboxEnvironmentRequest` + +
+
+ +
+
+ +**requestOptions:** `SandboxEnvironmentsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.sandboxEnvironments.delete(sandbox_environment_id) -> TrueForge.DeleteSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a caller-owned sandbox environment. Fails if any agent references it. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.sandboxEnvironments.delete("sandbox_environment_id"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**sandbox_environment_id:** `string` — Immutable sandbox environment identifier. + +
+
+ +
+
+ +**requestOptions:** `SandboxEnvironmentsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/packages/trueforge-sdk/src/Client.ts b/packages/trueforge-sdk/src/Client.ts index d8dab2878..757773d88 100644 --- a/packages/trueforge-sdk/src/Client.ts +++ b/packages/trueforge-sdk/src/Client.ts @@ -6,6 +6,7 @@ import { CatalogsClient } from "./api/resources/catalogs/client/Client.js"; import { InternalClient } from "./api/resources/internal/client/Client.js"; import { McpServersClient } from "./api/resources/mcpServers/client/Client.js"; import { ModelsClient } from "./api/resources/models/client/Client.js"; +import { SandboxEnvironmentsClient } from "./api/resources/sandboxEnvironments/client/Client.js"; import { SchedulesClient } from "./api/resources/schedules/client/Client.js"; import { ServerClient } from "./api/resources/server/client/Client.js"; import { SessionsClient } from "./api/resources/sessions/client/Client.js"; @@ -29,6 +30,7 @@ export class TrueForge { protected _server: ServerClient | undefined; protected _mcpServers: McpServersClient | undefined; protected _models: ModelsClient | undefined; + protected _sandboxEnvironments: SandboxEnvironmentsClient | undefined; protected _schedules: SchedulesClient | undefined; protected _sessions: SessionsClient | undefined; protected _skills: SkillsClient | undefined; @@ -63,6 +65,10 @@ export class TrueForge { return (this._models ??= new ModelsClient(this._options)); } + public get sandboxEnvironments(): SandboxEnvironmentsClient { + return (this._sandboxEnvironments ??= new SandboxEnvironmentsClient(this._options)); + } + public get schedules(): SchedulesClient { return (this._schedules ??= new SchedulesClient(this._options)); } diff --git a/packages/trueforge-sdk/src/api/resources/index.ts b/packages/trueforge-sdk/src/api/resources/index.ts index 000fb4979..bef7cbe19 100644 --- a/packages/trueforge-sdk/src/api/resources/index.ts +++ b/packages/trueforge-sdk/src/api/resources/index.ts @@ -7,6 +7,8 @@ export * as internal from "./internal/index.js"; export * from "./mcpServers/client/requests/index.js"; export * as mcpServers from "./mcpServers/index.js"; export * as models from "./models/index.js"; +export * from "./sandboxEnvironments/client/requests/index.js"; +export * as sandboxEnvironments from "./sandboxEnvironments/index.js"; export * from "./schedules/client/requests/index.js"; export * as schedules from "./schedules/index.js"; export * as server from "./server/index.js"; diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/Client.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/Client.ts new file mode 100644 index 000000000..53d01c45b --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/Client.ts @@ -0,0 +1,642 @@ +// This file was auto-generated by Fern from our API Definition. + +import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js"; +import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../core/headers.js"; +import * as core from "../../../../core/index.js"; +import { mergeAdditionalBodyParameters } from "../../../../core/requestBody.js"; +import { handleNonStatusCodeError } from "../../../../errors/handleNonStatusCodeError.js"; +import * as errors from "../../../../errors/index.js"; +import * as serializers from "../../../../serialization/index.js"; +import * as TrueForge from "../../../index.js"; + +export declare namespace SandboxEnvironmentsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class SandboxEnvironmentsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: SandboxEnvironmentsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * List sandbox environments created by the authenticated subject, newest first. + * + * @param {TrueForge.ListSandboxEnvironmentsRequest} request + * @param {SandboxEnvironmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link TrueForge.BadRequestError} + * @throws {@link TrueForge.UnauthorizedError} + * @throws {@link errors.TrueForgeError} + * @throws {@link errors.TrueForgeTimeoutError} + * + * @example + * await client.sandboxEnvironments.list() + */ + public async list( + request: TrueForge.ListSandboxEnvironmentsRequest = {}, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): Promise> { + const list = core.HttpResponsePromise.interceptFunction( + async ( + request: TrueForge.ListSandboxEnvironmentsRequest, + ): Promise> => { + const { limit = 25, pageToken } = request; + const _queryParams: Record = { + limit, + page_token: pageToken, + }; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + "api/v1/sandbox-environments", + ), + method: "GET", + headers: _headers, + queryString: core.url + .queryBuilder() + .addMany(_queryParams) + .mergeAdditional(requestOptions?.queryParams) + .build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.ListSandboxEnvironmentsResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new TrueForge.BadRequestError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 401: + throw new TrueForge.UnauthorizedError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.TrueForgeError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/api/v1/sandbox-environments", + ); + }, + ); + const dataWithRawResponse = await list(request).withRawResponse(); + return new core.Page({ + response: dataWithRawResponse.data, + rawResponse: dataWithRawResponse.rawResponse, + hasNextPage: (response) => + response?.pagination.nextPageToken != null && + !(typeof response?.pagination.nextPageToken === "string" && response?.pagination.nextPageToken === ""), + getItems: (response) => response?.data ?? [], + loadPage: (response) => { + return list(core.setObjectProperty(request, "pageToken", response?.pagination.nextPageToken)); + }, + }); + } + + /** + * Create a Daytona sandbox environment owned by the authenticated subject. + * + * @param {TrueForge.CreateSandboxEnvironmentRequest} request + * @param {SandboxEnvironmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link TrueForge.BadRequestError} + * @throws {@link TrueForge.UnauthorizedError} + * @throws {@link TrueForge.ConflictError} + * @throws {@link TrueForge.UnprocessableEntityError} + * @throws {@link errors.TrueForgeError} + * @throws {@link errors.TrueForgeTimeoutError} + * + * @example + * await client.sandboxEnvironments.create({ + * manifest: { + * image: { + * ref: "ref", + * type: "docker" + * }, + * provider: "daytona", + * type: "daytona" + * }, + * name: "name" + * }) + */ + public create( + request: TrueForge.CreateSandboxEnvironmentRequest, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: TrueForge.CreateSandboxEnvironmentRequest, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + "api/v1/sandbox-environments", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: mergeAdditionalBodyParameters( + serializers.CreateSandboxEnvironmentRequest.jsonOrThrow(request, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + omitUndefined: true, + }), + requestOptions?.additionalBodyParameters, + ), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.GetSandboxEnvironmentResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new TrueForge.BadRequestError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 401: + throw new TrueForge.UnauthorizedError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 409: + throw new TrueForge.ConflictError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 422: + throw new TrueForge.UnprocessableEntityError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.TrueForgeError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/api/v1/sandbox-environments"); + } + + /** + * Get a sandbox environment owned by the authenticated subject. + * + * @param {string} sandbox_environment_id - Immutable sandbox environment identifier. + * @param {SandboxEnvironmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link TrueForge.UnauthorizedError} + * @throws {@link TrueForge.NotFoundError} + * @throws {@link errors.TrueForgeError} + * @throws {@link errors.TrueForgeTimeoutError} + * + * @example + * await client.sandboxEnvironments.get("sandbox_environment_id") + */ + public get( + sandbox_environment_id: string, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(sandbox_environment_id, requestOptions)); + } + + private async __get( + sandbox_environment_id: string, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `api/v1/sandbox-environments/${core.url.encodePathParam(sandbox_environment_id)}`, + ), + method: "GET", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.GetSandboxEnvironmentResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new TrueForge.UnauthorizedError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 404: + throw new TrueForge.NotFoundError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.TrueForgeError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/api/v1/sandbox-environments/{sandbox_environment_id}", + ); + } + + /** + * Replace the manifest (and optionally description). Name is immutable. + * + * @param {string} sandbox_environment_id - Immutable sandbox environment identifier. + * @param {TrueForge.UpdateSandboxEnvironmentRequest} request + * @param {SandboxEnvironmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link TrueForge.BadRequestError} + * @throws {@link TrueForge.UnauthorizedError} + * @throws {@link TrueForge.NotFoundError} + * @throws {@link TrueForge.UnprocessableEntityError} + * @throws {@link errors.TrueForgeError} + * @throws {@link errors.TrueForgeTimeoutError} + * + * @example + * await client.sandboxEnvironments.update("sandbox_environment_id", { + * manifest: { + * image: { + * ref: "ref", + * type: "docker" + * }, + * provider: "daytona", + * type: "daytona" + * } + * }) + */ + public update( + sandbox_environment_id: string, + request: TrueForge.UpdateSandboxEnvironmentRequest, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(sandbox_environment_id, request, requestOptions)); + } + + private async __update( + sandbox_environment_id: string, + request: TrueForge.UpdateSandboxEnvironmentRequest, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `api/v1/sandbox-environments/${core.url.encodePathParam(sandbox_environment_id)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: mergeAdditionalBodyParameters( + serializers.UpdateSandboxEnvironmentRequest.jsonOrThrow(request, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + omitUndefined: true, + }), + requestOptions?.additionalBodyParameters, + ), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.GetSandboxEnvironmentResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new TrueForge.BadRequestError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 401: + throw new TrueForge.UnauthorizedError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 404: + throw new TrueForge.NotFoundError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 422: + throw new TrueForge.UnprocessableEntityError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.TrueForgeError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "PUT", + "/api/v1/sandbox-environments/{sandbox_environment_id}", + ); + } + + /** + * Delete a caller-owned sandbox environment. Fails if any agent references it. + * + * @param {string} sandbox_environment_id - Immutable sandbox environment identifier. + * @param {SandboxEnvironmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link TrueForge.UnauthorizedError} + * @throws {@link TrueForge.NotFoundError} + * @throws {@link TrueForge.ConflictError} + * @throws {@link errors.TrueForgeError} + * @throws {@link errors.TrueForgeTimeoutError} + * + * @example + * await client.sandboxEnvironments.delete("sandbox_environment_id") + */ + public delete( + sandbox_environment_id: string, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(sandbox_environment_id, requestOptions)); + } + + private async __delete( + sandbox_environment_id: string, + requestOptions?: SandboxEnvironmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `api/v1/sandbox-environments/${core.url.encodePathParam(sandbox_environment_id)}`, + ), + method: "DELETE", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.DeleteSandboxEnvironmentResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new TrueForge.UnauthorizedError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 404: + throw new TrueForge.NotFoundError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 409: + throw new TrueForge.ConflictError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.TrueForgeError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "DELETE", + "/api/v1/sandbox-environments/{sandbox_environment_id}", + ); + } +} diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/index.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/index.ts new file mode 100644 index 000000000..195f9aa8a --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts new file mode 100644 index 000000000..af9348dd0 --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../../../index.js"; + +/** + * @example + * { + * manifest: { + * image: { + * ref: "ref", + * type: "docker" + * }, + * provider: "daytona", + * type: "daytona" + * }, + * name: "name" + * } + */ +export interface CreateSandboxEnvironmentRequest { + /** Optional human-readable description. */ + description?: string; + manifest: TrueForge.SandboxEnvironmentManifest; + name: TrueForge.ResourceName; +} diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/ListSandboxEnvironmentsRequest.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/ListSandboxEnvironmentsRequest.ts new file mode 100644 index 000000000..2efd1793b --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/ListSandboxEnvironmentsRequest.ts @@ -0,0 +1,12 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * {} + */ +export interface ListSandboxEnvironmentsRequest { + /** Page size. Defaults to 25 */ + limit?: number; + /** Opaque token from a previous response `next_page_token`. */ + pageToken?: string; +} diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts new file mode 100644 index 000000000..e44ba8223 --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../../../index.js"; + +/** + * @example + * { + * manifest: { + * image: { + * ref: "ref", + * type: "docker" + * }, + * provider: "daytona", + * type: "daytona" + * } + * } + */ +export interface UpdateSandboxEnvironmentRequest { + /** Optional human-readable description. */ + description?: string | null; + manifest: TrueForge.SandboxEnvironmentManifest; +} diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/index.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/index.ts new file mode 100644 index 000000000..f20f36488 --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/client/requests/index.ts @@ -0,0 +1,3 @@ +export type { CreateSandboxEnvironmentRequest } from "./CreateSandboxEnvironmentRequest.js"; +export type { ListSandboxEnvironmentsRequest } from "./ListSandboxEnvironmentsRequest.js"; +export type { UpdateSandboxEnvironmentRequest } from "./UpdateSandboxEnvironmentRequest.js"; diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/exports.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/exports.ts new file mode 100644 index 000000000..fc58f1b3e --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/exports.ts @@ -0,0 +1,4 @@ +// This file was auto-generated by Fern from our API Definition. + +export { SandboxEnvironmentsClient } from "./client/Client.js"; +export * from "./client/index.js"; diff --git a/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/index.ts b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/index.ts new file mode 100644 index 000000000..914b8c3c7 --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sandboxEnvironments/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/packages/trueforge-sdk/src/api/types/DaytonaDockerImage.ts b/packages/trueforge-sdk/src/api/types/DaytonaDockerImage.ts new file mode 100644 index 000000000..aeba33f8c --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaDockerImage.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface DaytonaDockerImage { + /** Container image reference passed to Daytona create-from-image. */ + ref: string; + type: "docker"; +} diff --git a/packages/trueforge-sdk/src/api/types/DaytonaGpuType.ts b/packages/trueforge-sdk/src/api/types/DaytonaGpuType.ts new file mode 100644 index 000000000..e9de0407d --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaGpuType.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** Preferred Daytona GPU type. */ +export const DaytonaGpuType = { + H100: "H100", + H200: "H200", + RtxPro6000: "RTX-PRO-6000", + Rtx4090: "RTX-4090", + Rtx5090: "RTX-5090", +} as const; +export type DaytonaGpuType = (typeof DaytonaGpuType)[keyof typeof DaytonaGpuType]; diff --git a/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentImage.ts b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentImage.ts new file mode 100644 index 000000000..765912cf9 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentImage.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export type DaytonaSandboxEnvironmentImage = + | TrueForge.DaytonaDockerImage + | TrueForge.DaytonaSnapshotImage + | TrueForge.DaytonaTrueforgeDefaultImage; diff --git a/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentLifecycle.ts b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentLifecycle.ts new file mode 100644 index 000000000..f1443f40f --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentLifecycle.ts @@ -0,0 +1,10 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface DaytonaSandboxEnvironmentLifecycle { + /** Minutes before Daytona auto-archives the sandbox (0 disables). */ + autoArchiveIntervalInMinutes?: number; + /** Minutes before Daytona auto-deletes the sandbox (0 disables). */ + autoDeleteIntervalInMinutes?: number; + /** Minutes of idle time before Daytona auto-stops the sandbox (0 disables). */ + autoStopIntervalInMinutes?: number; +} diff --git a/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentNetworking.ts b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentNetworking.ts new file mode 100644 index 000000000..69d8e9253 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentNetworking.ts @@ -0,0 +1,12 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface DaytonaSandboxEnvironmentNetworking { + /** Comma-separated allowed domains. */ + domainAllowList?: string; + /** Comma-separated allowed CIDR network addresses. */ + networkAllowList?: string; + /** Block all outbound network access. */ + networkBlockAll?: boolean; + /** Outbound HTTP(S) proxy URL. */ + outboundProxyUrl?: string; +} diff --git a/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResources.ts b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResources.ts new file mode 100644 index 000000000..168bc67a9 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResources.ts @@ -0,0 +1,16 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface DaytonaSandboxEnvironmentResources { + /** CPU allocation in cores. */ + cpu?: number; + /** Disk allocation in GiB. */ + disk?: number; + /** GPU allocation in Daytona GPU units. */ + gpu?: number; + /** Preferred GPU type, or an ordered fallback list. */ + gpuType?: TrueForge.DaytonaSandboxEnvironmentResourcesGpuType; + /** Memory allocation in GiB. */ + memory?: number; +} diff --git a/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResourcesGpuType.ts b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResourcesGpuType.ts new file mode 100644 index 000000000..cb55bbd15 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaSandboxEnvironmentResourcesGpuType.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +/** + * Preferred GPU type, or an ordered fallback list. + */ +export type DaytonaSandboxEnvironmentResourcesGpuType = TrueForge.DaytonaGpuType | TrueForge.DaytonaGpuType[]; diff --git a/packages/trueforge-sdk/src/api/types/DaytonaSnapshotImage.ts b/packages/trueforge-sdk/src/api/types/DaytonaSnapshotImage.ts new file mode 100644 index 000000000..2ac83bcdf --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaSnapshotImage.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface DaytonaSnapshotImage { + /** Daytona snapshot name. */ + name: string; + type: "snapshot"; +} diff --git a/packages/trueforge-sdk/src/api/types/DaytonaTrueforgeDefaultImage.ts b/packages/trueforge-sdk/src/api/types/DaytonaTrueforgeDefaultImage.ts new file mode 100644 index 000000000..f50ee4bbe --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DaytonaTrueforgeDefaultImage.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface DaytonaTrueforgeDefaultImage { + type: "trueforge-default"; +} diff --git a/packages/trueforge-sdk/src/api/types/DeleteSandboxEnvironmentResponse.ts b/packages/trueforge-sdk/src/api/types/DeleteSandboxEnvironmentResponse.ts new file mode 100644 index 000000000..f9bb1fc09 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/DeleteSandboxEnvironmentResponse.ts @@ -0,0 +1,3 @@ +// This file was auto-generated by Fern from our API Definition. + +export type DeleteSandboxEnvironmentResponse = {}; diff --git a/packages/trueforge-sdk/src/api/types/GetSandboxEnvironmentResponse.ts b/packages/trueforge-sdk/src/api/types/GetSandboxEnvironmentResponse.ts new file mode 100644 index 000000000..eaaa4d921 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/GetSandboxEnvironmentResponse.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface GetSandboxEnvironmentResponse { + data: TrueForge.SandboxEnvironment; +} diff --git a/packages/trueforge-sdk/src/api/types/ListPermissionsData.ts b/packages/trueforge-sdk/src/api/types/ListPermissionsData.ts index e66887730..b4f62f10d 100644 --- a/packages/trueforge-sdk/src/api/types/ListPermissionsData.ts +++ b/packages/trueforge-sdk/src/api/types/ListPermissionsData.ts @@ -3,7 +3,7 @@ import type * as TrueForge from "../index.js"; export interface ListPermissionsData { - /** For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` → `CREATE`). */ + /** For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` or `sandbox-environment` → `CREATE`). */ permissions: Record; type: TrueForge.PermissionResourceType; } diff --git a/packages/trueforge-sdk/src/api/types/ListSandboxEnvironmentsResponse.ts b/packages/trueforge-sdk/src/api/types/ListSandboxEnvironmentsResponse.ts new file mode 100644 index 000000000..93b9b399e --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/ListSandboxEnvironmentsResponse.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface ListSandboxEnvironmentsResponse { + data: TrueForge.SandboxEnvironment[]; + pagination: TrueForge.TokenPagination; +} diff --git a/packages/trueforge-sdk/src/api/types/SandboxConfig.ts b/packages/trueforge-sdk/src/api/types/SandboxConfig.ts index ebd8ca110..f7c6bb649 100644 --- a/packages/trueforge-sdk/src/api/types/SandboxConfig.ts +++ b/packages/trueforge-sdk/src/api/types/SandboxConfig.ts @@ -3,6 +3,8 @@ export interface SandboxConfig { /** Give the agent a sandbox. Required for skills and Code Mode. */ enabled: boolean; + /** Caller-owned sandbox environment name. Omit to use the tenant provider defaults. */ + environment?: string; /** Allow downloading agent-produced files via the turn download endpoint. Default: true. */ fileDownloads?: boolean; } diff --git a/packages/trueforge-sdk/src/api/types/SandboxCreatedEvent.ts b/packages/trueforge-sdk/src/api/types/SandboxCreatedEvent.ts index 388e16b1f..edb56a71f 100644 --- a/packages/trueforge-sdk/src/api/types/SandboxCreatedEvent.ts +++ b/packages/trueforge-sdk/src/api/types/SandboxCreatedEvent.ts @@ -3,6 +3,8 @@ export interface SandboxCreatedEvent { /** ISO 8601 event timestamp. */ createdAt: string; + /** Sandbox environment name when created with one; omit for provider defaults. */ + environment?: string; /** Unique identifier for the event (monotonic ULID). */ id: string; /** Provider sandbox id. */ diff --git a/packages/trueforge-sdk/src/api/types/SandboxEnvironment.ts b/packages/trueforge-sdk/src/api/types/SandboxEnvironment.ts new file mode 100644 index 000000000..30ee28f96 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/SandboxEnvironment.ts @@ -0,0 +1,17 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface SandboxEnvironment { + /** ISO-8601 create time. */ + createdAt: Date; + createdBySubject: TrueForge.CreatedBySubject; + /** Optional human-readable description. */ + description: string | null; + /** Immutable server-generated environment identifier. */ + id: string; + manifest: TrueForge.SandboxEnvironmentManifest; + name: TrueForge.ResourceName; + /** ISO-8601 last update time. */ + updatedAt: Date; +} diff --git a/packages/trueforge-sdk/src/api/types/SandboxEnvironmentManifest.ts b/packages/trueforge-sdk/src/api/types/SandboxEnvironmentManifest.ts new file mode 100644 index 000000000..c3cab7ace --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/SandboxEnvironmentManifest.ts @@ -0,0 +1,16 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface SandboxEnvironmentManifest { + image: TrueForge.DaytonaSandboxEnvironmentImage; + lifecycle?: TrueForge.DaytonaSandboxEnvironmentLifecycle; + networking?: TrueForge.DaytonaSandboxEnvironmentNetworking; + /** Must match the configured sandbox provider name/type. */ + provider: "daytona"; + resources?: TrueForge.DaytonaSandboxEnvironmentResources; + /** Map of sandbox env var name to an existing Daytona organization secret name. */ + secrets?: Record; + /** Daytona sandbox environment. */ + type: "daytona"; +} diff --git a/packages/trueforge-sdk/src/api/types/index.ts b/packages/trueforge-sdk/src/api/types/index.ts index 9b29138b5..2985092b1 100644 --- a/packages/trueforge-sdk/src/api/types/index.ts +++ b/packages/trueforge-sdk/src/api/types/index.ts @@ -49,8 +49,18 @@ export * from "./CreateScheduleRunResponse.js"; export * from "./CreateSessionAgent.js"; export * from "./CronExpression.js"; export * from "./CustomModelProvider.js"; +export * from "./DaytonaDockerImage.js"; +export * from "./DaytonaGpuType.js"; +export * from "./DaytonaSandboxEnvironmentImage.js"; +export * from "./DaytonaSandboxEnvironmentLifecycle.js"; +export * from "./DaytonaSandboxEnvironmentNetworking.js"; +export * from "./DaytonaSandboxEnvironmentResources.js"; +export * from "./DaytonaSandboxEnvironmentResourcesGpuType.js"; export * from "./DaytonaSandboxProviderAuth.js"; +export * from "./DaytonaSnapshotImage.js"; +export * from "./DaytonaTrueforgeDefaultImage.js"; export * from "./DeleteAgentResponse.js"; +export * from "./DeleteSandboxEnvironmentResponse.js"; export * from "./DeleteScheduleResponse.js"; export * from "./DynamicSubAgentsConfig.js"; export * from "./ExtendedChunkDeltaToolCall.js"; @@ -68,6 +78,7 @@ export * from "./GetMeResponse.js"; export * from "./GetMeSubject.js"; export * from "./GetModelProviderCatalogResponse.js"; export * from "./GetModelProviderResponse.js"; +export * from "./GetSandboxEnvironmentResponse.js"; export * from "./GetSandboxProviderCatalogResponse.js"; export * from "./GetSandboxProviderResponse.js"; export * from "./GetScheduleResponse.js"; @@ -92,6 +103,7 @@ export * from "./ListMcpServerToolsResponse.js"; export * from "./ListModelProvidersResponse.js"; export * from "./ListPermissionsData.js"; export * from "./ListPermissionsResponse.js"; +export * from "./ListSandboxEnvironmentsResponse.js"; export * from "./ListScheduleRunsResponse.js"; export * from "./ListSchedulesResponse.js"; export * from "./ListSessionEventsResponse.js"; @@ -155,6 +167,8 @@ export * from "./SandboxBuildStatus.js"; export * from "./SandboxCapability.js"; export * from "./SandboxConfig.js"; export * from "./SandboxCreatedEvent.js"; +export * from "./SandboxEnvironment.js"; +export * from "./SandboxEnvironmentManifest.js"; export * from "./SandboxProviderManifest.js"; export * from "./Schedule.js"; export * from "./ScheduleManifest.js"; diff --git a/packages/trueforge-sdk/src/serialization/resources/index.ts b/packages/trueforge-sdk/src/serialization/resources/index.ts index 4d4bfd3a0..fb0e61aaf 100644 --- a/packages/trueforge-sdk/src/serialization/resources/index.ts +++ b/packages/trueforge-sdk/src/serialization/resources/index.ts @@ -2,6 +2,8 @@ export * from "./agents/client/requests/index.js"; export * as agents from "./agents/index.js"; export * from "./internal/client/requests/index.js"; export * as internal from "./internal/index.js"; +export * from "./sandboxEnvironments/client/requests/index.js"; +export * as sandboxEnvironments from "./sandboxEnvironments/index.js"; export * from "./schedules/client/requests/index.js"; export * as schedules from "./schedules/index.js"; export * from "./sessions/client/requests/index.js"; diff --git a/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/index.ts b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/index.ts new file mode 100644 index 000000000..195f9aa8a --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts new file mode 100644 index 000000000..fa8f816a6 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/CreateSandboxEnvironmentRequest.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../../../../api/index.js"; +import * as core from "../../../../../core/index.js"; +import type * as serializers from "../../../../index.js"; +import { ResourceName } from "../../../../types/ResourceName.js"; +import { SandboxEnvironmentManifest } from "../../../../types/SandboxEnvironmentManifest.js"; + +export const CreateSandboxEnvironmentRequest: core.serialization.Schema< + serializers.CreateSandboxEnvironmentRequest.Raw, + TrueForge.CreateSandboxEnvironmentRequest +> = core.serialization.object({ + description: core.serialization.string().optional(), + manifest: SandboxEnvironmentManifest, + name: ResourceName, +}); + +export declare namespace CreateSandboxEnvironmentRequest { + export interface Raw { + description?: string | null; + manifest: SandboxEnvironmentManifest.Raw; + name: ResourceName.Raw; + } +} diff --git a/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts new file mode 100644 index 000000000..7b733a6ee --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/UpdateSandboxEnvironmentRequest.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../../../../api/index.js"; +import * as core from "../../../../../core/index.js"; +import type * as serializers from "../../../../index.js"; +import { SandboxEnvironmentManifest } from "../../../../types/SandboxEnvironmentManifest.js"; + +export const UpdateSandboxEnvironmentRequest: core.serialization.Schema< + serializers.UpdateSandboxEnvironmentRequest.Raw, + TrueForge.UpdateSandboxEnvironmentRequest +> = core.serialization.object({ + description: core.serialization.string().optionalNullable(), + manifest: SandboxEnvironmentManifest, +}); + +export declare namespace UpdateSandboxEnvironmentRequest { + export interface Raw { + description?: (string | null | undefined) | null; + manifest: SandboxEnvironmentManifest.Raw; + } +} diff --git a/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/index.ts b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/index.ts new file mode 100644 index 000000000..77023ee76 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/client/requests/index.ts @@ -0,0 +1,2 @@ +export { CreateSandboxEnvironmentRequest } from "./CreateSandboxEnvironmentRequest.js"; +export { UpdateSandboxEnvironmentRequest } from "./UpdateSandboxEnvironmentRequest.js"; diff --git a/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/index.ts b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/index.ts new file mode 100644 index 000000000..914b8c3c7 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/resources/sandboxEnvironments/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaDockerImage.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaDockerImage.ts new file mode 100644 index 000000000..5dbc1058c --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaDockerImage.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DaytonaDockerImage: core.serialization.ObjectSchema< + serializers.DaytonaDockerImage.Raw, + TrueForge.DaytonaDockerImage +> = core.serialization.object({ + ref: core.serialization.string(), + type: core.serialization.stringLiteral("docker"), +}); + +export declare namespace DaytonaDockerImage { + export interface Raw { + ref: string; + type: "docker"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaGpuType.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaGpuType.ts new file mode 100644 index 000000000..3640aa8ff --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaGpuType.ts @@ -0,0 +1,12 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DaytonaGpuType: core.serialization.Schema = + core.serialization.enum_(["H100", "H200", "RTX-PRO-6000", "RTX-4090", "RTX-5090"]); + +export declare namespace DaytonaGpuType { + export type Raw = "H100" | "H200" | "RTX-PRO-6000" | "RTX-4090" | "RTX-5090"; +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentImage.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentImage.ts new file mode 100644 index 000000000..5e52642cb --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentImage.ts @@ -0,0 +1,17 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { DaytonaDockerImage } from "./DaytonaDockerImage.js"; +import { DaytonaSnapshotImage } from "./DaytonaSnapshotImage.js"; +import { DaytonaTrueforgeDefaultImage } from "./DaytonaTrueforgeDefaultImage.js"; + +export const DaytonaSandboxEnvironmentImage: core.serialization.Schema< + serializers.DaytonaSandboxEnvironmentImage.Raw, + TrueForge.DaytonaSandboxEnvironmentImage +> = core.serialization.undiscriminatedUnion([DaytonaDockerImage, DaytonaSnapshotImage, DaytonaTrueforgeDefaultImage]); + +export declare namespace DaytonaSandboxEnvironmentImage { + export type Raw = DaytonaDockerImage.Raw | DaytonaSnapshotImage.Raw | DaytonaTrueforgeDefaultImage.Raw; +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentLifecycle.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentLifecycle.ts new file mode 100644 index 000000000..83132d770 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentLifecycle.ts @@ -0,0 +1,31 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DaytonaSandboxEnvironmentLifecycle: core.serialization.ObjectSchema< + serializers.DaytonaSandboxEnvironmentLifecycle.Raw, + TrueForge.DaytonaSandboxEnvironmentLifecycle +> = core.serialization.object({ + autoArchiveIntervalInMinutes: core.serialization.property( + "auto_archive_interval_in_minutes", + core.serialization.number().optional(), + ), + autoDeleteIntervalInMinutes: core.serialization.property( + "auto_delete_interval_in_minutes", + core.serialization.number().optional(), + ), + autoStopIntervalInMinutes: core.serialization.property( + "auto_stop_interval_in_minutes", + core.serialization.number().optional(), + ), +}); + +export declare namespace DaytonaSandboxEnvironmentLifecycle { + export interface Raw { + auto_archive_interval_in_minutes?: number | null; + auto_delete_interval_in_minutes?: number | null; + auto_stop_interval_in_minutes?: number | null; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentNetworking.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentNetworking.ts new file mode 100644 index 000000000..599ba6355 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentNetworking.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DaytonaSandboxEnvironmentNetworking: core.serialization.ObjectSchema< + serializers.DaytonaSandboxEnvironmentNetworking.Raw, + TrueForge.DaytonaSandboxEnvironmentNetworking +> = core.serialization.object({ + domainAllowList: core.serialization.property("domain_allow_list", core.serialization.string().optional()), + networkAllowList: core.serialization.property("network_allow_list", core.serialization.string().optional()), + networkBlockAll: core.serialization.property("network_block_all", core.serialization.boolean().optional()), + outboundProxyUrl: core.serialization.property("outbound_proxy_url", core.serialization.string().optional()), +}); + +export declare namespace DaytonaSandboxEnvironmentNetworking { + export interface Raw { + domain_allow_list?: string | null; + network_allow_list?: string | null; + network_block_all?: boolean | null; + outbound_proxy_url?: string | null; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResources.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResources.ts new file mode 100644 index 000000000..fae5eb8ba --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResources.ts @@ -0,0 +1,27 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { DaytonaSandboxEnvironmentResourcesGpuType } from "./DaytonaSandboxEnvironmentResourcesGpuType.js"; + +export const DaytonaSandboxEnvironmentResources: core.serialization.ObjectSchema< + serializers.DaytonaSandboxEnvironmentResources.Raw, + TrueForge.DaytonaSandboxEnvironmentResources +> = core.serialization.object({ + cpu: core.serialization.number().optional(), + disk: core.serialization.number().optional(), + gpu: core.serialization.number().optional(), + gpuType: core.serialization.property("gpu_type", DaytonaSandboxEnvironmentResourcesGpuType.optional()), + memory: core.serialization.number().optional(), +}); + +export declare namespace DaytonaSandboxEnvironmentResources { + export interface Raw { + cpu?: number | null; + disk?: number | null; + gpu?: number | null; + gpu_type?: DaytonaSandboxEnvironmentResourcesGpuType.Raw | null; + memory?: number | null; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResourcesGpuType.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResourcesGpuType.ts new file mode 100644 index 000000000..a19a2c72a --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaSandboxEnvironmentResourcesGpuType.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { DaytonaGpuType } from "./DaytonaGpuType.js"; + +export const DaytonaSandboxEnvironmentResourcesGpuType: core.serialization.Schema< + serializers.DaytonaSandboxEnvironmentResourcesGpuType.Raw, + TrueForge.DaytonaSandboxEnvironmentResourcesGpuType +> = core.serialization.undiscriminatedUnion([DaytonaGpuType, core.serialization.list(DaytonaGpuType)]); + +export declare namespace DaytonaSandboxEnvironmentResourcesGpuType { + export type Raw = DaytonaGpuType.Raw | DaytonaGpuType.Raw[]; +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaSnapshotImage.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaSnapshotImage.ts new file mode 100644 index 000000000..00462f552 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaSnapshotImage.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DaytonaSnapshotImage: core.serialization.ObjectSchema< + serializers.DaytonaSnapshotImage.Raw, + TrueForge.DaytonaSnapshotImage +> = core.serialization.object({ + name: core.serialization.string(), + type: core.serialization.stringLiteral("snapshot"), +}); + +export declare namespace DaytonaSnapshotImage { + export interface Raw { + name: string; + type: "snapshot"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/DaytonaTrueforgeDefaultImage.ts b/packages/trueforge-sdk/src/serialization/types/DaytonaTrueforgeDefaultImage.ts new file mode 100644 index 000000000..30cf8f0f1 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DaytonaTrueforgeDefaultImage.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DaytonaTrueforgeDefaultImage: core.serialization.ObjectSchema< + serializers.DaytonaTrueforgeDefaultImage.Raw, + TrueForge.DaytonaTrueforgeDefaultImage +> = core.serialization.object({ + type: core.serialization.stringLiteral("trueforge-default"), +}); + +export declare namespace DaytonaTrueforgeDefaultImage { + export interface Raw { + type: "trueforge-default"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/DeleteSandboxEnvironmentResponse.ts b/packages/trueforge-sdk/src/serialization/types/DeleteSandboxEnvironmentResponse.ts new file mode 100644 index 000000000..11565de1b --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/DeleteSandboxEnvironmentResponse.ts @@ -0,0 +1,14 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const DeleteSandboxEnvironmentResponse: core.serialization.ObjectSchema< + serializers.DeleteSandboxEnvironmentResponse.Raw, + TrueForge.DeleteSandboxEnvironmentResponse +> = core.serialization.object({}); + +export declare namespace DeleteSandboxEnvironmentResponse { + export type Raw = {}; +} diff --git a/packages/trueforge-sdk/src/serialization/types/GetSandboxEnvironmentResponse.ts b/packages/trueforge-sdk/src/serialization/types/GetSandboxEnvironmentResponse.ts new file mode 100644 index 000000000..400aa98b2 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/GetSandboxEnvironmentResponse.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { SandboxEnvironment } from "./SandboxEnvironment.js"; + +export const GetSandboxEnvironmentResponse: core.serialization.ObjectSchema< + serializers.GetSandboxEnvironmentResponse.Raw, + TrueForge.GetSandboxEnvironmentResponse +> = core.serialization.object({ + data: SandboxEnvironment, +}); + +export declare namespace GetSandboxEnvironmentResponse { + export interface Raw { + data: SandboxEnvironment.Raw; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/ListSandboxEnvironmentsResponse.ts b/packages/trueforge-sdk/src/serialization/types/ListSandboxEnvironmentsResponse.ts new file mode 100644 index 000000000..c33d25772 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/ListSandboxEnvironmentsResponse.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { SandboxEnvironment } from "./SandboxEnvironment.js"; +import { TokenPagination } from "./TokenPagination.js"; + +export const ListSandboxEnvironmentsResponse: core.serialization.ObjectSchema< + serializers.ListSandboxEnvironmentsResponse.Raw, + TrueForge.ListSandboxEnvironmentsResponse +> = core.serialization.object({ + data: core.serialization.list(SandboxEnvironment), + pagination: TokenPagination, +}); + +export declare namespace ListSandboxEnvironmentsResponse { + export interface Raw { + data: SandboxEnvironment.Raw[]; + pagination: TokenPagination.Raw; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/SandboxConfig.ts b/packages/trueforge-sdk/src/serialization/types/SandboxConfig.ts index 6476134b7..e274591f1 100644 --- a/packages/trueforge-sdk/src/serialization/types/SandboxConfig.ts +++ b/packages/trueforge-sdk/src/serialization/types/SandboxConfig.ts @@ -7,12 +7,14 @@ import type * as serializers from "../index.js"; export const SandboxConfig: core.serialization.ObjectSchema = core.serialization.object({ enabled: core.serialization.boolean(), + environment: core.serialization.string().optional(), fileDownloads: core.serialization.property("file_downloads", core.serialization.boolean().optional()), }); export declare namespace SandboxConfig { export interface Raw { enabled: boolean; + environment?: string | null; file_downloads?: boolean | null; } } diff --git a/packages/trueforge-sdk/src/serialization/types/SandboxCreatedEvent.ts b/packages/trueforge-sdk/src/serialization/types/SandboxCreatedEvent.ts index 61ea7c167..ce1850c3a 100644 --- a/packages/trueforge-sdk/src/serialization/types/SandboxCreatedEvent.ts +++ b/packages/trueforge-sdk/src/serialization/types/SandboxCreatedEvent.ts @@ -9,6 +9,7 @@ export const SandboxCreatedEvent: core.serialization.ObjectSchema< TrueForge.SandboxCreatedEvent > = core.serialization.object({ createdAt: core.serialization.property("created_at", core.serialization.string()), + environment: core.serialization.string().optional(), id: core.serialization.string(), sandboxId: core.serialization.property("sandbox_id", core.serialization.string()), threadId: core.serialization.property("thread_id", core.serialization.string().nullable()), @@ -18,6 +19,7 @@ export const SandboxCreatedEvent: core.serialization.ObjectSchema< export declare namespace SandboxCreatedEvent { export interface Raw { created_at: string; + environment?: string | null; id: string; sandbox_id: string; thread_id?: string | null; diff --git a/packages/trueforge-sdk/src/serialization/types/SandboxEnvironment.ts b/packages/trueforge-sdk/src/serialization/types/SandboxEnvironment.ts new file mode 100644 index 000000000..129422971 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/SandboxEnvironment.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { CreatedBySubject } from "./CreatedBySubject.js"; +import { ResourceName } from "./ResourceName.js"; +import { SandboxEnvironmentManifest } from "./SandboxEnvironmentManifest.js"; + +export const SandboxEnvironment: core.serialization.ObjectSchema< + serializers.SandboxEnvironment.Raw, + TrueForge.SandboxEnvironment +> = core.serialization.object({ + createdAt: core.serialization.property("created_at", core.serialization.date()), + createdBySubject: core.serialization.property("created_by_subject", CreatedBySubject), + description: core.serialization.string().nullable(), + id: core.serialization.string(), + manifest: SandboxEnvironmentManifest, + name: ResourceName, + updatedAt: core.serialization.property("updated_at", core.serialization.date()), +}); + +export declare namespace SandboxEnvironment { + export interface Raw { + created_at: string; + created_by_subject: CreatedBySubject.Raw; + description?: string | null; + id: string; + manifest: SandboxEnvironmentManifest.Raw; + name: ResourceName.Raw; + updated_at: string; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/SandboxEnvironmentManifest.ts b/packages/trueforge-sdk/src/serialization/types/SandboxEnvironmentManifest.ts new file mode 100644 index 000000000..a6ebc45ba --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/SandboxEnvironmentManifest.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { DaytonaSandboxEnvironmentImage } from "./DaytonaSandboxEnvironmentImage.js"; +import { DaytonaSandboxEnvironmentLifecycle } from "./DaytonaSandboxEnvironmentLifecycle.js"; +import { DaytonaSandboxEnvironmentNetworking } from "./DaytonaSandboxEnvironmentNetworking.js"; +import { DaytonaSandboxEnvironmentResources } from "./DaytonaSandboxEnvironmentResources.js"; + +export const SandboxEnvironmentManifest: core.serialization.ObjectSchema< + serializers.SandboxEnvironmentManifest.Raw, + TrueForge.SandboxEnvironmentManifest +> = core.serialization.object({ + image: DaytonaSandboxEnvironmentImage, + lifecycle: DaytonaSandboxEnvironmentLifecycle.optional(), + networking: DaytonaSandboxEnvironmentNetworking.optional(), + provider: core.serialization.stringLiteral("daytona"), + resources: DaytonaSandboxEnvironmentResources.optional(), + secrets: core.serialization.record(core.serialization.string(), core.serialization.string()).optional(), + type: core.serialization.stringLiteral("daytona"), +}); + +export declare namespace SandboxEnvironmentManifest { + export interface Raw { + image: DaytonaSandboxEnvironmentImage.Raw; + lifecycle?: DaytonaSandboxEnvironmentLifecycle.Raw | null; + networking?: DaytonaSandboxEnvironmentNetworking.Raw | null; + provider: "daytona"; + resources?: DaytonaSandboxEnvironmentResources.Raw | null; + secrets?: Record | null; + type: "daytona"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/index.ts b/packages/trueforge-sdk/src/serialization/types/index.ts index 9b29138b5..2985092b1 100644 --- a/packages/trueforge-sdk/src/serialization/types/index.ts +++ b/packages/trueforge-sdk/src/serialization/types/index.ts @@ -49,8 +49,18 @@ export * from "./CreateScheduleRunResponse.js"; export * from "./CreateSessionAgent.js"; export * from "./CronExpression.js"; export * from "./CustomModelProvider.js"; +export * from "./DaytonaDockerImage.js"; +export * from "./DaytonaGpuType.js"; +export * from "./DaytonaSandboxEnvironmentImage.js"; +export * from "./DaytonaSandboxEnvironmentLifecycle.js"; +export * from "./DaytonaSandboxEnvironmentNetworking.js"; +export * from "./DaytonaSandboxEnvironmentResources.js"; +export * from "./DaytonaSandboxEnvironmentResourcesGpuType.js"; export * from "./DaytonaSandboxProviderAuth.js"; +export * from "./DaytonaSnapshotImage.js"; +export * from "./DaytonaTrueforgeDefaultImage.js"; export * from "./DeleteAgentResponse.js"; +export * from "./DeleteSandboxEnvironmentResponse.js"; export * from "./DeleteScheduleResponse.js"; export * from "./DynamicSubAgentsConfig.js"; export * from "./ExtendedChunkDeltaToolCall.js"; @@ -68,6 +78,7 @@ export * from "./GetMeResponse.js"; export * from "./GetMeSubject.js"; export * from "./GetModelProviderCatalogResponse.js"; export * from "./GetModelProviderResponse.js"; +export * from "./GetSandboxEnvironmentResponse.js"; export * from "./GetSandboxProviderCatalogResponse.js"; export * from "./GetSandboxProviderResponse.js"; export * from "./GetScheduleResponse.js"; @@ -92,6 +103,7 @@ export * from "./ListMcpServerToolsResponse.js"; export * from "./ListModelProvidersResponse.js"; export * from "./ListPermissionsData.js"; export * from "./ListPermissionsResponse.js"; +export * from "./ListSandboxEnvironmentsResponse.js"; export * from "./ListScheduleRunsResponse.js"; export * from "./ListSchedulesResponse.js"; export * from "./ListSessionEventsResponse.js"; @@ -155,6 +167,8 @@ export * from "./SandboxBuildStatus.js"; export * from "./SandboxCapability.js"; export * from "./SandboxConfig.js"; export * from "./SandboxCreatedEvent.js"; +export * from "./SandboxEnvironment.js"; +export * from "./SandboxEnvironmentManifest.js"; export * from "./SandboxProviderManifest.js"; export * from "./Schedule.js"; export * from "./ScheduleManifest.js"; diff --git a/packages/trueforge-sdk/tests/wire/sandboxEnvironments.test.ts b/packages/trueforge-sdk/tests/wire/sandboxEnvironments.test.ts new file mode 100644 index 000000000..5324dae9a --- /dev/null +++ b/packages/trueforge-sdk/tests/wire/sandboxEnvironments.test.ts @@ -0,0 +1,691 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as TrueForgeTypes from "../../src/api/index"; +import { TrueForge } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("SandboxEnvironmentsClient", () => { + test("list (1)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { + data: [ + { + created_at: "2024-01-15T09:30:00Z", + created_by_subject: { + subject_display_name: "subject_display_name", + subject_id: "subject_id", + subject_type: "subject_type", + }, + description: "description", + id: "id", + manifest: { image: { ref: "ref", type: "docker" }, provider: "daytona", type: "daytona" }, + name: "name", + updated_at: "2024-01-15T09:30:00Z", + }, + ], + pagination: { limit: 1, next_page_token: "next_page_token", previous_page_token: "previous_page_token" }, + }; + + server + .mockEndpoint({ once: false }) + .get("/api/v1/sandbox-environments") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const expected = { + data: [ + { + createdAt: new Date("2024-01-15T09:30:00.000Z"), + createdBySubject: { + subjectDisplayName: "subject_display_name", + subjectId: "subject_id", + subjectType: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { + ref: "ref", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + name: "name", + updatedAt: new Date("2024-01-15T09:30:00.000Z"), + }, + ], + pagination: { + limit: 1, + nextPageToken: "next_page_token", + previousPageToken: "previous_page_token", + }, + }; + const page = await client.sandboxEnvironments.list(); + + expect(expected.data).toEqual(page.data); + expect(page.hasNextPage()).toBe(true); + const nextPage = await page.getNextPage(); + expect(expected.data).toEqual(nextPage.data); + }); + + test("list (2)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .get("/api/v1/sandbox-environments") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.list(); + }).rejects.toThrow(TrueForgeTypes.BadRequestError); + }); + + test("list (3)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .get("/api/v1/sandbox-environments") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.list(); + }).rejects.toThrow(TrueForgeTypes.UnauthorizedError); + }); + + test("create (1)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "ref", type: "docker" }, provider: "daytona", type: "daytona" }, + name: "name", + }; + const rawResponseBody = { + data: { + created_at: "2024-01-15T09:30:00Z", + created_by_subject: { + subject_display_name: "subject_display_name", + subject_id: "subject_id", + subject_type: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { ref: "ref", type: "docker" }, + provider: "daytona", + secrets: { key: "value" }, + type: "daytona", + }, + name: "name", + updated_at: "2024-01-15T09:30:00Z", + }, + }; + + server + .mockEndpoint() + .post("/api/v1/sandbox-environments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.sandboxEnvironments.create({ + manifest: { + image: { + ref: "ref", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + name: "name", + }); + expect(response).toEqual({ + data: { + createdAt: new Date("2024-01-15T09:30:00.000Z"), + createdBySubject: { + subjectDisplayName: "subject_display_name", + subjectId: "subject_id", + subjectType: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { + ref: "ref", + type: "docker", + }, + provider: "daytona", + secrets: { + key: "value", + }, + type: "daytona", + }, + name: "name", + updatedAt: new Date("2024-01-15T09:30:00.000Z"), + }, + }); + }); + + test("create (2)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + name: "xy", + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sandbox-environments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.create({ + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + name: "xy", + }); + }).rejects.toThrow(TrueForgeTypes.BadRequestError); + }); + + test("create (3)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + name: "xy", + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sandbox-environments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.create({ + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + name: "xy", + }); + }).rejects.toThrow(TrueForgeTypes.UnauthorizedError); + }); + + test("create (4)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + name: "xy", + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sandbox-environments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.create({ + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + name: "xy", + }); + }).rejects.toThrow(TrueForgeTypes.ConflictError); + }); + + test("create (5)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + name: "xy", + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sandbox-environments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.create({ + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + name: "xy", + }); + }).rejects.toThrow(TrueForgeTypes.UnprocessableEntityError); + }); + + test("get (1)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { + data: { + created_at: "2024-01-15T09:30:00Z", + created_by_subject: { + subject_display_name: "subject_display_name", + subject_id: "subject_id", + subject_type: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { ref: "ref", type: "docker" }, + provider: "daytona", + secrets: { key: "value" }, + type: "daytona", + }, + name: "name", + updated_at: "2024-01-15T09:30:00Z", + }, + }; + + server + .mockEndpoint() + .get("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.sandboxEnvironments.get("sandbox_environment_id"); + expect(response).toEqual({ + data: { + createdAt: new Date("2024-01-15T09:30:00.000Z"), + createdBySubject: { + subjectDisplayName: "subject_display_name", + subjectId: "subject_id", + subjectType: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { + ref: "ref", + type: "docker", + }, + provider: "daytona", + secrets: { + key: "value", + }, + type: "daytona", + }, + name: "name", + updatedAt: new Date("2024-01-15T09:30:00.000Z"), + }, + }); + }); + + test("get (2)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .get("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.get("sandbox_environment_id"); + }).rejects.toThrow(TrueForgeTypes.UnauthorizedError); + }); + + test("get (3)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .get("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.get("sandbox_environment_id"); + }).rejects.toThrow(TrueForgeTypes.NotFoundError); + }); + + test("update (1)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "ref", type: "docker" }, provider: "daytona", type: "daytona" }, + }; + const rawResponseBody = { + data: { + created_at: "2024-01-15T09:30:00Z", + created_by_subject: { + subject_display_name: "subject_display_name", + subject_id: "subject_id", + subject_type: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { ref: "ref", type: "docker" }, + provider: "daytona", + secrets: { key: "value" }, + type: "daytona", + }, + name: "name", + updated_at: "2024-01-15T09:30:00Z", + }, + }; + + server + .mockEndpoint() + .put("/api/v1/sandbox-environments/sandbox_environment_id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.sandboxEnvironments.update("sandbox_environment_id", { + manifest: { + image: { + ref: "ref", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + }); + expect(response).toEqual({ + data: { + createdAt: new Date("2024-01-15T09:30:00.000Z"), + createdBySubject: { + subjectDisplayName: "subject_display_name", + subjectId: "subject_id", + subjectType: "subject_type", + }, + description: "description", + id: "id", + manifest: { + image: { + ref: "ref", + type: "docker", + }, + provider: "daytona", + secrets: { + key: "value", + }, + type: "daytona", + }, + name: "name", + updatedAt: new Date("2024-01-15T09:30:00.000Z"), + }, + }); + }); + + test("update (2)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .put("/api/v1/sandbox-environments/sandbox_environment_id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.update("sandbox_environment_id", { + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + }); + }).rejects.toThrow(TrueForgeTypes.BadRequestError); + }); + + test("update (3)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .put("/api/v1/sandbox-environments/sandbox_environment_id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.update("sandbox_environment_id", { + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + }); + }).rejects.toThrow(TrueForgeTypes.UnauthorizedError); + }); + + test("update (4)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .put("/api/v1/sandbox-environments/sandbox_environment_id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.update("sandbox_environment_id", { + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + }); + }).rejects.toThrow(TrueForgeTypes.NotFoundError); + }); + + test("update (5)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + manifest: { image: { ref: "x", type: "docker" }, provider: "daytona", type: "daytona" }, + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .put("/api/v1/sandbox-environments/sandbox_environment_id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.update("sandbox_environment_id", { + manifest: { + image: { + ref: "x", + type: "docker", + }, + provider: "daytona", + type: "daytona", + }, + }); + }).rejects.toThrow(TrueForgeTypes.UnprocessableEntityError); + }); + + test("delete (1)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = {}; + + server + .mockEndpoint() + .delete("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.sandboxEnvironments.delete("sandbox_environment_id"); + expect(response).toEqual({}); + }); + + test("delete (2)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .delete("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.delete("sandbox_environment_id"); + }).rejects.toThrow(TrueForgeTypes.UnauthorizedError); + }); + + test("delete (3)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .delete("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.delete("sandbox_environment_id"); + }).rejects.toThrow(TrueForgeTypes.NotFoundError); + }); + + test("delete (4)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .delete("/api/v1/sandbox-environments/sandbox_environment_id") + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sandboxEnvironments.delete("sandbox_environment_id"); + }).rejects.toThrow(TrueForgeTypes.ConflictError); + }); +}); diff --git a/python/trueforge_sdk/reference.md b/python/trueforge_sdk/reference.md index aa8c83132..3a399ffb1 100644 --- a/python/trueforge_sdk/reference.md +++ b/python/trueforge_sdk/reference.md @@ -1045,6 +1045,421 @@ client.models.list() + + + + +## SandboxEnvironments +
client.sandbox_environments.list(...) -> ListSandboxEnvironmentsResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +List sandbox environments created by the authenticated subject, newest first. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from trueforge_sdk import TrueForge + +client = TrueForge( + token="", + base_url="https://yourhost.com/path/to/api", +) + +client.sandbox_environments.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**limit:** `typing.Optional[int]` — Page size. Defaults to 25 + +
+
+ +
+
+ +**page_token:** `typing.Optional[str]` — Opaque token from a previous response `next_page_token`. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.sandbox_environments.create(...) -> GetSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a Daytona sandbox environment owned by the authenticated subject. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from trueforge_sdk import TrueForge, SandboxEnvironmentManifest, DaytonaDockerImage + +client = TrueForge( + token="", + base_url="https://yourhost.com/path/to/api", +) + +client.sandbox_environments.create( + manifest=SandboxEnvironmentManifest( + image=DaytonaDockerImage( + ref="ref", + type="docker", + ), + provider="daytona", + type="daytona", + ), + name="name", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**manifest:** `SandboxEnvironmentManifest` + +
+
+ +
+
+ +**name:** `ResourceName` + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — Optional human-readable description. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.sandbox_environments.get(...) -> GetSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Get a sandbox environment owned by the authenticated subject. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from trueforge_sdk import TrueForge + +client = TrueForge( + token="", + base_url="https://yourhost.com/path/to/api", +) + +client.sandbox_environments.get( + sandbox_environment_id="sandbox_environment_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**sandbox_environment_id:** `str` — Immutable sandbox environment identifier. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.sandbox_environments.update(...) -> GetSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Replace the manifest (and optionally description). Name is immutable. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from trueforge_sdk import TrueForge, SandboxEnvironmentManifest, DaytonaDockerImage + +client = TrueForge( + token="", + base_url="https://yourhost.com/path/to/api", +) + +client.sandbox_environments.update( + sandbox_environment_id="sandbox_environment_id", + manifest=SandboxEnvironmentManifest( + image=DaytonaDockerImage( + ref="ref", + type="docker", + ), + provider="daytona", + type="daytona", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**sandbox_environment_id:** `str` — Immutable sandbox environment identifier. + +
+
+ +
+
+ +**manifest:** `SandboxEnvironmentManifest` + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — Optional human-readable description. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.sandbox_environments.delete(...) -> DeleteSandboxEnvironmentResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a caller-owned sandbox environment. Fails if any agent references it. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from trueforge_sdk import TrueForge + +client = TrueForge( + token="", + base_url="https://yourhost.com/path/to/api", +) + +client.sandbox_environments.delete( + sandbox_environment_id="sandbox_environment_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**sandbox_environment_id:** `str` — Immutable sandbox environment identifier. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/python/trueforge_sdk/src/trueforge_sdk/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/__init__.py index 7242c3c63..891908359 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/__init__.py +++ b/python/trueforge_sdk/src/trueforge_sdk/__init__.py @@ -58,8 +58,18 @@ CreatedBySubject, CronExpression, CustomModelProvider, + DaytonaDockerImage, + DaytonaGpuType, + DaytonaSandboxEnvironmentImage, + DaytonaSandboxEnvironmentLifecycle, + DaytonaSandboxEnvironmentNetworking, + DaytonaSandboxEnvironmentResources, + DaytonaSandboxEnvironmentResourcesGpuType, DaytonaSandboxProviderAuth, + DaytonaSnapshotImage, + DaytonaTrueforgeDefaultImage, DeleteAgentResponse, + DeleteSandboxEnvironmentResponse, DeleteScheduleResponse, DynamicSubAgentsConfig, ExtendedChunkDeltaToolCall, @@ -77,6 +87,7 @@ GetMeSubject, GetModelProviderCatalogResponse, GetModelProviderResponse, + GetSandboxEnvironmentResponse, GetSandboxProviderCatalogResponse, GetSandboxProviderResponse, GetScheduleResponse, @@ -101,6 +112,7 @@ ListModelProvidersResponse, ListPermissionsData, ListPermissionsResponse, + ListSandboxEnvironmentsResponse, ListScheduleRunsResponse, ListSchedulesResponse, ListSessionEventsResponse, @@ -164,6 +176,8 @@ SandboxCapability, SandboxConfig, SandboxCreatedEvent, + SandboxEnvironment, + SandboxEnvironmentManifest, SandboxProviderManifest, Schedule, ScheduleManifest, @@ -259,7 +273,20 @@ UnauthorizedError, UnprocessableEntityError, ) - from . import agents, auth, catalogs, internal, mcp_servers, models, schedules, server, sessions, settings, skills + from . import ( + agents, + auth, + catalogs, + internal, + mcp_servers, + models, + sandbox_environments, + schedules, + server, + sessions, + settings, + skills, + ) from ._default_clients import DefaultAioHttpClient, DefaultAsyncHttpxClient from .client import AsyncTrueForge, TrueForge from .version import __version__ @@ -320,10 +347,20 @@ "CreatedBySubject": ".types", "CronExpression": ".types", "CustomModelProvider": ".types", + "DaytonaDockerImage": ".types", + "DaytonaGpuType": ".types", + "DaytonaSandboxEnvironmentImage": ".types", + "DaytonaSandboxEnvironmentLifecycle": ".types", + "DaytonaSandboxEnvironmentNetworking": ".types", + "DaytonaSandboxEnvironmentResources": ".types", + "DaytonaSandboxEnvironmentResourcesGpuType": ".types", "DaytonaSandboxProviderAuth": ".types", + "DaytonaSnapshotImage": ".types", + "DaytonaTrueforgeDefaultImage": ".types", "DefaultAioHttpClient": "._default_clients", "DefaultAsyncHttpxClient": "._default_clients", "DeleteAgentResponse": ".types", + "DeleteSandboxEnvironmentResponse": ".types", "DeleteScheduleResponse": ".types", "DynamicSubAgentsConfig": ".types", "ExtendedChunkDeltaToolCall": ".types", @@ -343,6 +380,7 @@ "GetMeSubject": ".types", "GetModelProviderCatalogResponse": ".types", "GetModelProviderResponse": ".types", + "GetSandboxEnvironmentResponse": ".types", "GetSandboxProviderCatalogResponse": ".types", "GetSandboxProviderResponse": ".types", "GetScheduleResponse": ".types", @@ -369,6 +407,7 @@ "ListModelProvidersResponse": ".types", "ListPermissionsData": ".types", "ListPermissionsResponse": ".types", + "ListSandboxEnvironmentsResponse": ".types", "ListScheduleRunsResponse": ".types", "ListSchedulesResponse": ".types", "ListSessionEventsResponse": ".types", @@ -434,6 +473,8 @@ "SandboxCapability": ".types", "SandboxConfig": ".types", "SandboxCreatedEvent": ".types", + "SandboxEnvironment": ".types", + "SandboxEnvironmentManifest": ".types", "SandboxProviderManifest": ".types", "Schedule": ".types", "ScheduleManifest": ".types", @@ -524,6 +565,7 @@ "internal": ".internal", "mcp_servers": ".mcp_servers", "models": ".models", + "sandbox_environments": ".sandbox_environments", "schedules": ".schedules", "server": ".server", "sessions": ".sessions", @@ -610,10 +652,20 @@ def __dir__(): "CreatedBySubject", "CronExpression", "CustomModelProvider", + "DaytonaDockerImage", + "DaytonaGpuType", + "DaytonaSandboxEnvironmentImage", + "DaytonaSandboxEnvironmentLifecycle", + "DaytonaSandboxEnvironmentNetworking", + "DaytonaSandboxEnvironmentResources", + "DaytonaSandboxEnvironmentResourcesGpuType", "DaytonaSandboxProviderAuth", + "DaytonaSnapshotImage", + "DaytonaTrueforgeDefaultImage", "DefaultAioHttpClient", "DefaultAsyncHttpxClient", "DeleteAgentResponse", + "DeleteSandboxEnvironmentResponse", "DeleteScheduleResponse", "DynamicSubAgentsConfig", "ExtendedChunkDeltaToolCall", @@ -633,6 +685,7 @@ def __dir__(): "GetMeSubject", "GetModelProviderCatalogResponse", "GetModelProviderResponse", + "GetSandboxEnvironmentResponse", "GetSandboxProviderCatalogResponse", "GetSandboxProviderResponse", "GetScheduleResponse", @@ -659,6 +712,7 @@ def __dir__(): "ListModelProvidersResponse", "ListPermissionsData", "ListPermissionsResponse", + "ListSandboxEnvironmentsResponse", "ListScheduleRunsResponse", "ListSchedulesResponse", "ListSessionEventsResponse", @@ -724,6 +778,8 @@ def __dir__(): "SandboxCapability", "SandboxConfig", "SandboxCreatedEvent", + "SandboxEnvironment", + "SandboxEnvironmentManifest", "SandboxProviderManifest", "Schedule", "ScheduleManifest", @@ -814,6 +870,7 @@ def __dir__(): "internal", "mcp_servers", "models", + "sandbox_environments", "schedules", "server", "sessions", diff --git a/python/trueforge_sdk/src/trueforge_sdk/base_client.py b/python/trueforge_sdk/src/trueforge_sdk/base_client.py index e086197b1..f2fbb2e2c 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/base_client.py +++ b/python/trueforge_sdk/src/trueforge_sdk/base_client.py @@ -15,6 +15,7 @@ from .internal.client import AsyncInternalClient, InternalClient from .mcp_servers.client import AsyncMcpServersClient, McpServersClient from .models.client import AsyncModelsClient, ModelsClient + from .sandbox_environments.client import AsyncSandboxEnvironmentsClient, SandboxEnvironmentsClient from .schedules.client import AsyncSchedulesClient, SchedulesClient from .server.client import AsyncServerClient, ServerClient from .sessions.client import AsyncSessionsClient, SessionsClient @@ -103,6 +104,7 @@ def __init__( self._server: typing.Optional[ServerClient] = None self._mcp_servers: typing.Optional[McpServersClient] = None self._models: typing.Optional[ModelsClient] = None + self._sandbox_environments: typing.Optional[SandboxEnvironmentsClient] = None self._schedules: typing.Optional[SchedulesClient] = None self._sessions: typing.Optional[SessionsClient] = None self._skills: typing.Optional[SkillsClient] = None @@ -157,6 +159,14 @@ def models(self): self._models = ModelsClient(client_wrapper=self._client_wrapper) return self._models + @property + def sandbox_environments(self): + if self._sandbox_environments is None: + from .sandbox_environments.client import SandboxEnvironmentsClient # noqa: E402 + + self._sandbox_environments = SandboxEnvironmentsClient(client_wrapper=self._client_wrapper) + return self._sandbox_environments + @property def schedules(self): if self._schedules is None: @@ -300,6 +310,7 @@ def __init__( self._server: typing.Optional[AsyncServerClient] = None self._mcp_servers: typing.Optional[AsyncMcpServersClient] = None self._models: typing.Optional[AsyncModelsClient] = None + self._sandbox_environments: typing.Optional[AsyncSandboxEnvironmentsClient] = None self._schedules: typing.Optional[AsyncSchedulesClient] = None self._sessions: typing.Optional[AsyncSessionsClient] = None self._skills: typing.Optional[AsyncSkillsClient] = None @@ -354,6 +365,14 @@ def models(self): self._models = AsyncModelsClient(client_wrapper=self._client_wrapper) return self._models + @property + def sandbox_environments(self): + if self._sandbox_environments is None: + from .sandbox_environments.client import AsyncSandboxEnvironmentsClient # noqa: E402 + + self._sandbox_environments = AsyncSandboxEnvironmentsClient(client_wrapper=self._client_wrapper) + return self._sandbox_environments + @property def schedules(self): if self._schedules is None: diff --git a/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/__init__.py new file mode 100644 index 000000000..5cde0202d --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/client.py b/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/client.py new file mode 100644 index 000000000..bbf512221 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/client.py @@ -0,0 +1,542 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..types.delete_sandbox_environment_response import DeleteSandboxEnvironmentResponse +from ..types.get_sandbox_environment_response import GetSandboxEnvironmentResponse +from ..types.list_sandbox_environments_response import ListSandboxEnvironmentsResponse +from ..types.resource_name import ResourceName +from ..types.sandbox_environment import SandboxEnvironment +from ..types.sandbox_environment_manifest import SandboxEnvironmentManifest +from .raw_client import AsyncRawSandboxEnvironmentsClient, RawSandboxEnvironmentsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class SandboxEnvironmentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSandboxEnvironmentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSandboxEnvironmentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSandboxEnvironmentsClient + """ + return self._raw_client + + def list( + self, + *, + limit: typing.Optional[int] = 25, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse]: + """ + List sandbox environments created by the authenticated subject, newest first. + + Parameters + ---------- + limit : typing.Optional[int] + Page size. Defaults to 25 + + page_token : typing.Optional[str] + Opaque token from a previous response `next_page_token`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse] + Paginated caller-owned sandbox environments. + + Examples + -------- + from trueforge_sdk import TrueForge + + client = TrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + response = client.sandbox_environments.list() + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(limit=limit, page_token=page_token, request_options=request_options) + + def create( + self, + *, + manifest: SandboxEnvironmentManifest, + name: ResourceName, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetSandboxEnvironmentResponse: + """ + Create a Daytona sandbox environment owned by the authenticated subject. + + Parameters + ---------- + manifest : SandboxEnvironmentManifest + + name : ResourceName + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetSandboxEnvironmentResponse + Created sandbox environment. + + Examples + -------- + from trueforge_sdk import ( + DaytonaDockerImage, + SandboxEnvironmentManifest, + TrueForge, + ) + + client = TrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.sandbox_environments.create( + manifest=SandboxEnvironmentManifest( + image=DaytonaDockerImage( + ref="ref", + ), + ), + name="name", + ) + """ + _response = self._raw_client.create( + manifest=manifest, name=name, description=description, request_options=request_options + ) + return _response.data + + def get( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> GetSandboxEnvironmentResponse: + """ + Get a sandbox environment owned by the authenticated subject. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetSandboxEnvironmentResponse + The sandbox environment. + + Examples + -------- + from trueforge_sdk import TrueForge + + client = TrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.sandbox_environments.get( + sandbox_environment_id="sandbox_environment_id", + ) + """ + _response = self._raw_client.get(sandbox_environment_id=sandbox_environment_id, request_options=request_options) + return _response.data + + def update( + self, + *, + sandbox_environment_id: str, + manifest: SandboxEnvironmentManifest, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetSandboxEnvironmentResponse: + """ + Replace the manifest (and optionally description). Name is immutable. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + manifest : SandboxEnvironmentManifest + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetSandboxEnvironmentResponse + Updated sandbox environment. + + Examples + -------- + from trueforge_sdk import ( + DaytonaDockerImage, + SandboxEnvironmentManifest, + TrueForge, + ) + + client = TrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.sandbox_environments.update( + sandbox_environment_id="sandbox_environment_id", + manifest=SandboxEnvironmentManifest( + image=DaytonaDockerImage( + ref="ref", + ), + ), + ) + """ + _response = self._raw_client.update( + sandbox_environment_id=sandbox_environment_id, + manifest=manifest, + description=description, + request_options=request_options, + ) + return _response.data + + def delete( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteSandboxEnvironmentResponse: + """ + Delete a caller-owned sandbox environment. Fails if any agent references it. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteSandboxEnvironmentResponse + Deleted. + + Examples + -------- + from trueforge_sdk import TrueForge + + client = TrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.sandbox_environments.delete( + sandbox_environment_id="sandbox_environment_id", + ) + """ + _response = self._raw_client.delete( + sandbox_environment_id=sandbox_environment_id, request_options=request_options + ) + return _response.data + + +class AsyncSandboxEnvironmentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSandboxEnvironmentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSandboxEnvironmentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSandboxEnvironmentsClient + """ + return self._raw_client + + async def list( + self, + *, + limit: typing.Optional[int] = 25, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse]: + """ + List sandbox environments created by the authenticated subject, newest first. + + Parameters + ---------- + limit : typing.Optional[int] + Page size. Defaults to 25 + + page_token : typing.Optional[str] + Opaque token from a previous response `next_page_token`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse] + Paginated caller-owned sandbox environments. + + Examples + -------- + import asyncio + + from trueforge_sdk import AsyncTrueForge + + client = AsyncTrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + response = await client.sandbox_environments.list() + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(limit=limit, page_token=page_token, request_options=request_options) + + async def create( + self, + *, + manifest: SandboxEnvironmentManifest, + name: ResourceName, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetSandboxEnvironmentResponse: + """ + Create a Daytona sandbox environment owned by the authenticated subject. + + Parameters + ---------- + manifest : SandboxEnvironmentManifest + + name : ResourceName + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetSandboxEnvironmentResponse + Created sandbox environment. + + Examples + -------- + import asyncio + + from trueforge_sdk import ( + AsyncTrueForge, + DaytonaDockerImage, + SandboxEnvironmentManifest, + ) + + client = AsyncTrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.sandbox_environments.create( + manifest=SandboxEnvironmentManifest( + image=DaytonaDockerImage( + ref="ref", + ), + ), + name="name", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + manifest=manifest, name=name, description=description, request_options=request_options + ) + return _response.data + + async def get( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> GetSandboxEnvironmentResponse: + """ + Get a sandbox environment owned by the authenticated subject. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetSandboxEnvironmentResponse + The sandbox environment. + + Examples + -------- + import asyncio + + from trueforge_sdk import AsyncTrueForge + + client = AsyncTrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.sandbox_environments.get( + sandbox_environment_id="sandbox_environment_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + sandbox_environment_id=sandbox_environment_id, request_options=request_options + ) + return _response.data + + async def update( + self, + *, + sandbox_environment_id: str, + manifest: SandboxEnvironmentManifest, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetSandboxEnvironmentResponse: + """ + Replace the manifest (and optionally description). Name is immutable. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + manifest : SandboxEnvironmentManifest + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetSandboxEnvironmentResponse + Updated sandbox environment. + + Examples + -------- + import asyncio + + from trueforge_sdk import ( + AsyncTrueForge, + DaytonaDockerImage, + SandboxEnvironmentManifest, + ) + + client = AsyncTrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.sandbox_environments.update( + sandbox_environment_id="sandbox_environment_id", + manifest=SandboxEnvironmentManifest( + image=DaytonaDockerImage( + ref="ref", + ), + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + sandbox_environment_id=sandbox_environment_id, + manifest=manifest, + description=description, + request_options=request_options, + ) + return _response.data + + async def delete( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteSandboxEnvironmentResponse: + """ + Delete a caller-owned sandbox environment. Fails if any agent references it. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteSandboxEnvironmentResponse + Deleted. + + Examples + -------- + import asyncio + + from trueforge_sdk import AsyncTrueForge + + client = AsyncTrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.sandbox_environments.delete( + sandbox_environment_id="sandbox_environment_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete( + sandbox_environment_id=sandbox_environment_id, request_options=request_options + ) + return _response.data diff --git a/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/raw_client.py b/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/raw_client.py new file mode 100644 index 000000000..9a4dce64f --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/sandbox_environments/raw_client.py @@ -0,0 +1,927 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.jsonable_encoder import encode_path_param +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError +from ..core.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.conflict_error import ConflictError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.delete_sandbox_environment_response import DeleteSandboxEnvironmentResponse +from ..types.get_sandbox_environment_response import GetSandboxEnvironmentResponse +from ..types.list_sandbox_environments_response import ListSandboxEnvironmentsResponse +from ..types.request_error_response import RequestErrorResponse +from ..types.resource_name import ResourceName +from ..types.sandbox_environment import SandboxEnvironment +from ..types.sandbox_environment_manifest import SandboxEnvironmentManifest +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawSandboxEnvironmentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + limit: typing.Optional[int] = 25, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse]: + """ + List sandbox environments created by the authenticated subject, newest first. + + Parameters + ---------- + limit : typing.Optional[int] + Page size. Defaults to 25 + + page_token : typing.Optional[str] + Opaque token from a previous response `next_page_token`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse] + Paginated caller-owned sandbox environments. + """ + _response = self._client_wrapper.httpx_client.request( + "api/v1/sandbox-environments", + method="GET", + params={ + "limit": limit, + "page_token": page_token, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListSandboxEnvironmentsResponse, + construct_type( + type_=ListSandboxEnvironmentsResponse, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = False + _get_next = None + if _parsed_response.pagination is not None: + _parsed_next = _parsed_response.pagination.next_page_token + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + limit=limit, + page_token=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def create( + self, + *, + manifest: SandboxEnvironmentManifest, + name: ResourceName, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[GetSandboxEnvironmentResponse]: + """ + Create a Daytona sandbox environment owned by the authenticated subject. + + Parameters + ---------- + manifest : SandboxEnvironmentManifest + + name : ResourceName + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetSandboxEnvironmentResponse] + Created sandbox environment. + """ + _response = self._client_wrapper.httpx_client.request( + "api/v1/sandbox-environments", + method="POST", + json={ + "description": description, + "manifest": convert_and_respect_annotation_metadata( + object_=manifest, annotation=SandboxEnvironmentManifest, direction="write" + ), + "name": name, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetSandboxEnvironmentResponse, + construct_type( + type_=GetSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def get( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[GetSandboxEnvironmentResponse]: + """ + Get a sandbox environment owned by the authenticated subject. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetSandboxEnvironmentResponse] + The sandbox environment. + """ + _response = self._client_wrapper.httpx_client.request( + f"api/v1/sandbox-environments/{encode_path_param(sandbox_environment_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetSandboxEnvironmentResponse, + construct_type( + type_=GetSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def update( + self, + *, + sandbox_environment_id: str, + manifest: SandboxEnvironmentManifest, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[GetSandboxEnvironmentResponse]: + """ + Replace the manifest (and optionally description). Name is immutable. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + manifest : SandboxEnvironmentManifest + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetSandboxEnvironmentResponse] + Updated sandbox environment. + """ + _response = self._client_wrapper.httpx_client.request( + f"api/v1/sandbox-environments/{encode_path_param(sandbox_environment_id)}", + method="PUT", + json={ + "description": description, + "manifest": convert_and_respect_annotation_metadata( + object_=manifest, annotation=SandboxEnvironmentManifest, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetSandboxEnvironmentResponse, + construct_type( + type_=GetSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def delete( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteSandboxEnvironmentResponse]: + """ + Delete a caller-owned sandbox environment. Fails if any agent references it. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteSandboxEnvironmentResponse] + Deleted. + """ + _response = self._client_wrapper.httpx_client.request( + f"api/v1/sandbox-environments/{encode_path_param(sandbox_environment_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteSandboxEnvironmentResponse, + construct_type( + type_=DeleteSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawSandboxEnvironmentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + limit: typing.Optional[int] = 25, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse]: + """ + List sandbox environments created by the authenticated subject, newest first. + + Parameters + ---------- + limit : typing.Optional[int] + Page size. Defaults to 25 + + page_token : typing.Optional[str] + Opaque token from a previous response `next_page_token`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[SandboxEnvironment, ListSandboxEnvironmentsResponse] + Paginated caller-owned sandbox environments. + """ + _response = await self._client_wrapper.httpx_client.request( + "api/v1/sandbox-environments", + method="GET", + params={ + "limit": limit, + "page_token": page_token, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListSandboxEnvironmentsResponse, + construct_type( + type_=ListSandboxEnvironmentsResponse, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = False + _get_next = None + if _parsed_response.pagination is not None: + _parsed_next = _parsed_response.pagination.next_page_token + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + limit=limit, + page_token=_parsed_next, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def create( + self, + *, + manifest: SandboxEnvironmentManifest, + name: ResourceName, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[GetSandboxEnvironmentResponse]: + """ + Create a Daytona sandbox environment owned by the authenticated subject. + + Parameters + ---------- + manifest : SandboxEnvironmentManifest + + name : ResourceName + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetSandboxEnvironmentResponse] + Created sandbox environment. + """ + _response = await self._client_wrapper.httpx_client.request( + "api/v1/sandbox-environments", + method="POST", + json={ + "description": description, + "manifest": convert_and_respect_annotation_metadata( + object_=manifest, annotation=SandboxEnvironmentManifest, direction="write" + ), + "name": name, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetSandboxEnvironmentResponse, + construct_type( + type_=GetSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def get( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[GetSandboxEnvironmentResponse]: + """ + Get a sandbox environment owned by the authenticated subject. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetSandboxEnvironmentResponse] + The sandbox environment. + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/v1/sandbox-environments/{encode_path_param(sandbox_environment_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetSandboxEnvironmentResponse, + construct_type( + type_=GetSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def update( + self, + *, + sandbox_environment_id: str, + manifest: SandboxEnvironmentManifest, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[GetSandboxEnvironmentResponse]: + """ + Replace the manifest (and optionally description). Name is immutable. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + manifest : SandboxEnvironmentManifest + + description : typing.Optional[str] + Optional human-readable description. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetSandboxEnvironmentResponse] + Updated sandbox environment. + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/v1/sandbox-environments/{encode_path_param(sandbox_environment_id)}", + method="PUT", + json={ + "description": description, + "manifest": convert_and_respect_annotation_metadata( + object_=manifest, annotation=SandboxEnvironmentManifest, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetSandboxEnvironmentResponse, + construct_type( + type_=GetSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def delete( + self, *, sandbox_environment_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteSandboxEnvironmentResponse]: + """ + Delete a caller-owned sandbox environment. Fails if any agent references it. + + Parameters + ---------- + sandbox_environment_id : str + Immutable sandbox environment identifier. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteSandboxEnvironmentResponse] + Deleted. + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/v1/sandbox-environments/{encode_path_param(sandbox_environment_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteSandboxEnvironmentResponse, + construct_type( + type_=DeleteSandboxEnvironmentResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py index 2e83715b6..114cda8c3 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py @@ -57,8 +57,18 @@ from .created_by_subject import CreatedBySubject from .cron_expression import CronExpression from .custom_model_provider import CustomModelProvider + from .daytona_docker_image import DaytonaDockerImage + from .daytona_gpu_type import DaytonaGpuType + from .daytona_sandbox_environment_image import DaytonaSandboxEnvironmentImage + from .daytona_sandbox_environment_lifecycle import DaytonaSandboxEnvironmentLifecycle + from .daytona_sandbox_environment_networking import DaytonaSandboxEnvironmentNetworking + from .daytona_sandbox_environment_resources import DaytonaSandboxEnvironmentResources + from .daytona_sandbox_environment_resources_gpu_type import DaytonaSandboxEnvironmentResourcesGpuType from .daytona_sandbox_provider_auth import DaytonaSandboxProviderAuth + from .daytona_snapshot_image import DaytonaSnapshotImage + from .daytona_trueforge_default_image import DaytonaTrueforgeDefaultImage from .delete_agent_response import DeleteAgentResponse + from .delete_sandbox_environment_response import DeleteSandboxEnvironmentResponse from .delete_schedule_response import DeleteScheduleResponse from .dynamic_sub_agents_config import DynamicSubAgentsConfig from .extended_chunk_delta_tool_call import ExtendedChunkDeltaToolCall @@ -76,6 +86,7 @@ from .get_me_subject import GetMeSubject from .get_model_provider_catalog_response import GetModelProviderCatalogResponse from .get_model_provider_response import GetModelProviderResponse + from .get_sandbox_environment_response import GetSandboxEnvironmentResponse from .get_sandbox_provider_catalog_response import GetSandboxProviderCatalogResponse from .get_sandbox_provider_response import GetSandboxProviderResponse from .get_schedule_response import GetScheduleResponse @@ -100,6 +111,7 @@ from .list_model_providers_response import ListModelProvidersResponse from .list_permissions_data import ListPermissionsData from .list_permissions_response import ListPermissionsResponse + from .list_sandbox_environments_response import ListSandboxEnvironmentsResponse from .list_schedule_runs_response import ListScheduleRunsResponse from .list_schedules_response import ListSchedulesResponse from .list_session_events_response import ListSessionEventsResponse @@ -163,6 +175,8 @@ from .sandbox_capability import SandboxCapability from .sandbox_config import SandboxConfig from .sandbox_created_event import SandboxCreatedEvent + from .sandbox_environment import SandboxEnvironment + from .sandbox_environment_manifest import SandboxEnvironmentManifest from .sandbox_provider_manifest import SandboxProviderManifest from .schedule import Schedule from .schedule_manifest import ScheduleManifest @@ -295,8 +309,18 @@ "CreatedBySubject": ".created_by_subject", "CronExpression": ".cron_expression", "CustomModelProvider": ".custom_model_provider", + "DaytonaDockerImage": ".daytona_docker_image", + "DaytonaGpuType": ".daytona_gpu_type", + "DaytonaSandboxEnvironmentImage": ".daytona_sandbox_environment_image", + "DaytonaSandboxEnvironmentLifecycle": ".daytona_sandbox_environment_lifecycle", + "DaytonaSandboxEnvironmentNetworking": ".daytona_sandbox_environment_networking", + "DaytonaSandboxEnvironmentResources": ".daytona_sandbox_environment_resources", + "DaytonaSandboxEnvironmentResourcesGpuType": ".daytona_sandbox_environment_resources_gpu_type", "DaytonaSandboxProviderAuth": ".daytona_sandbox_provider_auth", + "DaytonaSnapshotImage": ".daytona_snapshot_image", + "DaytonaTrueforgeDefaultImage": ".daytona_trueforge_default_image", "DeleteAgentResponse": ".delete_agent_response", + "DeleteSandboxEnvironmentResponse": ".delete_sandbox_environment_response", "DeleteScheduleResponse": ".delete_schedule_response", "DynamicSubAgentsConfig": ".dynamic_sub_agents_config", "ExtendedChunkDeltaToolCall": ".extended_chunk_delta_tool_call", @@ -314,6 +338,7 @@ "GetMeSubject": ".get_me_subject", "GetModelProviderCatalogResponse": ".get_model_provider_catalog_response", "GetModelProviderResponse": ".get_model_provider_response", + "GetSandboxEnvironmentResponse": ".get_sandbox_environment_response", "GetSandboxProviderCatalogResponse": ".get_sandbox_provider_catalog_response", "GetSandboxProviderResponse": ".get_sandbox_provider_response", "GetScheduleResponse": ".get_schedule_response", @@ -338,6 +363,7 @@ "ListModelProvidersResponse": ".list_model_providers_response", "ListPermissionsData": ".list_permissions_data", "ListPermissionsResponse": ".list_permissions_response", + "ListSandboxEnvironmentsResponse": ".list_sandbox_environments_response", "ListScheduleRunsResponse": ".list_schedule_runs_response", "ListSchedulesResponse": ".list_schedules_response", "ListSessionEventsResponse": ".list_session_events_response", @@ -401,6 +427,8 @@ "SandboxCapability": ".sandbox_capability", "SandboxConfig": ".sandbox_config", "SandboxCreatedEvent": ".sandbox_created_event", + "SandboxEnvironment": ".sandbox_environment", + "SandboxEnvironmentManifest": ".sandbox_environment_manifest", "SandboxProviderManifest": ".sandbox_provider_manifest", "Schedule": ".schedule", "ScheduleManifest": ".schedule_manifest", @@ -557,8 +585,18 @@ def __dir__(): "CreatedBySubject", "CronExpression", "CustomModelProvider", + "DaytonaDockerImage", + "DaytonaGpuType", + "DaytonaSandboxEnvironmentImage", + "DaytonaSandboxEnvironmentLifecycle", + "DaytonaSandboxEnvironmentNetworking", + "DaytonaSandboxEnvironmentResources", + "DaytonaSandboxEnvironmentResourcesGpuType", "DaytonaSandboxProviderAuth", + "DaytonaSnapshotImage", + "DaytonaTrueforgeDefaultImage", "DeleteAgentResponse", + "DeleteSandboxEnvironmentResponse", "DeleteScheduleResponse", "DynamicSubAgentsConfig", "ExtendedChunkDeltaToolCall", @@ -576,6 +614,7 @@ def __dir__(): "GetMeSubject", "GetModelProviderCatalogResponse", "GetModelProviderResponse", + "GetSandboxEnvironmentResponse", "GetSandboxProviderCatalogResponse", "GetSandboxProviderResponse", "GetScheduleResponse", @@ -600,6 +639,7 @@ def __dir__(): "ListModelProvidersResponse", "ListPermissionsData", "ListPermissionsResponse", + "ListSandboxEnvironmentsResponse", "ListScheduleRunsResponse", "ListSchedulesResponse", "ListSessionEventsResponse", @@ -663,6 +703,8 @@ def __dir__(): "SandboxCapability", "SandboxConfig", "SandboxCreatedEvent", + "SandboxEnvironment", + "SandboxEnvironmentManifest", "SandboxProviderManifest", "Schedule", "ScheduleManifest", diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_docker_image.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_docker_image.py new file mode 100644 index 000000000..e79bb5d30 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_docker_image.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DaytonaDockerImage(UncheckedBaseModel): + ref: str = pydantic.Field() + """ + Container image reference passed to Daytona create-from-image. + """ + + type: typing.Literal["docker"] = "docker" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_gpu_type.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_gpu_type.py new file mode 100644 index 000000000..de3da4844 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_gpu_type.py @@ -0,0 +1,50 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core import enum + +T_Result = typing.TypeVar("T_Result") + + +class DaytonaGpuType(enum.StrEnum): + """ + Preferred Daytona GPU type. + """ + + H100 = "H100" + H200 = "H200" + RTX_PRO6000 = "RTX-PRO-6000" + RTX4090 = "RTX-4090" + RTX5090 = "RTX-5090" + _UNKNOWN = "__DAYTONAGPUTYPE_UNKNOWN__" + """ + This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`. + """ + + @classmethod + def _missing_(cls, value: typing.Any) -> "DaytonaGpuType": + unknown = cls._UNKNOWN + unknown._value_ = value + return unknown + + def visit( + self, + h100: typing.Callable[[], T_Result], + h200: typing.Callable[[], T_Result], + rtx_pro6000: typing.Callable[[], T_Result], + rtx4090: typing.Callable[[], T_Result], + rtx5090: typing.Callable[[], T_Result], + _unknown_member: typing.Callable[[str], T_Result], + ) -> T_Result: + if self is DaytonaGpuType.H100: + return h100() + if self is DaytonaGpuType.H200: + return h200() + if self is DaytonaGpuType.RTX_PRO6000: + return rtx_pro6000() + if self is DaytonaGpuType.RTX4090: + return rtx4090() + if self is DaytonaGpuType.RTX5090: + return rtx5090() + return _unknown_member(self._value_) diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_image.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_image.py new file mode 100644 index 000000000..ca966c3a6 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_image.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .daytona_docker_image import DaytonaDockerImage +from .daytona_snapshot_image import DaytonaSnapshotImage +from .daytona_trueforge_default_image import DaytonaTrueforgeDefaultImage + +DaytonaSandboxEnvironmentImage = typing.Union[DaytonaDockerImage, DaytonaSnapshotImage, DaytonaTrueforgeDefaultImage] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_lifecycle.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_lifecycle.py new file mode 100644 index 000000000..245d1cccc --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_lifecycle.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DaytonaSandboxEnvironmentLifecycle(UncheckedBaseModel): + auto_archive_interval_in_minutes: typing.Optional[int] = pydantic.Field(default=None) + """ + Minutes before Daytona auto-archives the sandbox (0 disables). + """ + + auto_delete_interval_in_minutes: typing.Optional[int] = pydantic.Field(default=None) + """ + Minutes before Daytona auto-deletes the sandbox (0 disables). + """ + + auto_stop_interval_in_minutes: typing.Optional[int] = pydantic.Field(default=None) + """ + Minutes of idle time before Daytona auto-stops the sandbox (0 disables). + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_networking.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_networking.py new file mode 100644 index 000000000..6b64599dc --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_networking.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DaytonaSandboxEnvironmentNetworking(UncheckedBaseModel): + domain_allow_list: typing.Optional[str] = pydantic.Field(default=None) + """ + Comma-separated allowed domains. + """ + + network_allow_list: typing.Optional[str] = pydantic.Field(default=None) + """ + Comma-separated allowed CIDR network addresses. + """ + + network_block_all: typing.Optional[bool] = pydantic.Field(default=None) + """ + Block all outbound network access. + """ + + outbound_proxy_url: typing.Optional[str] = pydantic.Field(default=None) + """ + Outbound HTTP(S) proxy URL. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources.py new file mode 100644 index 000000000..265b378e4 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .daytona_sandbox_environment_resources_gpu_type import DaytonaSandboxEnvironmentResourcesGpuType + + +class DaytonaSandboxEnvironmentResources(UncheckedBaseModel): + cpu: typing.Optional[float] = pydantic.Field(default=None) + """ + CPU allocation in cores. + """ + + disk: typing.Optional[float] = pydantic.Field(default=None) + """ + Disk allocation in GiB. + """ + + gpu: typing.Optional[float] = pydantic.Field(default=None) + """ + GPU allocation in Daytona GPU units. + """ + + gpu_type: typing.Optional[DaytonaSandboxEnvironmentResourcesGpuType] = pydantic.Field(default=None) + """ + Preferred GPU type, or an ordered fallback list. + """ + + memory: typing.Optional[float] = pydantic.Field(default=None) + """ + Memory allocation in GiB. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources_gpu_type.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources_gpu_type.py new file mode 100644 index 000000000..f0589953a --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_sandbox_environment_resources_gpu_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .daytona_gpu_type import DaytonaGpuType + +DaytonaSandboxEnvironmentResourcesGpuType = typing.Union[DaytonaGpuType, typing.List[DaytonaGpuType]] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_snapshot_image.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_snapshot_image.py new file mode 100644 index 000000000..06c1be73f --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_snapshot_image.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DaytonaSnapshotImage(UncheckedBaseModel): + name: str = pydantic.Field() + """ + Daytona snapshot name. + """ + + type: typing.Literal["snapshot"] = "snapshot" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/daytona_trueforge_default_image.py b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_trueforge_default_image.py new file mode 100644 index 000000000..064b102d3 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/daytona_trueforge_default_image.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DaytonaTrueforgeDefaultImage(UncheckedBaseModel): + type: typing.Literal["trueforge-default"] = "trueforge-default" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/delete_sandbox_environment_response.py b/python/trueforge_sdk/src/trueforge_sdk/types/delete_sandbox_environment_response.py new file mode 100644 index 000000000..11de44ee7 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/delete_sandbox_environment_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DeleteSandboxEnvironmentResponse(UncheckedBaseModel): + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/get_sandbox_environment_response.py b/python/trueforge_sdk/src/trueforge_sdk/types/get_sandbox_environment_response.py new file mode 100644 index 000000000..9bb4ff8b9 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/get_sandbox_environment_response.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .sandbox_environment import SandboxEnvironment + + +class GetSandboxEnvironmentResponse(UncheckedBaseModel): + data: SandboxEnvironment + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/list_permissions_data.py b/python/trueforge_sdk/src/trueforge_sdk/types/list_permissions_data.py index ea978b7fc..b14db41f4 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/list_permissions_data.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/list_permissions_data.py @@ -12,7 +12,7 @@ class ListPermissionsData(UncheckedBaseModel): permissions: typing.Dict[str, typing.List[ResourcePermission]] = pydantic.Field() """ - For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` → `CREATE`). + For agent/schedule/session: keyed by resource id. For tenant: keyed by entity kind (e.g. `agent` or `sandbox-environment` → `CREATE`). """ type: PermissionResourceType diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/list_sandbox_environments_response.py b/python/trueforge_sdk/src/trueforge_sdk/types/list_sandbox_environments_response.py new file mode 100644 index 000000000..1f93f47e8 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/list_sandbox_environments_response.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .sandbox_environment import SandboxEnvironment +from .token_pagination import TokenPagination + + +class ListSandboxEnvironmentsResponse(UncheckedBaseModel): + data: typing.List[SandboxEnvironment] + pagination: TokenPagination + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_config.py b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_config.py index d3e7b3ae5..acc99c7e9 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_config.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_config.py @@ -13,6 +13,11 @@ class SandboxConfig(UncheckedBaseModel): Give the agent a sandbox. Required for skills and Code Mode. """ + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + Caller-owned sandbox environment name. Omit to use the tenant provider defaults. + """ + file_downloads: typing.Optional[bool] = pydantic.Field(default=True) """ Allow downloading agent-produced files via the turn download endpoint. Default: true. diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_created_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_created_event.py index c7217ff30..cae6b4750 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_created_event.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_created_event.py @@ -13,6 +13,11 @@ class SandboxCreatedEvent(UncheckedBaseModel): ISO 8601 event timestamp. """ + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + Sandbox environment name when created with one; omit for provider defaults. + """ + id: str = pydantic.Field() """ Unique identifier for the event (monotonic ULID). diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment.py b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment.py new file mode 100644 index 000000000..3638c6d7b --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .created_by_subject import CreatedBySubject +from .resource_name import ResourceName +from .sandbox_environment_manifest import SandboxEnvironmentManifest + + +class SandboxEnvironment(UncheckedBaseModel): + created_at: dt.datetime = pydantic.Field() + """ + ISO-8601 create time. + """ + + created_by_subject: CreatedBySubject + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional human-readable description. + """ + + id: str = pydantic.Field() + """ + Immutable server-generated environment identifier. + """ + + manifest: SandboxEnvironmentManifest + name: ResourceName + updated_at: dt.datetime = pydantic.Field() + """ + ISO-8601 last update time. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment_manifest.py b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment_manifest.py new file mode 100644 index 000000000..4863e6a02 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/sandbox_environment_manifest.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .daytona_sandbox_environment_image import DaytonaSandboxEnvironmentImage +from .daytona_sandbox_environment_lifecycle import DaytonaSandboxEnvironmentLifecycle +from .daytona_sandbox_environment_networking import DaytonaSandboxEnvironmentNetworking +from .daytona_sandbox_environment_resources import DaytonaSandboxEnvironmentResources + + +class SandboxEnvironmentManifest(UncheckedBaseModel): + image: DaytonaSandboxEnvironmentImage + lifecycle: typing.Optional[DaytonaSandboxEnvironmentLifecycle] = None + networking: typing.Optional[DaytonaSandboxEnvironmentNetworking] = None + provider: typing.Literal["daytona"] = pydantic.Field(default="daytona") + """ + Must match the configured sandbox provider name/type. + """ + + resources: typing.Optional[DaytonaSandboxEnvironmentResources] = None + secrets: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Map of sandbox env var name to an existing Daytona organization secret name. + """ + + type: typing.Literal["daytona"] = pydantic.Field(default="daytona") + """ + Daytona sandbox environment. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow From 0390cfaaefc940e28cbb3acfe7f87cf43a1b76bb Mon Sep 17 00:00:00 2001 From: thesujai Date: Mon, 21 Sep 2026 12:57:27 +0530 Subject: [PATCH 3/3] feat: enforce GPU resource requirements in Daytona sandbox environment --- .../core/sandbox/provider/DaytonaProvider.ts | 13 ++++++++---- .../src/schemas/sandboxEnvironment.ts | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts b/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts index 263e9274d..66dc4356d 100644 --- a/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts +++ b/packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts @@ -204,24 +204,29 @@ export class DaytonaSandboxProvider implements SandboxProvider { throw new Error('Daytona create overrides must not set both snapshot and image'); } + const resources = createParams?.resources; + const wantsGpu = !!resources?.gpu || !!resources?.gpuType; const name = `${this.tenantName}.${randomUUID()}`; const base: CreateSandboxBaseParams = { name, autoStopInterval: createParams?.autoStopInterval ?? this.autoStopIntervalInMinutes, autoArchiveInterval: createParams?.autoArchiveInterval ?? this.autoArchiveIntervalInMinutes, - autoDeleteInterval: createParams?.autoDeleteInterval ?? this.autoDeleteIntervalInMinutes, + // Daytona requires GPU sandboxes to be ephemeral (autoDeleteInterval = 0). + autoDeleteInterval: wantsGpu + ? 0 + : (createParams?.autoDeleteInterval ?? this.autoDeleteIntervalInMinutes), }; applyDaytonaCreateOverrides(base, createParams); if (createParams?.image) { const params: CreateSandboxFromImageParams = { ...base, image: createParams.image }; - if (createParams.resources) { - params.resources = createParams.resources; + if (resources) { + params.resources = resources; } return await this.daytona.create(params); } - if (createParams?.resources) { + if (resources) { throw new Error('Resources require a docker image environment (not supported on snapshot create)'); } diff --git a/packages/trueforge/src/schemas/sandboxEnvironment.ts b/packages/trueforge/src/schemas/sandboxEnvironment.ts index 1541c227a..bbdb29678 100644 --- a/packages/trueforge/src/schemas/sandboxEnvironment.ts +++ b/packages/trueforge/src/schemas/sandboxEnvironment.ts @@ -128,6 +128,27 @@ export const DaytonaSandboxEnvironmentManifestSchema = z lifecycle: DaytonaSandboxEnvironmentLifecycleSchema.optional(), }) .strict() + .superRefine((value, ctx) => { + const wantsGpu = value.resources?.gpu != null || value.resources?.gpu_type != null; + if (!wantsGpu) { + return; + } + if (value.image.type !== 'docker') { + ctx.addIssue({ + code: 'custom', + path: ['resources'], + message: 'GPU resources require image.type "docker"', + }); + } + const autoDelete = value.lifecycle?.auto_delete_interval_in_minutes; + if (autoDelete != null && autoDelete !== 0) { + ctx.addIssue({ + code: 'custom', + path: ['lifecycle', 'auto_delete_interval_in_minutes'], + message: 'GPU sandboxes must be ephemeral; set auto_delete_interval_in_minutes to 0', + }); + } + }) .openapi('DaytonaSandboxEnvironmentManifest'); /** Settings / OpenAPI — Daytona only until a second provider ships. */