chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
import type { DaytonaCreateSandboxParams, DaytonaSandboxResponse } from '@/tools/daytona/types'
import {
DAYTONA_API_BASE_URL,
DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
extractDaytonaError,
mapDaytonaSandbox,
toOptionalBoolean,
toOptionalNumber,
} from '@/tools/daytona/utils'
import { transformTable } from '@/tools/shared/table'
import type { ToolConfig } from '@/tools/types'
export const daytonaCreateSandboxTool: ToolConfig<
DaytonaCreateSandboxParams,
DaytonaSandboxResponse
> = {
id: 'daytona_create_sandbox',
name: 'Daytona Create Sandbox',
description: 'Create a new Daytona sandbox for running AI-generated code in isolation',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
snapshot: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID or name of the snapshot to create the sandbox from (uses default if empty)',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Name for the sandbox (defaults to the sandbox ID)',
},
target: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Region where the sandbox will be created (e.g., us, eu)',
},
user: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'User associated with the sandbox',
},
env: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Environment variables to set in the sandbox as key-value pairs',
},
labels: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Labels to attach to the sandbox as key-value pairs',
},
cpu: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'CPU cores to allocate to the sandbox',
},
memory: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Memory to allocate to the sandbox in GB',
},
disk: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Disk space to allocate to the sandbox in GB',
},
autoStopInterval: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Auto-stop interval in minutes (0 disables auto-stop)',
},
autoArchiveInterval: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Auto-archive interval in minutes (0 uses the maximum interval)',
},
autoDeleteInterval: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description:
'Auto-delete interval in minutes (negative disables, 0 deletes immediately on stop)',
},
public: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the sandbox HTTP preview is publicly accessible',
},
},
request: {
url: `${DAYTONA_API_BASE_URL}/sandbox`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.snapshot) body.snapshot = params.snapshot
if (params.name) body.name = params.name
if (params.target) body.target = params.target
if (params.user) body.user = params.user
const env = transformTable(params.env ?? null)
if (Object.keys(env).length > 0) body.env = env
const labels = transformTable(params.labels ?? null)
if (Object.keys(labels).length > 0) body.labels = labels
const cpu = toOptionalNumber(params.cpu)
if (cpu !== undefined) body.cpu = cpu
const memory = toOptionalNumber(params.memory)
if (memory !== undefined) body.memory = memory
const disk = toOptionalNumber(params.disk)
if (disk !== undefined) body.disk = disk
const autoStopInterval = toOptionalNumber(params.autoStopInterval)
if (autoStopInterval !== undefined) body.autoStopInterval = autoStopInterval
const autoArchiveInterval = toOptionalNumber(params.autoArchiveInterval)
if (autoArchiveInterval !== undefined) body.autoArchiveInterval = autoArchiveInterval
const autoDeleteInterval = toOptionalNumber(params.autoDeleteInterval)
if (autoDeleteInterval !== undefined) body.autoDeleteInterval = autoDeleteInterval
const isPublic = toOptionalBoolean(params.public)
if (isPublic !== undefined) body.public = isPublic
return body
},
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to create sandbox'))
}
const data = await response.json()
return {
success: true,
output: {
sandbox: mapDaytonaSandbox(data),
},
}
},
outputs: {
sandbox: {
type: 'json',
description: 'The created sandbox',
properties: DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
},
},
}
+68
View File
@@ -0,0 +1,68 @@
import type { DaytonaDeleteSandboxParams, DaytonaSandboxResponse } from '@/tools/daytona/types'
import {
DAYTONA_API_BASE_URL,
DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
encodeSandboxId,
extractDaytonaError,
mapDaytonaSandbox,
parseDaytonaJson,
} from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
export const daytonaDeleteSandboxTool: ToolConfig<
DaytonaDeleteSandboxParams,
DaytonaSandboxResponse
> = {
id: 'daytona_delete_sandbox',
name: 'Daytona Delete Sandbox',
description: 'Delete a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID or name of the sandbox',
},
},
request: {
url: (params) => `${DAYTONA_API_BASE_URL}/sandbox/${encodeSandboxId(params.sandboxId)}`,
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to delete sandbox'))
}
const data = await parseDaytonaJson(response)
const sandbox = mapDaytonaSandbox(data)
if (!sandbox.id && params) {
sandbox.id = params.sandboxId.trim()
}
return {
success: true,
output: {
sandbox,
},
}
},
outputs: {
sandbox: {
type: 'json',
description: 'The deleted sandbox',
properties: DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
},
},
}
+94
View File
@@ -0,0 +1,94 @@
import type { DaytonaDownloadFileParams, DaytonaDownloadFileResponse } from '@/tools/daytona/types'
import { daytonaToolboxUrl, extractDaytonaError } from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
const MAX_DOWNLOAD_SIZE_BYTES = 100 * 1024 * 1024
function downloadSizeError(bytes: number): Error {
const sizeMB = (bytes / (1024 * 1024)).toFixed(2)
return new Error(`File size (${sizeMB}MB) exceeds download limit of 100MB`)
}
export const daytonaDownloadFileTool: ToolConfig<
DaytonaDownloadFileParams,
DaytonaDownloadFileResponse
> = {
id: 'daytona_download_file',
name: 'Daytona Download File',
description: 'Download a file from a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sandbox to download the file from',
},
filePath: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Path of the file in the sandbox',
},
},
request: {
url: (params) =>
daytonaToolboxUrl(
params.sandboxId,
`/files/download?path=${encodeURIComponent(params.filePath.trim())}`
),
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to download file'))
}
const contentLength = Number(response.headers.get('content-length'))
if (Number.isFinite(contentLength) && contentLength > MAX_DOWNLOAD_SIZE_BYTES) {
throw downloadSizeError(contentLength)
}
const mimeType = response.headers.get('content-type') || 'application/octet-stream'
const fileName = params?.filePath.trim().split('/').filter(Boolean).pop() || 'download'
const arrayBuffer = await response.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
if (buffer.length > MAX_DOWNLOAD_SIZE_BYTES) {
throw downloadSizeError(buffer.length)
}
return {
success: true,
output: {
file: {
name: fileName,
mimeType,
data: buffer.toString('base64'),
size: buffer.length,
},
name: fileName,
mimeType,
size: buffer.length,
},
}
},
outputs: {
file: { type: 'file', description: 'Downloaded file stored in execution files' },
name: { type: 'string', description: 'Name of the downloaded file' },
mimeType: { type: 'string', description: 'MIME type of the downloaded file' },
size: { type: 'number', description: 'Size of the downloaded file in bytes' },
},
}
+98
View File
@@ -0,0 +1,98 @@
import type {
DaytonaExecuteCommandParams,
DaytonaExecuteCommandResponse,
} from '@/tools/daytona/types'
import { daytonaToolboxUrl, extractDaytonaError, toOptionalNumber } from '@/tools/daytona/utils'
import { transformTable } from '@/tools/shared/table'
import type { ToolConfig } from '@/tools/types'
export const daytonaExecuteCommandTool: ToolConfig<
DaytonaExecuteCommandParams,
DaytonaExecuteCommandResponse
> = {
id: 'daytona_execute_command',
name: 'Daytona Execute Command',
description: 'Execute a shell command inside a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sandbox to execute the command in',
},
command: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Shell command to execute',
},
cwd: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Working directory for the command (defaults to the sandbox working directory)',
},
env: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Environment variables to set for the command as key-value pairs',
},
timeout: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timeout in seconds (defaults to 10 seconds)',
},
},
request: {
url: (params) => daytonaToolboxUrl(params.sandboxId, '/process/execute'),
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, unknown> = {
command: params.command,
}
if (params.cwd) body.cwd = params.cwd
const envs = transformTable(params.env ?? null)
if (Object.keys(envs).length > 0) body.envs = envs
const timeout = toOptionalNumber(params.timeout)
if (timeout !== undefined) body.timeout = timeout
return body
},
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to execute command'))
}
const data = await response.json()
return {
success: true,
output: {
exitCode: data.exitCode ?? -1,
result: data.result ?? '',
},
}
},
outputs: {
exitCode: {
type: 'number',
description: 'Exit code of the command (-1 if missing from the response)',
},
result: { type: 'string', description: 'Combined stdout/stderr output of the command' },
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { DaytonaGetSandboxParams, DaytonaSandboxResponse } from '@/tools/daytona/types'
import {
DAYTONA_API_BASE_URL,
DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
encodeSandboxId,
extractDaytonaError,
mapDaytonaSandbox,
} from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
export const daytonaGetSandboxTool: ToolConfig<DaytonaGetSandboxParams, DaytonaSandboxResponse> = {
id: 'daytona_get_sandbox',
name: 'Daytona Get Sandbox',
description: 'Get details of a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID or name of the sandbox',
},
},
request: {
url: (params) => `${DAYTONA_API_BASE_URL}/sandbox/${encodeSandboxId(params.sandboxId)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to get sandbox'))
}
const data = await response.json()
return {
success: true,
output: {
sandbox: mapDaytonaSandbox(data),
},
}
},
outputs: {
sandbox: {
type: 'json',
description: 'The sandbox details',
properties: DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
},
},
}
+99
View File
@@ -0,0 +1,99 @@
import type { DaytonaGitCloneParams, DaytonaGitCloneResponse } from '@/tools/daytona/types'
import { daytonaToolboxUrl, extractDaytonaError } from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
export const daytonaGitCloneTool: ToolConfig<DaytonaGitCloneParams, DaytonaGitCloneResponse> = {
id: 'daytona_git_clone',
name: 'Daytona Git Clone',
description: 'Clone a Git repository into a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sandbox to clone the repository into',
},
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'URL of the Git repository to clone',
},
path: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Path in the sandbox to clone the repository into',
},
branch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Branch to clone (defaults to the default branch)',
},
commitId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Specific commit to check out after cloning',
},
username: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Username for authenticating to private repositories',
},
password: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Password or personal access token for private repositories',
},
},
request: {
url: (params) => daytonaToolboxUrl(params.sandboxId, '/git/clone'),
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, unknown> = {
url: params.url,
path: params.path,
}
if (params.branch) body.branch = params.branch
if (params.commitId) body.commit_id = params.commitId
if (params.username) body.username = params.username
if (params.password) body.password = params.password
return body
},
},
transformResponse: async (response, params) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to clone repository'))
}
return {
success: true,
output: {
repoUrl: params?.url ?? '',
clonePath: params?.path ?? '',
},
}
},
outputs: {
repoUrl: { type: 'string', description: 'URL of the cloned repository' },
clonePath: { type: 'string', description: 'Path the repository was cloned into' },
},
}
+12
View File
@@ -0,0 +1,12 @@
export { daytonaCreateSandboxTool } from '@/tools/daytona/create_sandbox'
export { daytonaDeleteSandboxTool } from '@/tools/daytona/delete_sandbox'
export { daytonaDownloadFileTool } from '@/tools/daytona/download_file'
export { daytonaExecuteCommandTool } from '@/tools/daytona/execute_command'
export { daytonaGetSandboxTool } from '@/tools/daytona/get_sandbox'
export { daytonaGitCloneTool } from '@/tools/daytona/git_clone'
export { daytonaListFilesTool } from '@/tools/daytona/list_files'
export { daytonaListSandboxesTool } from '@/tools/daytona/list_sandboxes'
export { daytonaRunCodeTool } from '@/tools/daytona/run_code'
export { daytonaStartSandboxTool } from '@/tools/daytona/start_sandbox'
export { daytonaStopSandboxTool } from '@/tools/daytona/stop_sandbox'
export { daytonaUploadFileTool } from '@/tools/daytona/upload_file'
+85
View File
@@ -0,0 +1,85 @@
import type { DaytonaListFilesParams, DaytonaListFilesResponse } from '@/tools/daytona/types'
import { daytonaToolboxUrl, extractDaytonaError } from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
export const daytonaListFilesTool: ToolConfig<DaytonaListFilesParams, DaytonaListFilesResponse> = {
id: 'daytona_list_files',
name: 'Daytona List Files',
description: 'List files in a directory of a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sandbox to list files in',
},
path: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Directory path to list (defaults to the sandbox working directory)',
},
},
request: {
url: (params) => {
const query = params.path ? `?path=${encodeURIComponent(params.path.trim())}` : ''
return daytonaToolboxUrl(params.sandboxId, `/files${query}`)
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to list files'))
}
const data = await response.json()
const files = Array.isArray(data) ? data : []
return {
success: true,
output: {
files: files.map((file: Record<string, any>) => ({
name: file.name ?? '',
isDir: file.isDir ?? false,
size: file.size ?? 0,
mode: file.mode ?? '',
permissions: file.permissions ?? '',
owner: file.owner ?? '',
group: file.group ?? '',
modifiedAt: file.modifiedAt ?? '',
})),
},
}
},
outputs: {
files: {
type: 'array',
description: 'Files and directories at the given path',
items: {
type: 'json',
properties: {
name: { type: 'string', description: 'File or directory name' },
isDir: { type: 'boolean', description: 'Whether the entry is a directory' },
size: { type: 'number', description: 'Size in bytes' },
mode: { type: 'string', description: 'File mode string' },
permissions: { type: 'string', description: 'Permission string' },
owner: { type: 'string', description: 'Owning user' },
group: { type: 'string', description: 'Owning group' },
modifiedAt: { type: 'string', description: 'Last modification timestamp' },
},
},
},
},
}
+107
View File
@@ -0,0 +1,107 @@
import type {
DaytonaListSandboxesParams,
DaytonaListSandboxesResponse,
} from '@/tools/daytona/types'
import {
DAYTONA_API_BASE_URL,
DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
extractDaytonaError,
mapDaytonaSandbox,
toOptionalNumber,
} from '@/tools/daytona/utils'
import { transformTable } from '@/tools/shared/table'
import type { ToolConfig } from '@/tools/types'
export const daytonaListSandboxesTool: ToolConfig<
DaytonaListSandboxesParams,
DaytonaListSandboxesResponse
> = {
id: 'daytona_list_sandboxes',
name: 'Daytona List Sandboxes',
description: 'List Daytona sandboxes in the organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of sandboxes to return (1-200)',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter sandboxes by name prefix (case-insensitive)',
},
labels: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Filter sandboxes by labels as key-value pairs',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
const limit = toOptionalNumber(params.limit)
if (limit !== undefined) {
query.set('limit', String(Math.min(Math.max(Math.trunc(limit), 1), 200)))
}
if (params.name) query.set('name', params.name)
const labels = transformTable(params.labels ?? null)
if (Object.keys(labels).length > 0) query.set('labels', JSON.stringify(labels))
if (params.cursor) query.set('cursor', params.cursor)
const queryString = query.toString()
return `${DAYTONA_API_BASE_URL}/sandbox${queryString ? `?${queryString}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to list sandboxes'))
}
const data = await response.json()
const items = Array.isArray(data?.items) ? data.items : []
return {
success: true,
output: {
sandboxes: items.map(mapDaytonaSandbox),
nextCursor: data?.nextCursor ?? null,
},
}
},
outputs: {
sandboxes: {
type: 'array',
description: 'Sandboxes in the organization',
items: {
type: 'json',
properties: DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
},
},
nextCursor: {
type: 'string',
description: 'Cursor for the next page of results',
optional: true,
},
},
}
+98
View File
@@ -0,0 +1,98 @@
import type { DaytonaRunCodeParams, DaytonaRunCodeResponse } from '@/tools/daytona/types'
import { daytonaToolboxUrl, extractDaytonaError, toOptionalNumber } from '@/tools/daytona/utils'
import { transformTable } from '@/tools/shared/table'
import type { ToolConfig } from '@/tools/types'
export const daytonaRunCodeTool: ToolConfig<DaytonaRunCodeParams, DaytonaRunCodeResponse> = {
id: 'daytona_run_code',
name: 'Daytona Run Code',
description: 'Run Python, JavaScript, or TypeScript code inside a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sandbox to run the code in',
},
code: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Code to run',
},
language: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Language of the code: python, javascript, or typescript',
},
env: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Environment variables to set for the run as key-value pairs',
},
timeout: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timeout in seconds (defaults to 10 seconds)',
},
},
request: {
url: (params) => daytonaToolboxUrl(params.sandboxId, '/process/code-run'),
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, unknown> = {
code: params.code,
language: params.language,
}
const envs = transformTable(params.env ?? null)
if (Object.keys(envs).length > 0) body.envs = envs
const timeout = toOptionalNumber(params.timeout)
if (timeout !== undefined) body.timeout = timeout
return body
},
},
transformResponse: async (response) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to run code'))
}
const data = await response.json()
return {
success: true,
output: {
exitCode: data.exitCode ?? -1,
result: data.result ?? '',
artifacts: data.artifacts ?? null,
},
}
},
outputs: {
exitCode: {
type: 'number',
description: 'Exit code of the code run (-1 if missing from the response)',
},
result: { type: 'string', description: 'Combined stdout/stderr output of the code run' },
artifacts: {
type: 'json',
description: 'Artifacts produced by the run (e.g., matplotlib charts)',
optional: true,
},
},
}
+68
View File
@@ -0,0 +1,68 @@
import type { DaytonaSandboxResponse, DaytonaStartSandboxParams } from '@/tools/daytona/types'
import {
DAYTONA_API_BASE_URL,
DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
encodeSandboxId,
extractDaytonaError,
mapDaytonaSandbox,
parseDaytonaJson,
} from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
export const daytonaStartSandboxTool: ToolConfig<
DaytonaStartSandboxParams,
DaytonaSandboxResponse
> = {
id: 'daytona_start_sandbox',
name: 'Daytona Start Sandbox',
description: 'Start a stopped Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID or name of the sandbox',
},
},
request: {
url: (params) => `${DAYTONA_API_BASE_URL}/sandbox/${encodeSandboxId(params.sandboxId)}/start`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to start sandbox'))
}
const data = await parseDaytonaJson(response)
const sandbox = mapDaytonaSandbox(data)
if (!sandbox.id && params) {
sandbox.id = params.sandboxId.trim()
}
return {
success: true,
output: {
sandbox,
},
}
},
outputs: {
sandbox: {
type: 'json',
description: 'The started sandbox',
properties: DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
},
},
}
+66
View File
@@ -0,0 +1,66 @@
import type { DaytonaSandboxResponse, DaytonaStopSandboxParams } from '@/tools/daytona/types'
import {
DAYTONA_API_BASE_URL,
DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
encodeSandboxId,
extractDaytonaError,
mapDaytonaSandbox,
parseDaytonaJson,
} from '@/tools/daytona/utils'
import type { ToolConfig } from '@/tools/types'
export const daytonaStopSandboxTool: ToolConfig<DaytonaStopSandboxParams, DaytonaSandboxResponse> =
{
id: 'daytona_stop_sandbox',
name: 'Daytona Stop Sandbox',
description: 'Stop a running Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID or name of the sandbox',
},
},
request: {
url: (params) => `${DAYTONA_API_BASE_URL}/sandbox/${encodeSandboxId(params.sandboxId)}/stop`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params) => {
if (!response.ok) {
throw new Error(await extractDaytonaError(response, 'Failed to stop sandbox'))
}
const data = await parseDaytonaJson(response)
const sandbox = mapDaytonaSandbox(data)
if (!sandbox.id && params) {
sandbox.id = params.sandboxId.trim()
}
return {
success: true,
output: {
sandbox,
},
}
},
outputs: {
sandbox: {
type: 'json',
description: 'The stopped sandbox',
properties: DAYTONA_SANDBOX_OUTPUT_PROPERTIES,
},
},
}
+165
View File
@@ -0,0 +1,165 @@
import type { TableRow, ToolResponse } from '@/tools/types'
export interface DaytonaSandboxSummary {
id: string
name: string
state: string | null
snapshot: string | null
target: string | null
cpu: number | null
gpu: number | null
memory: number | null
disk: number | null
labels: Record<string, string>
public: boolean | null
errorReason: string | null
autoStopInterval: number | null
createdAt: string | null
updatedAt: string | null
}
export interface DaytonaFileInfo {
name: string
isDir: boolean
size: number
mode: string
permissions: string
owner: string
group: string
modifiedAt: string
}
interface DaytonaBaseParams {
apiKey: string
}
interface DaytonaSandboxScopedParams extends DaytonaBaseParams {
sandboxId: string
}
export interface DaytonaCreateSandboxParams extends DaytonaBaseParams {
snapshot?: string
name?: string
target?: string
user?: string
env?: TableRow[] | Record<string, string> | string
labels?: TableRow[] | Record<string, string> | string
cpu?: number
memory?: number
disk?: number
autoStopInterval?: number
autoArchiveInterval?: number
autoDeleteInterval?: number
public?: boolean
}
export interface DaytonaListSandboxesParams extends DaytonaBaseParams {
limit?: number
name?: string
labels?: TableRow[] | Record<string, string> | string
cursor?: string
}
export type DaytonaGetSandboxParams = DaytonaSandboxScopedParams
export type DaytonaStartSandboxParams = DaytonaSandboxScopedParams
export type DaytonaStopSandboxParams = DaytonaSandboxScopedParams
export type DaytonaDeleteSandboxParams = DaytonaSandboxScopedParams
export interface DaytonaExecuteCommandParams extends DaytonaSandboxScopedParams {
command: string
cwd?: string
env?: TableRow[] | Record<string, string> | string
timeout?: number
}
export interface DaytonaRunCodeParams extends DaytonaSandboxScopedParams {
code: string
language: 'python' | 'javascript' | 'typescript'
env?: TableRow[] | Record<string, string> | string
timeout?: number
}
export interface DaytonaUploadFileParams extends DaytonaSandboxScopedParams {
destinationPath: string
file?: unknown
fileContent?: string
fileName?: string
}
export interface DaytonaDownloadFileParams extends DaytonaSandboxScopedParams {
filePath: string
}
export interface DaytonaListFilesParams extends DaytonaSandboxScopedParams {
path?: string
}
export interface DaytonaGitCloneParams extends DaytonaSandboxScopedParams {
url: string
path: string
branch?: string
commitId?: string
username?: string
password?: string
}
export interface DaytonaSandboxResponse extends ToolResponse {
output: {
sandbox: DaytonaSandboxSummary
}
}
export interface DaytonaListSandboxesResponse extends ToolResponse {
output: {
sandboxes: DaytonaSandboxSummary[]
nextCursor: string | null
}
}
export interface DaytonaExecuteCommandResponse extends ToolResponse {
output: {
exitCode: number
result: string
}
}
export interface DaytonaRunCodeResponse extends ToolResponse {
output: {
exitCode: number
result: string
artifacts: Record<string, unknown> | null
}
}
export interface DaytonaUploadFileResponse extends ToolResponse {
output: {
uploadedPath: string
name: string
size: number
}
}
export interface DaytonaDownloadFileResponse extends ToolResponse {
output: {
file: unknown
name: string
mimeType: string
size: number
}
}
export interface DaytonaListFilesResponse extends ToolResponse {
output: {
files: DaytonaFileInfo[]
}
}
export interface DaytonaGitCloneResponse extends ToolResponse {
output: {
repoUrl: string
clonePath: string
}
}
+90
View File
@@ -0,0 +1,90 @@
import type { DaytonaUploadFileParams, DaytonaUploadFileResponse } from '@/tools/daytona/types'
import type { ToolConfig } from '@/tools/types'
export const daytonaUploadFileTool: ToolConfig<DaytonaUploadFileParams, DaytonaUploadFileResponse> =
{
id: 'daytona_upload_file',
name: 'Daytona Upload File',
description: 'Upload a file to a Daytona sandbox',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Daytona API key',
},
sandboxId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the sandbox to upload the file to',
},
destinationPath: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Destination path in the sandbox (a trailing slash uploads into that directory using the file name)',
},
file: {
type: 'file',
required: false,
visibility: 'user-or-llm',
description: 'The file to upload',
},
fileContent: {
type: 'string',
required: false,
visibility: 'hidden',
description: 'Legacy: base64 encoded file content',
},
fileName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional file name override',
},
},
request: {
url: '/api/tools/daytona/upload',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
apiKey: params.apiKey,
sandboxId: params.sandboxId,
destinationPath: params.destinationPath,
file: params.file,
fileContent: params.fileContent,
fileName: params.fileName,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok || !data.success) {
throw new Error(data.error || 'Failed to upload file')
}
return {
success: true,
output: {
uploadedPath: data.uploadedPath,
name: data.name,
size: data.size,
},
}
},
outputs: {
uploadedPath: {
type: 'string',
description: 'Path of the uploaded file in the sandbox',
},
name: { type: 'string', description: 'Name of the uploaded file' },
size: { type: 'number', description: 'Size of the uploaded file in bytes' },
},
}
+128
View File
@@ -0,0 +1,128 @@
import type { DaytonaSandboxSummary } from '@/tools/daytona/types'
export const DAYTONA_API_BASE_URL = 'https://app.daytona.io/api'
export const DAYTONA_TOOLBOX_BASE_URL = 'https://proxy.app.daytona.io/toolbox'
/**
* Trims and URL-encodes a sandbox identifier, rejecting empty values so a
* blank ID can never resolve to a different API route.
*/
export function encodeSandboxId(sandboxId: string): string {
const trimmed = sandboxId?.trim()
if (!trimmed) {
throw new Error('Sandbox ID is required')
}
return encodeURIComponent(trimmed)
}
/**
* Builds a toolbox API URL for a sandbox-scoped endpoint.
*/
export function daytonaToolboxUrl(sandboxId: string, path: string): string {
return `${DAYTONA_TOOLBOX_BASE_URL}/${encodeSandboxId(sandboxId)}${path}`
}
/**
* Parses a Daytona API JSON response, tolerating empty bodies (e.g., 204 or
* empty 200 responses from lifecycle endpoints).
*/
export async function parseDaytonaJson(response: Response): Promise<Record<string, any>> {
const text = await response.text()
if (!text) return {}
try {
return JSON.parse(text)
} catch {
return {}
}
}
/**
* Extracts a human-readable error message from a Daytona API error response.
*/
export async function extractDaytonaError(response: Response, fallback: string): Promise<string> {
try {
const data = await response.json()
if (typeof data?.message === 'string') return data.message
if (Array.isArray(data?.message)) return data.message.join(', ')
if (typeof data?.error === 'string') return data.error
} catch {
// Non-JSON error body; fall through to the fallback message
}
return `${fallback} (status ${response.status})`
}
/**
* Coerces an optional user- or LLM-provided value to a number, treating
* empty/missing values as undefined.
*/
export function toOptionalNumber(value: unknown): number | undefined {
if (value === undefined || value === null || value === '') return undefined
const num = Number(value)
return Number.isNaN(num) ? undefined : num
}
/**
* Coerces an optional user- or LLM-provided value to a boolean, treating
* empty/missing values as undefined.
*/
export function toOptionalBoolean(value: unknown): boolean | undefined {
if (value === undefined || value === null || value === '') return undefined
if (typeof value === 'boolean') return value
const normalized = String(value).trim().toLowerCase()
if (normalized === 'true') return true
if (normalized === 'false') return false
return undefined
}
/**
* Maps a raw Daytona sandbox object to the normalized summary shape.
*/
export function mapDaytonaSandbox(sandbox: Record<string, any>): DaytonaSandboxSummary {
return {
id: sandbox.id ?? '',
name: sandbox.name ?? '',
state: sandbox.state ?? null,
snapshot: sandbox.snapshot ?? null,
target: sandbox.target ?? null,
cpu: sandbox.cpu ?? null,
gpu: sandbox.gpu ?? null,
memory: sandbox.memory ?? null,
disk: sandbox.disk ?? null,
labels: sandbox.labels ?? {},
public: sandbox.public ?? null,
errorReason: sandbox.errorReason ?? null,
autoStopInterval: sandbox.autoStopInterval ?? null,
createdAt: sandbox.createdAt ?? null,
updatedAt: sandbox.updatedAt ?? null,
}
}
/**
* Shared output property map for sandbox summary outputs.
*/
export const DAYTONA_SANDBOX_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Sandbox ID' },
name: { type: 'string', description: 'Sandbox name' },
state: { type: 'string', description: 'Sandbox state (e.g., started, stopped)', optional: true },
snapshot: {
type: 'string',
description: 'Snapshot the sandbox was created from',
optional: true,
},
target: { type: 'string', description: 'Region the sandbox runs in', optional: true },
cpu: { type: 'number', description: 'CPU cores allocated', optional: true },
gpu: { type: 'number', description: 'GPU units allocated', optional: true },
memory: { type: 'number', description: 'Memory allocated in GB', optional: true },
disk: { type: 'number', description: 'Disk space allocated in GB', optional: true },
labels: { type: 'json', description: 'Labels attached to the sandbox', optional: true },
public: { type: 'boolean', description: 'Whether the HTTP preview is public', optional: true },
errorReason: { type: 'string', description: 'Error reason if in error state', optional: true },
autoStopInterval: {
type: 'number',
description: 'Auto-stop interval in minutes (0 means disabled)',
optional: true,
},
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
} as const