diff --git a/cmd/picoclaw/main.go b/cmd/picoclaw/main.go index 0867203a6..3f94f8188 100644 --- a/cmd/picoclaw/main.go +++ b/cmd/picoclaw/main.go @@ -9,6 +9,8 @@ package main import ( "fmt" "os" + "runtime" + "strings" "time" "github.com/spf13/cobra" @@ -17,8 +19,10 @@ import ( "github.com/sipeed/picoclaw/cmd/picoclaw/internal/agent" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/auth" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/cliui" + configcmd "github.com/sipeed/picoclaw/cmd/picoclaw/internal/config" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/cron" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/gateway" + "github.com/sipeed/picoclaw/cmd/picoclaw/internal/mcp" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/migrate" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/model" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/onboard" @@ -31,6 +35,49 @@ import ( var rootNoColor bool +// initTermuxSSL detects Termux environment and sets SSL_CERT_FILE if not already set. +// This fixes X509 certificate errors when running PicoClaw inside Termux or termux-chroot. +// See: https://github.com/sipeed/picoclaw/issues/2944 +func initTermuxSSL() { + // Only applicable on Linux/Android + if runtime.GOOS != "linux" && runtime.GOOS != "android" { + return + } + + // Skip if already set + if os.Getenv("SSL_CERT_FILE") != "" { + return + } + + // Check for Termux prefix in PATH or HOME + home := os.Getenv("HOME") + path := os.Getenv("PATH") + + isTermux := strings.Contains(home, "com.termux") || + strings.Contains(path, "com.termux") || + strings.Contains(home, "/data/data/com.termux") + + if !isTermux { + return + } + + // Check common CA bundle locations in Termux + caPaths := []string{ + "$PREFIX/etc/tls/cert.pem", + os.Getenv("PREFIX") + "/etc/tls/cert.pem", + "/data/data/com.termux/files/usr/etc/tls/cert.pem", + "/usr/etc/tls/cert.pem", + } + + for _, caPath := range caPaths { + expanded := os.ExpandEnv(caPath) + if _, err := os.Stat(expanded); err == nil { + os.Setenv("SSL_CERT_FILE", expanded) + return + } + } +} + func syncCliUIColor(root *cobra.Command) { no, _ := root.PersistentFlags().GetBool("no-color") cliui.Init(no || os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb") @@ -81,12 +128,14 @@ picoclaw --no-color status`, }) cmd.AddCommand( + configcmd.NewConfigCommand(), onboard.NewOnboardCommand(), agent.NewAgentCommand(), auth.NewAuthCommand(), gateway.NewGatewayCommand(), status.NewStatusCommand(), cron.NewCronCommand(), + mcp.NewMCPCommand(), migrate.NewMigrateCommand(), skills.NewSkillsCommand(), model.NewModelCommand(), @@ -119,6 +168,9 @@ const ( ) func main() { + // Initialize Termux SSL certificate detection before anything else + initTermuxSSL() + cliui.Init(earlyColorDisabled()) if earlyColorDisabled() {