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:
committed by
GitHub
co-authored by
MDzaja
Toma Puljak
parent
af77e33105
commit
0e810536ad
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -20,5 +20,7 @@ header:
|
||||
- 'libs/api-client-python/**'
|
||||
- 'libs/api-client-python-async/**'
|
||||
- 'apps/docs/**'
|
||||
- 'libs/computer-use/**'
|
||||
- 'hack/**'
|
||||
|
||||
comment: on-failure
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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,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,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")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -14,4 +14,4 @@ daytona autocomplete [bash|zsh|fish|powershell] [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [daytona](daytona.md) - Daytona CLI
|
||||
- [daytona](daytona.md) - Daytona CLI
|
||||
|
||||
@@ -14,4 +14,4 @@ daytona docs [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [daytona](daytona.md) - Daytona CLI
|
||||
- [daytona](daytona.md) - Daytona CLI
|
||||
|
||||
@@ -20,4 +20,4 @@ daytona login [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [daytona](daytona.md) - Daytona CLI
|
||||
- [daytona](daytona.md) - Daytona CLI
|
||||
|
||||
@@ -14,4 +14,4 @@ daytona logout [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [daytona](daytona.md) - Daytona CLI
|
||||
- [daytona](daytona.md) - Daytona CLI
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,4 +14,4 @@ daytona version [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [daytona](daytona.md) - Daytona CLI
|
||||
- [daytona](daytona.md) - Daytona CLI
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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("/")
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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("/")
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,5 +16,8 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
proxy.StartProxy(config)
|
||||
err = proxy.StartProxy(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
+19
-16
@@ -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
@@ -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=
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ use (
|
||||
./apps/runner
|
||||
./libs/api-client-go
|
||||
./libs/common-go
|
||||
./libs/computer-use
|
||||
)
|
||||
|
||||
+97
-8
@@ -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=
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user