Computer use (#2064)

* feat: computer use api

Signed-off-by: Vedran <vedran.jukic@gmail.com>

---------

Signed-off-by: Vedran <vedran.jukic@gmail.com>
Signed-off-by: MDzaja <mirkodzaja0@gmail.com>
Signed-off-by: Toma Puljak <toma.puljak@hotmail.com>
Co-authored-by: MDzaja <mirkodzaja0@gmail.com>
Co-authored-by: Toma Puljak <toma.puljak@hotmail.com>
This commit is contained in:
Vedran Jukic
2025-07-08 11:41:10 +02:00
committed by GitHub
co-authored by MDzaja Toma Puljak
parent af77e33105
commit 0e810536ad
357 changed files with 42300 additions and 613 deletions
+3 -1
View File
@@ -5,7 +5,9 @@ ARG TARGETARCH
# common tools
RUN apt update && export DEBIAN_FRONTEND=noninteractive \
&& apt -y install --no-install-recommends apt-utils vim htop telnet socat expect-dev tini psmisc libgit2-dev \
python3 python3-pip
python3 python3-pip libx11-dev libxtst-dev libxext-dev libxrandr-dev libxinerama-dev libxi-dev \
libx11-6 libxrandr2 libxext6 libxrender1 libxfixes3 libxss1 libxtst6 libxi6 \
xvfb x11vnc novnc xfce4 xfce4-terminal dbus-x11
# build tools
RUN apt update && export DEBIAN_FRONTEND=noninteractive \
+39 -1
View File
@@ -14,12 +14,27 @@ concurrency:
cancel-in-progress: true
jobs:
go-work:
name: Go work
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.work
cache: false
- name: go work
run: |
go work sync
git diff --exit-code go.work || (echo "go.work is not up to date! Please run 'go work sync' and commit" && exit 1)
git diff --exit-code go.work.sum || (echo "go.work.sum is not up to date! Please run 'go work sync' and commit" && exit 1)
golangci:
name: Go lint
runs-on: ubuntu-latest
strategy:
matrix:
working-directory: [apps/daemon, apps/runner, apps/cli]
working-directory: [apps/daemon, apps/runner, apps/cli, apps/proxy]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
@@ -38,6 +53,28 @@ jobs:
go fmt ./...
git diff --exit-code '**/*.go' || (echo "Code is not formatted! Please run 'go fmt ./...' and commit" && exit 1)
lint-computer-use:
name: Go lint (Computer Use)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.work
cache: false
- name: format
run: |
sudo apt-get update && sudo apt-get install -y gcc libx11-dev libxtst-dev
cd libs/computer-use
go fmt ./...
git diff --exit-code '**/*.go' || (echo "Code is not formatted! Please run 'go fmt ./...' and commit" && exit 1)
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
working-directory: libs/computer-use
args: --timeout=5m ./...
format-lint-api-clients:
name: Format, lint and generate API clients
runs-on: ubuntu-latest
@@ -90,6 +127,7 @@ jobs:
- name: Build all
run: |
python3 -m pip install poetry==2.1.3
sudo apt-get update && sudo apt-get install -y gcc libx11-dev libxtst-dev
yarn
yarn build
+2
View File
@@ -20,5 +20,7 @@ header:
- 'libs/api-client-python/**'
- 'libs/api-client-python-async/**'
- 'apps/docs/**'
- 'libs/computer-use/**'
- 'hack/**'
comment: on-failure
+2
View File
@@ -15,9 +15,11 @@ header:
paths-ignore:
- 'libs/**'
- '!libs/computer-use/**'
- 'apps/api/src/generate-openapi.ts'
- 'apps/runner/pkg/api/docs/docs.go'
- 'examples/**'
- 'apps/docs/**'
- 'hack/**'
comment: on-failure
@@ -57,6 +57,31 @@ import {
SessionExecuteResponseDto,
SessionDto,
CommandDto,
MousePositionDto,
MouseMoveRequestDto,
MouseMoveResponseDto,
MouseClickRequestDto,
MouseClickResponseDto,
MouseDragRequestDto,
MouseDragResponseDto,
MouseScrollRequestDto,
MouseScrollResponseDto,
KeyboardTypeRequestDto,
KeyboardPressRequestDto,
KeyboardHotkeyRequestDto,
ScreenshotResponseDto,
RegionScreenshotRequestDto,
RegionScreenshotResponseDto,
CompressedScreenshotResponseDto,
DisplayInfoResponseDto,
WindowsResponseDto,
ComputerUseStartResponseDto,
ComputerUseStopResponseDto,
ComputerUseStatusResponseDto,
ProcessStatusResponseDto,
ProcessRestartResponseDto,
ProcessLogsResponseDto,
ProcessErrorsResponseDto,
} from '../dto/toolbox.dto'
import { ToolboxService } from '../services/toolbox.service'
import { ContentTypeInterceptor } from '../../common/interceptors/content-type.interceptors'
@@ -1088,4 +1113,491 @@ export class ToolboxController {
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
// Computer Use endpoints
// Computer use management endpoints
@Post(':sandboxId/toolbox/computeruse/start')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Start computer use processes',
description: 'Start all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc)',
operationId: 'startComputerUse',
})
@ApiResponse({
status: 200,
description: 'Computer use processes started successfully',
type: ComputerUseStartResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async startComputerUse(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/stop')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Stop computer use processes',
description: 'Stop all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc)',
operationId: 'stopComputerUse',
})
@ApiResponse({
status: 200,
description: 'Computer use processes stopped successfully',
type: ComputerUseStopResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async stopComputerUse(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/status')
@ApiOperation({
summary: 'Get computer use status',
description: 'Get status of all VNC desktop processes',
operationId: 'getComputerUseStatus',
})
@ApiResponse({
status: 200,
description: 'Computer use status retrieved successfully',
type: ComputerUseStatusResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async getComputerUseStatus(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/process/:processName/status')
@ApiOperation({
summary: 'Get process status',
description: 'Get status of a specific VNC process',
operationId: 'getProcessStatus',
})
@ApiResponse({
status: 200,
description: 'Process status retrieved successfully',
type: ProcessStatusResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
@ApiParam({ name: 'processName', type: String, required: true })
async getProcessStatus(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/process/:processName/restart')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Restart process',
description: 'Restart a specific VNC process',
operationId: 'restartProcess',
})
@ApiResponse({
status: 200,
description: 'Process restarted successfully',
type: ProcessRestartResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
@ApiParam({ name: 'processName', type: String, required: true })
async restartProcess(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/process/:processName/logs')
@ApiOperation({
summary: 'Get process logs',
description: 'Get logs for a specific VNC process',
operationId: 'getProcessLogs',
})
@ApiResponse({
status: 200,
description: 'Process logs retrieved successfully',
type: ProcessLogsResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
@ApiParam({ name: 'processName', type: String, required: true })
async getProcessLogs(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/process/:processName/errors')
@ApiOperation({
summary: 'Get process errors',
description: 'Get error logs for a specific VNC process',
operationId: 'getProcessErrors',
})
@ApiResponse({
status: 200,
description: 'Process errors retrieved successfully',
type: ProcessErrorsResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
@ApiParam({ name: 'processName', type: String, required: true })
async getProcessErrors(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
// Mouse endpoints
@Get(':sandboxId/toolbox/computeruse/mouse/position')
@ApiOperation({
summary: 'Get mouse position',
description: 'Get current mouse cursor position',
operationId: 'getMousePosition',
})
@ApiResponse({
status: 200,
description: 'Mouse position retrieved successfully',
type: MousePositionDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async getMousePosition(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/mouse/move')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Move mouse',
description: 'Move mouse cursor to specified coordinates',
operationId: 'moveMouse',
})
@ApiResponse({
status: 200,
description: 'Mouse moved successfully',
type: MouseMoveResponseDto,
})
@ApiBody({
type: MouseMoveRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async moveMouse(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/mouse/click')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Click mouse',
description: 'Click mouse at specified coordinates',
operationId: 'clickMouse',
})
@ApiResponse({
status: 200,
description: 'Mouse clicked successfully',
type: MouseClickResponseDto,
})
@ApiBody({
type: MouseClickRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async clickMouse(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/mouse/drag')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Drag mouse',
description: 'Drag mouse from start to end coordinates',
operationId: 'dragMouse',
})
@ApiResponse({
status: 200,
description: 'Mouse dragged successfully',
type: MouseDragResponseDto,
})
@ApiBody({
type: MouseDragRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async dragMouse(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/mouse/scroll')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Scroll mouse',
description: 'Scroll mouse at specified coordinates',
operationId: 'scrollMouse',
})
@ApiResponse({
status: 200,
description: 'Mouse scrolled successfully',
type: MouseScrollResponseDto,
})
@ApiBody({
type: MouseScrollRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async scrollMouse(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
// Keyboard endpoints
@Post(':sandboxId/toolbox/computeruse/keyboard/type')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Type text',
description: 'Type text using keyboard',
operationId: 'typeText',
})
@ApiResponse({
status: 200,
description: 'Text typed successfully',
})
@ApiBody({
type: KeyboardTypeRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async typeText(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/keyboard/key')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Press key',
description: 'Press a key with optional modifiers',
operationId: 'pressKey',
})
@ApiResponse({
status: 200,
description: 'Key pressed successfully',
})
@ApiBody({
type: KeyboardPressRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async pressKey(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Post(':sandboxId/toolbox/computeruse/keyboard/hotkey')
@HttpCode(200)
@UseInterceptors(ContentTypeInterceptor)
@ApiOperation({
summary: 'Press hotkey',
description: 'Press a hotkey combination',
operationId: 'pressHotkey',
})
@ApiResponse({
status: 200,
description: 'Hotkey pressed successfully',
})
@ApiBody({
type: KeyboardHotkeyRequestDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async pressHotkey(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
// Screenshot endpoints
@Get(':sandboxId/toolbox/computeruse/screenshot')
@ApiOperation({
summary: 'Take screenshot',
description: 'Take a screenshot of the entire screen',
operationId: 'takeScreenshot',
})
@ApiResponse({
status: 200,
description: 'Screenshot taken successfully',
type: ScreenshotResponseDto,
})
@ApiQuery({ name: 'show_cursor', type: Boolean, required: false })
@ApiParam({ name: 'sandboxId', type: String, required: true })
async takeScreenshot(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/screenshot/region')
@ApiOperation({
summary: 'Take region screenshot',
description: 'Take a screenshot of a specific region',
operationId: 'takeRegionScreenshot',
})
@ApiResponse({
status: 200,
description: 'Region screenshot taken successfully',
type: RegionScreenshotResponseDto,
})
@ApiQuery({ name: 'x', type: Number, required: true })
@ApiQuery({ name: 'y', type: Number, required: true })
@ApiQuery({ name: 'width', type: Number, required: true })
@ApiQuery({ name: 'height', type: Number, required: true })
@ApiQuery({ name: 'show_cursor', type: Boolean, required: false })
@ApiParam({ name: 'sandboxId', type: String, required: true })
async takeRegionScreenshot(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/screenshot/compressed')
@ApiOperation({
summary: 'Take compressed screenshot',
description: 'Take a compressed screenshot with format, quality, and scale options',
operationId: 'takeCompressedScreenshot',
})
@ApiResponse({
status: 200,
description: 'Compressed screenshot taken successfully',
type: CompressedScreenshotResponseDto,
})
@ApiQuery({ name: 'show_cursor', type: Boolean, required: false })
@ApiQuery({ name: 'format', type: String, required: false })
@ApiQuery({ name: 'quality', type: Number, required: false })
@ApiQuery({ name: 'scale', type: Number, required: false })
@ApiParam({ name: 'sandboxId', type: String, required: true })
async takeCompressedScreenshot(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/screenshot/region/compressed')
@ApiOperation({
summary: 'Take compressed region screenshot',
description: 'Take a compressed screenshot of a specific region',
operationId: 'takeCompressedRegionScreenshot',
})
@ApiResponse({
status: 200,
description: 'Compressed region screenshot taken successfully',
type: CompressedScreenshotResponseDto,
})
@ApiQuery({ name: 'x', type: Number, required: true })
@ApiQuery({ name: 'y', type: Number, required: true })
@ApiQuery({ name: 'width', type: Number, required: true })
@ApiQuery({ name: 'height', type: Number, required: true })
@ApiQuery({ name: 'show_cursor', type: Boolean, required: false })
@ApiQuery({ name: 'format', type: String, required: false })
@ApiQuery({ name: 'quality', type: Number, required: false })
@ApiQuery({ name: 'scale', type: Number, required: false })
@ApiParam({ name: 'sandboxId', type: String, required: true })
async takeCompressedRegionScreenshot(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
// Display endpoints
@Get(':sandboxId/toolbox/computeruse/display/info')
@ApiOperation({
summary: 'Get display info',
description: 'Get information about displays',
operationId: 'getDisplayInfo',
})
@ApiResponse({
status: 200,
description: 'Display info retrieved successfully',
type: DisplayInfoResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async getDisplayInfo(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
@Get(':sandboxId/toolbox/computeruse/display/windows')
@ApiOperation({
summary: 'Get windows',
description: 'Get list of open windows',
operationId: 'getWindows',
})
@ApiResponse({
status: 200,
description: 'Windows list retrieved successfully',
type: WindowsResponseDto,
})
@ApiParam({ name: 'sandboxId', type: String, required: true })
async getWindows(
@Request() req: RawBodyRequest<IncomingMessage>,
@Res() res: ServerResponse<IncomingMessage>,
@Next() next: NextFunction,
): Promise<void> {
return await this.toolboxProxy(req, res, next)
}
}
+456
View File
@@ -379,3 +379,459 @@ export class SessionDto {
@IsOptional()
commands?: CommandDto[] | null
}
// Computer Use DTOs
@ApiSchema({ name: 'MousePosition' })
export class MousePositionDto {
@ApiProperty({
description: 'The X coordinate of the mouse cursor position',
example: 100,
})
x: number
@ApiProperty({
description: 'The Y coordinate of the mouse cursor position',
example: 200,
})
y: number
}
@ApiSchema({ name: 'MouseMoveRequest' })
export class MouseMoveRequestDto {
@ApiProperty({
description: 'The target X coordinate to move the mouse cursor to',
example: 150,
})
x: number
@ApiProperty({
description: 'The target Y coordinate to move the mouse cursor to',
example: 250,
})
y: number
}
@ApiSchema({ name: 'MouseMoveResponse' })
export class MouseMoveResponseDto {
@ApiProperty({
description: 'The actual X coordinate where the mouse cursor ended up',
example: 150,
})
x: number
@ApiProperty({
description: 'The actual Y coordinate where the mouse cursor ended up',
example: 250,
})
y: number
}
@ApiSchema({ name: 'MouseClickRequest' })
export class MouseClickRequestDto {
@ApiProperty({
description: 'The X coordinate where to perform the mouse click',
example: 100,
})
x: number
@ApiProperty({
description: 'The Y coordinate where to perform the mouse click',
example: 200,
})
y: number
@ApiPropertyOptional({
description: 'The mouse button to click (left, right, middle). Defaults to left',
example: 'left',
})
button?: string
@ApiPropertyOptional({
description: 'Whether to perform a double-click instead of a single click',
example: false,
})
double?: boolean
}
@ApiSchema({ name: 'MouseClickResponse' })
export class MouseClickResponseDto {
@ApiProperty({
description: 'The actual X coordinate where the click occurred',
example: 100,
})
x: number
@ApiProperty({
description: 'The actual Y coordinate where the click occurred',
example: 200,
})
y: number
}
@ApiSchema({ name: 'MouseDragRequest' })
export class MouseDragRequestDto {
@ApiProperty({
description: 'The starting X coordinate for the drag operation',
example: 100,
})
startX: number
@ApiProperty({
description: 'The starting Y coordinate for the drag operation',
example: 200,
})
startY: number
@ApiProperty({
description: 'The ending X coordinate for the drag operation',
example: 300,
})
endX: number
@ApiProperty({
description: 'The ending Y coordinate for the drag operation',
example: 400,
})
endY: number
@ApiPropertyOptional({
description: 'The mouse button to use for dragging (left, right, middle). Defaults to left',
example: 'left',
})
button?: string
}
@ApiSchema({ name: 'MouseDragResponse' })
export class MouseDragResponseDto {
@ApiProperty({
description: 'The actual X coordinate where the drag ended',
example: 300,
})
x: number
@ApiProperty({
description: 'The actual Y coordinate where the drag ended',
example: 400,
})
y: number
}
@ApiSchema({ name: 'MouseScrollRequest' })
export class MouseScrollRequestDto {
@ApiProperty({
description: 'The X coordinate where to perform the scroll operation',
example: 100,
})
x: number
@ApiProperty({
description: 'The Y coordinate where to perform the scroll operation',
example: 200,
})
y: number
@ApiProperty({
description: 'The scroll direction (up, down)',
example: 'down',
})
direction: string
@ApiPropertyOptional({
description: 'The number of scroll units to scroll. Defaults to 1',
example: 3,
})
amount?: number
}
@ApiSchema({ name: 'MouseScrollResponse' })
export class MouseScrollResponseDto {
@ApiProperty({
description: 'Whether the mouse scroll operation was successful',
example: true,
})
success: boolean
}
@ApiSchema({ name: 'KeyboardTypeRequest' })
export class KeyboardTypeRequestDto {
@ApiProperty({
description: 'The text to type using the keyboard',
example: 'Hello, World!',
})
text: string
@ApiPropertyOptional({
description: 'Delay in milliseconds between keystrokes. Defaults to 0',
example: 100,
})
delay?: number
}
@ApiSchema({ name: 'KeyboardPressRequest' })
export class KeyboardPressRequestDto {
@ApiProperty({
description: 'The key to press (e.g., a, b, c, enter, space, etc.)',
example: 'enter',
})
key: string
@ApiPropertyOptional({
description: 'Array of modifier keys to press along with the main key (ctrl, alt, shift, cmd)',
type: [String],
example: ['ctrl', 'shift'],
})
modifiers?: string[]
}
@ApiSchema({ name: 'KeyboardHotkeyRequest' })
export class KeyboardHotkeyRequestDto {
@ApiProperty({
description: 'The hotkey combination to press (e.g., "ctrl+c", "cmd+v", "alt+tab")',
example: 'ctrl+c',
})
keys: string
}
@ApiSchema({ name: 'ScreenshotResponse' })
export class ScreenshotResponseDto {
@ApiProperty({
description: 'Base64 encoded screenshot image data',
example: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
})
screenshot: string
@ApiPropertyOptional({
description: 'The current cursor position when the screenshot was taken',
example: { x: 500, y: 300 },
})
cursorPosition?: { x: number; y: number }
@ApiPropertyOptional({
description: 'The size of the screenshot data in bytes',
example: 24576,
})
sizeBytes?: number
}
@ApiSchema({ name: 'RegionScreenshotRequest' })
export class RegionScreenshotRequestDto {
@ApiProperty({
description: 'The X coordinate of the top-left corner of the region to capture',
example: 100,
})
x: number
@ApiProperty({
description: 'The Y coordinate of the top-left corner of the region to capture',
example: 100,
})
y: number
@ApiProperty({
description: 'The width of the region to capture in pixels',
example: 800,
})
width: number
@ApiProperty({
description: 'The height of the region to capture in pixels',
example: 600,
})
height: number
}
@ApiSchema({ name: 'RegionScreenshotResponse' })
export class RegionScreenshotResponseDto {
@ApiProperty({
description: 'Base64 encoded screenshot image data of the specified region',
example: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
})
screenshot: string
@ApiPropertyOptional({
description: 'The current cursor position when the region screenshot was taken',
example: { x: 500, y: 300 },
})
cursorPosition?: { x: number; y: number }
@ApiPropertyOptional({
description: 'The size of the screenshot data in bytes',
example: 24576,
})
sizeBytes?: number
}
@ApiSchema({ name: 'CompressedScreenshotResponse' })
export class CompressedScreenshotResponseDto {
@ApiProperty({
description: 'Base64 encoded compressed screenshot image data',
example: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
})
screenshot: string
@ApiPropertyOptional({
description: 'The current cursor position when the compressed screenshot was taken',
example: { x: 250, y: 150 },
})
cursorPosition?: { x: number; y: number }
@ApiPropertyOptional({
description: 'The size of the compressed screenshot data in bytes',
example: 12288,
})
sizeBytes?: number
}
@ApiSchema({ name: 'DisplayInfoResponse' })
export class DisplayInfoResponseDto {
@ApiProperty({
description: 'Array of display information for all connected displays',
type: [Object],
example: [
{
id: 0,
x: 0,
y: 0,
width: 1920,
height: 1080,
is_active: true,
},
],
})
displays: Array<{ id: number; x: number; y: number; width: number; height: number; is_active: boolean }>
}
@ApiSchema({ name: 'WindowsResponse' })
export class WindowsResponseDto {
@ApiProperty({
description: 'Array of window information for all visible windows',
type: [Object],
example: [
{
id: 12345,
title: 'Terminal',
},
],
})
windows: Array<{ id: number; title: string }>
@ApiProperty({
description: 'The total number of windows found',
example: 5,
})
count: number
}
// Computer Use Management Response DTOs
@ApiSchema({ name: 'ComputerUseStartResponse' })
export class ComputerUseStartResponseDto {
@ApiProperty({
description: 'A message indicating the result of starting computer use processes',
example: 'Computer use processes started successfully',
})
message: string
@ApiProperty({
description: 'Status information about all VNC desktop processes after starting',
type: Object,
example: {
xvfb: { running: true, priority: 100, autoRestart: true, pid: 12345 },
xfce4: { running: true, priority: 200, autoRestart: true, pid: 12346 },
x11vnc: { running: true, priority: 300, autoRestart: true, pid: 12347 },
novnc: { running: true, priority: 400, autoRestart: true, pid: 12348 },
},
})
status: Record<string, any>
}
@ApiSchema({ name: 'ComputerUseStopResponse' })
export class ComputerUseStopResponseDto {
@ApiProperty({
description: 'A message indicating the result of stopping computer use processes',
example: 'Computer use processes stopped successfully',
})
message: string
@ApiProperty({
description: 'Status information about all VNC desktop processes after stopping',
type: Object,
example: {
xvfb: { running: false, priority: 100, autoRestart: true },
xfce4: { running: false, priority: 200, autoRestart: true },
x11vnc: { running: false, priority: 300, autoRestart: true },
novnc: { running: false, priority: 400, autoRestart: true },
},
})
status: Record<string, any>
}
@ApiSchema({ name: 'ComputerUseStatusResponse' })
export class ComputerUseStatusResponseDto {
@ApiProperty({
description: 'Status of computer use services (active, partial, inactive, error)',
example: 'active',
enum: ['active', 'partial', 'inactive', 'error'],
})
status: string
}
@ApiSchema({ name: 'ProcessStatusResponse' })
export class ProcessStatusResponseDto {
@ApiProperty({
description: 'The name of the VNC process being checked',
example: 'xfce4',
})
processName: string
@ApiProperty({
description: 'Whether the specified VNC process is currently running',
example: true,
})
running: boolean
}
@ApiSchema({ name: 'ProcessRestartResponse' })
export class ProcessRestartResponseDto {
@ApiProperty({
description: 'A message indicating the result of restarting the process',
example: 'Process xfce4 restarted successfully',
})
message: string
@ApiProperty({
description: 'The name of the VNC process that was restarted',
example: 'xfce4',
})
processName: string
}
@ApiSchema({ name: 'ProcessLogsResponse' })
export class ProcessLogsResponseDto {
@ApiProperty({
description: 'The name of the VNC process whose logs were retrieved',
example: 'novnc',
})
processName: string
@ApiProperty({
description: 'The log output from the specified VNC process',
example: '2024-01-15 10:30:45 [INFO] NoVNC server started on port 6901',
})
logs: string
}
@ApiSchema({ name: 'ProcessErrorsResponse' })
export class ProcessErrorsResponseDto {
@ApiProperty({
description: 'The name of the VNC process whose error logs were retrieved',
example: 'x11vnc',
})
processName: string
@ApiProperty({
description: 'The error log output from the specified VNC process',
example: '2024-01-15 10:30:45 [ERROR] Failed to bind to port 5901',
})
errors: string
}
+7 -7
View File
@@ -7,16 +7,16 @@ import (
"context"
"net/http"
apiclient "github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/auth"
"github.com/daytonaio/daytona/cli/config"
"github.com/daytonaio/daytona/daytonaapiclient"
)
var apiClient *daytonaapiclient.APIClient
var apiClient *apiclient.APIClient
const DaytonaSourceHeader = "X-Daytona-Source"
func GetApiClient(profile *config.Profile, defaultHeaders map[string]string) (*daytonaapiclient.APIClient, error) {
func GetApiClient(profile *config.Profile, defaultHeaders map[string]string) (*apiclient.APIClient, error) {
c, err := config.GetConfig()
if err != nil {
return nil, err
@@ -42,12 +42,12 @@ func GetApiClient(profile *config.Profile, defaultHeaders map[string]string) (*d
return apiClient, nil
}
var newApiClient *daytonaapiclient.APIClient
var newApiClient *apiclient.APIClient
serverUrl := activeProfile.Api.Url
clientConfig := daytonaapiclient.NewConfiguration()
clientConfig.Servers = daytonaapiclient.ServerConfigurations{
clientConfig := apiclient.NewConfiguration()
clientConfig.Servers = apiclient.ServerConfigurations{
{
URL: serverUrl,
},
@@ -69,7 +69,7 @@ func GetApiClient(profile *config.Profile, defaultHeaders map[string]string) (*d
clientConfig.AddDefaultHeader(headerKey, headerValue)
}
newApiClient = daytonaapiclient.NewAPIClient(clientConfig)
newApiClient = apiclient.NewAPIClient(clientConfig)
newApiClient.GetConfig().HTTPClient = &http.Client{
Transport: http.DefaultTransport,
+8 -8
View File
@@ -10,13 +10,13 @@ import (
"os"
"path/filepath"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/pkg/minio"
"github.com/daytonaio/daytona/daytonaapiclient"
)
// Create MinIO client from access parameters
func CreateMinioClient(accessParams *daytonaapiclient.StorageAccessDto) (*minio.Client, error) {
func CreateMinioClient(accessParams *apiclient.StorageAccessDto) (*minio.Client, error) {
storageURL, err := url.Parse(accessParams.StorageUrl)
if err != nil {
return nil, fmt.Errorf("invalid storage URL: %w", err)
@@ -52,7 +52,7 @@ func ListExistingObjects(ctx context.Context, minioClient *minio.Client, orgID s
}
// getContextHashes processes context paths and returns their hashes
func getContextHashes(ctx context.Context, apiClient *daytonaapiclient.APIClient, contextPaths []string) ([]string, error) {
func getContextHashes(ctx context.Context, apiClient *apiclient.APIClient, contextPaths []string) ([]string, error) {
contextHashes := []string{}
if len(contextPaths) == 0 {
return contextHashes, nil
@@ -61,7 +61,7 @@ func getContextHashes(ctx context.Context, apiClient *daytonaapiclient.APIClient
// Get storage access parameters
accessParams, res, err := apiClient.ObjectStorageAPI.GetPushAccess(ctx).Execute()
if err != nil {
return nil, apiclient.HandleErrorResponse(res, err)
return nil, apiclient_cli.HandleErrorResponse(res, err)
}
// Create MinIO client
@@ -108,7 +108,7 @@ func getContextHashes(ctx context.Context, apiClient *daytonaapiclient.APIClient
return contextHashes, nil
}
func GetCreateBuildInfoDto(ctx context.Context, dockerfilePath string, contextPaths []string) (*daytonaapiclient.CreateBuildInfo, error) {
func GetCreateBuildInfoDto(ctx context.Context, dockerfilePath string, contextPaths []string) (*apiclient.CreateBuildInfo, error) {
dockerfileAbsPath, err := filepath.Abs(dockerfilePath)
if err != nil {
return nil, fmt.Errorf("invalid dockerfile path: %w", err)
@@ -123,7 +123,7 @@ func GetCreateBuildInfoDto(ctx context.Context, dockerfilePath string, contextPa
return nil, fmt.Errorf("failed to read dockerfile: %w", err)
}
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return nil, err
}
@@ -133,7 +133,7 @@ func GetCreateBuildInfoDto(ctx context.Context, dockerfilePath string, contextPa
return nil, err
}
return &daytonaapiclient.CreateBuildInfo{
return &apiclient.CreateBuildInfo{
DockerfileContent: string(dockerfileContent),
ContextHashes: contextHashes,
}, nil
+6 -6
View File
@@ -6,20 +6,20 @@ package common
import (
"context"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/config"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
)
func GetPersonalOrganizationId(profile config.Profile) (string, error) {
apiClient, err := apiclient.GetApiClient(&profile, nil)
apiClient, err := apiclient_cli.GetApiClient(&profile, nil)
if err != nil {
return "", err
}
organizationList, res, err := apiClient.OrganizationsAPI.ListOrganizations(context.Background()).Execute()
if err != nil {
return "", apiclient.HandleErrorResponse(res, err)
return "", apiclient_cli.HandleErrorResponse(res, err)
}
for _, organization := range organizationList {
@@ -31,7 +31,7 @@ func GetPersonalOrganizationId(profile config.Profile) (string, error) {
return "", nil
}
func GetActiveOrganizationName(apiClient *daytonaapiclient.APIClient, ctx context.Context) (string, error) {
func GetActiveOrganizationName(apiClient *apiclient.APIClient, ctx context.Context) (string, error) {
activeOrganizationId, err := config.GetActiveOrganizationId()
if err != nil {
return "", err
@@ -43,7 +43,7 @@ func GetActiveOrganizationName(apiClient *daytonaapiclient.APIClient, ctx contex
activeOrganization, res, err := apiClient.OrganizationsAPI.GetOrganization(ctx, activeOrganizationId).Execute()
if err != nil {
return "", apiclient.HandleErrorResponse(res, err)
return "", apiclient_cli.HandleErrorResponse(res, err)
}
return activeOrganization.Name, nil
+8 -8
View File
@@ -8,22 +8,22 @@ import (
"fmt"
"time"
"github.com/daytonaio/daytona/cli/apiclient"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
)
func AwaitSnapshotState(ctx context.Context, apiClient *daytonaapiclient.APIClient, targetImage string, state daytonaapiclient.SnapshotState) error {
func AwaitSnapshotState(ctx context.Context, apiClient *apiclient.APIClient, targetImage string, state apiclient.SnapshotState) error {
for {
snapshots, res, err := apiClient.SnapshotsAPI.GetAllSnapshots(ctx).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
for _, snapshot := range snapshots.Items {
if snapshot.Name == targetImage {
if snapshot.State == state {
return nil
} else if snapshot.State == daytonaapiclient.SNAPSHOTSTATE_ERROR || snapshot.State == daytonaapiclient.SNAPSHOTSTATE_BUILD_FAILED {
} else if snapshot.State == apiclient.SNAPSHOTSTATE_ERROR || snapshot.State == apiclient.SNAPSHOTSTATE_BUILD_FAILED {
if !snapshot.ErrorReason.IsSet() {
return fmt.Errorf("snapshot processing failed")
}
@@ -36,18 +36,18 @@ func AwaitSnapshotState(ctx context.Context, apiClient *daytonaapiclient.APIClie
}
}
func AwaitSandboxState(ctx context.Context, apiClient *daytonaapiclient.APIClient, targetSandbox string, state daytonaapiclient.SandboxState) error {
func AwaitSandboxState(ctx context.Context, apiClient *apiclient.APIClient, targetSandbox string, state apiclient.SandboxState) error {
for {
sandboxes, res, err := apiClient.SandboxAPI.ListSandboxes(ctx).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
for _, sandbox := range sandboxes {
if sandbox.Id == targetSandbox {
if sandbox.State != nil && *sandbox.State == state {
return nil
} else if sandbox.State != nil && (*sandbox.State == daytonaapiclient.SANDBOXSTATE_ERROR || *sandbox.State == daytonaapiclient.SANDBOXSTATE_BUILD_FAILED) {
} else if sandbox.State != nil && (*sandbox.State == apiclient.SANDBOXSTATE_ERROR || *sandbox.State == apiclient.SANDBOXSTATE_BUILD_FAILED) {
if sandbox.ErrorReason == nil {
return fmt.Errorf("sandbox processing failed")
}
+5 -5
View File
@@ -6,11 +6,11 @@ package organization
import (
"context"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/config"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/organization"
"github.com/daytonaio/daytona/daytonaapiclient"
"github.com/spf13/cobra"
)
@@ -21,18 +21,18 @@ var CreateCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
createOrganizationDto := daytonaapiclient.CreateOrganization{
createOrganizationDto := apiclient.CreateOrganization{
Name: args[0],
}
org, res, err := apiClient.OrganizationsAPI.CreateOrganization(ctx).CreateOrganization(createOrganizationDto).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
c, err := config.GetConfig()
+6 -6
View File
@@ -7,13 +7,13 @@ import (
"context"
"fmt"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/cmd/common"
"github.com/daytonaio/daytona/cli/config"
view_common "github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/organization"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"github.com/spf13/cobra"
)
@@ -23,17 +23,17 @@ var DeleteCmd = &cobra.Command{
Args: cobra.MaximumNArgs(1),
Aliases: common.GetAliases("delete"),
RunE: func(cmd *cobra.Command, args []string) error {
var chosenOrganization *daytonaapiclient.Organization
var chosenOrganization *apiclient.Organization
ctx := context.Background()
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
orgList, res, err := apiClient.OrganizationsAPI.ListOrganizations(ctx).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
if len(orgList) == 0 {
@@ -65,7 +65,7 @@ var DeleteCmd = &cobra.Command{
res, err = apiClient.OrganizationsAPI.DeleteOrganization(ctx, chosenOrganization.Id).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
view_common.RenderInfoMessageBold(fmt.Sprintf("Organization %s has been deleted", chosenOrganization.Name))
+5 -5
View File
@@ -7,12 +7,12 @@ import (
"context"
"fmt"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/config"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/organization"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"github.com/spf13/cobra"
)
@@ -21,17 +21,17 @@ var UseCmd = &cobra.Command{
Short: "Set active organization",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var chosenOrganization *daytonaapiclient.Organization
var chosenOrganization *apiclient.Organization
ctx := context.Background()
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
orgList, res, err := apiClient.OrganizationsAPI.ListOrganizations(ctx).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
if len(orgList) == 0 {
+12 -12
View File
@@ -9,12 +9,12 @@ import (
"strings"
"time"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/cmd/common"
"github.com/daytonaio/daytona/cli/config"
"github.com/daytonaio/daytona/cli/util"
views_common "github.com/daytonaio/daytona/cli/views/common"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/spf13/cobra"
)
@@ -28,12 +28,12 @@ var CreateCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
createSandbox := daytonaapiclient.NewCreateSandbox()
createSandbox := apiclient.NewCreateSandbox()
// Add non-zero values to the request
if snapshotFlag != "" {
@@ -99,13 +99,13 @@ var CreateCmd = &cobra.Command{
}
if len(volumesFlag) > 0 {
volumes := make([]daytonaapiclient.SandboxVolume, 0, len(volumesFlag))
volumes := make([]apiclient.SandboxVolume, 0, len(volumesFlag))
for _, v := range volumesFlag {
parts := strings.SplitN(v, ":", 2)
if len(parts) == 2 {
volumeId := parts[0]
mountPath := parts[1]
volume := daytonaapiclient.SandboxVolume{
volume := apiclient.SandboxVolume{
VolumeId: volumeId,
MountPath: mountPath,
}
@@ -117,14 +117,14 @@ var CreateCmd = &cobra.Command{
}
}
var sandbox *daytonaapiclient.Sandbox
var sandbox *apiclient.Sandbox
sandbox, res, err := apiClient.SandboxAPI.CreateSandbox(ctx).CreateSandbox(*createSandbox).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
if sandbox.State != nil && *sandbox.State == daytonaapiclient.SANDBOXSTATE_PENDING_BUILD {
if sandbox.State != nil && *sandbox.State == apiclient.SANDBOXSTATE_PENDING_BUILD {
c, err := config.GetConfig()
if err != nil {
return err
@@ -135,7 +135,7 @@ var CreateCmd = &cobra.Command{
return err
}
err = common.AwaitSandboxState(ctx, apiClient, sandbox.Id, daytonaapiclient.SANDBOXSTATE_BUILDING_SNAPSHOT)
err = common.AwaitSandboxState(ctx, apiClient, sandbox.Id, apiclient.SANDBOXSTATE_BUILDING_SNAPSHOT)
if err != nil {
return err
}
@@ -152,7 +152,7 @@ var CreateCmd = &cobra.Command{
ResourceType: common.ResourceTypeSandbox,
})
err = common.AwaitSandboxState(ctx, apiClient, sandbox.Id, daytonaapiclient.SANDBOXSTATE_STARTED)
err = common.AwaitSandboxState(ctx, apiClient, sandbox.Id, apiclient.SANDBOXSTATE_STARTED)
if err != nil {
return err
}
@@ -168,7 +168,7 @@ var CreateCmd = &cobra.Command{
var getSandboxErr error
sandbox, res, getSandboxErr = apiClient.SandboxAPI.GetSandbox(ctx, sandbox.Id).Execute()
if getSandboxErr != nil {
return apiclient.HandleErrorResponse(res, getSandboxErr)
return apiclient_cli.HandleErrorResponse(res, getSandboxErr)
}
runnerDomain = sandbox.RunnerDomain
}
+7 -7
View File
@@ -9,13 +9,13 @@ import (
"strings"
"time"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/cmd/common"
"github.com/daytonaio/daytona/cli/config"
"github.com/daytonaio/daytona/cli/util"
view_common "github.com/daytonaio/daytona/cli/views/common"
views_util "github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"github.com/spf13/cobra"
)
@@ -35,12 +35,12 @@ var CreateCmd = &cobra.Command{
return fmt.Errorf("must specify either --dockerfile or --image")
}
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
createSnapshot := daytonaapiclient.NewCreateSnapshot(snapshotName)
createSnapshot := apiclient.NewCreateSnapshot(snapshotName)
if cpuFlag != 0 {
createSnapshot.SetCpu(cpuFlag)
@@ -74,7 +74,7 @@ var CreateCmd = &cobra.Command{
// Send create request
snapshot, res, err := apiClient.SnapshotsAPI.CreateSnapshot(ctx).CreateSnapshot(*createSnapshot).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
// If we're building from a Dockerfile, show build logs
@@ -101,7 +101,7 @@ var CreateCmd = &cobra.Command{
ResourceType: common.ResourceTypeSnapshot,
})
err = common.AwaitSnapshotState(ctx, apiClient, snapshotName, daytonaapiclient.SNAPSHOTSTATE_PENDING)
err = common.AwaitSnapshotState(ctx, apiClient, snapshotName, apiclient.SNAPSHOTSTATE_PENDING)
if err != nil {
return err
}
@@ -112,7 +112,7 @@ var CreateCmd = &cobra.Command{
}
err = views_util.WithInlineSpinner("Waiting for the snapshot to be validated", func() error {
return common.AwaitSnapshotState(ctx, apiClient, snapshotName, daytonaapiclient.SNAPSHOTSTATE_ACTIVE)
return common.AwaitSnapshotState(ctx, apiClient, snapshotName, apiclient.SNAPSHOTSTATE_ACTIVE)
})
if err != nil {
return err
+7 -7
View File
@@ -12,12 +12,12 @@ import (
"strings"
"time"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/cmd/common"
"github.com/daytonaio/daytona/cli/docker"
views_common "github.com/daytonaio/daytona/cli/views/common"
views_util "github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
@@ -64,14 +64,14 @@ var PushCmd = &cobra.Command{
return fmt.Errorf("image '%s' is not compatible with AMD architecture", sourceImage)
}
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
tokenResponse, res, err := apiClient.DockerRegistryAPI.GetTransientPushAccess(ctx).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
encodedAuthConfig, err := json.Marshal(registry.AuthConfig{
@@ -104,7 +104,7 @@ var PushCmd = &cobra.Command{
return err
}
createSnapshot := daytonaapiclient.NewCreateSnapshot(nameFlag)
createSnapshot := apiclient.NewCreateSnapshot(nameFlag)
createSnapshot.SetImageName(targetImage)
@@ -135,13 +135,13 @@ var PushCmd = &cobra.Command{
_, res, err = apiClient.SnapshotsAPI.CreateSnapshot(ctx).CreateSnapshot(*createSnapshot).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
views_common.RenderInfoMessageBold(fmt.Sprintf("Successfully pushed %s to Daytona", sourceImage))
err = views_util.WithInlineSpinner("Waiting for the snapshot to be validated", func() error {
return common.AwaitSnapshotState(ctx, apiClient, nameFlag, daytonaapiclient.SNAPSHOTSTATE_ACTIVE)
return common.AwaitSnapshotState(ctx, apiClient, nameFlag, apiclient.SNAPSHOTSTATE_ACTIVE)
})
if err != nil {
return err
+5 -5
View File
@@ -7,10 +7,10 @@ import (
"context"
"fmt"
"github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/daytonaio/daytona/cli/cmd/common"
view_common "github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/daytonaapiclient"
"github.com/spf13/cobra"
)
@@ -22,16 +22,16 @@ var CreateCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
apiClient, err := apiclient.GetApiClient(nil, nil)
apiClient, err := apiclient_cli.GetApiClient(nil, nil)
if err != nil {
return err
}
volume, res, err := apiClient.VolumesAPI.CreateVolume(ctx).CreateVolume(daytonaapiclient.CreateVolume{
volume, res, err := apiClient.VolumesAPI.CreateVolume(ctx).CreateVolume(apiclient.CreateVolume{
Name: args[0],
}).Execute()
if err != nil {
return apiclient.HandleErrorResponse(res, err)
return apiclient_cli.HandleErrorResponse(res, err)
}
view_common.RenderInfoMessageBold(fmt.Sprintf("Volume %s successfully created", volume.Name))
+10 -10
View File
@@ -19,13 +19,13 @@ daytona [flags]
### SEE ALSO
* [daytona autocomplete](daytona_autocomplete.md) - Adds a completion script for your shell environment
* [daytona docs](daytona_docs.md) - Opens the Daytona documentation in your default browser.
* [daytona login](daytona_login.md) - Log in to Daytona
* [daytona logout](daytona_logout.md) - Logout from Daytona
* [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
* [daytona organization](daytona_organization.md) - Manage Daytona organizations
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
* [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
* [daytona version](daytona_version.md) - Print the version number
* [daytona volume](daytona_volume.md) - Manage Daytona volumes
- [daytona autocomplete](daytona_autocomplete.md) - Adds a completion script for your shell environment
- [daytona docs](daytona_docs.md) - Opens the Daytona documentation in your default browser.
- [daytona login](daytona_login.md) - Log in to Daytona
- [daytona logout](daytona_logout.md) - Logout from Daytona
- [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
- [daytona organization](daytona_organization.md) - Manage Daytona organizations
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
- [daytona version](daytona_version.md) - Print the version number
- [daytona volume](daytona_volume.md) - Manage Daytona volumes
+1 -1
View File
@@ -14,4 +14,4 @@ daytona autocomplete [bash|zsh|fish|powershell] [flags]
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
- [daytona](daytona.md) - Daytona CLI
+1 -1
View File
@@ -14,4 +14,4 @@ daytona docs [flags]
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
- [daytona](daytona.md) - Daytona CLI
+1 -1
View File
@@ -20,4 +20,4 @@ daytona login [flags]
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
- [daytona](daytona.md) - Daytona CLI
+1 -1
View File
@@ -14,4 +14,4 @@ daytona logout [flags]
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
- [daytona](daytona.md) - Daytona CLI
+4 -4
View File
@@ -14,7 +14,7 @@ Commands for managing Daytona MCP Server
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
* [daytona mcp config](daytona_mcp_config.md) - Outputs JSON configuration for Daytona MCP Server
* [daytona mcp init](daytona_mcp_init.md) - Initialize Daytona MCP Server with an agent (currently supported: claude, windsurf, cursor)
* [daytona mcp start](daytona_mcp_start.md) - Start Daytona MCP Server
- [daytona](daytona.md) - Daytona CLI
- [daytona mcp config](daytona_mcp_config.md) - Outputs JSON configuration for Daytona MCP Server
- [daytona mcp init](daytona_mcp_init.md) - Initialize Daytona MCP Server with an agent (currently supported: claude, windsurf, cursor)
- [daytona mcp start](daytona_mcp_start.md) - Start Daytona MCP Server
+1 -1
View File
@@ -14,4 +14,4 @@ daytona mcp config [AGENT_NAME] [flags]
### SEE ALSO
* [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
- [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
+1 -1
View File
@@ -14,4 +14,4 @@ daytona mcp init [AGENT_NAME] [flags]
### SEE ALSO
* [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
- [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
+1 -1
View File
@@ -14,4 +14,4 @@ daytona mcp start [flags]
### SEE ALSO
* [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
- [daytona mcp](daytona_mcp.md) - Manage Daytona MCP Server
+5 -5
View File
@@ -14,8 +14,8 @@ Commands for managing Daytona organizations
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
* [daytona organization create](daytona_organization_create.md) - Create a new organization and set it as active
* [daytona organization delete](daytona_organization_delete.md) - Delete an organization
* [daytona organization list](daytona_organization_list.md) - List all organizations
* [daytona organization use](daytona_organization_use.md) - Set active organization
- [daytona](daytona.md) - Daytona CLI
- [daytona organization create](daytona_organization_create.md) - Create a new organization and set it as active
- [daytona organization delete](daytona_organization_delete.md) - Delete an organization
- [daytona organization list](daytona_organization_list.md) - List all organizations
- [daytona organization use](daytona_organization_use.md) - Set active organization
+1 -1
View File
@@ -14,4 +14,4 @@ daytona organization create [ORGANIZATION_NAME] [flags]
### SEE ALSO
* [daytona organization](daytona_organization.md) - Manage Daytona organizations
- [daytona organization](daytona_organization.md) - Manage Daytona organizations
+1 -1
View File
@@ -14,4 +14,4 @@ daytona organization delete [ORGANIZATION] [flags]
### SEE ALSO
* [daytona organization](daytona_organization.md) - Manage Daytona organizations
- [daytona organization](daytona_organization.md) - Manage Daytona organizations
+1 -1
View File
@@ -20,4 +20,4 @@ daytona organization list [flags]
### SEE ALSO
* [daytona organization](daytona_organization.md) - Manage Daytona organizations
- [daytona organization](daytona_organization.md) - Manage Daytona organizations
+1 -1
View File
@@ -14,4 +14,4 @@ daytona organization use [ORGANIZATION] [flags]
### SEE ALSO
* [daytona organization](daytona_organization.md) - Manage Daytona organizations
- [daytona organization](daytona_organization.md) - Manage Daytona organizations
+7 -7
View File
@@ -14,10 +14,10 @@ Commands for managing Daytona sandboxes
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
* [daytona sandbox create](daytona_sandbox_create.md) - Create a new sandbox
* [daytona sandbox delete](daytona_sandbox_delete.md) - Delete a sandbox
* [daytona sandbox info](daytona_sandbox_info.md) - Get sandbox info
* [daytona sandbox list](daytona_sandbox_list.md) - List sandboxes
* [daytona sandbox start](daytona_sandbox_start.md) - Start a sandbox
* [daytona sandbox stop](daytona_sandbox_stop.md) - Stop a sandbox
- [daytona](daytona.md) - Daytona CLI
- [daytona sandbox create](daytona_sandbox_create.md) - Create a new sandbox
- [daytona sandbox delete](daytona_sandbox_delete.md) - Delete a sandbox
- [daytona sandbox info](daytona_sandbox_info.md) - Get sandbox info
- [daytona sandbox list](daytona_sandbox_list.md) - List sandboxes
- [daytona sandbox start](daytona_sandbox_start.md) - Start a sandbox
- [daytona sandbox stop](daytona_sandbox_stop.md) - Stop a sandbox
+1 -1
View File
@@ -35,4 +35,4 @@ daytona sandbox create [flags]
### SEE ALSO
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
+1 -1
View File
@@ -21,4 +21,4 @@ daytona sandbox delete [SANDBOX_ID] [flags]
### SEE ALSO
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
+1 -1
View File
@@ -21,4 +21,4 @@ daytona sandbox info [SANDBOX_ID] [flags]
### SEE ALSO
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
+1 -1
View File
@@ -23,4 +23,4 @@ daytona sandbox list [flags]
### SEE ALSO
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
+1 -1
View File
@@ -20,4 +20,4 @@ daytona sandbox start [SANDBOX_ID] [flags]
### SEE ALSO
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
+1 -1
View File
@@ -20,4 +20,4 @@ daytona sandbox stop [SANDBOX_ID] [flags]
### SEE ALSO
* [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
- [daytona sandbox](daytona_sandbox.md) - Manage Daytona sandboxes
+5 -5
View File
@@ -14,8 +14,8 @@ Commands for managing Daytona snapshots
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
* [daytona snapshot create](daytona_snapshot_create.md) - Create a snapshot
* [daytona snapshot delete](daytona_snapshot_delete.md) - Delete a snapshot
* [daytona snapshot list](daytona_snapshot_list.md) - List all snapshots
* [daytona snapshot push](daytona_snapshot_push.md) - Push local snapshot
- [daytona](daytona.md) - Daytona CLI
- [daytona snapshot create](daytona_snapshot_create.md) - Create a snapshot
- [daytona snapshot delete](daytona_snapshot_delete.md) - Delete a snapshot
- [daytona snapshot list](daytona_snapshot_list.md) - List all snapshots
- [daytona snapshot push](daytona_snapshot_push.md) - Push local snapshot
+1 -1
View File
@@ -26,4 +26,4 @@ daytona snapshot create [SNAPSHOT] [flags]
### SEE ALSO
* [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
- [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
+1 -1
View File
@@ -20,4 +20,4 @@ daytona snapshot delete [SNAPSHOT_ID] [flags]
### SEE ALSO
* [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
- [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
+1 -1
View File
@@ -26,4 +26,4 @@ daytona snapshot list [flags]
### SEE ALSO
* [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
- [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
+1 -1
View File
@@ -28,4 +28,4 @@ daytona snapshot push [SNAPSHOT] [flags]
### SEE ALSO
* [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
- [daytona snapshot](daytona_snapshot.md) - Manage Daytona snapshots
+1 -1
View File
@@ -14,4 +14,4 @@ daytona version [flags]
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
- [daytona](daytona.md) - Daytona CLI
+5 -5
View File
@@ -14,8 +14,8 @@ Commands for managing Daytona volumes
### SEE ALSO
* [daytona](daytona.md) - Daytona CLI
* [daytona volume create](daytona_volume_create.md) - Create a volume
* [daytona volume delete](daytona_volume_delete.md) - Delete a volume
* [daytona volume get](daytona_volume_get.md) - Get volume details
* [daytona volume list](daytona_volume_list.md) - List all volumes
- [daytona](daytona.md) - Daytona CLI
- [daytona volume create](daytona_volume_create.md) - Create a volume
- [daytona volume delete](daytona_volume_delete.md) - Delete a volume
- [daytona volume get](daytona_volume_get.md) - Get volume details
- [daytona volume list](daytona_volume_list.md) - List all volumes
+1 -1
View File
@@ -20,4 +20,4 @@ daytona volume create [NAME] [flags]
### SEE ALSO
* [daytona volume](daytona_volume.md) - Manage Daytona volumes
- [daytona volume](daytona_volume.md) - Manage Daytona volumes
+1 -1
View File
@@ -14,4 +14,4 @@ daytona volume delete [VOLUME_ID] [flags]
### SEE ALSO
* [daytona volume](daytona_volume.md) - Manage Daytona volumes
- [daytona volume](daytona_volume.md) - Manage Daytona volumes
+1 -1
View File
@@ -20,4 +20,4 @@ daytona volume get [VOLUME_ID] [flags]
### SEE ALSO
* [daytona volume](daytona_volume.md) - Manage Daytona volumes
- [daytona volume](daytona_volume.md) - Manage Daytona volumes
+1 -1
View File
@@ -20,4 +20,4 @@ daytona volume list [flags]
### SEE ALSO
* [daytona volume](daytona_volume.md) - Manage Daytona volumes
- [daytona volume](daytona_volume.md) - Manage Daytona volumes
+7 -9
View File
@@ -6,7 +6,6 @@ toolchain go1.23.5
require (
github.com/charmbracelet/bubbletea v1.1.0
github.com/daytonaio/daytona/daytonaapiclient v0.0.0-00010101000000-000000000000
github.com/docker/docker v27.5.1+incompatible
github.com/mark3labs/mcp-go v0.32.0
github.com/sirupsen/logrus v1.9.3
@@ -14,8 +13,6 @@ require (
golang.org/x/oauth2 v0.25.0
)
replace github.com/daytonaio/daytona/daytonaapiclient => ../../libs/api-client-go
require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -41,6 +38,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
@@ -74,11 +72,11 @@ require (
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.10.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -95,6 +93,6 @@ require (
github.com/minio/minio-go/v7 v7.0.91
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/spf13/pflag v1.0.6 // indirect
golang.org/x/term v0.31.0
golang.org/x/term v0.32.0
gopkg.in/yaml.v2 v2.4.0
)
+7 -14
View File
@@ -71,8 +71,7 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
@@ -184,23 +183,20 @@ go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -209,14 +205,11 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+21 -21
View File
@@ -9,29 +9,29 @@ import (
"strings"
"time"
"github.com/daytonaio/daytona/cli/apiclient"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/mark3labs/mcp-go/mcp"
log "github.com/sirupsen/logrus"
)
type CreateSandboxArgs struct {
Id *string `json:"id,omitempty"`
Target *string `json:"target,omitempty"`
Snapshot *string `json:"snapshot,omitempty"`
User *string `json:"user,omitempty"`
Env *map[string]string `json:"env,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
Public *bool `json:"public,omitempty"`
Class *string `json:"class,omitempty"`
Cpu *int32 `json:"cpu,omitempty"`
Memory *int32 `json:"memory,omitempty"`
Disk *int32 `json:"disk,omitempty"`
AutoStopInterval *int32 `json:"autoStopInterval,omitempty"`
AutoArchiveInterval *int32 `json:"autoArchiveInterval,omitempty"`
Volumes *[]daytonaapiclient.SandboxVolume `json:"volumes,omitempty"`
BuildInfo *daytonaapiclient.CreateBuildInfo `json:"buildInfo,omitempty"`
Id *string `json:"id,omitempty"`
Target *string `json:"target,omitempty"`
Snapshot *string `json:"snapshot,omitempty"`
User *string `json:"user,omitempty"`
Env *map[string]string `json:"env,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
Public *bool `json:"public,omitempty"`
Class *string `json:"class,omitempty"`
Cpu *int32 `json:"cpu,omitempty"`
Memory *int32 `json:"memory,omitempty"`
Disk *int32 `json:"disk,omitempty"`
AutoStopInterval *int32 `json:"autoStopInterval,omitempty"`
AutoArchiveInterval *int32 `json:"autoArchiveInterval,omitempty"`
Volumes *[]apiclient.SandboxVolume `json:"volumes,omitempty"`
BuildInfo *apiclient.CreateBuildInfo `json:"buildInfo,omitempty"`
}
func GetCreateSandboxTool() mcp.Tool {
@@ -56,7 +56,7 @@ func GetCreateSandboxTool() mcp.Tool {
}
func CreateSandbox(ctx context.Context, request mcp.CallToolRequest, args CreateSandboxArgs) (*mcp.CallToolResult, error) {
apiClient, err := apiclient.GetApiClient(nil, daytonaMCPHeaders)
apiClient, err := apiclient_cli.GetApiClient(nil, daytonaMCPHeaders)
if err != nil {
return &mcp.CallToolResult{IsError: true}, err
}
@@ -68,7 +68,7 @@ func CreateSandbox(ctx context.Context, request mcp.CallToolRequest, args Create
if sandboxId != "" {
sandbox, _, err := apiClient.SandboxAPI.GetSandbox(ctx, sandboxId).Execute()
if err == nil && sandbox.State != nil && *sandbox.State == daytonaapiclient.SANDBOXSTATE_STARTED {
if err == nil && sandbox.State != nil && *sandbox.State == apiclient.SANDBOXSTATE_STARTED {
return mcp.NewToolResultText(fmt.Sprintf("Reusing existing sandbox %s", sandboxId)), nil
}
@@ -107,8 +107,8 @@ func CreateSandbox(ctx context.Context, request mcp.CallToolRequest, args Create
return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to create sandbox after %d retries", maxRetries)
}
func createSandboxRequest(args CreateSandboxArgs) *daytonaapiclient.CreateSandbox {
createSandbox := daytonaapiclient.NewCreateSandbox()
func createSandboxRequest(args CreateSandboxArgs) *apiclient.CreateSandbox {
createSandbox := apiclient.NewCreateSandbox()
if args.Snapshot != nil && *args.Snapshot != "" {
createSandbox.SetSnapshot(*args.Snapshot)
+4 -4
View File
@@ -7,8 +7,8 @@ import (
"context"
"fmt"
"github.com/daytonaio/daytona/cli/apiclient"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/mark3labs/mcp-go/mcp"
log "github.com/sirupsen/logrus"
@@ -28,7 +28,7 @@ func GetDeleteFileTool() mcp.Tool {
}
func DeleteFile(ctx context.Context, request mcp.CallToolRequest, args DeleteFileArgs) (*mcp.CallToolResult, error) {
apiClient, err := apiclient.GetApiClient(nil, daytonaMCPHeaders)
apiClient, err := apiclient_cli.GetApiClient(nil, daytonaMCPHeaders)
if err != nil {
return &mcp.CallToolResult{IsError: true}, err
}
@@ -43,7 +43,7 @@ func DeleteFile(ctx context.Context, request mcp.CallToolRequest, args DeleteFil
// Execute delete command
execResponse, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).
ExecuteRequest(*daytonaapiclient.NewExecuteRequest(fmt.Sprintf("rm -rf %s", *args.FilePath))).
ExecuteRequest(*apiclient.NewExecuteRequest(fmt.Sprintf("rm -rf %s", *args.FilePath))).
Execute()
if err != nil {
return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error deleting file: %v", err)
+4 -4
View File
@@ -9,8 +9,8 @@ import (
"fmt"
"strings"
"github.com/daytonaio/daytona/cli/apiclient"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/mark3labs/mcp-go/mcp"
log "github.com/sirupsen/logrus"
@@ -37,7 +37,7 @@ func GetExecuteCommandTool() mcp.Tool {
}
func ExecuteCommand(ctx context.Context, request mcp.CallToolRequest, args ExecuteCommandArgs) (*mcp.CallToolResult, error) {
apiClient, err := apiclient.GetApiClient(nil, daytonaMCPHeaders)
apiClient, err := apiclient_cli.GetApiClient(nil, daytonaMCPHeaders)
if err != nil {
return &mcp.CallToolResult{IsError: true}, err
}
@@ -61,7 +61,7 @@ func ExecuteCommand(ctx context.Context, request mcp.CallToolRequest, args Execu
// Execute the command
result, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).
ExecuteRequest(*daytonaapiclient.NewExecuteRequest(command)).
ExecuteRequest(*apiclient.NewExecuteRequest(command)).
Execute()
if err != nil {
+5 -5
View File
@@ -7,8 +7,8 @@ import (
"context"
"fmt"
"github.com/daytonaio/daytona/cli/apiclient"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/mark3labs/mcp-go/mcp"
log "github.com/sirupsen/logrus"
@@ -38,7 +38,7 @@ func GetGitCloneTool() mcp.Tool {
}
func GitClone(ctx context.Context, request mcp.CallToolRequest, args GitCloneArgs) (*mcp.CallToolResult, error) {
apiClient, err := apiclient.GetApiClient(nil, daytonaMCPHeaders)
apiClient, err := apiclient_cli.GetApiClient(nil, daytonaMCPHeaders)
if err != nil {
return &mcp.CallToolResult{IsError: true}, err
}
@@ -62,8 +62,8 @@ func GitClone(ctx context.Context, request mcp.CallToolRequest, args GitCloneArg
return mcp.NewToolResultText(fmt.Sprintf("Cloned repository: %s to %s", gitCloneRequest.Url, gitCloneRequest.Path)), nil
}
func getGitCloneRequest(args GitCloneArgs) (*daytonaapiclient.GitCloneRequest, error) {
gitCloneRequest := daytonaapiclient.GitCloneRequest{}
func getGitCloneRequest(args GitCloneArgs) (*apiclient.GitCloneRequest, error) {
gitCloneRequest := apiclient.GitCloneRequest{}
if args.Url == nil || *args.Url == "" {
return nil, fmt.Errorf("url parameter is required")
+6 -6
View File
@@ -9,8 +9,8 @@ import (
"strconv"
"strings"
"github.com/daytonaio/daytona/cli/apiclient"
daytonaapiclient "github.com/daytonaio/daytona/daytonaapiclient"
"github.com/daytonaio/apiclient"
apiclient_cli "github.com/daytonaio/daytona/cli/apiclient"
"github.com/mark3labs/mcp-go/mcp"
log "github.com/sirupsen/logrus"
@@ -34,7 +34,7 @@ func GetPreviewLinkTool() mcp.Tool {
}
func PreviewLink(ctx context.Context, request mcp.CallToolRequest, args PreviewLinkArgs) (*mcp.CallToolResult, error) {
apiClient, err := apiclient.GetApiClient(nil, daytonaMCPHeaders)
apiClient, err := apiclient_cli.GetApiClient(nil, daytonaMCPHeaders)
if err != nil {
return nil, err
}
@@ -69,7 +69,7 @@ func PreviewLink(ctx context.Context, request mcp.CallToolRequest, args PreviewL
log.Infof("Checking if server is running - port: %d", *args.Port)
checkCmd := fmt.Sprintf("curl -s -o /dev/null -w '%%{http_code}' http://localhost:%d --max-time 2 || echo 'error'", *args.Port)
result, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).ExecuteRequest(*daytonaapiclient.NewExecuteRequest(checkCmd)).Execute()
result, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).ExecuteRequest(*apiclient.NewExecuteRequest(checkCmd)).Execute()
if err != nil {
return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error checking server: %v", err)
}
@@ -80,7 +80,7 @@ func PreviewLink(ctx context.Context, request mcp.CallToolRequest, args PreviewL
// Check what might be using the port
psCmd := fmt.Sprintf("ps aux | grep ':%d' | grep -v grep || echo 'No process found'", *args.Port)
psResult, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).ExecuteRequest(*daytonaapiclient.NewExecuteRequest(psCmd)).Execute()
psResult, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).ExecuteRequest(*apiclient.NewExecuteRequest(psCmd)).Execute()
if err != nil {
return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error checking processes: %v", err)
}
@@ -102,7 +102,7 @@ func PreviewLink(ctx context.Context, request mcp.CallToolRequest, args PreviewL
var statusCode string
if checkServer {
checkCmd := fmt.Sprintf("curl -s -o /dev/null -w '%%{http_code}' %s --max-time 3 || echo 'error'", previewURL)
result, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).ExecuteRequest(*daytonaapiclient.NewExecuteRequest(checkCmd)).Execute()
result, _, err := apiClient.ToolboxAPI.ExecuteCommand(ctx, *args.Id).ExecuteRequest(*apiclient.NewExecuteRequest(checkCmd)).Execute()
if err != nil {
log.Errorf("Error checking preview URL: %v", err)
} else {
+2 -2
View File
@@ -8,13 +8,13 @@ import (
"os"
"github.com/charmbracelet/lipgloss"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"golang.org/x/term"
)
func RenderInfo(organization *daytonaapiclient.Organization, forceUnstyled bool) {
func RenderInfo(organization *apiclient.Organization, forceUnstyled bool) {
var output string
nameLabel := "Organization"
+5 -5
View File
@@ -7,9 +7,9 @@ import (
"fmt"
"sort"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
)
type RowData struct {
@@ -18,7 +18,7 @@ type RowData struct {
Created string
}
func ListOrganizations(organizationList []daytonaapiclient.Organization, activeOrganizationId *string) {
func ListOrganizations(organizationList []apiclient.Organization, activeOrganizationId *string) {
if len(organizationList) == 0 {
util.NotifyEmptyOrganizationList(true)
return
@@ -46,13 +46,13 @@ func ListOrganizations(organizationList []daytonaapiclient.Organization, activeO
fmt.Println(table)
}
func SortOrganizations(organizationList *[]daytonaapiclient.Organization) {
func SortOrganizations(organizationList *[]apiclient.Organization) {
sort.Slice(*organizationList, func(i, j int) bool {
return (*organizationList)[i].CreatedAt.After((*organizationList)[j].CreatedAt)
})
}
func getTableRowData(organization daytonaapiclient.Organization, activeOrganizationId *string) *RowData {
func getTableRowData(organization apiclient.Organization, activeOrganizationId *string) *RowData {
rowData := RowData{"", "", ""}
rowData.Name = organization.Name + util.AdditionalPropertyPadding
@@ -66,7 +66,7 @@ func getTableRowData(organization daytonaapiclient.Organization, activeOrganizat
return &rowData
}
func renderUnstyledList(organizationList []daytonaapiclient.Organization) {
func renderUnstyledList(organizationList []apiclient.Organization) {
for _, organization := range organizationList {
RenderInfo(&organization, true)
+2 -2
View File
@@ -5,11 +5,11 @@ package organization
import (
"github.com/charmbracelet/huh"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/daytonaapiclient"
)
func GetOrganizationIdFromPrompt(organizationList []daytonaapiclient.Organization) (*daytonaapiclient.Organization, error) {
func GetOrganizationIdFromPrompt(organizationList []apiclient.Organization) (*apiclient.Organization, error) {
var chosenOrganizationId string
var organizationOptions []huh.Option[string]
+17 -17
View File
@@ -9,13 +9,13 @@ import (
"strings"
"github.com/charmbracelet/lipgloss"
apiclient "github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"golang.org/x/term"
)
func RenderInfo(sandbox *daytonaapiclient.Sandbox, forceUnstyled bool) {
func RenderInfo(sandbox *apiclient.Sandbox, forceUnstyled bool) {
var output string
output += "\n"
@@ -93,35 +93,35 @@ func getInfoLine(key, value string) string {
return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n"
}
func getStateLabel(state daytonaapiclient.SandboxState) string {
func getStateLabel(state apiclient.SandboxState) string {
switch state {
case daytonaapiclient.SANDBOXSTATE_CREATING:
case apiclient.SANDBOXSTATE_CREATING:
return common.CreatingStyle.Render("CREATING")
case daytonaapiclient.SANDBOXSTATE_RESTORING:
case apiclient.SANDBOXSTATE_RESTORING:
return common.CreatingStyle.Render("RESTORING")
case daytonaapiclient.SANDBOXSTATE_DESTROYED:
case apiclient.SANDBOXSTATE_DESTROYED:
return common.DeletedStyle.Render("DESTROYED")
case daytonaapiclient.SANDBOXSTATE_DESTROYING:
case apiclient.SANDBOXSTATE_DESTROYING:
return common.DeletedStyle.Render("DESTROYING")
case daytonaapiclient.SANDBOXSTATE_STARTED:
case apiclient.SANDBOXSTATE_STARTED:
return common.StartedStyle.Render("STARTED")
case daytonaapiclient.SANDBOXSTATE_STOPPED:
case apiclient.SANDBOXSTATE_STOPPED:
return common.StoppedStyle.Render("STOPPED")
case daytonaapiclient.SANDBOXSTATE_STARTING:
case apiclient.SANDBOXSTATE_STARTING:
return common.StartingStyle.Render("STARTING")
case daytonaapiclient.SANDBOXSTATE_STOPPING:
case apiclient.SANDBOXSTATE_STOPPING:
return common.StoppingStyle.Render("STOPPING")
case daytonaapiclient.SANDBOXSTATE_PULLING_SNAPSHOT:
case apiclient.SANDBOXSTATE_PULLING_SNAPSHOT:
return common.CreatingStyle.Render("PULLING SNAPSHOT")
case daytonaapiclient.SANDBOXSTATE_ARCHIVING:
case apiclient.SANDBOXSTATE_ARCHIVING:
return common.CreatingStyle.Render("ARCHIVING")
case daytonaapiclient.SANDBOXSTATE_ARCHIVED:
case apiclient.SANDBOXSTATE_ARCHIVED:
return common.StoppedStyle.Render("ARCHIVED")
case daytonaapiclient.SANDBOXSTATE_ERROR:
case apiclient.SANDBOXSTATE_ERROR:
return common.ErrorStyle.Render("ERROR")
case daytonaapiclient.SANDBOXSTATE_BUILD_FAILED:
case apiclient.SANDBOXSTATE_BUILD_FAILED:
return common.ErrorStyle.Render("BUILD FAILED")
case daytonaapiclient.SANDBOXSTATE_UNKNOWN:
case apiclient.SANDBOXSTATE_UNKNOWN:
return common.UndefinedStyle.Render("UNKNOWN")
default:
return common.UndefinedStyle.Render("/")
+7 -7
View File
@@ -7,9 +7,9 @@ import (
"fmt"
"sort"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
)
type RowData struct {
@@ -20,7 +20,7 @@ type RowData struct {
LastEvent string
}
func ListSandboxes(sandboxList []daytonaapiclient.Sandbox, activeOrganizationName *string) {
func ListSandboxes(sandboxList []apiclient.Sandbox, activeOrganizationName *string) {
if len(sandboxList) == 0 {
util.NotifyEmptySandboxList(true)
return
@@ -46,7 +46,7 @@ func ListSandboxes(sandboxList []daytonaapiclient.Sandbox, activeOrganizationNam
fmt.Println(table)
}
func SortSandboxes(sandboxList *[]daytonaapiclient.Sandbox) {
func SortSandboxes(sandboxList *[]apiclient.Sandbox) {
sort.Slice(*sandboxList, func(i, j int) bool {
pi, pj := getStateSortPriorities(*(*sandboxList)[i].State, *(*sandboxList)[j].State)
if pi != pj {
@@ -62,7 +62,7 @@ func SortSandboxes(sandboxList *[]daytonaapiclient.Sandbox) {
})
}
func getTableRowData(sandbox daytonaapiclient.Sandbox) *RowData {
func getTableRowData(sandbox apiclient.Sandbox) *RowData {
rowData := RowData{"", "", "", "", ""}
rowData.Name = sandbox.Id + util.AdditionalPropertyPadding
if sandbox.State != nil {
@@ -81,7 +81,7 @@ func getTableRowData(sandbox daytonaapiclient.Sandbox) *RowData {
return &rowData
}
func renderUnstyledList(sandboxList []daytonaapiclient.Sandbox) {
func renderUnstyledList(sandboxList []apiclient.Sandbox) {
for _, sandbox := range sandboxList {
RenderInfo(&sandbox, true)
@@ -104,7 +104,7 @@ func getRowFromRowData(rowData RowData) []string {
return row
}
func getStateSortPriorities(state1, state2 daytonaapiclient.SandboxState) (int, int) {
func getStateSortPriorities(state1, state2 apiclient.SandboxState) (int, int) {
pi, ok := sandboxListStatePriorities[state1]
if !ok {
pi = 99
@@ -118,7 +118,7 @@ func getStateSortPriorities(state1, state2 daytonaapiclient.SandboxState) (int,
}
// Sandboxes that have actions being performed on them have a higher priority when listing
var sandboxListStatePriorities = map[daytonaapiclient.SandboxState]int{
var sandboxListStatePriorities = map[apiclient.SandboxState]int{
"pending": 1,
"pending-start": 1,
"deleting": 1,
+10 -10
View File
@@ -8,13 +8,13 @@ import (
"os"
"github.com/charmbracelet/lipgloss"
apiclient "github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"golang.org/x/term"
)
func RenderInfo(snapshot *daytonaapiclient.SnapshotDto, forceUnstyled bool) {
func RenderInfo(snapshot *apiclient.SnapshotDto, forceUnstyled bool) {
var output string
nameLabel := "Snapshot"
@@ -65,21 +65,21 @@ func getInfoLine(key, value string) string {
return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n"
}
func getStateLabel(state daytonaapiclient.SnapshotState) string {
func getStateLabel(state apiclient.SnapshotState) string {
switch state {
case daytonaapiclient.SNAPSHOTSTATE_PENDING:
case apiclient.SNAPSHOTSTATE_PENDING:
return common.CreatingStyle.Render("PENDING")
case daytonaapiclient.SNAPSHOTSTATE_PULLING:
case apiclient.SNAPSHOTSTATE_PULLING:
return common.CreatingStyle.Render("PULLING SNAPSHOT")
case daytonaapiclient.SNAPSHOTSTATE_VALIDATING:
case apiclient.SNAPSHOTSTATE_VALIDATING:
return common.CreatingStyle.Render("VALIDATING")
case daytonaapiclient.SNAPSHOTSTATE_ACTIVE:
case apiclient.SNAPSHOTSTATE_ACTIVE:
return common.StartedStyle.Render("ACTIVE")
case daytonaapiclient.SNAPSHOTSTATE_ERROR:
case apiclient.SNAPSHOTSTATE_ERROR:
return common.ErrorStyle.Render("ERROR")
case daytonaapiclient.SNAPSHOTSTATE_BUILD_FAILED:
case apiclient.SNAPSHOTSTATE_BUILD_FAILED:
return common.ErrorStyle.Render("BUILD FAILED")
case daytonaapiclient.SNAPSHOTSTATE_REMOVING:
case apiclient.SNAPSHOTSTATE_REMOVING:
return common.DeletedStyle.Render("REMOVING")
default:
return common.UndefinedStyle.Render("/")
+13 -13
View File
@@ -7,9 +7,9 @@ import (
"fmt"
"sort"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
)
type RowData struct {
@@ -20,7 +20,7 @@ type RowData struct {
Created string
}
func ListSnapshots(snapshotList []daytonaapiclient.SnapshotDto, activeOrganizationName *string) {
func ListSnapshots(snapshotList []apiclient.SnapshotDto, activeOrganizationName *string) {
if len(snapshotList) == 0 {
util.NotifyEmptySnapshotList(true)
return
@@ -48,7 +48,7 @@ func ListSnapshots(snapshotList []daytonaapiclient.SnapshotDto, activeOrganizati
fmt.Println(table)
}
func SortSnapshots(snapshotList *[]daytonaapiclient.SnapshotDto) {
func SortSnapshots(snapshotList *[]apiclient.SnapshotDto) {
sort.Slice(*snapshotList, func(i, j int) bool {
pi, pj := getStateSortPriorities((*snapshotList)[i].State, (*snapshotList)[j].State)
if pi != pj {
@@ -60,7 +60,7 @@ func SortSnapshots(snapshotList *[]daytonaapiclient.SnapshotDto) {
})
}
func getTableRowData(snapshot daytonaapiclient.SnapshotDto) *RowData {
func getTableRowData(snapshot apiclient.SnapshotDto) *RowData {
rowData := RowData{"", "", "", "", ""}
rowData.Name = snapshot.Name + util.AdditionalPropertyPadding
rowData.State = getStateLabel(snapshot.State)
@@ -81,7 +81,7 @@ func getTableRowData(snapshot daytonaapiclient.SnapshotDto) *RowData {
return &rowData
}
func renderUnstyledList(snapshotList []daytonaapiclient.SnapshotDto) {
func renderUnstyledList(snapshotList []apiclient.SnapshotDto) {
for _, snapshot := range snapshotList {
RenderInfo(&snapshot, true)
@@ -103,7 +103,7 @@ func getRowFromRowData(rowData RowData) []string {
return row
}
func getStateSortPriorities(state1, state2 daytonaapiclient.SnapshotState) (int, int) {
func getStateSortPriorities(state1, state2 apiclient.SnapshotState) (int, int) {
pi, ok := snapshotListStatePriorities[state1]
if !ok {
pi = 99
@@ -117,11 +117,11 @@ func getStateSortPriorities(state1, state2 daytonaapiclient.SnapshotState) (int,
}
// snapshots that have actions being performed on them have a higher priority when listing
var snapshotListStatePriorities = map[daytonaapiclient.SnapshotState]int{
daytonaapiclient.SNAPSHOTSTATE_PENDING: 1,
daytonaapiclient.SNAPSHOTSTATE_PULLING: 1,
daytonaapiclient.SNAPSHOTSTATE_VALIDATING: 1,
daytonaapiclient.SNAPSHOTSTATE_ERROR: 2,
daytonaapiclient.SNAPSHOTSTATE_ACTIVE: 3,
daytonaapiclient.SNAPSHOTSTATE_REMOVING: 4,
var snapshotListStatePriorities = map[apiclient.SnapshotState]int{
apiclient.SNAPSHOTSTATE_PENDING: 1,
apiclient.SNAPSHOTSTATE_PULLING: 1,
apiclient.SNAPSHOTSTATE_VALIDATING: 1,
apiclient.SNAPSHOTSTATE_ERROR: 2,
apiclient.SNAPSHOTSTATE_ACTIVE: 3,
apiclient.SNAPSHOTSTATE_REMOVING: 4,
}
+10 -10
View File
@@ -8,13 +8,13 @@ import (
"os"
"github.com/charmbracelet/lipgloss"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
"golang.org/x/term"
)
func RenderInfo(volume *daytonaapiclient.VolumeDto, forceUnstyled bool) {
func RenderInfo(volume *apiclient.VolumeDto, forceUnstyled bool) {
var output string
nameLabel := "Volume"
@@ -58,21 +58,21 @@ func getInfoLine(key, value string) string {
return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n"
}
func getStateLabel(state daytonaapiclient.VolumeState) string {
func getStateLabel(state apiclient.VolumeState) string {
switch state {
case daytonaapiclient.VOLUMESTATE_PENDING_CREATE:
case apiclient.VOLUMESTATE_PENDING_CREATE:
return common.CreatingStyle.Render("PENDING CREATE")
case daytonaapiclient.VOLUMESTATE_CREATING:
case apiclient.VOLUMESTATE_CREATING:
return common.CreatingStyle.Render("CREATING")
case daytonaapiclient.VOLUMESTATE_READY:
case apiclient.VOLUMESTATE_READY:
return common.StartedStyle.Render("READY")
case daytonaapiclient.VOLUMESTATE_PENDING_DELETE:
case apiclient.VOLUMESTATE_PENDING_DELETE:
return common.DeletedStyle.Render("PENDING DELETE")
case daytonaapiclient.VOLUMESTATE_DELETING:
case apiclient.VOLUMESTATE_DELETING:
return common.DeletedStyle.Render("DELETING")
case daytonaapiclient.VOLUMESTATE_DELETED:
case apiclient.VOLUMESTATE_DELETED:
return common.DeletedStyle.Render("DELETED")
case daytonaapiclient.VOLUMESTATE_ERROR:
case apiclient.VOLUMESTATE_ERROR:
return common.ErrorStyle.Render("ERROR")
default:
return common.UndefinedStyle.Render("/")
+14 -14
View File
@@ -7,9 +7,9 @@ import (
"fmt"
"sort"
"github.com/daytonaio/apiclient"
"github.com/daytonaio/daytona/cli/views/common"
"github.com/daytonaio/daytona/cli/views/util"
"github.com/daytonaio/daytona/daytonaapiclient"
)
type RowData struct {
@@ -19,7 +19,7 @@ type RowData struct {
Created string
}
func ListVolumes(volumeList []daytonaapiclient.VolumeDto, activeOrganizationName *string) {
func ListVolumes(volumeList []apiclient.VolumeDto, activeOrganizationName *string) {
if len(volumeList) == 0 {
util.NotifyEmptyVolumeList(true)
return
@@ -47,7 +47,7 @@ func ListVolumes(volumeList []daytonaapiclient.VolumeDto, activeOrganizationName
fmt.Println(table)
}
func SortVolumes(volumeList *[]daytonaapiclient.VolumeDto) {
func SortVolumes(volumeList *[]apiclient.VolumeDto) {
sort.Slice(*volumeList, func(i, j int) bool {
if (*volumeList)[i].State != (*volumeList)[j].State {
pi, pj := getStateSortPriorities((*volumeList)[i].State, (*volumeList)[j].State)
@@ -59,7 +59,7 @@ func SortVolumes(volumeList *[]daytonaapiclient.VolumeDto) {
})
}
func getTableRowData(volume daytonaapiclient.VolumeDto) *RowData {
func getTableRowData(volume apiclient.VolumeDto) *RowData {
rowData := RowData{"", "", "", ""}
rowData.Name = volume.Name + util.AdditionalPropertyPadding
rowData.State = getStateLabel(volume.State)
@@ -67,7 +67,7 @@ func getTableRowData(volume daytonaapiclient.VolumeDto) *RowData {
return &rowData
}
func renderUnstyledList(volumeList []daytonaapiclient.VolumeDto) {
func renderUnstyledList(volumeList []apiclient.VolumeDto) {
for _, volume := range volumeList {
RenderInfo(&volume, true)
@@ -88,7 +88,7 @@ func getRowFromRowData(rowData RowData) []string {
return row
}
func getStateSortPriorities(state1, state2 daytonaapiclient.VolumeState) (int, int) {
func getStateSortPriorities(state1, state2 apiclient.VolumeState) (int, int) {
pi, ok := volumeListStatePriorities[state1]
if !ok {
pi = 99
@@ -102,12 +102,12 @@ func getStateSortPriorities(state1, state2 daytonaapiclient.VolumeState) (int, i
}
// Volumes that have actions being performed on them have a higher priority when listing
var volumeListStatePriorities = map[daytonaapiclient.VolumeState]int{
daytonaapiclient.VOLUMESTATE_PENDING_CREATE: 1,
daytonaapiclient.VOLUMESTATE_CREATING: 1,
daytonaapiclient.VOLUMESTATE_PENDING_DELETE: 1,
daytonaapiclient.VOLUMESTATE_DELETING: 1,
daytonaapiclient.VOLUMESTATE_READY: 2,
daytonaapiclient.VOLUMESTATE_ERROR: 3,
daytonaapiclient.VOLUMESTATE_DELETED: 4,
var volumeListStatePriorities = map[apiclient.VolumeState]int{
apiclient.VOLUMESTATE_PENDING_CREATE: 1,
apiclient.VOLUMESTATE_CREATING: 1,
apiclient.VOLUMESTATE_PENDING_DELETE: 1,
apiclient.VOLUMESTATE_DELETING: 1,
apiclient.VOLUMESTATE_READY: 2,
apiclient.VOLUMESTATE_ERROR: 3,
apiclient.VOLUMESTATE_DELETED: 4,
}
+23 -3
View File
@@ -7,7 +7,9 @@ import (
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
"syscall"
golog "log"
@@ -68,10 +70,28 @@ func main() {
}
}()
err = <-errChan
if err != nil {
log.Fatalf("Error: %v", err)
// Set up signal handling for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Wait for either an error or shutdown signal
select {
case err := <-errChan:
log.Errorf("Error: %v", err)
case sig := <-sigChan:
log.Infof("Received signal %v, shutting down gracefully...", sig)
}
// Graceful shutdown
log.Info("Stopping computer use processes...")
if toolBoxServer.ComputerUse != nil {
_, err := toolBoxServer.ComputerUse.Stop()
if err != nil {
log.Errorf("Failed to stop computer use: %v", err)
}
}
log.Info("Shutdown complete")
}
func initLogs(logWriter io.Writer) {
+18 -10
View File
@@ -17,13 +17,16 @@ require (
github.com/go-playground/validator/v10 v10.26.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-plugin v1.6.3
github.com/kelseyhightower/envconfig v1.4.0
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/pkg/sftp v1.13.6
github.com/sirupsen/logrus v1.9.3
github.com/sourcegraph/jsonrpc2 v0.2.0
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.37.0
golang.org/x/crypto v0.39.0
golang.org/x/sys v0.33.0
gopkg.in/ini.v1 v1.67.0
)
@@ -38,6 +41,8 @@ require (
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -46,15 +51,20 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/gomega v1.33.1 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
@@ -64,16 +74,14 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
golang.org/x/arch v0.16.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/grpc v1.69.4 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
require (
github.com/emirpasic/gods v1.18.1 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.32.0
golang.org/x/text v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+56 -12
View File
@@ -2,23 +2,26 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g=
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
@@ -32,9 +35,11 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcej
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
@@ -47,6 +52,10 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.12.1-0.20240617075238-c127d1b35535 h1:JDd/LIwWwltun7EqGK2dMMuPFyuIBOgDslh6wKR4zzk=
github.com/go-git/go-git/v5 v5.12.1-0.20240617075238-c127d1b35535/go.mod h1:VP749I36z+bJ9YP1Fu2/IQc3Vt/fKM+r957BooWKlk0=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -54,20 +63,29 @@ github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/Nu
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
@@ -89,6 +107,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -96,8 +117,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
@@ -125,6 +147,7 @@ github.com/sourcegraph/jsonrpc2 v0.2.0/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -132,6 +155,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@@ -141,14 +165,28 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -159,8 +197,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -178,14 +216,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -193,13 +233,17 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -0,0 +1,469 @@
// Copyright 2025 Daytona Platforms Inc.
// SPDX-License-Identifier: AGPL-3.0
package computeruse
import (
"net/http"
"net/rpc"
"strconv"
"github.com/gin-gonic/gin"
"github.com/hashicorp/go-plugin"
)
// PluginInterface defines the interface that the computeruse plugin must implement
type IComputerUse interface {
// Process management
Initialize() (*Empty, error)
Start() (*Empty, error)
Stop() (*Empty, error)
GetProcessStatus() (map[string]ProcessStatus, error)
IsProcessRunning(req *ProcessRequest) (bool, error)
RestartProcess(req *ProcessRequest) (*Empty, error)
GetProcessLogs(req *ProcessRequest) (string, error)
GetProcessErrors(req *ProcessRequest) (string, error)
// Screenshot methods
TakeScreenshot(*ScreenshotRequest) (*ScreenshotResponse, error)
TakeRegionScreenshot(*RegionScreenshotRequest) (*ScreenshotResponse, error)
TakeCompressedScreenshot(*CompressedScreenshotRequest) (*ScreenshotResponse, error)
TakeCompressedRegionScreenshot(*CompressedRegionScreenshotRequest) (*ScreenshotResponse, error)
// Mouse control methods
GetMousePosition() (*MousePositionResponse, error)
MoveMouse(*MoveMouseRequest) (*MousePositionResponse, error)
Click(*ClickRequest) (*MouseClickResponse, error)
Drag(*DragRequest) (*MouseDragResponse, error)
Scroll(*ScrollRequest) (*ScrollResponse, error)
// Keyboard control methods
TypeText(*TypeTextRequest) (*Empty, error)
PressKey(*PressKeyRequest) (*Empty, error)
PressHotkey(*PressHotkeyRequest) (*Empty, error)
// Display info methods
GetDisplayInfo() (*DisplayInfoResponse, error)
GetWindows() (*WindowsResponse, error)
// Status method
GetStatus() (*StatusResponse, error)
}
type ComputerUsePlugin struct {
Impl IComputerUse
}
// Common structs for better composition
type Position struct {
X int `json:"x"`
Y int `json:"y"`
}
type Size struct {
Width int `json:"width"`
Height int `json:"height"`
}
// Screenshot parameter structs
type ScreenshotRequest struct {
ShowCursor bool `json:"showCursor"`
}
type RegionScreenshotRequest struct {
Position
Size
ShowCursor bool `json:"showCursor"`
}
type CompressedScreenshotRequest struct {
ShowCursor bool `json:"showCursor"`
Format string `json:"format"` // "png" or "jpeg"
Quality int `json:"quality"` // 1-100 for JPEG quality
Scale float64 `json:"scale"` // 0.1-1.0 for scaling down
}
type CompressedRegionScreenshotRequest struct {
Position
Size
ShowCursor bool `json:"showCursor"`
Format string `json:"format"` // "png" or "jpeg"
Quality int `json:"quality"` // 1-100 for JPEG quality
Scale float64 `json:"scale"` // 0.1-1.0 for scaling down
}
// Mouse parameter structs
type MoveMouseRequest struct {
Position
}
type ClickRequest struct {
Position
Button string `json:"button"` // left, right, middle
Double bool `json:"double"`
}
type DragRequest struct {
StartX int `json:"startX"`
StartY int `json:"startY"`
EndX int `json:"endX"`
EndY int `json:"endY"`
Button string `json:"button"`
}
type ScrollRequest struct {
Position
Direction string `json:"direction"` // up, down
Amount int `json:"amount"`
}
// Keyboard parameter structs
type TypeTextRequest struct {
Text string `json:"text"`
Delay int `json:"delay"` // milliseconds between keystrokes
}
type PressKeyRequest struct {
Key string `json:"key"`
Modifiers []string `json:"modifiers"` // ctrl, alt, shift, cmd
}
type PressHotkeyRequest struct {
Keys string `json:"keys"` // e.g., "ctrl+c", "cmd+v"
}
// Response structs for keyboard operations
type ScrollResponse struct {
Success bool `json:"success"`
}
// Response structs
type ScreenshotResponse struct {
Screenshot string `json:"screenshot"`
CursorPosition *Position `json:"cursorPosition,omitempty"`
SizeBytes int `json:"sizeBytes,omitempty"`
}
// Mouse response structs - separated by operation type
type MousePositionResponse struct {
Position
}
type MouseClickResponse struct {
Position
}
type MouseDragResponse struct {
Position // Final position
}
type DisplayInfoResponse struct {
Displays []DisplayInfo `json:"displays"`
}
type DisplayInfo struct {
ID int `json:"id"`
Position
Size
IsActive bool `json:"isActive"`
}
type WindowsResponse struct {
Windows []WindowInfo `json:"windows"`
}
type WindowInfo struct {
ID int `json:"id"`
Title string `json:"title"`
Position
Size
IsActive bool `json:"isActive"`
}
type StatusResponse struct {
Status string `json:"status"`
}
type ProcessStatus struct {
Running bool
Priority int
AutoRestart bool
Pid *int
}
type ProcessRequest struct {
ProcessName string
}
type Empty struct{}
func (p *ComputerUsePlugin) Server(*plugin.MuxBroker) (any, error) {
return &ComputerUseRPCServer{Impl: p.Impl}, nil
}
func (p *ComputerUsePlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (any, error) {
return &ComputerUseRPCClient{client: c}, nil
}
// Helper function to create handlers that convert gin context to specific request structs
func WrapScreenshotHandler(fn func(*ScreenshotRequest) (*ScreenshotResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
req := &ScreenshotRequest{
ShowCursor: c.Query("showCursor") == "true",
}
response, err := fn(req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapRegionScreenshotHandler(fn func(*RegionScreenshotRequest) (*ScreenshotResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req RegionScreenshotRequest
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameters"})
return
}
req.ShowCursor = c.Query("showCursor") == "true"
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapCompressedScreenshotHandler(fn func(*CompressedScreenshotRequest) (*ScreenshotResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
req := &CompressedScreenshotRequest{
ShowCursor: c.Query("showCursor") == "true",
Format: c.Query("format"),
Quality: 85,
Scale: 1.0,
}
// Parse quality
if qualityStr := c.Query("quality"); qualityStr != "" {
if quality, err := strconv.Atoi(qualityStr); err == nil && quality >= 1 && quality <= 100 {
req.Quality = quality
}
}
// Parse scale
if scaleStr := c.Query("scale"); scaleStr != "" {
if scale, err := strconv.ParseFloat(scaleStr, 64); err == nil && scale >= 0.1 && scale <= 1.0 {
req.Scale = scale
}
}
response, err := fn(req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapCompressedRegionScreenshotHandler(fn func(*CompressedRegionScreenshotRequest) (*ScreenshotResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req CompressedRegionScreenshotRequest
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameters"})
return
}
req.ShowCursor = c.Query("showCursor") == "true"
req.Format = c.Query("format")
req.Quality = 85
req.Scale = 1.0
// Parse quality
if qualityStr := c.Query("quality"); qualityStr != "" {
if quality, err := strconv.Atoi(qualityStr); err == nil && quality >= 1 && quality <= 100 {
req.Quality = quality
}
}
// Parse scale
if scaleStr := c.Query("scale"); scaleStr != "" {
if scale, err := strconv.ParseFloat(scaleStr, 64); err == nil && scale >= 0.1 && scale <= 1.0 {
req.Scale = scale
}
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapMousePositionHandler(fn func() (*MousePositionResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
response, err := fn()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapMoveMouseHandler(fn func(*MoveMouseRequest) (*MousePositionResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req MoveMouseRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid coordinates"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapClickHandler(fn func(*ClickRequest) (*MouseClickResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req ClickRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid click parameters"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapDragHandler(fn func(*DragRequest) (*MouseDragResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req DragRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid drag parameters"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapScrollHandler(fn func(*ScrollRequest) (*ScrollResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req ScrollRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid scroll parameters"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapTypeTextHandler(fn func(*TypeTextRequest) (*Empty, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req TypeTextRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapPressKeyHandler(fn func(*PressKeyRequest) (*Empty, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req PressKeyRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid key"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapPressHotkeyHandler(fn func(*PressHotkeyRequest) (*Empty, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var req PressHotkeyRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid hotkey"})
return
}
response, err := fn(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapDisplayInfoHandler(fn func() (*DisplayInfoResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
response, err := fn()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapWindowsHandler(fn func() (*WindowsResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
response, err := fn()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
func WrapStatusHandler(fn func() (*StatusResponse, error)) gin.HandlerFunc {
return func(c *gin.Context) {
response, err := fn()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
@@ -0,0 +1,334 @@
// Copyright 2025 Daytona Platforms Inc.
// SPDX-License-Identifier: AGPL-3.0
package manager
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/daytonaio/daemon/pkg/toolbox/computeruse"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
log "github.com/sirupsen/logrus"
)
type pluginRef struct {
client *plugin.Client
impl computeruse.IComputerUse
path string
}
var ComputerUseHandshakeConfig = plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "DAYTONA_COMPUTER_USE_PLUGIN",
MagicCookieValue: "daytona_computer_use",
}
var computerUse = &pluginRef{}
// ComputerUseError represents a computer-use plugin error with context
type ComputerUseError struct {
Type string // "dependency", "system", "plugin"
Message string
Details string
}
func (e *ComputerUseError) Error() string {
return e.Message
}
// detectPluginError tries to execute the plugin binary directly to get detailed error information
func detectPluginError(path string) *ComputerUseError {
// Try to execute the plugin directly to get error output
cmd := exec.Command(path)
// Capture both stdout and stderr
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
// Execute the command
err := cmd.Run()
// Get the combined output
output := stdout.String() + stderr.String()
if err == nil {
// Plugin executed successfully, this shouldn't happen in normal flow
return &ComputerUseError{
Type: "plugin",
Message: "Plugin executed successfully but failed during handshake",
Details: "This may indicate a protocol version mismatch or plugin configuration issue.",
}
}
// Get exit code if available
exitCode := -1
if exitErr, ok := err.(*exec.ExitError); ok {
exitCode = exitErr.ExitCode()
}
// Log the raw error for debugging
log.Debugf("Plugin execution failed - Exit code: %d, Error: %v", exitCode, err)
log.Debugf("Plugin stdout: %s", stdout.String())
log.Debugf("Plugin stderr: %s", stderr.String())
// Check for missing X11 runtime dependencies
if strings.Contains(output, "libX11.so.6") ||
strings.Contains(output, "libXext.so.6") ||
strings.Contains(output, "libXtst.so.6") ||
strings.Contains(output, "libXrandr.so.2") ||
strings.Contains(output, "libXrender.so.1") ||
strings.Contains(output, "libXfixes.so.3") ||
strings.Contains(output, "libXss.so.1") ||
strings.Contains(output, "libXi.so.6") ||
strings.Contains(output, "libXinerama.so.1") {
missingLibs := []string{}
if strings.Contains(output, "libX11.so.6") {
missingLibs = append(missingLibs, "libX11")
}
if strings.Contains(output, "libXext.so.6") {
missingLibs = append(missingLibs, "libXext")
}
if strings.Contains(output, "libXtst.so.6") {
missingLibs = append(missingLibs, "libXtst")
}
if strings.Contains(output, "libXrandr.so.2") {
missingLibs = append(missingLibs, "libXrandr")
}
if strings.Contains(output, "libXrender.so.1") {
missingLibs = append(missingLibs, "libXrender")
}
if strings.Contains(output, "libXfixes.so.3") {
missingLibs = append(missingLibs, "libXfixes")
}
if strings.Contains(output, "libXss.so.1") {
missingLibs = append(missingLibs, "libXScrnSaver")
}
if strings.Contains(output, "libXi.so.6") {
missingLibs = append(missingLibs, "libXi")
}
if strings.Contains(output, "libXinerama.so.1") {
missingLibs = append(missingLibs, "libXinerama")
}
return &ComputerUseError{
Type: "dependency",
Message: fmt.Sprintf("Computer-use plugin requires X11 runtime libraries that are not available (missing: %s)", strings.Join(missingLibs, ", ")),
Details: fmt.Sprintf(`To enable computer-use functionality, install the required dependencies:
For Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y \\
libx11-6 libxrandr2 libxext6 libxrender1 libxfixes3 libxss1 libxtst6 libxi6 libxinerama1 \\
xvfb x11vnc novnc xfce4 xfce4-terminal dbus-x11
For CentOS/RHEL/Fedora:
sudo yum install -y libX11 libXrandr libXext libXrender libXfixes libXScrnSaver libXtst libXi libXinerama \\
xorg-x11-server-Xvfb x11vnc novnc xfce4 xfce4-terminal dbus-x11
For Alpine:
apk add --no-cache \\
libx11 libxrandr libxext libxrender libxfixes libxss libxtst libxi libxinerama \\
xvfb x11vnc novnc xfce4 xfce4-terminal dbus-x11
Raw error output: %s
Note: Computer-use features will be disabled until dependencies are installed.`, output),
}
}
// Check for missing development libraries (build-time dependencies)
if strings.Contains(output, "X11/extensions/XTest.h") ||
strings.Contains(output, "X11/Xlib.h") ||
strings.Contains(output, "X11/Xutil.h") ||
strings.Contains(output, "X11/X.h") {
return &ComputerUseError{
Type: "dependency",
Message: "Computer-use plugin requires X11 development libraries",
Details: fmt.Sprintf(`To build computer-use functionality, install the required development dependencies:
For Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y \\
libx11-dev libxtst-dev libxext-dev libxrandr-dev libxinerama-dev libxi-dev
For CentOS/RHEL/Fedora:
sudo yum install -y libX11-devel libXtst-devel libXext-devel libXrandr-devel libXinerama-devel libXi-devel
For Alpine:
apk add --no-cache \\
libx11-dev libxtst-dev libxext-dev libxrandr-dev libxinerama-dev libxi-dev
Raw error output: %s
Note: Computer-use features will be disabled until dependencies are installed.`, output),
}
}
// Check for permission issues
if strings.Contains(output, "Permission denied") ||
strings.Contains(output, "not executable") ||
strings.Contains(output, "EACCES") {
return &ComputerUseError{
Type: "system",
Message: "Computer-use plugin has permission issues",
Details: fmt.Sprintf("The plugin at %s is not executable. Please check file permissions and ensure the binary is executable.\n\nRaw error output: %s", path, output),
}
}
// Check for architecture mismatch
if strings.Contains(output, "wrong ELF class") ||
strings.Contains(output, "architecture") ||
strings.Contains(output, "ELF") ||
strings.Contains(output, "exec format error") {
return &ComputerUseError{
Type: "system",
Message: "Computer-use plugin architecture mismatch",
Details: fmt.Sprintf("The plugin was compiled for a different architecture. Please rebuild the plugin for the current system architecture.\n\nRaw error output: %s", output),
}
}
// Check for missing system libraries
if strings.Contains(output, "libc.so") ||
strings.Contains(output, "libm.so") ||
strings.Contains(output, "libdl.so") ||
strings.Contains(output, "libpthread.so") {
return &ComputerUseError{
Type: "system",
Message: "Computer-use plugin requires basic system libraries",
Details: fmt.Sprintf("The plugin is missing basic system libraries. This may indicate a corrupted binary or system issue.\n\nRaw error output: %s", output),
}
}
// Check for file not found
if strings.Contains(output, "No such file or directory") ||
strings.Contains(output, "ENOENT") {
return &ComputerUseError{
Type: "system",
Message: "Computer-use plugin file not found",
Details: fmt.Sprintf("The plugin file at %s could not be found or accessed.\n\nRaw error output: %s", path, output),
}
}
// Check for Go runtime issues
if strings.Contains(output, "go:") ||
strings.Contains(output, "runtime:") ||
strings.Contains(output, "panic:") {
return &ComputerUseError{
Type: "plugin",
Message: "Computer-use plugin has Go runtime issues",
Details: fmt.Sprintf("The plugin encountered a Go runtime error.\n\nRaw error output: %s", output),
}
}
// Generic plugin error with full details
return &ComputerUseError{
Type: "plugin",
Message: fmt.Sprintf("Computer-use plugin failed to start (exit code: %d)", exitCode),
Details: fmt.Sprintf("Error: %v\nExit Code: %d\nOutput: %s", err, exitCode, output),
}
}
func GetComputerUse(path string) (computeruse.IComputerUse, error) {
if computerUse.impl != nil {
return computerUse.impl, nil
}
if _, err := os.Stat(path); os.IsNotExist(err) {
log.Infof("Computer use plugin not found at %s. Skipping...", path)
return nil, nil
}
pluginName := filepath.Base(path)
pluginBasePath := filepath.Dir(path)
if runtime.GOOS == "windows" && strings.HasSuffix(path, ".exe") {
pluginName = strings.TrimSuffix(pluginName, ".exe")
}
logger := hclog.New(&hclog.LoggerOptions{
Name: pluginName,
// Output: log.New().WriterLevel(log.DebugLevel),
Output: os.Stdout,
Level: hclog.Debug,
})
pluginMap := map[string]plugin.Plugin{}
pluginMap[pluginName] = &computeruse.ComputerUsePlugin{}
client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: ComputerUseHandshakeConfig,
Plugins: pluginMap,
Cmd: exec.Command(path),
Logger: logger,
Managed: true,
})
log.Infof("Computer use %s registered", pluginName)
rpcClient, err := client.Client()
if err != nil {
// Try to get detailed error information
pluginErr := detectPluginError(path)
switch pluginErr.Type {
case "dependency":
log.Warn(pluginErr.Message)
log.Info(pluginErr.Details)
log.Info("Continuing without computer-use functionality...")
return nil, nil // Return nil to continue without the plugin
case "system":
log.Error(pluginErr.Message)
log.Info(pluginErr.Details)
log.Info("Continuing without computer-use functionality...")
return nil, nil // Return nil to continue without the plugin
default:
log.Error(pluginErr.Message)
log.Info(pluginErr.Details)
log.Info("Continuing without computer-use functionality...")
return nil, nil // Return nil to continue without the plugin
}
}
raw, err := rpcClient.Dispense(pluginName)
if err != nil {
log.Errorf("Failed to dispense computer-use plugin: %v", err)
log.Info("Continuing without computer-use functionality...")
return nil, nil
}
impl, ok := raw.(computeruse.IComputerUse)
if !ok {
log.Errorf("Unexpected type from computer-use plugin")
log.Info("Continuing without computer-use functionality...")
return nil, nil
}
_, err = impl.Initialize()
if err != nil {
log.Errorf("Failed to initialize computer-use plugin: %v", err)
log.Info("Continuing without computer-use functionality...")
return nil, nil
}
log.Info("Computer-use plugin initialized successfully")
computerUse.client = client
computerUse.impl = impl
computerUse.path = pluginBasePath
return impl, nil
}
@@ -0,0 +1,152 @@
// Copyright 2025 Daytona Platforms Inc.
// SPDX-License-Identifier: AGPL-3.0
package computeruse
import (
"net/rpc"
)
type ComputerUseRPCClient struct {
client *rpc.Client
}
// Type check
var _ IComputerUse = &ComputerUseRPCClient{}
// Process management methods
func (m *ComputerUseRPCClient) Initialize() (*Empty, error) {
err := m.client.Call("Plugin.Initialize", new(any), new(Empty))
return new(Empty), err
}
func (m *ComputerUseRPCClient) Start() (*Empty, error) {
err := m.client.Call("Plugin.Start", new(any), new(Empty))
return new(Empty), err
}
func (m *ComputerUseRPCClient) Stop() (*Empty, error) {
err := m.client.Call("Plugin.Stop", new(any), new(Empty))
return new(Empty), err
}
func (m *ComputerUseRPCClient) GetProcessStatus() (map[string]ProcessStatus, error) {
resp := map[string]ProcessStatus{}
err := m.client.Call("Plugin.GetProcessStatus", new(any), &resp)
return resp, err
}
func (m *ComputerUseRPCClient) IsProcessRunning(req *ProcessRequest) (bool, error) {
var resp bool
err := m.client.Call("Plugin.IsProcessRunning", req, &resp)
return resp, err
}
func (m *ComputerUseRPCClient) RestartProcess(req *ProcessRequest) (*Empty, error) {
err := m.client.Call("Plugin.RestartProcess", req, new(Empty))
return new(Empty), err
}
func (m *ComputerUseRPCClient) GetProcessLogs(req *ProcessRequest) (string, error) {
var resp string
err := m.client.Call("Plugin.GetProcessLogs", req, &resp)
return resp, err
}
func (m *ComputerUseRPCClient) GetProcessErrors(req *ProcessRequest) (string, error) {
var resp string
err := m.client.Call("Plugin.GetProcessErrors", req, &resp)
return resp, err
}
// Screenshot methods
func (m *ComputerUseRPCClient) TakeScreenshot(request *ScreenshotRequest) (*ScreenshotResponse, error) {
var resp ScreenshotResponse
err := m.client.Call("Plugin.TakeScreenshot", request, &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) TakeRegionScreenshot(request *RegionScreenshotRequest) (*ScreenshotResponse, error) {
var resp ScreenshotResponse
err := m.client.Call("Plugin.TakeRegionScreenshot", request, &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) TakeCompressedScreenshot(request *CompressedScreenshotRequest) (*ScreenshotResponse, error) {
var resp ScreenshotResponse
err := m.client.Call("Plugin.TakeCompressedScreenshot", request, &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) TakeCompressedRegionScreenshot(request *CompressedRegionScreenshotRequest) (*ScreenshotResponse, error) {
var resp ScreenshotResponse
err := m.client.Call("Plugin.TakeCompressedRegionScreenshot", request, &resp)
return &resp, err
}
// Mouse control methods
func (m *ComputerUseRPCClient) GetMousePosition() (*MousePositionResponse, error) {
var resp MousePositionResponse
err := m.client.Call("Plugin.GetMousePosition", new(any), &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) MoveMouse(request *MoveMouseRequest) (*MousePositionResponse, error) {
var resp MousePositionResponse
err := m.client.Call("Plugin.MoveMouse", request, &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) Click(request *ClickRequest) (*MouseClickResponse, error) {
var resp MouseClickResponse
err := m.client.Call("Plugin.Click", request, &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) Drag(request *DragRequest) (*MouseDragResponse, error) {
var resp MouseDragResponse
err := m.client.Call("Plugin.Drag", request, &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) Scroll(request *ScrollRequest) (*ScrollResponse, error) {
var resp ScrollResponse
err := m.client.Call("Plugin.Scroll", request, &resp)
return &resp, err
}
// Keyboard control methods
func (m *ComputerUseRPCClient) TypeText(request *TypeTextRequest) (*Empty, error) {
err := m.client.Call("Plugin.TypeText", request, new(Empty))
return new(Empty), err
}
func (m *ComputerUseRPCClient) PressKey(request *PressKeyRequest) (*Empty, error) {
err := m.client.Call("Plugin.PressKey", request, new(Empty))
return new(Empty), err
}
func (m *ComputerUseRPCClient) PressHotkey(request *PressHotkeyRequest) (*Empty, error) {
err := m.client.Call("Plugin.PressHotkey", request, new(Empty))
return new(Empty), err
}
// Display info methods
func (m *ComputerUseRPCClient) GetDisplayInfo() (*DisplayInfoResponse, error) {
var resp DisplayInfoResponse
err := m.client.Call("Plugin.GetDisplayInfo", new(any), &resp)
return &resp, err
}
func (m *ComputerUseRPCClient) GetWindows() (*WindowsResponse, error) {
var resp WindowsResponse
err := m.client.Call("Plugin.GetWindows", new(any), &resp)
return &resp, err
}
// Status method
func (m *ComputerUseRPCClient) GetStatus() (*StatusResponse, error) {
var resp StatusResponse
err := m.client.Call("Plugin.GetStatus", new(any), &resp)
return &resp, err
}
@@ -0,0 +1,193 @@
// Copyright 2025 Daytona Platforms Inc.
// SPDX-License-Identifier: AGPL-3.0
package computeruse
type ComputerUseRPCServer struct {
Impl IComputerUse
}
// Process management methods
func (m *ComputerUseRPCServer) Initialize(arg any, resp *Empty) error {
_, err := m.Impl.Initialize()
return err
}
func (m *ComputerUseRPCServer) Start(arg any, resp *Empty) error {
_, err := m.Impl.Start()
return err
}
func (m *ComputerUseRPCServer) Stop(arg any, resp *Empty) error {
_, err := m.Impl.Stop()
return err
}
func (m *ComputerUseRPCServer) GetProcessStatus(arg any, resp *map[string]ProcessStatus) error {
status, err := m.Impl.GetProcessStatus()
if err != nil {
return err
}
*resp = status
return nil
}
func (m *ComputerUseRPCServer) IsProcessRunning(arg *ProcessRequest, resp *bool) error {
isRunning, err := m.Impl.IsProcessRunning(arg)
if err != nil {
return err
}
*resp = isRunning
return nil
}
func (m *ComputerUseRPCServer) RestartProcess(arg *ProcessRequest, resp *Empty) error {
_, err := m.Impl.RestartProcess(arg)
return err
}
func (m *ComputerUseRPCServer) GetProcessLogs(arg *ProcessRequest, resp *string) error {
logs, err := m.Impl.GetProcessLogs(arg)
if err != nil {
return err
}
*resp = logs
return nil
}
func (m *ComputerUseRPCServer) GetProcessErrors(arg *ProcessRequest, resp *string) error {
errors, err := m.Impl.GetProcessErrors(arg)
if err != nil {
return err
}
*resp = errors
return nil
}
// Screenshot methods
func (m *ComputerUseRPCServer) TakeScreenshot(arg *ScreenshotRequest, resp *ScreenshotResponse) error {
response, err := m.Impl.TakeScreenshot(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) TakeRegionScreenshot(arg *RegionScreenshotRequest, resp *ScreenshotResponse) error {
response, err := m.Impl.TakeRegionScreenshot(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) TakeCompressedScreenshot(arg *CompressedScreenshotRequest, resp *ScreenshotResponse) error {
response, err := m.Impl.TakeCompressedScreenshot(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) TakeCompressedRegionScreenshot(arg *CompressedRegionScreenshotRequest, resp *ScreenshotResponse) error {
response, err := m.Impl.TakeCompressedRegionScreenshot(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
// Mouse control methods
func (m *ComputerUseRPCServer) GetMousePosition(arg any, resp *MousePositionResponse) error {
response, err := m.Impl.GetMousePosition()
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) MoveMouse(arg *MoveMouseRequest, resp *MousePositionResponse) error {
response, err := m.Impl.MoveMouse(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) Click(arg *ClickRequest, resp *MouseClickResponse) error {
response, err := m.Impl.Click(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) Drag(arg *DragRequest, resp *MouseDragResponse) error {
response, err := m.Impl.Drag(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) Scroll(arg *ScrollRequest, resp *ScrollResponse) error {
response, err := m.Impl.Scroll(arg)
if err != nil {
return err
}
*resp = *response
return nil
}
// Keyboard control methods
func (m *ComputerUseRPCServer) TypeText(arg *TypeTextRequest, resp *Empty) error {
_, err := m.Impl.TypeText(arg)
return err
}
func (m *ComputerUseRPCServer) PressKey(arg *PressKeyRequest, resp *Empty) error {
_, err := m.Impl.PressKey(arg)
return err
}
func (m *ComputerUseRPCServer) PressHotkey(arg *PressHotkeyRequest, resp *Empty) error {
_, err := m.Impl.PressHotkey(arg)
return err
}
// Display info methods
func (m *ComputerUseRPCServer) GetDisplayInfo(arg any, resp *DisplayInfoResponse) error {
response, err := m.Impl.GetDisplayInfo()
if err != nil {
return err
}
*resp = *response
return nil
}
func (m *ComputerUseRPCServer) GetWindows(arg any, resp *WindowsResponse) error {
response, err := m.Impl.GetWindows()
if err != nil {
return err
}
*resp = *response
return nil
}
// Status method
func (m *ComputerUseRPCServer) GetStatus(arg any, resp *StatusResponse) error {
response, err := m.Impl.GetStatus()
if err != nil {
return err
}
*resp = *response
return nil
}
+236 -1
View File
@@ -13,6 +13,8 @@ import (
common_proxy "github.com/daytonaio/common-go/pkg/proxy"
"github.com/daytonaio/daemon/internal"
"github.com/daytonaio/daemon/pkg/toolbox/computeruse"
"github.com/daytonaio/daemon/pkg/toolbox/computeruse/manager"
"github.com/daytonaio/daemon/pkg/toolbox/config"
"github.com/daytonaio/daemon/pkg/toolbox/fs"
"github.com/daytonaio/daemon/pkg/toolbox/git"
@@ -30,7 +32,8 @@ import (
)
type Server struct {
ProjectDir string
ProjectDir string
ComputerUse computeruse.IComputerUse
}
type ProjectDirResponse struct {
@@ -147,6 +150,82 @@ func (s *Server) Start() error {
lspController.GET("/workspacesymbols", lsp.WorkspaceSymbols)
}
// Initialize plugin-based computer use
pluginPath := "/usr/local/lib/daytona-computer-use"
// Fallback to local config directory for development
if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
pluginPath = path.Join(configDir, "daytona-computer-use")
}
s.ComputerUse, err = manager.GetComputerUse(pluginPath)
if err != nil {
log.Errorf("Failed to initialize computer-use plugin: %v", err)
log.Info("Continuing without computer-use functionality...")
}
// Always register computer-use endpoints, but handle the case when plugin is nil
computerUseController := r.Group("/computeruse")
{
if s.ComputerUse != nil {
// Computer use status endpoint
computerUseController.GET("/status", computeruse.WrapStatusHandler(s.ComputerUse.GetStatus))
// Computer use management endpoints
computerUseController.POST("/start", s.startComputerUse)
computerUseController.POST("/stop", s.stopComputerUse)
computerUseController.GET("/process-status", s.getComputerUseStatus)
computerUseController.GET("/process/:processName/status", s.getProcessStatus)
computerUseController.POST("/process/:processName/restart", s.restartProcess)
computerUseController.GET("/process/:processName/logs", s.getProcessLogs)
computerUseController.GET("/process/:processName/errors", s.getProcessErrors)
// Screenshot endpoints
computerUseController.GET("/screenshot", computeruse.WrapScreenshotHandler(s.ComputerUse.TakeScreenshot))
computerUseController.GET("/screenshot/region", computeruse.WrapRegionScreenshotHandler(s.ComputerUse.TakeRegionScreenshot))
computerUseController.GET("/screenshot/compressed", computeruse.WrapCompressedScreenshotHandler(s.ComputerUse.TakeCompressedScreenshot))
computerUseController.GET("/screenshot/region/compressed", computeruse.WrapCompressedRegionScreenshotHandler(s.ComputerUse.TakeCompressedRegionScreenshot))
// Mouse control endpoints
computerUseController.GET("/mouse/position", computeruse.WrapMousePositionHandler(s.ComputerUse.GetMousePosition))
computerUseController.POST("/mouse/move", computeruse.WrapMoveMouseHandler(s.ComputerUse.MoveMouse))
computerUseController.POST("/mouse/click", computeruse.WrapClickHandler(s.ComputerUse.Click))
computerUseController.POST("/mouse/drag", computeruse.WrapDragHandler(s.ComputerUse.Drag))
computerUseController.POST("/mouse/scroll", computeruse.WrapScrollHandler(s.ComputerUse.Scroll))
// Keyboard control endpoints
computerUseController.POST("/keyboard/type", computeruse.WrapTypeTextHandler(s.ComputerUse.TypeText))
computerUseController.POST("/keyboard/key", computeruse.WrapPressKeyHandler(s.ComputerUse.PressKey))
computerUseController.POST("/keyboard/hotkey", computeruse.WrapPressHotkeyHandler(s.ComputerUse.PressHotkey))
// Display info endpoints
computerUseController.GET("/display/info", computeruse.WrapDisplayInfoHandler(s.ComputerUse.GetDisplayInfo))
computerUseController.GET("/display/windows", computeruse.WrapWindowsHandler(s.ComputerUse.GetWindows))
} else {
// Register all endpoints with disabled middleware when plugin is not available
computerUseController.GET("/status", s.computerUseDisabledMiddleware())
computerUseController.POST("/start", s.computerUseDisabledMiddleware())
computerUseController.POST("/stop", s.computerUseDisabledMiddleware())
computerUseController.GET("/process-status", s.computerUseDisabledMiddleware())
computerUseController.GET("/process/:processName/status", s.computerUseDisabledMiddleware())
computerUseController.POST("/process/:processName/restart", s.computerUseDisabledMiddleware())
computerUseController.GET("/process/:processName/logs", s.computerUseDisabledMiddleware())
computerUseController.GET("/process/:processName/errors", s.computerUseDisabledMiddleware())
computerUseController.GET("/screenshot", s.computerUseDisabledMiddleware())
computerUseController.GET("/screenshot/region", s.computerUseDisabledMiddleware())
computerUseController.GET("/screenshot/compressed", s.computerUseDisabledMiddleware())
computerUseController.GET("/screenshot/region/compressed", s.computerUseDisabledMiddleware())
computerUseController.GET("/mouse/position", s.computerUseDisabledMiddleware())
computerUseController.POST("/mouse/move", s.computerUseDisabledMiddleware())
computerUseController.POST("/mouse/click", s.computerUseDisabledMiddleware())
computerUseController.POST("/mouse/drag", s.computerUseDisabledMiddleware())
computerUseController.POST("/mouse/scroll", s.computerUseDisabledMiddleware())
computerUseController.POST("/keyboard/type", s.computerUseDisabledMiddleware())
computerUseController.POST("/keyboard/key", s.computerUseDisabledMiddleware())
computerUseController.POST("/keyboard/hotkey", s.computerUseDisabledMiddleware())
computerUseController.GET("/display/info", s.computerUseDisabledMiddleware())
computerUseController.GET("/display/windows", s.computerUseDisabledMiddleware())
}
}
portDetector := port.NewPortsDetector()
portController := r.Group("/port")
@@ -177,3 +256,159 @@ func (s *Server) Start() error {
return httpServer.Serve(listener)
}
// computerUseDisabledMiddleware returns a middleware that handles requests when computer-use is disabled
func (s *Server) computerUseDisabledMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.JSON(http.StatusServiceUnavailable, gin.H{
"message": "Computer-use functionality is not available",
"details": "The computer-use plugin failed to initialize due to missing dependencies in the runtime environment.",
"solution": "Install the required X11 dependencies (x11-apps, xvfb, etc.) to enable computer-use functionality. Check the daemon logs for specific error details.",
})
c.Abort()
}
}
// Computer use management handlers
func (s *Server) startComputerUse(ctx *gin.Context) {
_, err := s.ComputerUse.Start()
if err != nil {
ctx.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Failed to start computer use",
"details": err.Error(),
})
return
}
status, err := s.ComputerUse.GetProcessStatus()
if err != nil {
ctx.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Failed to get computer use status",
"details": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"message": "Computer use processes started successfully",
"status": status,
})
}
func (s *Server) stopComputerUse(ctx *gin.Context) {
_, err := s.ComputerUse.Stop()
if err != nil {
ctx.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Failed to stop computer use",
"details": err.Error(),
})
return
}
status, err := s.ComputerUse.GetProcessStatus()
if err != nil {
ctx.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Failed to get computer use status",
"details": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"message": "Computer use processes stopped successfully",
"status": status,
})
}
func (s *Server) getComputerUseStatus(ctx *gin.Context) {
status, err := s.ComputerUse.GetProcessStatus()
if err != nil {
ctx.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Failed to get computer use status",
"details": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"status": status,
})
}
func (s *Server) getProcessStatus(ctx *gin.Context) {
processName := ctx.Param("processName")
req := &computeruse.ProcessRequest{
ProcessName: processName,
}
isRunning, err := s.ComputerUse.IsProcessRunning(req)
if err != nil {
ctx.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Failed to get process status",
"details": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"processName": processName,
"running": isRunning,
})
}
func (s *Server) restartProcess(ctx *gin.Context) {
processName := ctx.Param("processName")
req := &computeruse.ProcessRequest{
ProcessName: processName,
}
_, err := s.ComputerUse.RestartProcess(req)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("Process %s restarted successfully", processName),
"processName": processName,
})
}
func (s *Server) getProcessLogs(ctx *gin.Context) {
processName := ctx.Param("processName")
req := &computeruse.ProcessRequest{
ProcessName: processName,
}
logs, err := s.ComputerUse.GetProcessLogs(req)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"processName": processName,
"logs": logs,
})
}
func (s *Server) getProcessErrors(ctx *gin.Context) {
processName := ctx.Param("processName")
req := &computeruse.ProcessRequest{
ProcessName: processName,
}
errors, err := s.ComputerUse.GetProcessErrors(req)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"processName": processName,
"errors": errors,
})
}
+1 -1
View File
@@ -56,7 +56,7 @@
"format": {
"executor": "nx:run-commands",
"options": {
"command": "cd {projectRoot} && go fmt ./... && prettier --write \"**/*.{html,js,css,js}\""
"command": "cd {projectRoot} && go fmt ./... && prettier --write \"**/*.{yaml,html}\""
}
},
"test": {
+7
View File
@@ -13,6 +13,7 @@ import {
UsersApi,
VolumesApi,
SandboxApi,
ToolboxApi,
} from '@daytonaio/api-client'
import axios, { AxiosError } from 'axios'
import { DaytonaError } from './errors'
@@ -27,6 +28,7 @@ export class ApiClient {
private _organizationsApi: OrganizationsApi
private _billingApi: BillingApiClient
private _volumeApi: VolumesApi
private _toolboxApi: ToolboxApi
constructor(accessToken: string) {
this.config = new Configuration({
@@ -61,6 +63,7 @@ export class ApiClient {
this._organizationsApi = new OrganizationsApi(this.config, undefined, axiosInstance)
this._billingApi = new BillingApiClient(import.meta.env.VITE_BILLING_API_URL || window.location.origin, accessToken)
this._volumeApi = new VolumesApi(this.config, undefined, axiosInstance)
this._toolboxApi = new ToolboxApi(this.config, undefined, axiosInstance)
}
public setAccessToken(accessToken: string) {
@@ -98,4 +101,8 @@ export class ApiClient {
public get volumeApi() {
return this._volumeApi
}
public get toolboxApi() {
return this._toolboxApi
}
}
+31 -19
View File
@@ -30,6 +30,7 @@ import {
ArrowUpDown,
Archive,
Container,
Monitor,
} from 'lucide-react'
import { TableHeader, TableRow, TableHead, TableBody, TableCell, Table } from './ui/table'
import { Button } from './ui/button'
@@ -63,6 +64,7 @@ interface DataTableProps {
handleDelete: (id: string) => void
handleBulkDelete: (ids: string[]) => void
handleArchive: (id: string) => void
handleVnc: (id: string) => void
}
export function SandboxTable({
@@ -74,6 +76,7 @@ export function SandboxTable({
handleDelete,
handleBulkDelete,
handleArchive,
handleVnc,
}: DataTableProps) {
const { authenticatedUserHasPermission } = useSelectedOrganization()
const navigate = useNavigate()
@@ -115,6 +118,7 @@ export function SandboxTable({
handleStop,
handleDelete,
handleArchive,
handleVnc,
loadingSandboxes,
writePermitted,
deletePermitted,
@@ -343,6 +347,7 @@ const getColumns = ({
handleStop,
handleDelete,
handleArchive,
handleVnc,
loadingSandboxes,
writePermitted,
deletePermitted,
@@ -351,6 +356,7 @@ const getColumns = ({
handleStop: (id: string) => void
handleDelete: (id: string) => void
handleArchive: (id: string) => void
handleVnc: (id: string) => void
loadingSandboxes: Record<string, boolean>
writePermitted: boolean
deletePermitted: boolean
@@ -609,29 +615,26 @@ const getColumns = ({
cell: ({ row }) => {
if (row.original.state !== SandboxState.STARTED) return ''
let terminalUrl: string | null = null
if (!row.original.daemonVersion) {
return (
<a
href={`https://22222-${row.original.id}.${row.original.runnerDomain}`}
target="_blank"
rel="noopener noreferrer"
>
<Terminal className="w-4 h-4" />
</a>
)
terminalUrl = `https://22222-${row.original.id}.${row.original.runnerDomain}`
} else {
terminalUrl =
import.meta.env.VITE_PROXY_TEMPLATE_URL?.replace('{{PORT}}', '22222').replace(
'{{sandboxId}}',
row.original.id,
) || null
}
const terminalUrl = import.meta.env.VITE_PROXY_TEMPLATE_URL?.replace('{{PORT}}', '22222').replace(
'{{sandboxId}}',
row.original.id,
)
if (!terminalUrl) {
return null
}
return (
<a href={terminalUrl} target="_blank" rel="noopener noreferrer">
<Terminal className="w-4 h-4" />
</a>
<div className="flex items-center gap-2">
{terminalUrl && (
<a href={terminalUrl} target="_blank" rel="noopener noreferrer">
<Terminal className="w-4 h-4" />
</a>
)}
</div>
)
},
},
@@ -656,6 +659,15 @@ const getColumns = ({
<DropdownMenuContent align="end">
{writePermitted && (
<>
{sandbox.state === SandboxState.STARTED && (
<DropdownMenuItem
onClick={() => handleVnc(sandbox.id)}
className="cursor-pointer"
disabled={loadingSandboxes[sandbox.id]}
>
VNC
</DropdownMenuItem>
)}
{sandbox.state === SandboxState.STARTED && (
<DropdownMenuItem
onClick={() => handleStop(sandbox.id)}
+104 -1
View File
@@ -30,7 +30,7 @@ import { getLocalStorageItem, setLocalStorageItem } from '@/lib/local-storage'
import { DAYTONA_DOCS_URL } from '@/constants/ExternalLinks'
const Sandboxes: React.FC = () => {
const { sandboxApi, apiKeyApi } = useApi()
const { sandboxApi, apiKeyApi, toolboxApi } = useApi()
const { user } = useAuth()
const { notificationSocket } = useNotificationSocket()
@@ -248,6 +248,108 @@ const Sandboxes: React.FC = () => {
}
}
const getVncUrl = (sandboxId: string) => {
const sandbox = sandboxes.find((s) => s.id === sandboxId)
if (!sandbox) {
return null
}
if (!sandbox.daemonVersion) {
return `https://6080-${sandbox.id}.${sandbox.runnerDomain}/vnc.html`
}
return (
import.meta.env.VITE_PROXY_TEMPLATE_URL?.replace('{{PORT}}', '6080').replace('{{sandboxId}}', sandbox.id) +
'/vnc.html'
)
}
const handleVnc = async (id: string) => {
setLoadingSandboxes((prev) => ({ ...prev, [id]: true }))
// Notify user immediately that we're checking VNC status
toast.info('Checking VNC desktop status...')
try {
// First, check if computer use is already started
const statusResponse = await toolboxApi.getComputerUseStatus(id, selectedOrganization?.id)
const status = statusResponse.data.status
// Check if computer use is active (all processes running)
if (status === 'active') {
const vncUrl = getVncUrl(id)
if (vncUrl) {
window.open(vncUrl, '_blank')
toast.success('Opening VNC desktop...')
} else {
toast.error('Failed to construct VNC URL')
}
} else {
// Computer use is not active, try to start it
try {
await toolboxApi.startComputerUse(id, selectedOrganization?.id)
toast.success('Starting VNC desktop...')
// Wait a moment for processes to start, then open VNC
await new Promise((resolve) => setTimeout(resolve, 5000))
try {
const newStatusResponse = await toolboxApi.getComputerUseStatus(id, selectedOrganization?.id)
const newStatus = newStatusResponse.data.status
if (newStatus === 'active') {
const vncUrl = getVncUrl(id)
if (vncUrl) {
window.open(vncUrl, '_blank')
toast.success('VNC desktop is ready!', {
action: (
<Button variant="secondary" onClick={() => window.open(vncUrl, '_blank')}>
Open in new tab
</Button>
),
})
}
} else {
toast.error(`VNC desktop failed to start. Status: ${newStatus}`)
}
} catch (error) {
handleApiError(error, 'Failed to check VNC status after start')
}
} catch (startError: any) {
// Check if this is a computer-use availability error
const errorMessage = startError?.response?.data?.message || startError?.message || String(startError)
if (errorMessage === 'Computer-use functionality is not available') {
toast.error('Computer-use functionality is not available', {
description: (
<div>
<div>Computer-use dependencies are missing in the runtime environment.</div>
<div className="mt-2">
<a
href={`${DAYTONA_DOCS_URL}/getting-started/computer-use`}
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
See documentation on how to configure the runtime for computer-use
</a>
</div>
</div>
),
})
} else {
handleApiError(startError, 'Failed to start VNC desktop')
}
}
}
} catch (error) {
handleApiError(error, 'Failed to check VNC status')
} finally {
setLoadingSandboxes((prev) => ({ ...prev, [id]: false }))
}
}
// Redirect user to the onboarding page if they haven't created an api key yet
// Perform only once per user
useEffect(() => {
@@ -308,6 +410,7 @@ const Sandboxes: React.FC = () => {
}}
handleBulkDelete={handleBulkDelete}
handleArchive={handleArchive}
handleVnc={handleVnc}
data={sandboxes}
loading={loadingTable}
/>
@@ -0,0 +1,707 @@
---
title: "AsyncComputerUse"
hideTitleOnPage: true
---
## AsyncComputerUse
```python
class AsyncComputerUse()
```
Computer Use functionality for interacting with the desktop environment.
Provides access to mouse, keyboard, screenshot, and display operations
for automating desktop interactions within a sandbox.
**Attributes**:
- `mouse` _AsyncMouse_ - Mouse operations interface.
- `keyboard` _AsyncKeyboard_ - Keyboard operations interface.
- `screenshot` _AsyncScreenshot_ - Screenshot operations interface.
- `display` _AsyncDisplay_ - Display operations interface.
#### AsyncComputerUse.start
```python
@intercept_errors(message_prefix="Failed to start computer use: ")
async def start() -> ComputerUseStartResponse
```
Starts all computer use processes (Xvfb, xfce4, x11vnc, novnc).
**Returns**:
- `ComputerUseStartResponse` - Computer use start response.
**Example**:
```python
result = await sandbox.computer_use.start()
print("Computer use processes started:", result.message)
```
#### AsyncComputerUse.stop
```python
@intercept_errors(message_prefix="Failed to stop computer use: ")
async def stop() -> ComputerUseStopResponse
```
Stops all computer use processes.
**Returns**:
- `ComputerUseStopResponse` - Computer use stop response.
**Example**:
```python
result = await sandbox.computer_use.stop()
print("Computer use processes stopped:", result.message)
```
#### AsyncComputerUse.get\_status
```python
@intercept_errors(message_prefix="Failed to get computer use status: ")
async def get_status() -> ComputerUseStatusResponse
```
Gets the status of all computer use processes.
**Returns**:
- `ComputerUseStatusResponse` - Status information about all VNC desktop processes.
**Example**:
```python
response = await sandbox.computer_use.get_status()
print("Computer use status:", response.status)
```
#### AsyncComputerUse.get\_process\_status
```python
@intercept_errors(message_prefix="Failed to get process status: ")
async def get_process_status(process_name: str) -> ProcessStatusResponse
```
Gets the status of a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to check.
**Returns**:
- `ProcessStatusResponse` - Status information about the specific process.
**Example**:
```python
xvfb_status = await sandbox.computer_use.get_process_status("xvfb")
no_vnc_status = await sandbox.computer_use.get_process_status("novnc")
```
#### AsyncComputerUse.restart\_process
```python
@intercept_errors(message_prefix="Failed to restart process: ")
async def restart_process(process_name: str) -> ProcessRestartResponse
```
Restarts a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to restart.
**Returns**:
- `ProcessRestartResponse` - Process restart response.
**Example**:
```python
result = await sandbox.computer_use.restart_process("xfce4")
print("XFCE4 process restarted:", result.message)
```
#### AsyncComputerUse.get\_process\_logs
```python
@intercept_errors(message_prefix="Failed to get process logs: ")
async def get_process_logs(process_name: str) -> ProcessLogsResponse
```
Gets logs for a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to get logs for.
**Returns**:
- `ProcessLogsResponse` - Process logs.
**Example**:
```python
logs = await sandbox.computer_use.get_process_logs("novnc")
print("NoVNC logs:", logs)
```
#### AsyncComputerUse.get\_process\_errors
```python
@intercept_errors(message_prefix="Failed to get process errors: ")
async def get_process_errors(process_name: str) -> ProcessErrorsResponse
```
Gets error logs for a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to get error logs for.
**Returns**:
- `ProcessErrorsResponse` - Process error logs.
**Example**:
```python
errors = await sandbox.computer_use.get_process_errors("x11vnc")
print("X11VNC errors:", errors)
```
## AsyncMouse
```python
class AsyncMouse()
```
Mouse operations for computer use functionality.
#### AsyncMouse.get\_position
```python
@intercept_errors(message_prefix="Failed to get mouse position: ")
async def get_position() -> MousePosition
```
Gets the current mouse cursor position.
**Returns**:
- `MousePosition` - Current mouse position with x and y coordinates.
**Example**:
```python
position = await sandbox.computer_use.mouse.get_position()
print(f"Mouse is at: {position.x}, {position.y}")
```
#### AsyncMouse.move
```python
@intercept_errors(message_prefix="Failed to move mouse: ")
async def move(x: int, y: int) -> MouseMoveResponse
```
Moves the mouse cursor to the specified coordinates.
**Arguments**:
- `x` _int_ - The x coordinate to move to.
- `y` _int_ - The y coordinate to move to.
**Returns**:
- `MouseMoveResponse` - Move operation result.
**Example**:
```python
result = await sandbox.computer_use.mouse.move(100, 200)
print(f"Mouse moved to: {result.x}, {result.y}")
```
#### AsyncMouse.click
```python
@intercept_errors(message_prefix="Failed to click mouse: ")
async def click(x: int,
y: int,
button: str = "left",
double: bool = False) -> MouseClickResponse
```
Clicks the mouse at the specified coordinates.
**Arguments**:
- `x` _int_ - The x coordinate to click at.
- `y` _int_ - The y coordinate to click at.
- `button` _str_ - The mouse button to click ('left', 'right', 'middle').
- `double` _bool_ - Whether to perform a double-click.
**Returns**:
- `MouseClickResponse` - Click operation result.
**Example**:
```python
# Single left click
result = await sandbox.computer_use.mouse.click(100, 200)
# Double click
double_click = await sandbox.computer_use.mouse.click(100, 200, "left", True)
# Right click
right_click = await sandbox.computer_use.mouse.click(100, 200, "right")
```
#### AsyncMouse.drag
```python
@intercept_errors(message_prefix="Failed to drag mouse: ")
async def drag(start_x: int,
start_y: int,
end_x: int,
end_y: int,
button: str = "left") -> MouseDragResponse
```
Drags the mouse from start coordinates to end coordinates.
**Arguments**:
- `start_x` _int_ - The starting x coordinate.
- `start_y` _int_ - The starting y coordinate.
- `end_x` _int_ - The ending x coordinate.
- `end_y` _int_ - The ending y coordinate.
- `button` _str_ - The mouse button to use for dragging.
**Returns**:
- `MouseDragResponse` - Drag operation result.
**Example**:
```python
result = await sandbox.computer_use.mouse.drag(50, 50, 150, 150)
print(f"Dragged from {result.from_x},{result.from_y} to {result.to_x},{result.to_y}")
```
#### AsyncMouse.scroll
```python
@intercept_errors(message_prefix="Failed to scroll mouse: ")
async def scroll(x: int, y: int, direction: str, amount: int = 1) -> bool
```
Scrolls the mouse wheel at the specified coordinates.
**Arguments**:
- `x` _int_ - The x coordinate to scroll at.
- `y` _int_ - The y coordinate to scroll at.
- `direction` _str_ - The direction to scroll ('up' or 'down').
- `amount` _int_ - The amount to scroll.
**Returns**:
- `bool` - Whether the scroll operation was successful.
**Example**:
```python
# Scroll up
scroll_up = await sandbox.computer_use.mouse.scroll(100, 200, "up", 3)
# Scroll down
scroll_down = await sandbox.computer_use.mouse.scroll(100, 200, "down", 5)
```
## AsyncKeyboard
```python
class AsyncKeyboard()
```
Keyboard operations for computer use functionality.
#### AsyncKeyboard.type
```python
@intercept_errors(message_prefix="Failed to type text: ")
async def type(text: str, delay: Optional[int] = None) -> None
```
Types the specified text.
**Arguments**:
- `text` _str_ - The text to type.
- `delay` _int_ - Delay between characters in milliseconds.
**Raises**:
- `DaytonaError` - If the type operation fails.
**Example**:
```python
try:
await sandbox.computer_use.keyboard.type("Hello, World!")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# With delay between characters
try:
await sandbox.computer_use.keyboard.type("Slow typing", 100)
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
```
#### AsyncKeyboard.press
```python
@intercept_errors(message_prefix="Failed to press key: ")
async def press(key: str, modifiers: Optional[List[str]] = None) -> None
```
Presses a key with optional modifiers.
**Arguments**:
- `key` _str_ - The key to press (e.g., 'Enter', 'Escape', 'Tab', 'a', 'A').
- `modifiers` _List[str]_ - Modifier keys ('ctrl', 'alt', 'meta', 'shift').
**Raises**:
- `DaytonaError` - If the press operation fails.
**Example**:
```python
# Press Enter
try:
await sandbox.computer_use.keyboard.press("Return")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# Press Ctrl+C
try:
await sandbox.computer_use.keyboard.press("c", ["ctrl"])
print(f"Operation success")
# Press Ctrl+Shift+T
try:
await sandbox.computer_use.keyboard.press("t", ["ctrl", "shift"])
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
```
#### AsyncKeyboard.hotkey
```python
@intercept_errors(message_prefix="Failed to press hotkey: ")
async def hotkey(keys: str) -> None
```
Presses a hotkey combination.
**Arguments**:
- `keys` _str_ - The hotkey combination (e.g., 'ctrl+c', 'alt+tab', 'cmd+shift+t').
**Raises**:
- `DaytonaError` - If the hotkey operation fails.
**Example**:
```python
# Copy
try:
await sandbox.computer_use.keyboard.hotkey("ctrl+c")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# Paste
try:
await sandbox.computer_use.keyboard.hotkey("ctrl+v")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# Alt+Tab
try:
await sandbox.computer_use.keyboard.hotkey("alt+tab")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
```
## AsyncScreenshot
```python
class AsyncScreenshot()
```
Screenshot operations for computer use functionality.
#### AsyncScreenshot.take\_full\_screen
```python
@intercept_errors(message_prefix="Failed to take screenshot: ")
async def take_full_screen(show_cursor: bool = False) -> ScreenshotResponse
```
Takes a screenshot of the entire screen.
**Arguments**:
- `show_cursor` _bool_ - Whether to show the cursor in the screenshot.
**Returns**:
- `ScreenshotResponse` - Screenshot data with base64 encoded image.
**Example**:
```python
screenshot = await sandbox.computer_use.screenshot.take_full_screen()
print(f"Screenshot size: {screenshot.width}x{screenshot.height}")
# With cursor visible
with_cursor = await sandbox.computer_use.screenshot.take_full_screen(True)
```
#### AsyncScreenshot.take\_region
```python
@intercept_errors(message_prefix="Failed to take region screenshot: ")
async def take_region(region: ScreenshotRegion,
show_cursor: bool = False) -> RegionScreenshotResponse
```
Takes a screenshot of a specific region.
**Arguments**:
- `region` _ScreenshotRegion_ - The region to capture.
- `show_cursor` _bool_ - Whether to show the cursor in the screenshot.
**Returns**:
- `RegionScreenshotResponse` - Screenshot data with base64 encoded image.
**Example**:
```python
region = ScreenshotRegion(x=100, y=100, width=300, height=200)
screenshot = await sandbox.computer_use.screenshot.take_region(region)
print(f"Captured region: {screenshot.region.width}x{screenshot.region.height}")
```
#### AsyncScreenshot.take\_compressed
```python
@intercept_errors(message_prefix="Failed to take compressed screenshot: ")
async def take_compressed(
options: Optional[ScreenshotOptions] = None
) -> CompressedScreenshotResponse
```
Takes a compressed screenshot of the entire screen.
**Arguments**:
- `options` _ScreenshotOptions_ - Compression and display options.
**Returns**:
- `CompressedScreenshotResponse` - Compressed screenshot data.
**Example**:
```python
# Default compression
screenshot = await sandbox.computer_use.screenshot.take_compressed()
# High quality JPEG
jpeg = await sandbox.computer_use.screenshot.take_compressed(
ScreenshotOptions(format="jpeg", quality=95, show_cursor=True)
)
# Scaled down PNG
scaled = await sandbox.computer_use.screenshot.take_compressed(
ScreenshotOptions(format="png", scale=0.5)
)
```
#### AsyncScreenshot.take\_compressed\_region
```python
@intercept_errors(
message_prefix="Failed to take compressed region screenshot: ")
async def take_compressed_region(
region: ScreenshotRegion,
options: Optional[ScreenshotOptions] = None
) -> CompressedScreenshotResponse
```
Takes a compressed screenshot of a specific region.
**Arguments**:
- `region` _ScreenshotRegion_ - The region to capture.
- `options` _ScreenshotOptions_ - Compression and display options.
**Returns**:
- `CompressedScreenshotResponse` - Compressed screenshot data.
**Example**:
```python
region = ScreenshotRegion(x=0, y=0, width=800, height=600)
screenshot = await sandbox.computer_use.screenshot.take_compressed_region(
region,
ScreenshotOptions(format="webp", quality=80, show_cursor=True)
)
print(f"Compressed size: {screenshot.size_bytes} bytes")
```
## AsyncDisplay
```python
class AsyncDisplay()
```
Display operations for computer use functionality.
#### AsyncDisplay.get\_info
```python
@intercept_errors(message_prefix="Failed to get display info: ")
async def get_info() -> DisplayInfoResponse
```
Gets information about the displays.
**Returns**:
- `DisplayInfoResponse` - Display information including primary display and all available displays.
**Example**:
```python
info = await sandbox.computer_use.display.get_info()
print(f"Primary display: {info.primary_display.width}x{info.primary_display.height}")
print(f"Total displays: {info.total_displays}")
for i, display in enumerate(info.displays):
print(f"Display {i}: {display.width}x{display.height} at {display.x},{display.y}")
```
#### AsyncDisplay.get\_windows
```python
@intercept_errors(message_prefix="Failed to get windows: ")
async def get_windows() -> WindowsResponse
```
Gets the list of open windows.
**Returns**:
- `WindowsResponse` - List of open windows with their IDs and titles.
**Example**:
```python
windows = await sandbox.computer_use.display.get_windows()
print(f"Found {windows.count} open windows:")
for window in windows.windows:
print(f"- {window.title} (ID: {window.id})")
```
## ScreenshotRegion
```python
class ScreenshotRegion()
```
Region coordinates for screenshot operations.
**Attributes**:
- `x` _int_ - X coordinate of the region.
- `y` _int_ - Y coordinate of the region.
- `width` _int_ - Width of the region.
- `height` _int_ - Height of the region.
## ScreenshotOptions
```python
class ScreenshotOptions()
```
Options for screenshot compression and display.
**Attributes**:
- `show_cursor` _bool_ - Whether to show the cursor in the screenshot.
- `fmt` _str_ - Image format (e.g., 'png', 'jpeg', 'webp').
- `quality` _int_ - Compression quality (0-100).
- `scale` _float_ - Scale factor for the screenshot.
@@ -16,6 +16,7 @@ Represents a Daytona Sandbox.
- `fs` _AsyncFileSystem_ - File system operations interface.
- `git` _AsyncGit_ - Git operations interface.
- `process` _AsyncProcess_ - Process execution interface.
- `computer_use` _AsyncComputerUse_ - Computer use operations interface for desktop automation.
- `id` _str_ - Unique identifier for the Sandbox.
- `organization_id` _str_ - Organization ID of the Sandbox.
- `snapshot` _str_ - Daytona snapshot used to create the Sandbox.
@@ -0,0 +1,707 @@
---
title: "ComputerUse"
hideTitleOnPage: true
---
## ComputerUse
```python
class ComputerUse()
```
Computer Use functionality for interacting with the desktop environment.
Provides access to mouse, keyboard, screenshot, and display operations
for automating desktop interactions within a sandbox.
**Attributes**:
- `mouse` _Mouse_ - Mouse operations interface.
- `keyboard` _Keyboard_ - Keyboard operations interface.
- `screenshot` _Screenshot_ - Screenshot operations interface.
- `display` _Display_ - Display operations interface.
#### ComputerUse.start
```python
@intercept_errors(message_prefix="Failed to start computer use: ")
def start() -> ComputerUseStartResponse
```
Starts all computer use processes (Xvfb, xfce4, x11vnc, novnc).
**Returns**:
- `ComputerUseStartResponse` - Computer use start response.
**Example**:
```python
result = sandbox.computer_use.start()
print("Computer use processes started:", result.message)
```
#### ComputerUse.stop
```python
@intercept_errors(message_prefix="Failed to stop computer use: ")
def stop() -> ComputerUseStopResponse
```
Stops all computer use processes.
**Returns**:
- `ComputerUseStopResponse` - Computer use stop response.
**Example**:
```python
result = sandbox.computer_use.stop()
print("Computer use processes stopped:", result.message)
```
#### ComputerUse.get\_status
```python
@intercept_errors(message_prefix="Failed to get computer use status: ")
def get_status() -> ComputerUseStatusResponse
```
Gets the status of all computer use processes.
**Returns**:
- `ComputerUseStatusResponse` - Status information about all VNC desktop processes.
**Example**:
```python
response = sandbox.computer_use.get_status()
print("Computer use status:", response.status)
```
#### ComputerUse.get\_process\_status
```python
@intercept_errors(message_prefix="Failed to get process status: ")
def get_process_status(process_name: str) -> ProcessStatusResponse
```
Gets the status of a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to check.
**Returns**:
- `ProcessStatusResponse` - Status information about the specific process.
**Example**:
```python
xvfb_status = sandbox.computer_use.get_process_status("xvfb")
no_vnc_status = sandbox.computer_use.get_process_status("novnc")
```
#### ComputerUse.restart\_process
```python
@intercept_errors(message_prefix="Failed to restart process: ")
def restart_process(process_name: str) -> ProcessRestartResponse
```
Restarts a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to restart.
**Returns**:
- `ProcessRestartResponse` - Process restart response.
**Example**:
```python
result = sandbox.computer_use.restart_process("xfce4")
print("XFCE4 process restarted:", result.message)
```
#### ComputerUse.get\_process\_logs
```python
@intercept_errors(message_prefix="Failed to get process logs: ")
def get_process_logs(process_name: str) -> ProcessLogsResponse
```
Gets logs for a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to get logs for.
**Returns**:
- `ProcessLogsResponse` - Process logs.
**Example**:
```python
logs = sandbox.computer_use.get_process_logs("novnc")
print("NoVNC logs:", logs)
```
#### ComputerUse.get\_process\_errors
```python
@intercept_errors(message_prefix="Failed to get process errors: ")
def get_process_errors(process_name: str) -> ProcessErrorsResponse
```
Gets error logs for a specific VNC process.
**Arguments**:
- `process_name` _str_ - Name of the process to get error logs for.
**Returns**:
- `ProcessErrorsResponse` - Process error logs.
**Example**:
```python
errors = sandbox.computer_use.get_process_errors("x11vnc")
print("X11VNC errors:", errors)
```
## Mouse
```python
class Mouse()
```
Mouse operations for computer use functionality.
#### Mouse.get\_position
```python
@intercept_errors(message_prefix="Failed to get mouse position: ")
def get_position() -> MousePosition
```
Gets the current mouse cursor position.
**Returns**:
- `MousePosition` - Current mouse position with x and y coordinates.
**Example**:
```python
position = sandbox.computer_use.mouse.get_position()
print(f"Mouse is at: {position.x}, {position.y}")
```
#### Mouse.move
```python
@intercept_errors(message_prefix="Failed to move mouse: ")
def move(x: int, y: int) -> MouseMoveResponse
```
Moves the mouse cursor to the specified coordinates.
**Arguments**:
- `x` _int_ - The x coordinate to move to.
- `y` _int_ - The y coordinate to move to.
**Returns**:
- `MouseMoveResponse` - Move operation result.
**Example**:
```python
result = sandbox.computer_use.mouse.move(100, 200)
print(f"Mouse moved to: {result.x}, {result.y}")
```
#### Mouse.click
```python
@intercept_errors(message_prefix="Failed to click mouse: ")
def click(x: int,
y: int,
button: str = "left",
double: bool = False) -> MouseClickResponse
```
Clicks the mouse at the specified coordinates.
**Arguments**:
- `x` _int_ - The x coordinate to click at.
- `y` _int_ - The y coordinate to click at.
- `button` _str_ - The mouse button to click ('left', 'right', 'middle').
- `double` _bool_ - Whether to perform a double-click.
**Returns**:
- `MouseClickResponse` - Click operation result.
**Example**:
```python
# Single left click
result = sandbox.computer_use.mouse.click(100, 200)
# Double click
double_click = sandbox.computer_use.mouse.click(100, 200, "left", True)
# Right click
right_click = sandbox.computer_use.mouse.click(100, 200, "right")
```
#### Mouse.drag
```python
@intercept_errors(message_prefix="Failed to drag mouse: ")
def drag(start_x: int,
start_y: int,
end_x: int,
end_y: int,
button: str = "left") -> MouseDragResponse
```
Drags the mouse from start coordinates to end coordinates.
**Arguments**:
- `start_x` _int_ - The starting x coordinate.
- `start_y` _int_ - The starting y coordinate.
- `end_x` _int_ - The ending x coordinate.
- `end_y` _int_ - The ending y coordinate.
- `button` _str_ - The mouse button to use for dragging.
**Returns**:
- `MouseDragResponse` - Drag operation result.
**Example**:
```python
result = sandbox.computer_use.mouse.drag(50, 50, 150, 150)
print(f"Dragged from {result.from_x},{result.from_y} to {result.to_x},{result.to_y}")
```
#### Mouse.scroll
```python
@intercept_errors(message_prefix="Failed to scroll mouse: ")
def scroll(x: int, y: int, direction: str, amount: int = 1) -> bool
```
Scrolls the mouse wheel at the specified coordinates.
**Arguments**:
- `x` _int_ - The x coordinate to scroll at.
- `y` _int_ - The y coordinate to scroll at.
- `direction` _str_ - The direction to scroll ('up' or 'down').
- `amount` _int_ - The amount to scroll.
**Returns**:
- `bool` - Whether the scroll operation was successful.
**Example**:
```python
# Scroll up
scroll_up = sandbox.computer_use.mouse.scroll(100, 200, "up", 3)
# Scroll down
scroll_down = sandbox.computer_use.mouse.scroll(100, 200, "down", 5)
```
## Keyboard
```python
class Keyboard()
```
Keyboard operations for computer use functionality.
#### Keyboard.type
```python
@intercept_errors(message_prefix="Failed to type text: ")
def type(text: str, delay: Optional[int] = None) -> None
```
Types the specified text.
**Arguments**:
- `text` _str_ - The text to type.
- `delay` _int_ - Delay between characters in milliseconds.
**Raises**:
- `DaytonaError` - If the type operation fails.
**Example**:
```python
try:
sandbox.computer_use.keyboard.type("Hello, World!")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# With delay between characters
try:
sandbox.computer_use.keyboard.type("Slow typing", 100)
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
```
#### Keyboard.press
```python
@intercept_errors(message_prefix="Failed to press key: ")
def press(key: str, modifiers: Optional[List[str]] = None) -> None
```
Presses a key with optional modifiers.
**Arguments**:
- `key` _str_ - The key to press (e.g., 'Enter', 'Escape', 'Tab', 'a', 'A').
- `modifiers` _List[str]_ - Modifier keys ('ctrl', 'alt', 'meta', 'shift').
**Raises**:
- `DaytonaError` - If the press operation fails.
**Example**:
```python
# Press Enter
try:
sandbox.computer_use.keyboard.press("Return")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# Press Ctrl+C
try:
sandbox.computer_use.keyboard.press("c", ["ctrl"])
print(f"Operation success")
# Press Ctrl+Shift+T
try:
sandbox.computer_use.keyboard.press("t", ["ctrl", "shift"])
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
```
#### Keyboard.hotkey
```python
@intercept_errors(message_prefix="Failed to press hotkey: ")
def hotkey(keys: str) -> None
```
Presses a hotkey combination.
**Arguments**:
- `keys` _str_ - The hotkey combination (e.g., 'ctrl+c', 'alt+tab', 'cmd+shift+t').
**Raises**:
- `DaytonaError` - If the hotkey operation fails.
**Example**:
```python
# Copy
try:
sandbox.computer_use.keyboard.hotkey("ctrl+c")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# Paste
try:
sandbox.computer_use.keyboard.hotkey("ctrl+v")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
# Alt+Tab
try:
sandbox.computer_use.keyboard.hotkey("alt+tab")
print(f"Operation success")
except Exception as e:
print(f"Operation failed: {e}")
```
## Screenshot
```python
class Screenshot()
```
Screenshot operations for computer use functionality.
#### Screenshot.take\_full\_screen
```python
@intercept_errors(message_prefix="Failed to take screenshot: ")
def take_full_screen(show_cursor: bool = False) -> ScreenshotResponse
```
Takes a screenshot of the entire screen.
**Arguments**:
- `show_cursor` _bool_ - Whether to show the cursor in the screenshot.
**Returns**:
- `ScreenshotResponse` - Screenshot data with base64 encoded image.
**Example**:
```python
screenshot = sandbox.computer_use.screenshot.take_full_screen()
print(f"Screenshot size: {screenshot.width}x{screenshot.height}")
# With cursor visible
with_cursor = sandbox.computer_use.screenshot.take_full_screen(True)
```
#### Screenshot.take\_region
```python
@intercept_errors(message_prefix="Failed to take region screenshot: ")
def take_region(region: ScreenshotRegion,
show_cursor: bool = False) -> RegionScreenshotResponse
```
Takes a screenshot of a specific region.
**Arguments**:
- `region` _ScreenshotRegion_ - The region to capture.
- `show_cursor` _bool_ - Whether to show the cursor in the screenshot.
**Returns**:
- `RegionScreenshotResponse` - Screenshot data with base64 encoded image.
**Example**:
```python
region = ScreenshotRegion(x=100, y=100, width=300, height=200)
screenshot = sandbox.computer_use.screenshot.take_region(region)
print(f"Captured region: {screenshot.region.width}x{screenshot.region.height}")
```
#### Screenshot.take\_compressed
```python
@intercept_errors(message_prefix="Failed to take compressed screenshot: ")
def take_compressed(
options: Optional[ScreenshotOptions] = None
) -> CompressedScreenshotResponse
```
Takes a compressed screenshot of the entire screen.
**Arguments**:
- `options` _ScreenshotOptions_ - Compression and display options.
**Returns**:
- `CompressedScreenshotResponse` - Compressed screenshot data.
**Example**:
```python
# Default compression
screenshot = sandbox.computer_use.screenshot.take_compressed()
# High quality JPEG
jpeg = sandbox.computer_use.screenshot.take_compressed(
ScreenshotOptions(format="jpeg", quality=95, show_cursor=True)
)
# Scaled down PNG
scaled = sandbox.computer_use.screenshot.take_compressed(
ScreenshotOptions(format="png", scale=0.5)
)
```
#### Screenshot.take\_compressed\_region
```python
@intercept_errors(
message_prefix="Failed to take compressed region screenshot: ")
def take_compressed_region(
region: ScreenshotRegion,
options: Optional[ScreenshotOptions] = None
) -> CompressedScreenshotResponse
```
Takes a compressed screenshot of a specific region.
**Arguments**:
- `region` _ScreenshotRegion_ - The region to capture.
- `options` _ScreenshotOptions_ - Compression and display options.
**Returns**:
- `CompressedScreenshotResponse` - Compressed screenshot data.
**Example**:
```python
region = ScreenshotRegion(x=0, y=0, width=800, height=600)
screenshot = sandbox.computer_use.screenshot.take_compressed_region(
region,
ScreenshotOptions(format="webp", quality=80, show_cursor=True)
)
print(f"Compressed size: {screenshot.size_bytes} bytes")
```
## Display
```python
class Display()
```
Display operations for computer use functionality.
#### Display.get\_info
```python
@intercept_errors(message_prefix="Failed to get display info: ")
def get_info() -> DisplayInfoResponse
```
Gets information about the displays.
**Returns**:
- `DisplayInfoResponse` - Display information including primary display and all available displays.
**Example**:
```python
info = sandbox.computer_use.display.get_info()
print(f"Primary display: {info.primary_display.width}x{info.primary_display.height}")
print(f"Total displays: {info.total_displays}")
for i, display in enumerate(info.displays):
print(f"Display {i}: {display.width}x{display.height} at {display.x},{display.y}")
```
#### Display.get\_windows
```python
@intercept_errors(message_prefix="Failed to get windows: ")
def get_windows() -> WindowsResponse
```
Gets the list of open windows.
**Returns**:
- `WindowsResponse` - List of open windows with their IDs and titles.
**Example**:
```python
windows = sandbox.computer_use.display.get_windows()
print(f"Found {windows.count} open windows:")
for window in windows.windows:
print(f"- {window.title} (ID: {window.id})")
```
## ScreenshotRegion
```python
class ScreenshotRegion()
```
Region coordinates for screenshot operations.
**Attributes**:
- `x` _int_ - X coordinate of the region.
- `y` _int_ - Y coordinate of the region.
- `width` _int_ - Width of the region.
- `height` _int_ - Height of the region.
## ScreenshotOptions
```python
class ScreenshotOptions()
```
Options for screenshot compression and display.
**Attributes**:
- `show_cursor` _bool_ - Whether to show the cursor in the screenshot.
- `fmt` _str_ - Image format (e.g., 'png', 'jpeg', 'webp').
- `quality` _int_ - Compression quality (0-100).
- `scale` _float_ - Scale factor for the screenshot.
@@ -16,6 +16,7 @@ Represents a Daytona Sandbox.
- `fs` _FileSystem_ - File system operations interface.
- `git` _Git_ - Git operations interface.
- `process` _Process_ - Process execution interface.
- `computer_use` _ComputerUse_ - Computer use operations interface for desktop automation.
- `id` _str_ - Unique identifier for the Sandbox.
- `organization_id` _str_ - Organization ID of the Sandbox.
- `snapshot` _str_ - Daytona snapshot used to create the Sandbox.
@@ -0,0 +1,809 @@
---
title: "ComputerUse"
hideTitleOnPage: true
---
## ComputerUse
Computer Use functionality for interacting with the desktop environment.
**Properties**:
- `display` _Display_ - Display operations interface
- `keyboard` _Keyboard_ - Keyboard operations interface
- `mouse` _Mouse_ - Mouse operations interface
- `screenshot` _Screenshot_ - Screenshot operations interface
Provides access to mouse, keyboard, screenshot, and display operations
for automating desktop interactions within a sandbox.
### Constructors
#### new ComputerUse()
```ts
new ComputerUse(sandboxId: string, toolboxApi: ToolboxApi): ComputerUse
```
**Parameters**:
- `sandboxId` _string_
- `toolboxApi` _ToolboxApi_
**Returns**:
- `ComputerUse`
### Methods
#### getProcessErrors()
```ts
getProcessErrors(processName: string): Promise<ProcessErrorsResponse>
```
Gets error logs for a specific VNC process
**Parameters**:
- `processName` _string_ - Name of the process to get error logs for
**Returns**:
- `Promise<ProcessErrorsResponse>` - Process error logs
**Example:**
```typescript
const errorsResp = await sandbox.computerUse.getProcessErrors('x11vnc');
console.log('X11VNC errors:', errorsResp.errors);
```
***
#### getProcessLogs()
```ts
getProcessLogs(processName: string): Promise<ProcessLogsResponse>
```
Gets logs for a specific VNC process
**Parameters**:
- `processName` _string_ - Name of the process to get logs for
**Returns**:
- `Promise<ProcessLogsResponse>` - Process logs
**Example:**
```typescript
const logsResp = await sandbox.computerUse.getProcessLogs('novnc');
console.log('NoVNC logs:', logsResp.logs);
```
***
#### getProcessStatus()
```ts
getProcessStatus(processName: string): Promise<ProcessStatusResponse>
```
Gets the status of a specific VNC process
**Parameters**:
- `processName` _string_ - Name of the process to check
**Returns**:
- `Promise<ProcessStatusResponse>` - Status information about the specific process
**Example:**
```typescript
const xvfbStatus = await sandbox.computerUse.getProcessStatus('xvfb');
const noVncStatus = await sandbox.computerUse.getProcessStatus('novnc');
```
***
#### getStatus()
```ts
getStatus(): Promise<ComputerUseStatusResponse>
```
Gets the status of all computer use processes
**Returns**:
- `Promise<ComputerUseStatusResponse>` - Status information about all VNC desktop processes
**Example:**
```typescript
const status = await sandbox.computerUse.getStatus();
console.log('Computer use status:', status.status);
```
***
#### restartProcess()
```ts
restartProcess(processName: string): Promise<ProcessRestartResponse>
```
Restarts a specific VNC process
**Parameters**:
- `processName` _string_ - Name of the process to restart
**Returns**:
- `Promise<ProcessRestartResponse>` - Process restart response
**Example:**
```typescript
const result = await sandbox.computerUse.restartProcess('xfce4');
console.log('XFCE4 process restarted:', result.message);
```
***
#### start()
```ts
start(): Promise<ComputerUseStartResponse>
```
Starts all computer use processes (Xvfb, xfce4, x11vnc, novnc)
**Returns**:
- `Promise<ComputerUseStartResponse>` - Computer use start response
**Example:**
```typescript
const result = await sandbox.computerUse.start();
console.log('Computer use processes started:', result.message);
```
***
#### stop()
```ts
stop(): Promise<ComputerUseStopResponse>
```
Stops all computer use processes
**Returns**:
- `Promise<ComputerUseStopResponse>` - Computer use stop response
**Example:**
```typescript
const result = await sandbox.computerUse.stop();
console.log('Computer use processes stopped:', result.message);
```
## Display
Display operations for computer use functionality
### Constructors
#### new Display()
```ts
new Display(sandboxId: string, toolboxApi: ToolboxApi): Display
```
**Parameters**:
- `sandboxId` _string_
- `toolboxApi` _ToolboxApi_
**Returns**:
- `Display`
### Methods
#### getInfo()
```ts
getInfo(): Promise<DisplayInfoResponse>
```
Gets information about the displays
**Returns**:
- `Promise<DisplayInfoResponse>` - Display information including primary display and all available displays
**Example:**
```typescript
const info = await sandbox.computerUse.display.getInfo();
console.log(`Primary display: ${info.primary_display.width}x${info.primary_display.height}`);
console.log(`Total displays: ${info.total_displays}`);
info.displays.forEach((display, index) => {
console.log(`Display ${index}: ${display.width}x${display.height} at ${display.x},${display.y}`);
});
```
***
#### getWindows()
```ts
getWindows(): Promise<WindowsResponse>
```
Gets the list of open windows
**Returns**:
- `Promise<WindowsResponse>` - List of open windows with their IDs and titles
**Example:**
```typescript
const windows = await sandbox.computerUse.display.getWindows();
console.log(`Found ${windows.count} open windows:`);
windows.windows.forEach(window => {
console.log(`- ${window.title} (ID: ${window.id})`);
});
```
***
## Keyboard
Keyboard operations for computer use functionality
### Constructors
#### new Keyboard()
```ts
new Keyboard(sandboxId: string, toolboxApi: ToolboxApi): Keyboard
```
**Parameters**:
- `sandboxId` _string_
- `toolboxApi` _ToolboxApi_
**Returns**:
- `Keyboard`
### Methods
#### hotkey()
```ts
hotkey(keys: string): Promise<void>
```
Presses a hotkey combination
**Parameters**:
- `keys` _string_ - The hotkey combination (e.g., 'ctrl+c', 'alt+tab', 'cmd+shift+t')
**Returns**:
- `Promise<void>`
**Throws**:
If the hotkey operation fails
**Example:**
```typescript
// Copy
try {
await sandbox.computerUse.keyboard.hotkey('ctrl+c');
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
// Paste
try {
await sandbox.computerUse.keyboard.hotkey('ctrl+v');
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
// Alt+Tab
try {
await sandbox.computerUse.keyboard.hotkey('alt+tab');
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
```
***
#### press()
```ts
press(key: string, modifiers?: string[]): Promise<void>
```
Presses a key with optional modifiers
**Parameters**:
- `key` _string_ - The key to press (e.g., 'Enter', 'Escape', 'Tab', 'a', 'A')
- `modifiers?` _string\[\] = \[\]_ - Modifier keys ('ctrl', 'alt', 'meta', 'shift')
**Returns**:
- `Promise<void>`
**Throws**:
If the press operation fails
**Example:**
```typescript
// Press Enter
try {
await sandbox.computerUse.keyboard.press('Return');
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
// Press Ctrl+C
try {
await sandbox.computerUse.keyboard.press('c', ['ctrl']);
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
// Press Ctrl+Shift+T
try {
await sandbox.computerUse.keyboard.press('t', ['ctrl', 'shift']);
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
```
***
#### type()
```ts
type(text: string, delay?: number): Promise<void>
```
Types the specified text
**Parameters**:
- `text` _string_ - The text to type
- `delay?` _number_ - Delay between characters in milliseconds
**Returns**:
- `Promise<void>`
**Throws**:
If the type operation fails
**Example:**
```typescript
try {
await sandbox.computerUse.keyboard.type('Hello, World!');
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
// With delay between characters
try {
await sandbox.computerUse.keyboard.type('Slow typing', 100);
console.log('Operation success');
} catch (e) {
console.log('Operation failed:', e);
}
```
***
## Mouse
Mouse operations for computer use functionality
### Constructors
#### new Mouse()
```ts
new Mouse(sandboxId: string, toolboxApi: ToolboxApi): Mouse
```
**Parameters**:
- `sandboxId` _string_
- `toolboxApi` _ToolboxApi_
**Returns**:
- `Mouse`
### Methods
#### click()
```ts
click(
x: number,
y: number,
button?: string,
double?: boolean): Promise<MouseClickResponse>
```
Clicks the mouse at the specified coordinates
**Parameters**:
- `x` _number_ - The x coordinate to click at
- `y` _number_ - The y coordinate to click at
- `button?` _string = 'left'_ - The mouse button to click ('left', 'right', 'middle')
- `double?` _boolean = false_ - Whether to perform a double-click
**Returns**:
- `Promise<MouseClickResponse>` - Click operation result
**Example:**
```typescript
// Single left click
const result = await sandbox.computerUse.mouse.click(100, 200);
// Double click
const doubleClick = await sandbox.computerUse.mouse.click(100, 200, 'left', true);
// Right click
const rightClick = await sandbox.computerUse.mouse.click(100, 200, 'right');
```
***
#### drag()
```ts
drag(
startX: number,
startY: number,
endX: number,
endY: number,
button?: string): Promise<MouseDragResponse>
```
Drags the mouse from start coordinates to end coordinates
**Parameters**:
- `startX` _number_ - The starting x coordinate
- `startY` _number_ - The starting y coordinate
- `endX` _number_ - The ending x coordinate
- `endY` _number_ - The ending y coordinate
- `button?` _string = 'left'_ - The mouse button to use for dragging
**Returns**:
- `Promise<MouseDragResponse>` - Drag operation result
**Example:**
```typescript
const result = await sandbox.computerUse.mouse.drag(50, 50, 150, 150);
console.log(`Dragged from ${result.from.x},${result.from.y} to ${result.to.x},${result.to.y}`);
```
***
#### getPosition()
```ts
getPosition(): Promise<MousePosition>
```
Gets the current mouse cursor position
**Returns**:
- `Promise<MousePosition>` - Current mouse position with x and y coordinates
**Example:**
```typescript
const position = await sandbox.computerUse.mouse.getPosition();
console.log(`Mouse is at: ${position.x}, ${position.y}`);
```
***
#### move()
```ts
move(x: number, y: number): Promise<MouseMoveResponse>
```
Moves the mouse cursor to the specified coordinates
**Parameters**:
- `x` _number_ - The x coordinate to move to
- `y` _number_ - The y coordinate to move to
**Returns**:
- `Promise<MouseMoveResponse>` - Move operation result
**Example:**
```typescript
const result = await sandbox.computerUse.mouse.move(100, 200);
console.log(`Mouse moved to: ${result.x}, ${result.y}`);
```
***
#### scroll()
```ts
scroll(
x: number,
y: number,
direction: "up" | "down",
amount?: number): Promise<boolean>
```
Scrolls the mouse wheel at the specified coordinates
**Parameters**:
- `x` _number_ - The x coordinate to scroll at
- `y` _number_ - The y coordinate to scroll at
- `direction` _The direction to scroll_ - `"up"` | `"down"`
- `amount?` _number = 1_ - The amount to scroll
**Returns**:
- `Promise<boolean>` - Whether the scroll operation was successful
**Example:**
```typescript
// Scroll up
const scrollUp = await sandbox.computerUse.mouse.scroll(100, 200, 'up', 3);
// Scroll down
const scrollDown = await sandbox.computerUse.mouse.scroll(100, 200, 'down', 5);
```
***
## Screenshot
Screenshot operations for computer use functionality
### Constructors
#### new Screenshot()
```ts
new Screenshot(sandboxId: string, toolboxApi: ToolboxApi): Screenshot
```
**Parameters**:
- `sandboxId` _string_
- `toolboxApi` _ToolboxApi_
**Returns**:
- `Screenshot`
### Methods
#### takeCompressed()
```ts
takeCompressed(options?: ScreenshotOptions): Promise<CompressedScreenshotResponse>
```
Takes a compressed screenshot of the entire screen
**Parameters**:
- `options?` _ScreenshotOptions = {}_ - Compression and display options
**Returns**:
- `Promise<CompressedScreenshotResponse>` - Compressed screenshot data
**Example:**
```typescript
// Default compression
const screenshot = await sandbox.computerUse.screenshot.takeCompressed();
// High quality JPEG
const jpeg = await sandbox.computerUse.screenshot.takeCompressed({
format: 'jpeg',
quality: 95,
showCursor: true
});
// Scaled down PNG
const scaled = await sandbox.computerUse.screenshot.takeCompressed({
format: 'png',
scale: 0.5
});
```
***
#### takeCompressedRegion()
```ts
takeCompressedRegion(region: ScreenshotRegion, options?: ScreenshotOptions): Promise<CompressedScreenshotResponse>
```
Takes a compressed screenshot of a specific region
**Parameters**:
- `region` _ScreenshotRegion_ - The region to capture
- `options?` _ScreenshotOptions = {}_ - Compression and display options
**Returns**:
- `Promise<CompressedScreenshotResponse>` - Compressed screenshot data
**Example:**
```typescript
const region = { x: 0, y: 0, width: 800, height: 600 };
const screenshot = await sandbox.computerUse.screenshot.takeCompressedRegion(region, {
format: 'webp',
quality: 80,
showCursor: true
});
console.log(`Compressed size: ${screenshot.size_bytes} bytes`);
```
***
#### takeFullScreen()
```ts
takeFullScreen(showCursor?: boolean): Promise<ScreenshotResponse>
```
Takes a screenshot of the entire screen
**Parameters**:
- `showCursor?` _boolean = false_ - Whether to show the cursor in the screenshot
**Returns**:
- `Promise<ScreenshotResponse>` - Screenshot data with base64 encoded image
**Example:**
```typescript
const screenshot = await sandbox.computerUse.screenshot.takeFullScreen();
console.log(`Screenshot size: ${screenshot.width}x${screenshot.height}`);
// With cursor visible
const withCursor = await sandbox.computerUse.screenshot.takeFullScreen(true);
```
***
#### takeRegion()
```ts
takeRegion(region: ScreenshotRegion, showCursor?: boolean): Promise<RegionScreenshotResponse>
```
Takes a screenshot of a specific region
**Parameters**:
- `region` _ScreenshotRegion_ - The region to capture
- `showCursor?` _boolean = false_ - Whether to show the cursor in the screenshot
**Returns**:
- `Promise<RegionScreenshotResponse>` - Screenshot data with base64 encoded image
**Example:**
```typescript
const region = { x: 100, y: 100, width: 300, height: 200 };
const screenshot = await sandbox.computerUse.screenshot.takeRegion(region);
console.log(`Captured region: ${screenshot.region.width}x${screenshot.region.height}`);
```
***
## ScreenshotOptions
Interface for screenshot compression options
**Properties**:
- `format?` _string_
- `quality?` _number_
- `scale?` _number_
- `showCursor?` _boolean_
## ScreenshotRegion
Interface for region coordinates used in screenshot operations
**Properties**:
- `height` _number_
- `width` _number_
- `x` _number_
- `y` _number_
@@ -45,6 +45,7 @@ Represents a Daytona Sandbox.
```ts
SandboxDto.buildInfo
```
- `computerUse` _ComputerUse_ - Computer use operations interface for desktop automation
- `cpu` _number_ - Number of CPUs allocated to the Sandbox
##### Implementation of
+4 -1
View File
@@ -16,5 +16,8 @@ func main() {
log.Fatal(err)
}
proxy.StartProxy(config)
err = proxy.StartProxy(config)
if err != nil {
log.Fatal(err)
}
}
+19 -16
View File
@@ -2,28 +2,34 @@ module github.com/daytonaio/proxy
go 1.23.0
require github.com/gin-gonic/gin v1.10.1
require (
github.com/coreos/go-oidc/v3 v3.12.0
github.com/gin-contrib/cors v1.7.5
github.com/gin-gonic/gin v1.10.1
github.com/go-playground/validator/v10 v10.26.0
github.com/gorilla/securecookie v1.1.2
github.com/joho/godotenv v1.5.1
github.com/kelseyhightower/envconfig v1.4.0
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/redis/go-redis/v9 v9.10.0
github.com/sirupsen/logrus v1.9.3
golang.org/x/oauth2 v0.25.0
)
require (
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/cors v1.7.5 // indirect
github.com/gin-contrib/sessions v1.0.4 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.4.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/pretty v0.3.1 // indirect
@@ -33,17 +39,14 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/redis/go-redis/v9 v9.10.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.16.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+15 -37
View File
@@ -1,18 +1,16 @@
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -24,45 +22,36 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@@ -74,6 +63,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -85,6 +75,7 @@ github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -101,27 +92,14 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+4 -4
View File
@@ -17,8 +17,8 @@ import (
"github.com/gin-gonic/gin"
"golang.org/x/oauth2"
"github.com/daytonaio/apiclient"
common_errors "github.com/daytonaio/common-go/pkg/errors"
"github.com/daytonaio/daytona/daytonaapiclient"
)
func (p *Proxy) AuthCallback(ctx *gin.Context) {
@@ -153,15 +153,15 @@ func (p *Proxy) getAuthUrl(ctx *gin.Context, sandboxId string) (string, error) {
}
func (p *Proxy) hasSandboxAccess(ctx context.Context, sandboxId string, authToken string) bool {
clientConfig := daytonaapiclient.NewConfiguration()
clientConfig.Servers = daytonaapiclient.ServerConfigurations{
clientConfig := apiclient.NewConfiguration()
clientConfig.Servers = apiclient.ServerConfigurations{
{
URL: p.config.DaytonaApiUrl,
},
}
clientConfig.AddDefaultHeader("Authorization", "Bearer "+authToken)
apiClient := daytonaapiclient.NewAPIClient(clientConfig)
apiClient := apiclient.NewAPIClient(clientConfig)
res, _ := apiClient.PreviewAPI.HasSandboxAccess(ctx, sandboxId).Execute()
+18 -7
View File
@@ -14,6 +14,8 @@ import (
common_errors "github.com/daytonaio/common-go/pkg/errors"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
func (p *Proxy) GetProxyTarget(ctx *gin.Context) (*url.URL, string, map[string]string, error) {
@@ -41,7 +43,7 @@ func (p *Proxy) GetProxyTarget(ctx *gin.Context) (*url.URL, string, map[string]s
return nil, "", nil, fmt.Errorf("failed to get sandbox public status: %w", err)
}
if !*isPublic || targetPort == "22222" {
if !*isPublic || targetPort == "22222" || targetPort == "6080" {
err, didRedirect := p.Authenticate(ctx, sandboxID)
if err != nil {
if !didRedirect {
@@ -96,7 +98,7 @@ func (p *Proxy) getRunnerInfo(ctx context.Context, sandboxId string) (*RunnerInf
return p.runnerCache.Get(ctx, sandboxId)
}
runner, _, err := p.daytonaApiClient.RunnersAPI.GetRunnerBySandboxId(context.Background(), sandboxId).Execute()
runner, _, err := p.apiclient.RunnersAPI.GetRunnerBySandboxId(context.Background(), sandboxId).Execute()
if err != nil {
return nil, err
}
@@ -106,7 +108,10 @@ func (p *Proxy) getRunnerInfo(ctx context.Context, sandboxId string) (*RunnerInf
ApiKey: runner.ApiKey,
}
p.runnerCache.Set(ctx, sandboxId, info, 1*time.Hour)
err = p.runnerCache.Set(ctx, sandboxId, info, 1*time.Hour)
if err != nil {
log.Errorf("Failed to set runner info in cache: %v", err)
}
return &info, nil
}
@@ -122,12 +127,15 @@ func (p *Proxy) getSandboxPublic(ctx context.Context, sandboxId string) (*bool,
}
isPublic := false
_, resp, _ := p.daytonaApiClient.PreviewAPI.IsSandboxPublic(context.Background(), sandboxId).Execute()
_, resp, _ := p.apiclient.PreviewAPI.IsSandboxPublic(context.Background(), sandboxId).Execute()
if resp != nil && resp.StatusCode == http.StatusOK {
isPublic = true
}
p.sandboxPublicCache.Set(ctx, sandboxId, isPublic, 2*time.Minute)
err = p.sandboxPublicCache.Set(ctx, sandboxId, isPublic, 2*time.Minute)
if err != nil {
log.Errorf("Failed to set sandbox public in cache: %v", err)
}
return &isPublic, nil
}
@@ -143,12 +151,15 @@ func (p *Proxy) getSandboxAuthKeyValid(ctx context.Context, sandboxId string, au
}
isValid := false
_, resp, _ := p.daytonaApiClient.PreviewAPI.IsValidAuthToken(context.Background(), sandboxId, authKey).Execute()
_, resp, _ := p.apiclient.PreviewAPI.IsValidAuthToken(context.Background(), sandboxId, authKey).Execute()
if resp != nil && resp.StatusCode == http.StatusOK {
isValid = true
}
p.sandboxAuthKeyValidCache.Set(ctx, authKey, isValid, 2*time.Minute)
err = p.sandboxAuthKeyValidCache.Set(ctx, authKey, isValid, 2*time.Minute)
if err != nil {
log.Errorf("Failed to set sandbox auth key valid in cache: %v", err)
}
return &isValid, nil
}
+6 -6
View File
@@ -11,7 +11,7 @@ import (
"net/http"
"slices"
"github.com/daytonaio/daytona/daytonaapiclient"
apiclient "github.com/daytonaio/apiclient"
"github.com/daytonaio/proxy/cmd/proxy/config"
"github.com/daytonaio/proxy/pkg/cache"
"github.com/gin-contrib/cors"
@@ -37,7 +37,7 @@ type Proxy struct {
config *config.Config
secureCookie *securecookie.SecureCookie
daytonaApiClient *daytonaapiclient.APIClient
apiclient *apiclient.APIClient
runnerCache cache.ICache[RunnerInfo]
sandboxPublicCache cache.ICache[bool]
sandboxAuthKeyValidCache cache.ICache[bool]
@@ -50,8 +50,8 @@ func StartProxy(config *config.Config) error {
proxy.secureCookie = securecookie.New([]byte(config.ProxyApiKey), nil)
clientConfig := daytonaapiclient.NewConfiguration()
clientConfig.Servers = daytonaapiclient.ServerConfigurations{
clientConfig := apiclient.NewConfiguration()
clientConfig.Servers = apiclient.ServerConfigurations{
{
URL: config.DaytonaApiUrl,
},
@@ -59,9 +59,9 @@ func StartProxy(config *config.Config) error {
clientConfig.AddDefaultHeader("Authorization", "Bearer "+config.ProxyApiKey)
proxy.daytonaApiClient = daytonaapiclient.NewAPIClient(clientConfig)
proxy.apiclient = apiclient.NewAPIClient(clientConfig)
proxy.daytonaApiClient.GetConfig().HTTPClient = &http.Client{
proxy.apiclient.GetConfig().HTTPClient = &http.Client{
Transport: http.DefaultTransport,
}
+16 -9
View File
@@ -61,21 +61,28 @@ func main() {
runnerCache.Cleanup(ctx)
daemonPath, err := daemon.WriteDaemonBinary()
daemonPath, err := daemon.WriteStaticBinary("daemon-amd64")
if err != nil {
log.Error(err)
return
}
pluginPath, err := daemon.WriteStaticBinary("daytona-computer-use")
if err != nil {
log.Error(err)
return
}
dockerClient := docker.NewDockerClient(docker.DockerClientConfig{
ApiClient: cli,
Cache: runnerCache,
LogWriter: os.Stdout,
AWSRegion: cfg.AWSRegion,
AWSEndpointUrl: cfg.AWSEndpointUrl,
AWSAccessKeyId: cfg.AWSAccessKeyId,
AWSSecretAccessKey: cfg.AWSSecretAccessKey,
DaemonPath: daemonPath,
ApiClient: cli,
Cache: runnerCache,
LogWriter: os.Stdout,
AWSRegion: cfg.AWSRegion,
AWSEndpointUrl: cfg.AWSEndpointUrl,
AWSAccessKeyId: cfg.AWSAccessKeyId,
AWSSecretAccessKey: cfg.AWSSecretAccessKey,
DaemonPath: daemonPath,
ComputerUsePluginPath: pluginPath,
})
sandboxService := services.NewSandboxService(runnerCache, dockerClient)
+6 -5
View File
@@ -48,6 +48,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -84,12 +85,12 @@ require (
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/arch v0.16.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.30.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
+8 -16
View File
@@ -75,8 +75,7 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -213,13 +212,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -227,14 +224,12 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -248,8 +243,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -257,8 +251,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -266,8 +259,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+4 -3
View File
@@ -4,12 +4,13 @@
package daemon
import (
"fmt"
"os"
"path/filepath"
)
func WriteDaemonBinary() (string, error) {
daemonBinary, err := static.ReadFile("static/daemon-amd64")
func WriteStaticBinary(name string) (string, error) {
daemonBinary, err := static.ReadFile(fmt.Sprintf("static/%s", name))
if err != nil {
return "", err
}
@@ -25,7 +26,7 @@ func WriteDaemonBinary() (string, error) {
return "", err
}
daemonPath := filepath.Join(tmpBinariesDir, "daemon-amd64")
daemonPath := filepath.Join(tmpBinariesDir, name)
_, err = os.Stat(daemonPath)
if err != nil && !os.IsNotExist(err) {
return "", err
+30 -27
View File
@@ -12,39 +12,42 @@ import (
)
type DockerClientConfig struct {
ApiClient client.APIClient
Cache cache.IRunnerCache
LogWriter io.Writer
AWSRegion string
AWSEndpointUrl string
AWSAccessKeyId string
AWSSecretAccessKey string
DaemonPath string
ApiClient client.APIClient
Cache cache.IRunnerCache
LogWriter io.Writer
AWSRegion string
AWSEndpointUrl string
AWSAccessKeyId string
AWSSecretAccessKey string
DaemonPath string
ComputerUsePluginPath string
}
func NewDockerClient(config DockerClientConfig) *DockerClient {
return &DockerClient{
apiClient: config.ApiClient,
cache: config.Cache,
logWriter: config.LogWriter,
awsRegion: config.AWSRegion,
awsEndpointUrl: config.AWSEndpointUrl,
awsAccessKeyId: config.AWSAccessKeyId,
awsSecretAccessKey: config.AWSSecretAccessKey,
volumeMutexes: make(map[string]*sync.Mutex),
daemonPath: config.DaemonPath,
apiClient: config.ApiClient,
cache: config.Cache,
logWriter: config.LogWriter,
awsRegion: config.AWSRegion,
awsEndpointUrl: config.AWSEndpointUrl,
awsAccessKeyId: config.AWSAccessKeyId,
awsSecretAccessKey: config.AWSSecretAccessKey,
volumeMutexes: make(map[string]*sync.Mutex),
daemonPath: config.DaemonPath,
computerUsePluginPath: config.ComputerUsePluginPath,
}
}
type DockerClient struct {
apiClient client.APIClient
cache cache.IRunnerCache
logWriter io.Writer
awsRegion string
awsEndpointUrl string
awsAccessKeyId string
awsSecretAccessKey string
volumeMutexes map[string]*sync.Mutex
volumeMutexesMutex sync.Mutex
daemonPath string
apiClient client.APIClient
cache cache.IRunnerCache
logWriter io.Writer
awsRegion string
awsEndpointUrl string
awsAccessKeyId string
awsSecretAccessKey string
volumeMutexes map[string]*sync.Mutex
volumeMutexesMutex sync.Mutex
daemonPath string
computerUsePluginPath string
}
@@ -54,6 +54,11 @@ func (d *DockerClient) getContainerHostConfig(ctx context.Context, sandboxDto dt
binds = append(binds, fmt.Sprintf("%s:/usr/local/bin/daytona:ro", d.daemonPath))
// Mount the plugin if available
if d.computerUsePluginPath != "" {
binds = append(binds, fmt.Sprintf("%s:/usr/local/lib/daytona-computer-use:ro", d.computerUsePluginPath))
}
if len(volumeMountPathBinds) > 0 {
binds = append(binds, volumeMountPathBinds...)
}
+30 -15
View File
@@ -8,15 +8,27 @@
"copy-daemon-bin": {
"executor": "nx:run-commands",
"options": {
"command": "cp dist/apps/daemon-amd64 {projectRoot}/pkg/daemon/static"
"command": "cp dist/apps/daemon-amd64 {projectRoot}/pkg/daemon/static/daemon-amd64"
},
"dependsOn": [
{
"target": "build",
"target": "build-amd64",
"projects": "daemon"
}
]
},
"copy-computeruse-plugin": {
"executor": "nx:run-commands",
"options": {
"command": "cp dist/libs/computer-use-amd64 {projectRoot}/pkg/daemon/static/daytona-computer-use"
},
"dependsOn": [
{
"target": "build-amd64",
"projects": "computer-use"
}
]
},
"build": {
"executor": "@nx-go/nx-go:build",
"options": {
@@ -26,17 +38,18 @@
"configurations": {
"production": {}
},
"dependsOn": [
{
"target": "build",
"projects": "daemon"
},
{
"target": "build-amd64",
"projects": "daemon"
},
"copy-daemon-bin"
]
"dependsOn": ["copy-daemon-bin", "copy-computeruse-plugin"]
},
"build-amd64": {
"executor": "@nx-go/nx-go:build",
"options": {
"main": "{projectRoot}/cmd/runner/main.go",
"outputPath": "dist/apps/runner-amd64",
"env": {
"GOARCH": "amd64"
}
},
"dependsOn": ["copy-daemon-bin", "copy-computeruse-plugin"]
},
"serve": {
"executor": "@nx-go/nx-go:serve",
@@ -53,7 +66,8 @@
"target": "build",
"projects": "daemon"
},
"copy-daemon-bin"
"copy-daemon-bin",
"copy-computeruse-plugin"
]
},
"format": {
@@ -75,5 +89,6 @@
"command": "swag fmt && swag init --parseDependency --parseInternal --parseDepth 1 -o docs -g server.go"
}
}
}
},
"implicitDependencies": ["computer-use", "daemon"]
}
+1
View File
@@ -7,4 +7,5 @@ use (
./apps/runner
./libs/api-client-go
./libs/common-go
./libs/computer-use
)
+97 -8
View File
@@ -1,3 +1,4 @@
cel.dev/expr v0.16.2/go.mod h1:gXngZQMkWJoSbE8mOzehJlXQyubn/Vg0vR9/F3W7iw8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
@@ -18,7 +19,9 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@@ -33,37 +36,54 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/antonlindstrom/pgstore v0.0.0-20220421113606-e3a6e3fed12a/go.mod h1:Sdr/tmSOLEnncCuXS5TwZRxuk7deH1WXVY8cve3eVBM=
github.com/bazelbuild/rules_go v0.49.0/go.mod h1:Dhcz716Kqg1RHNWos+N6MlXNkjNP2EwZQ0LukRKJfMs=
github.com/boj/redistore v1.4.1/go.mod h1:c0Tvw6aMjslog4jHIAcNv6EtJM849YoOAhMY7JBbWpI=
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20240916143655-c0e34fd2f304/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/gen2brain/shm v0.0.0-20230802011745-f2460f5984f7/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-vgo/robotgo v0.110.1/go.mod h1:DdJUdi6mEU8ttHMbow6hKD1TjgsfgJC/H+4dusok8Uw=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -87,7 +107,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -99,7 +119,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -115,9 +135,12 @@ github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwg
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@@ -127,21 +150,50 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
github.com/laziness-coders/mongostore v0.0.14/go.mod h1:Rh+yJax2Vxc2QY62clIM/kRnLk+TxivgSLHOXENXPtk=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
github.com/memcachier/mc/v3 v3.0.3/go.mod h1:GzjocBahcXPxt2cmqzknrgqCOmMxiSzhVKPOe90Tpug=
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/otiai10/gosseract/v2 v2.4.1/go.mod h1:1gNWP4Hgr2o7yqWfs6r5bZxAatjOIdqWxJLWsTsembk=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
github.com/robotn/xgbutil v0.0.0-20190912154524-c861d6f87770/go.mod h1:svkDXUDQjUiWzLrA0OZgHc4lbOts3C+uRfP6/yjwYnU=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw=
github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vcaesar/gops v0.30.2/go.mod h1:2NSA2Q9M1irGnGD9tWdo0Z+MwKjUj4Q4EgUDukN/Vsk=
github.com/vcaesar/imgo v0.40.0/go.mod h1:E5uI53XkEfbI20VvcIZ/19G2hHidPfH9h4NtQooEY+8=
github.com/vcaesar/tt v0.20.0/go.mod h1:GHPxQYhn+7OgKakRusH7KJ0M5MhywoeLb8Fcffs/Gtg=
github.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
@@ -153,17 +205,23 @@ github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtX
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
go.opentelemetry.io/contrib/detectors/gcp v1.31.0/go.mod h1:tzQL6E1l+iV44YFTkcAeNQqzXUiekSYP9jjJjXwEd00=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -174,8 +232,13 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230127140709-cafedaf64729/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -195,6 +258,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -217,21 +281,23 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -242,6 +308,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -254,19 +321,29 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -312,6 +389,9 @@ golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -333,6 +413,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -363,6 +444,11 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0=
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -375,6 +461,7 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -385,7 +472,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
+28
View File
@@ -0,0 +1,28 @@
FROM ubuntu:22.04
COPY --from=golang:1.23 /usr/local/go /usr/local/go
ENV PATH="/usr/local/go/bin:${PATH}"
WORKDIR /app
COPY ./libs/computer-use /app/libs/computer-use
COPY ./libs/api-client-go /app/libs/api-client-go
COPY ./libs/common-go /app/libs/common-go
COPY ./apps/runner /app/apps/runner
COPY ./apps/cli /app/apps/cli
COPY ./apps/proxy /app/apps/proxy
COPY ./apps/daemon /app/apps/daemon
COPY go.work /app/go.work
COPY go.work.sum /app/go.sum
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates
RUN apt-get install -y libx11-dev libxtst-dev gcc
RUN go build -o computer-use /app/libs/computer-use/main.go
RUN chmod +x /app/computer-use
VOLUME ["/dist"]
ENTRYPOINT ["cp", "/app/computer-use", "/dist/libs/computer-use-amd64"]
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -e
# Check if current architecture is amd64
if [ "$(uname -m)" = "x86_64" ]; then
echo "Building computer-use for amd64 architecture..."
cd libs/computer-use
go build -o ../../dist/libs/computer-use-amd64 main.go
echo "Build completed successfully"
exit 0
fi
# Build using docker image builder
docker build --platform linux/amd64 -t computer-use-amd64:build -f hack/computer-use/Dockerfile --no-cache .
# Run the container to copy the amd binary
docker run --rm -v $(pwd)/dist:/dist computer-use-amd64:build

Some files were not shown because too many files have changed in this diff Show More