Ctrl K
ComunidadGuías…

internal/enrollment/local_provider.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package enrollment

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"os"
	"os/exec"
	"os/user"
	"strings"
)

type Answers struct {
	DisplayName              string
	Email                    string
	WorkspaceRootPath        string
	DatabaseConnectionString string
	LanguageModel            LanguageModelAccess
	Harness                  HarnessChoice
}

type LocalProvider struct {
	home    Home
	answers Answers
}

func NewLocalProvider(home Home, answers Answers) LocalProvider {
	return LocalProvider{home: home, answers: answers}
}

func (provider LocalProvider) Enroll(context.Context) (Enrollment, error) {
	answers := provider.answers
	enrollment := Enrollment{
		TenantID:                 newIdentifier(),
		Operator:                 Person{PersonID: newIdentifier(), DisplayName: answers.DisplayName, Email: answers.Email},
		WorkspaceRootPath:        firstNonEmptyString(answers.WorkspaceRootPath, provider.home.WorkspaceRootPath()),
		DatabaseConnectionString: answers.DatabaseConnectionString,
		LanguageModel:            answers.LanguageModel,
		Harness:                  answers.Harness,
	}
	if strings.TrimSpace(enrollment.Operator.DisplayName) == "" {
		enrollment.Operator.DisplayName = currentAccountName()
	}
	if errorValue := enrollment.Validate(); errorValue != nil {
		return Enrollment{}, errorValue
	}
	return enrollment, nil
}

func SuggestedAnswers(home Home) Answers {
	accountName := currentAccountName()
	return Answers{
		DisplayName:              accountName,
		Email:                    accountName + "@localhost",
		WorkspaceRootPath:        home.WorkspaceRootPath(),
		DatabaseConnectionString: detectedDatabaseConnectionString(home),
		LanguageModel:            detectedLanguageModelAccess(),
		Harness:                  detectedHarness(),
	}
}

var harnessCommandNames = []struct {
	harnessName string
	commandName string
}{
	{harnessName: "claude-code", commandName: "claude"},
	{harnessName: "codex", commandName: "codex"},
}

func detectedLanguageModelAccess() LanguageModelAccess {
	return LanguageModelAccess{
		EndpointURL:        strings.TrimSpace(os.Getenv("BLUECLAW_MODEL_ENDPOINT")),
		ModelName:          strings.TrimSpace(os.Getenv("BLUECLAW_MODEL_NAME")),
		APIKey:             strings.TrimSpace(os.Getenv("BLUECLAW_MODEL_API_KEY")),
		EmbeddingModelName: strings.TrimSpace(os.Getenv("BLUECLAW_EMBEDDING_MODEL_NAME")),
	}
}

func detectedHarness() HarnessChoice {
	for _, candidate := range harnessCommandNames {
		commandPath, errorValue := exec.LookPath(candidate.commandName)
		if errorValue == nil {
			return HarnessChoice{Name: candidate.harnessName, AgentCommandPath: commandPath}
		}
	}
	return HarnessChoice{}
}

func AvailableHarnesses() []HarnessChoice {
	available := []HarnessChoice{{Name: "bluecollar-acp"}}
	for _, candidate := range harnessCommandNames {
		if commandPath, errorValue := exec.LookPath(candidate.commandName); errorValue == nil {
			available = append(available, HarnessChoice{Name: candidate.harnessName, AgentCommandPath: commandPath})
		}
	}
	return available
}

func detectedDatabaseConnectionString(home Home) string {
	if configuredURL := strings.TrimSpace(os.Getenv("DATABASE_URL")); configuredURL != "" {
		return configuredURL
	}
	return NewManagedPostgres(home).ConnectionString()
}

func currentAccountName() string {
	currentUser, errorValue := user.Current()
	if errorValue != nil || strings.TrimSpace(currentUser.Username) == "" {
		return "blueclaw"
	}
	return currentUser.Username
}

func newIdentifier() string {
	identifierBytes := make([]byte, 16)
	if _, errorValue := rand.Read(identifierBytes); errorValue != nil {
		return ""
	}
	return hex.EncodeToString(identifierBytes)
}

func firstNonEmptyString(candidates ...string) string {
	for _, candidate := range candidates {
		if trimmed := strings.TrimSpace(candidate); trimmed != "" {
			return trimmed
		}
	}
	return ""
}