"use client"; import { Suspense, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { IconCloud, IconServer } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@kandev/ui/card"; import { Input } from "@kandev/ui/input"; import { Label } from "@kandev/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kandev/ui/select"; import { Separator } from "@kandev/ui/separator"; import { createExecutorAction } from "@/app/actions/executors"; import { getWebSocketClient } from "@/lib/ws/connection"; import { useAppStore } from "@/components/state-provider"; import type { Executor } from "@/lib/types/http"; const EXECUTOR_TYPES = ["local_docker", "remote_docker"] as const; type ExecutorType = (typeof EXECUTOR_TYPES)[number]; export default function ExecutorCreatePage() { return ( Loading...}> ); } type RemoteDockerFieldsProps = { dockerTlsVerify: string; onDockerTlsVerifyChange: (value: string) => void; dockerCertPath: string; onDockerCertPathChange: (value: string) => void; gitToken: string; onGitTokenChange: (value: string) => void; }; function RemoteDockerFields({ dockerTlsVerify, onDockerTlsVerifyChange, dockerCertPath, onDockerCertPathChange, gitToken, onGitTokenChange, }: RemoteDockerFieldsProps) { return ( <>
onDockerCertPathChange(event.target.value)} placeholder="/path/to/certs" />

Path to TLS certificates for the Docker host.

onGitTokenChange(event.target.value)} placeholder="ghp_..." />

Personal access token for cloning repositories inside the container. Auto-detected from host environment if not set.

); } type ExecutorFormCardProps = { type: ExecutorType; name: string; dockerHost: string; dockerTlsVerify: string; dockerCertPath: string; gitToken: string; onTypeChange: (value: ExecutorType) => void; onNameChange: (value: string) => void; onDockerHostChange: (value: string) => void; onDockerTlsVerifyChange: (value: string) => void; onDockerCertPathChange: (value: string) => void; onGitTokenChange: (value: string) => void; }; function ExecutorFormCard({ type, name, dockerHost, dockerTlsVerify, dockerCertPath, gitToken, onTypeChange, onNameChange, onDockerHostChange, onDockerTlsVerifyChange, onDockerCertPathChange, onGitTokenChange, }: ExecutorFormCardProps) { const isRemoteDocker = type === "remote_docker"; return ( {isRemoteDocker ? : } {isRemoteDocker ? "Remote Docker Executor" : "Local Docker Executor"} {isRemoteDocker ? "Connects to a remote Docker host. The repository will be cloned inside the container." : "Uses the local Docker daemon on this machine."}
onNameChange(event.target.value)} />
onDockerHostChange(event.target.value)} placeholder={ isRemoteDocker ? "tcp://remote:2376 or ssh://user@host" : "unix:///var/run/docker.sock" } />

{isRemoteDocker ? "The remote Docker host URL (tcp://, ssh://)." : "Repositories will be mounted as volumes at runtime."}

{isRemoteDocker && ( )}
); } function buildExecutorConfig( type: ExecutorType, dockerHost: string, dockerTlsVerify: string, dockerCertPath: string, gitToken: string, ): Record { const config: Record = { docker_host: dockerHost }; if (type === "remote_docker") { if (dockerTlsVerify) config.docker_tls_verify = dockerTlsVerify; if (dockerCertPath) config.docker_cert_path = dockerCertPath; if (gitToken) config.git_token = gitToken; } return config; } function ExecutorCreatePageContent() { const router = useRouter(); const searchParams = useSearchParams(); const initialType = searchParams.get("type"); const [type, setType] = useState(() => { if (EXECUTOR_TYPES.includes(initialType as ExecutorType)) return initialType as ExecutorType; return "local_docker"; }); const [name, setName] = useState(() => initialType === "remote_docker" ? "Remote Docker" : "Local Docker", ); const [dockerHost, setDockerHost] = useState(() => initialType === "remote_docker" ? "tcp://" : "unix:///var/run/docker.sock", ); const [dockerTlsVerify, setDockerTlsVerify] = useState(""); const [dockerCertPath, setDockerCertPath] = useState(""); const [gitToken, setGitToken] = useState(""); const [isCreating, setIsCreating] = useState(false); const executors = useAppStore((state) => state.executors.items); const setExecutors = useAppStore((state) => state.setExecutors); const handleTypeChange = (value: ExecutorType) => { setType(value); if (value === "local_docker") { setName("Local Docker"); setDockerHost("unix:///var/run/docker.sock"); } else if (value === "remote_docker") { setName("Remote Docker"); setDockerHost("tcp://"); } }; const handleCreate = async () => { setIsCreating(true); try { const config = buildExecutorConfig( type, dockerHost, dockerTlsVerify, dockerCertPath, gitToken, ); const payload = { name, type, status: "active", config }; const client = getWebSocketClient(); const created = client ? await client.request("executor.create", payload) : await createExecutorAction(payload); setExecutors([...executors.filter((item: Executor) => item.id !== created.id), created]); router.push("/settings/executors"); } finally { setIsCreating(false); } }; return (

Create Executor

Choose an executor type to run environments on your infrastructure.

); }