diff --git a/cmd/picoclaw/auth.go b/cmd/picoclaw/auth.go index b32ec980e..5fce95202 100644 --- a/cmd/picoclaw/auth.go +++ b/cmd/picoclaw/auth.go @@ -4,8 +4,6 @@ package main import ( - "fmt" - authpkg "github.com/sipeed/picoclaw/cmd/picoclaw/auth" "github.com/spf13/cobra" ) @@ -14,9 +12,6 @@ var authCmd = &cobra.Command{ Use: "auth", Short: "Manage authentication", Long: `Manage authentication for different providers (login, logout, status).`, - Run: func(cmd *cobra.Command, args []string) { - authHelp() - }, } func init() { @@ -28,21 +23,3 @@ func init() { authCmd.AddCommand(authpkg.LogoutCmd) authCmd.AddCommand(authpkg.StatusCmd) } - -func authHelp() { - fmt.Println("\nAuth commands:") - fmt.Println(" login Login via OAuth or paste token") - fmt.Println(" logout Remove stored credentials") - fmt.Println(" status Show current auth status") - fmt.Println() - fmt.Println("Login options:") - fmt.Println(" --provider Provider to login with (openai, anthropic)") - fmt.Println(" --device-code Use device code flow (for headless environments)") - fmt.Println() - fmt.Println("Examples:") - fmt.Println(" picoclaw auth login --provider openai") - fmt.Println(" picoclaw auth login --provider openai --device-code") - fmt.Println(" picoclaw auth login --provider anthropic") - fmt.Println(" picoclaw auth logout --provider openai") - fmt.Println(" picoclaw auth status") -} diff --git a/cmd/picoclaw/cron.go b/cmd/picoclaw/cron.go index 7a7ed74e2..daf36a7a9 100644 --- a/cmd/picoclaw/cron.go +++ b/cmd/picoclaw/cron.go @@ -14,9 +14,6 @@ var cronCmd = &cobra.Command{ Use: "cron", Short: "Manage scheduled tasks", Long: `Manage cron jobs and scheduled tasks.`, - Run: func(cmd *cobra.Command, args []string) { - cronHelp() - }, } func init() { @@ -36,21 +33,3 @@ func init() { cronCmd.AddCommand(cronpkg.EnableCmd) cronCmd.AddCommand(cronpkg.DisableCmd) } - -func cronHelp() { - fmt.Println("\nCron commands:") - fmt.Println(" list List all scheduled jobs") - fmt.Println(" add Add a new scheduled job") - fmt.Println(" remove Remove a job by ID") - fmt.Println(" enable Enable a job") - fmt.Println(" disable Disable a job") - fmt.Println() - fmt.Println("Add options:") - fmt.Println(" -n, --name Job name") - fmt.Println(" -m, --message Message for agent") - fmt.Println(" -e, --every Run every N seconds") - fmt.Println(" -c, --cron Cron expression (e.g. '0 9 * * *')") - fmt.Println(" -d, --deliver Deliver response to channel") - fmt.Println(" --to Recipient for delivery") - fmt.Println(" --channel Channel for delivery") -} diff --git a/cmd/picoclaw/cronpkg/cron.go b/cmd/picoclaw/cronpkg/cron.go index 0b9a426e5..7bb62119a 100644 --- a/cmd/picoclaw/cronpkg/cron.go +++ b/cmd/picoclaw/cronpkg/cron.go @@ -3,10 +3,12 @@ package cronpkg +import "path/filepath" + var cronStorePath string func SetCronStorePath(workspace string) { - cronStorePath = workspace + "/cron/jobs.json" + cronStorePath = filepath.Join(workspace, "cron", "jobs.json") } func GetCronStorePath() string { diff --git a/cmd/picoclaw/gateway.go b/cmd/picoclaw/gateway.go index 4a4fce4f8..a45383910 100644 --- a/cmd/picoclaw/gateway.go +++ b/cmd/picoclaw/gateway.go @@ -6,15 +6,19 @@ package main import ( "context" "fmt" + "net/http" "os" "os/signal" "path/filepath" + "time" "github.com/sipeed/picoclaw/pkg/agent" "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" + "github.com/sipeed/picoclaw/pkg/config" "github.com/sipeed/picoclaw/pkg/cron" "github.com/sipeed/picoclaw/pkg/devices" + "github.com/sipeed/picoclaw/pkg/health" "github.com/sipeed/picoclaw/pkg/heartbeat" "github.com/sipeed/picoclaw/pkg/logger" "github.com/sipeed/picoclaw/pkg/providers" @@ -79,7 +83,8 @@ func gatewayImpl() { }) // Setup cron tool and service - cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath()) + execTimeout := time.Duration(cfg.Tools.Cron.ExecTimeoutMinutes) * time.Minute + cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath(), cfg.Agents.Defaults.RestrictToWorkspace, execTimeout, cfg) heartbeatService := heartbeat.NewHeartbeatService( cfg.WorkspacePath(), @@ -111,6 +116,9 @@ func gatewayImpl() { os.Exit(1) } + // Inject channel manager into agent loop for command handling + agentLoop.SetChannelManager(channelManager) + var transcriber *voice.GroqTranscriber if cfg.Providers.Groq.APIKey != "" { transcriber = voice.NewGroqTranscriber(cfg.Providers.Groq.APIKey) @@ -136,6 +144,12 @@ func gatewayImpl() { logger.InfoC("voice", "Groq transcription attached to Slack channel") } } + if onebotChannel, ok := channelManager.GetChannel("onebot"); ok { + if oc, ok := onebotChannel.(*channels.OneBotChannel); ok { + oc.SetTranscriber(transcriber) + logger.InfoC("voice", "Groq transcription attached to OneBot channel") + } + } } enabledChannels := channelManager.GetEnabledChannels() @@ -177,6 +191,14 @@ func gatewayImpl() { fmt.Printf("Error starting channels: %v\n", err) } + healthServer := health.NewServer(cfg.Gateway.Host, cfg.Gateway.Port) + go func() { + if err := healthServer.Start(); err != nil && err != http.ErrServerClosed { + logger.ErrorCF("health", "Health server error", map[string]interface{}{"error": err.Error()}) + } + }() + fmt.Printf("✓ Health endpoints available at http://%s:%d/health and /ready\n", cfg.Gateway.Host, cfg.Gateway.Port) + go agentLoop.Run(ctx) sigChan := make(chan os.Signal, 1) @@ -185,6 +207,7 @@ func gatewayImpl() { fmt.Println("\nShutting down...") cancel() + healthServer.Stop(context.Background()) deviceService.Stop() heartbeatService.Stop() cronService.Stop() @@ -193,14 +216,14 @@ func gatewayImpl() { fmt.Println("✓ Gateway stopped") } -func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string) *cron.CronService { +func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration, config *config.Config) *cron.CronService { cronStorePath := filepath.Join(workspace, "cron", "jobs.json") // Create cron service cronService := cron.NewCronService(cronStorePath, nil) // Create and register CronTool - cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace) + cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, config) agentLoop.RegisterTool(cronTool) // Set the onJob handler diff --git a/cmd/picoclaw/skills.go b/cmd/picoclaw/skills.go index a26dfd622..ffd31f8d1 100644 --- a/cmd/picoclaw/skills.go +++ b/cmd/picoclaw/skills.go @@ -15,9 +15,6 @@ var skillsCmd = &cobra.Command{ Use: "skills", Short: "Manage skills", Long: `Manage skills installation and listing.`, - Run: func(cmd *cobra.Command, args []string) { - skillsHelp() - }, } func init() { @@ -47,21 +44,3 @@ func init() { skillsCmd.AddCommand(skillspkg.SearchCmd) skillsCmd.AddCommand(skillspkg.ShowCmd) } - -func skillsHelp() { - fmt.Println("\nSkills commands:") - fmt.Println(" list List installed skills") - fmt.Println(" install Install skill from GitHub") - fmt.Println(" install-builtin Install all builtin skills to workspace") - fmt.Println(" list-builtin List available builtin skills") - fmt.Println(" remove Remove installed skill") - fmt.Println(" search Search available skills") - fmt.Println(" show Show skill details") - fmt.Println() - fmt.Println("Examples:") - fmt.Println(" picoclaw skills list") - fmt.Println(" picoclaw skills install sipeed/picoclaw-skills/weather") - fmt.Println(" picoclaw skills install-builtin") - fmt.Println(" picoclaw skills list-builtin") - fmt.Println(" picoclaw skills remove weather") -}