internal/enrollment/local_provider.go
Rama main · réplica de lectura del repositorio original.
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 ""
}