Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions agent/rootfs/etc/s6-overlay/s6-rc.d/svc-desktop/run
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ fi
# Auto-detect browser
if command -v google-chrome-stable >/dev/null 2>&1; then
BROWSER=google-chrome-stable
EXTRA_ARGS+=(--no-sandbox)
EXTRA_ARGS+=(--no-sandbox --test-type)
elif command -v brave-browser >/dev/null 2>&1; then
BROWSER=brave-browser
EXTRA_ARGS+=(--no-sandbox)
EXTRA_ARGS+=(--no-sandbox --test-type)
else
BROWSER=/usr/bin/chromium
EXTRA_ARGS+=(--no-sandbox)
EXTRA_ARGS+=(--no-sandbox --test-type)
fi

# Run browser in a restart loop
Expand Down
6 changes: 5 additions & 1 deletion control-plane/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ RUN CGO_ENABLED=1 go build -ldflags "-X main.BuildDate=$(date -u +%Y-%m-%dT%H:%M

FROM alpine:3.20

RUN apk add --no-cache ca-certificates sqlite-libs
ARG USE_CHINA_MIRRORS=false
RUN if [ "$USE_CHINA_MIRRORS" = "true" ]; then \
sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories; \
fi && \
apk add --no-cache ca-certificates sqlite-libs

WORKDIR /app

Expand Down
100 changes: 97 additions & 3 deletions control-plane/internal/orchestrator/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package orchestrator

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -303,10 +304,103 @@ func (d *DockerOrchestrator) RestartInstance(ctx context.Context, name string, p
return d.createContainer(ctx, params)
}


// dockerConfig represents Docker config.json structure
type dockerConfig struct {
Auths map[string]struct {
Auth string `json:"auth"`
} `json:"auths"`
}

// getRegistryAuth reads ~/.docker/config.json and returns base64-encoded auth for Docker API
// Docker API expects: base64({"username":"xxx","password":"xxx","serveraddress":"xxx"})
func getRegistryAuth(registry string) string {
home, err := os.UserHomeDir()
if err != nil {
log.Printf("Failed to get home dir: %v", err)
return ""
}

configPath := home + "/.docker/config.json"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work for users running Claworc in kubernetes, but they may also want to use private repositories. Do you want to create a section on the settings page to put docker credentials?

data, err := os.ReadFile(configPath)
if err != nil {
log.Printf("Failed to read docker config: %v", err)
return ""
}

var config dockerConfig
if err := json.Unmarshal(data, &config); err != nil {
log.Printf("Failed to parse docker config: %v", err)
return ""
}

var rawAuth string

// Try exact match first
if auth, ok := config.Auths[registry]; ok {
rawAuth = auth.Auth
}
// Try with https:// prefix
if rawAuth == "" {
if auth, ok := config.Auths["https://"+registry]; ok {
rawAuth = auth.Auth
}
}
// Try without port
if rawAuth == "" {
host := strings.Split(registry, ":")[0]
if auth, ok := config.Auths[host]; ok {
rawAuth = auth.Auth
}
}

if rawAuth == "" {
log.Printf("No auth found for registry: %s", registry)
return ""
}

// Docker config stores auth as base64(username:password)
// We need to decode and re-encode as base64(JSON)
decoded, err := base64.StdEncoding.DecodeString(rawAuth)
if err != nil {
log.Printf("Failed to decode auth: %v", err)
return ""
}

parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
log.Printf("Invalid auth format")
return ""
}

// Build Docker API auth format
authJSON := fmt.Sprintf(`{"username":"%s","password":"%s","serveraddress":"%s"}`, parts[0], parts[1], registry)
return base64.StdEncoding.EncodeToString([]byte(authJSON))
}

// extractRegistry extracts registry host from image name
func extractRegistry(image string) string {
parts := strings.SplitN(image, "/", 2)
if len(parts) == 1 {
return "docker.io"
}
if !strings.Contains(parts[0], ".") && !strings.Contains(parts[0], ":") {
return "docker.io"
}
return parts[0]
}

func (d *DockerOrchestrator) UpdateImage(ctx context.Context, name string, params CreateParams) error {
// Force-pull the latest image (bypass local cache)
log.Printf("Force-pulling image %s for instance %s", params.ContainerImage, utils.SanitizeForLog(name))
reader, err := d.client.ImagePull(ctx, params.ContainerImage, image.PullOptions{})
// Pull the latest image with registry auth
log.Printf("Pulling image %s for instance %s", params.ContainerImage, utils.SanitizeForLog(name))

registry := extractRegistry(params.ContainerImage)
auth := getRegistryAuth(registry)
log.Printf("Registry: %s, Auth found: %v", registry, auth != "")

reader, err := d.client.ImagePull(ctx, params.ContainerImage, image.PullOptions{
RegistryAuth: auth,
})
if err != nil {
return fmt.Errorf("pull image %s: %w", params.ContainerImage, err)
}
Expand Down
Loading