From cad0a4de5bac8c3aad87e872c3e0cee6b3eae72f Mon Sep 17 00:00:00 2001 From: lpf Date: Thu, 12 Feb 2026 11:18:43 +0800 Subject: [PATCH] fix package --- .gitignore | 1 - README.md | 9 - README_EN.md | 10 - cmd/clawgo/main.go | 1294 ++++++++++++++++++++++++++++++++ go.mod | 2 +- pkg/agent/context.go | 10 +- pkg/agent/loop.go | 44 +- pkg/channels/base.go | 2 +- pkg/channels/dingtalk.go | 16 +- pkg/channels/discord.go | 8 +- pkg/channels/feishu.go | 6 +- pkg/channels/maixcam.go | 6 +- pkg/channels/manager.go | 6 +- pkg/channels/qq.go | 6 +- pkg/channels/telegram.go | 10 +- pkg/channels/whatsapp.go | 4 +- pkg/providers/http_provider.go | 2 +- pkg/server/server.go | 6 +- pkg/session/manager.go | 2 +- pkg/tools/registry.go | 2 +- pkg/tools/remind.go | 2 +- pkg/tools/subagent.go | 4 +- pkg/voice/transcriber.go | 2 +- 23 files changed, 1364 insertions(+), 90 deletions(-) create mode 100644 cmd/clawgo/main.go diff --git a/.gitignore b/.gitignore index 9c7a79d..2c83745 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,5 @@ coverage.html .DS_Store build -clawgo .idea \ No newline at end of file diff --git a/README.md b/README.md index e7ab374..f4645ac 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,9 @@ clawgo skills install-builtin ## ๐Ÿ› ๏ธ ๅฎ‰่ฃ… -### ้ข„็ผ–่ฏ‘ไบŒ่ฟ›ๅˆถๆ–‡ไปถ -ไปŽ [ๅ‘ๅธƒ้กต้ข](https://gitea.kkkk.dev/DBT/clawgo/releases) ไธ‹่ฝฝ้€‚ๅˆๆ‚จๅนณๅฐ็š„ๅ›บไปถ (Linux/macOS/Windows, x86/ARM/RISC-V)ใ€‚ ### ไปŽๆบ็ ็ผ–่ฏ‘ ```bash -git clone https://gitea.kkkk.dev/DBT/clawgo.git cd clawgo make deps make build @@ -93,12 +90,6 @@ make install | **ไบŒ่ฟ›ๅˆถๅคงๅฐ** | ๆ—  (ๆบ็ ) | ๆ—  (ๆบ็ ) | **ๅ•ๆ–‡ไปถ (~15MB)** | | **ๆžถๆž„ๆ”ฏๆŒ** | x86/ARM | x86/ARM | **x86/ARM/RISC-V** | -## ๐Ÿค ็คพๅŒบ - -ๅŠ ๅ…ฅ่ฎจ่ฎบ๏ผ -- **Discord**: [ๅŠ ๅ…ฅๆœๅŠกๅ™จ](https://discord.gg/V4sAZ9XWpN) -- **Issues**: [GitHub Issues](https://gitea.kkkk.dev/DBT/clawgo/issues) - ## ๐Ÿ“œ ่ฎธๅฏ่ฏ MIT ่ฎธๅฏ่ฏใ€‚ๆฐธ่ฟœๅ…่ดนๅผ€ๆบใ€‚ ๐Ÿฆ diff --git a/README_EN.md b/README_EN.md index af0ce35..dd78bf8 100644 --- a/README_EN.md +++ b/README_EN.md @@ -68,12 +68,8 @@ Run `clawgo gateway` to turn ClawGo into a 24/7 bot on your favorite platform. ## ๐Ÿ› ๏ธ Installation -### Pre-built Binaries -Download the latest release for your platform (Linux/macOS/Windows, x86/ARM/RISC-V) from the [Releases Page](https://gitea.kkkk.dev/DBT/clawgo/releases). - ### Build from Source ```bash -git clone https://gitea.kkkk.dev/DBT/clawgo.git cd clawgo make deps make build @@ -89,12 +85,6 @@ make install | **Binary Size** | N/A (Source) | N/A (Source) | **Single File (~15MB)** | | **Architecture** | x86/ARM | x86/ARM | **x86/ARM/RISC-V** | -## ๐Ÿค Community - -Join the discussion! -- **Discord**: [Join Server](https://discord.gg/V4sAZ9XWpN) -- **Issues**: [GitHub Issues](https://gitea.kkkk.dev/DBT/clawgo/issues) - ## ๐Ÿ“œ License MIT License. Free and open source forever. ๐Ÿฆ diff --git a/cmd/clawgo/main.go b/cmd/clawgo/main.go new file mode 100644 index 0000000..520ed53 --- /dev/null +++ b/cmd/clawgo/main.go @@ -0,0 +1,1294 @@ +// PicoClaw - Ultra-lightweight personal AI agent +// Inspired by and based on nanobot: https://github.com/HKUDS/nanobot +// License: MIT +// +// Copyright (c) 2026 PicoClaw contributors + +package main + +import ( + "bufio" + "context" + "fmt" + "io" + "os" + "os/exec" + + "os/signal" + "path/filepath" + "strings" + "time" + + "clawgo/pkg/agent" + "clawgo/pkg/bus" + "clawgo/pkg/channels" + "clawgo/pkg/config" + "clawgo/pkg/cron" + "clawgo/pkg/heartbeat" + "clawgo/pkg/logger" + "clawgo/pkg/providers" + "clawgo/pkg/skills" + "clawgo/pkg/voice" + + "github.com/chzyer/readline" +) + +const version = "0.1.0" +const logo = "๐Ÿฆž" + +func copyDirectory(src, dst string) error { + return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + relPath, err := filepath.Rel(src, path) + if err != nil { + return err + } + + dstPath := filepath.Join(dst, relPath) + + if info.IsDir() { + return os.MkdirAll(dstPath, info.Mode()) + } + + srcFile, err := os.Open(path) + if err != nil { + return err + } + defer srcFile.Close() + + dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, info.Mode()) + if err != nil { + return err + } + defer dstFile.Close() + + _, err = io.Copy(dstFile, srcFile) + return err + }) +} + +func main() { + // Detect debug mode early + for _, arg := range os.Args { + if arg == "--debug" || arg == "-d" { + config.SetDebugMode(true) + logger.SetLevel(logger.DEBUG) + break + } + } + + if len(os.Args) < 2 { + printHelp() + os.Exit(1) + } + + command := os.Args[1] + // Remove --debug/-d from args for command handling if it's there? + // Actually command handling already ignores them or handles them. + // But onboard/login need to know config path. + + switch command { + case "onboard": + onboard() + case "agent": + agentCmd() + case "gateway": + gatewayCmd() + case "status": + statusCmd() + case "cron": + cronCmd() + case "login": + loginCmd() + case "skills": + if len(os.Args) < 3 { + skillsHelp() + return + } + + subcommand := os.Args[2] + + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + os.Exit(1) + } + + workspace := cfg.WorkspacePath() + installer := skills.NewSkillInstaller(workspace) + // ่Žทๅ–ๅ…จๅฑ€้…็ฝฎ็›ฎๅฝ•ๅ’Œๅ†…็ฝฎ skills ็›ฎๅฝ• + globalDir := filepath.Dir(getConfigPath()) + globalSkillsDir := filepath.Join(globalDir, "skills") + builtinSkillsDir := filepath.Join(globalDir, "picoclaw", "skills") + skillsLoader := skills.NewSkillsLoader(workspace, globalSkillsDir, builtinSkillsDir) + + switch subcommand { + case "list": + skillsListCmd(skillsLoader) + case "install": + skillsInstallCmd(installer) + case "remove", "uninstall": + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw skills remove ") + return + } + skillsRemoveCmd(installer, os.Args[3]) + case "install-builtin": + skillsInstallBuiltinCmd(workspace) + case "list-builtin": + skillsListBuiltinCmd() + case "search": + skillsSearchCmd(installer) + case "show": + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw skills show ") + return + } + skillsShowCmd(skillsLoader, os.Args[3]) + default: + fmt.Printf("Unknown skills command: %s\n", subcommand) + skillsHelp() + } + case "version", "--version", "-v": + fmt.Printf("%s picoclaw v%s\n", logo, version) + default: + fmt.Printf("Unknown command: %s\n", command) + printHelp() + os.Exit(1) + } +} + +func printHelp() { + fmt.Printf("%s picoclaw - Personal AI Assistant v%s\n\n", logo, version) + fmt.Println("Usage: picoclaw ") + fmt.Println() + fmt.Println("Commands:") + fmt.Println(" onboard Initialize picoclaw configuration and workspace") + fmt.Println(" agent Interact with the agent directly") + fmt.Println(" gateway Start picoclaw gateway") + fmt.Println(" status Show picoclaw status") + fmt.Println(" cron Manage scheduled tasks") + fmt.Println(" login Configure model provider credentials") + fmt.Println(" skills Manage skills (install, list, remove)") + fmt.Println(" version Show version information") +} + +func onboard() { + configPath := getConfigPath() + + if _, err := os.Stat(configPath); err == nil { + fmt.Printf("Config already exists at %s\n", configPath) + fmt.Print("Overwrite? (y/n): ") + var response string + fmt.Scanln(&response) + if response != "y" { + fmt.Println("Aborted.") + return + } + } + + cfg := config.DefaultConfig() + if err := config.SaveConfig(configPath, cfg); err != nil { + fmt.Printf("Error saving config: %v\n", err) + os.Exit(1) + } + + workspace := cfg.WorkspacePath() + os.MkdirAll(workspace, 0755) + os.MkdirAll(filepath.Join(workspace, "memory"), 0755) + os.MkdirAll(filepath.Join(workspace, "skills"), 0755) + + createWorkspaceTemplates(workspace) + + fmt.Printf("%s picoclaw is ready!\n", logo) + fmt.Println("\nNext steps:") + fmt.Println(" 1. Add your API key to", configPath) + fmt.Println(" Get one at: https://openrouter.ai/keys") + fmt.Println(" 2. Chat: picoclaw agent -m \"Hello!\"") +} + +func createWorkspaceTemplates(workspace string) { + templates := map[string]string{ + "AGENTS.md": `# Agent Instructions + +You are a helpful AI assistant. Be concise, accurate, and friendly. + +## Guidelines + +- Always explain what you're doing before taking actions +- Ask for clarification when request is ambiguous +- Use tools to help accomplish tasks +- Remember important information in your memory files +- Be proactive and helpful +- Learn from user feedback +`, + "SOUL.md": `# Soul + +I am picoclaw, a lightweight AI assistant powered by AI. + +## Personality + +- Helpful and friendly +- Concise and to the point +- Curious and eager to learn +- Honest and transparent + +## Values + +- Accuracy over speed +- User privacy and safety +- Transparency in actions +- Continuous improvement +`, + "USER.md": `# User + +Information about user goes here. + +## Preferences + +- Communication style: (casual/formal) +- Timezone: (your timezone) +- Language: (your preferred language) + +## Personal Information + +- Name: (optional) +- Location: (optional) +- Occupation: (optional) + +## Learning Goals + +- What the user wants to learn from AI +- Preferred interaction style +- Areas of interest +`, + "IDENTITY.md": `# Identity + +## Name +PicoClaw ๐Ÿฆž + +## Description +Ultra-lightweight personal AI assistant written in Go, inspired by nanobot. + +## Version +0.1.0 + +## Purpose +- Provide intelligent AI assistance with minimal resource usage +- Support multiple LLM providers (OpenAI, Anthropic, Zhipu, etc.) +- Enable easy customization through skills system +- Run on minimal hardware ($10 boards, <10MB RAM) + +## Capabilities + +- Web search and content fetching +- File system operations (read, write, edit) +- Shell command execution +- Multi-channel messaging (Telegram, WhatsApp, Feishu) +- Skill-based extensibility +- Memory and context management + +## Philosophy + +- Simplicity over complexity +- Performance over features +- User control and privacy +- Transparent operation +- Community-driven development + +## Goals + +- Provide a fast, lightweight AI assistant +- Support offline-first operation where possible +- Enable easy customization and extension +- Maintain high quality responses +- Run efficiently on constrained hardware + +## License +MIT License - Free and open source + +## Repository +https://github.com/sipeed/picoclaw + +## Contact +Issues: https://github.com/sipeed/picoclaw/issues +Discussions: https://github.com/sipeed/picoclaw/discussions + +--- + +"Every bit helps, every bit matters." +- Picoclaw +`, + } + + for filename, content := range templates { + filePath := filepath.Join(workspace, filename) + if _, err := os.Stat(filePath); os.IsNotExist(err) { + os.WriteFile(filePath, []byte(content), 0644) + fmt.Printf(" Created %s\n", filename) + } + } + + memoryDir := filepath.Join(workspace, "memory") + os.MkdirAll(memoryDir, 0755) + memoryFile := filepath.Join(memoryDir, "MEMORY.md") + if _, err := os.Stat(memoryFile); os.IsNotExist(err) { + memoryContent := `# Long-term Memory + +This file stores important information that should persist across sessions. + +## User Information + +(Important facts about user) + +## Preferences + +(User preferences learned over time) + +## Important Notes + +(Things to remember) + +## Configuration + +- Model preferences +- Channel settings +- Skills enabled +` + os.WriteFile(memoryFile, []byte(memoryContent), 0644) + fmt.Println(" Created memory/MEMORY.md") + + skillsDir := filepath.Join(workspace, "skills") + if _, err := os.Stat(skillsDir); os.IsNotExist(err) { + os.MkdirAll(skillsDir, 0755) + fmt.Println(" Created skills/") + } + } + + for filename, content := range templates { + filePath := filepath.Join(workspace, filename) + if _, err := os.Stat(filePath); os.IsNotExist(err) { + os.WriteFile(filePath, []byte(content), 0644) + fmt.Printf(" Created %s\n", filename) + } + } +} + +func agentCmd() { + message := "" + sessionKey := "cli:default" + + args := os.Args[2:] + for i := 0; i < len(args); i++ { + switch args[i] { + case "--debug", "-d": + logger.SetLevel(logger.DEBUG) + fmt.Println("๐Ÿ” Debug mode enabled") + case "-m", "--message": + if i+1 < len(args) { + message = args[i+1] + i++ + } + case "-s", "--session": + if i+1 < len(args) { + sessionKey = args[i+1] + i++ + } + } + } + + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + os.Exit(1) + } + + provider, err := providers.CreateProvider(cfg) + if err != nil { + fmt.Printf("Error creating provider: %v\n", err) + os.Exit(1) + } + + msgBus := bus.NewMessageBus() + + // Initialize CronService for tools (shared storage with gateway) + cronStorePath := filepath.Join(filepath.Dir(getConfigPath()), "cron", "jobs.json") + cronService := cron.NewCronService(cronStorePath, nil) + + agentLoop := agent.NewAgentLoop(cfg, msgBus, provider, cronService) + + // Print agent startup info (only for interactive mode) + startupInfo := agentLoop.GetStartupInfo() + logger.InfoCF("agent", "Agent initialized", + map[string]interface{}{ + "tools_count": startupInfo["tools"].(map[string]interface{})["count"], + "skills_total": startupInfo["skills"].(map[string]interface{})["total"], + "skills_available": startupInfo["skills"].(map[string]interface{})["available"], + }) + + if message != "" { + ctx := context.Background() + response, err := agentLoop.ProcessDirect(ctx, message, sessionKey) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + fmt.Printf("\n%s %s\n", logo, response) + } else { + fmt.Printf("%s Interactive mode (Ctrl+C to exit)\n\n", logo) + interactiveMode(agentLoop, sessionKey) + } +} + +func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) { + prompt := fmt.Sprintf("%s You: ", logo) + + rl, err := readline.NewEx(&readline.Config{ + Prompt: prompt, + HistoryFile: filepath.Join(os.TempDir(), ".picoclaw_history"), + HistoryLimit: 100, + InterruptPrompt: "^C", + EOFPrompt: "exit", + }) + + if err != nil { + fmt.Printf("Error initializing readline: %v\n", err) + fmt.Println("Falling back to simple input mode...") + simpleInteractiveMode(agentLoop, sessionKey) + return + } + defer rl.Close() + + for { + line, err := rl.Readline() + if err != nil { + if err == readline.ErrInterrupt || err == io.EOF { + fmt.Println("\nGoodbye!") + return + } + fmt.Printf("Error reading input: %v\n", err) + continue + } + + input := strings.TrimSpace(line) + if input == "" { + continue + } + + if input == "exit" || input == "quit" { + fmt.Println("Goodbye!") + return + } + + ctx := context.Background() + response, err := agentLoop.ProcessDirect(ctx, input, sessionKey) + if err != nil { + fmt.Printf("Error: %v\n", err) + continue + } + + fmt.Printf("\n%s %s\n\n", logo, response) + } +} + +func simpleInteractiveMode(agentLoop *agent.AgentLoop, sessionKey string) { + reader := bufio.NewReader(os.Stdin) + for { + fmt.Print(fmt.Sprintf("%s You: ", logo)) + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + fmt.Println("\nGoodbye!") + return + } + fmt.Printf("Error reading input: %v\n", err) + continue + } + + input := strings.TrimSpace(line) + if input == "" { + continue + } + + if input == "exit" || input == "quit" { + fmt.Println("Goodbye!") + return + } + + ctx := context.Background() + response, err := agentLoop.ProcessDirect(ctx, input, sessionKey) + if err != nil { + fmt.Printf("Error: %v\n", err) + continue + } + + fmt.Printf("\n%s %s\n\n", logo, response) + } +} + +func gatewayCmd() { + // Check for --debug flag + args := os.Args[2:] + for _, arg := range args { + if arg == "--debug" || arg == "-d" { + logger.SetLevel(logger.DEBUG) + fmt.Println("๐Ÿ” Debug mode enabled") + break + } + } + + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + os.Exit(1) + } + + provider, err := providers.CreateProvider(cfg) + if err != nil { + fmt.Printf("Error creating provider: %v\n", err) + os.Exit(1) + } + + msgBus := bus.NewMessageBus() + + cronStorePath := filepath.Join(filepath.Dir(getConfigPath()), "cron", "jobs.json") + cronService := cron.NewCronService(cronStorePath, nil) + + agentLoop := agent.NewAgentLoop(cfg, msgBus, provider, cronService) + + // Print agent startup info + fmt.Println("\n๐Ÿ“ฆ Agent Status:") + startupInfo := agentLoop.GetStartupInfo() + toolsInfo := startupInfo["tools"].(map[string]interface{}) + skillsInfo := startupInfo["skills"].(map[string]interface{}) + fmt.Printf(" โ€ข Tools: %d loaded\n", toolsInfo["count"]) + fmt.Printf(" โ€ข Skills: %d/%d available\n", + skillsInfo["available"], + skillsInfo["total"]) + + // Log to file as well + logger.InfoCF("agent", "Agent initialized", + map[string]interface{}{ + "tools_count": toolsInfo["count"], + "skills_total": skillsInfo["total"], + "skills_available": skillsInfo["available"], + }) + + // Cron service initialized earlier + + heartbeatService := heartbeat.NewHeartbeatService( + cfg.WorkspacePath(), + nil, + 30*60, + true, + ) + + channelManager, err := channels.NewManager(cfg, msgBus) + if err != nil { + fmt.Printf("Error creating channel manager: %v\n", err) + os.Exit(1) + } + + var transcriber *voice.GroqTranscriber + if cfg.Providers.Groq.APIKey != "" { + transcriber = voice.NewGroqTranscriber(cfg.Providers.Groq.APIKey) + logger.InfoC("voice", "Groq voice transcription enabled") + } + + if transcriber != nil { + if telegramChannel, ok := channelManager.GetChannel("telegram"); ok { + if tc, ok := telegramChannel.(*channels.TelegramChannel); ok { + tc.SetTranscriber(transcriber) + logger.InfoC("voice", "Groq transcription attached to Telegram channel") + } + } + if discordChannel, ok := channelManager.GetChannel("discord"); ok { + if dc, ok := discordChannel.(*channels.DiscordChannel); ok { + dc.SetTranscriber(transcriber) + logger.InfoC("voice", "Groq transcription attached to Discord channel") + } + } + } + + enabledChannels := channelManager.GetEnabledChannels() + if len(enabledChannels) > 0 { + fmt.Printf("โœ“ Channels enabled: %s\n", enabledChannels) + } else { + fmt.Println("โš  Warning: No channels enabled") + } + + fmt.Printf("โœ“ Gateway started on %s:%d\n", cfg.Gateway.Host, cfg.Gateway.Port) + fmt.Println("Press Ctrl+C to stop") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if err := cronService.Start(); err != nil { + fmt.Printf("Error starting cron service: %v\n", err) + } + fmt.Println("โœ“ Cron service started") + + if err := heartbeatService.Start(); err != nil { + fmt.Printf("Error starting heartbeat service: %v\n", err) + } + fmt.Println("โœ“ Heartbeat service started") + + if err := channelManager.StartAll(ctx); err != nil { + fmt.Printf("Error starting channels: %v\n", err) + } + + go agentLoop.Run(ctx) + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt) + <-sigChan + + fmt.Println("\nShutting down...") + cancel() + heartbeatService.Stop() + cronService.Stop() + agentLoop.Stop() + channelManager.StopAll(ctx) + fmt.Println("โœ“ Gateway stopped") +} + +func statusCmd() { + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + return + } + + configPath := getConfigPath() + + fmt.Printf("%s picoclaw Status\n\n", logo) + + if _, err := os.Stat(configPath); err == nil { + fmt.Println("Config:", configPath, "โœ“") + } else { + fmt.Println("Config:", configPath, "โœ—") + } + + workspace := cfg.WorkspacePath() + if _, err := os.Stat(workspace); err == nil { + fmt.Println("Workspace:", workspace, "โœ“") + } else { + fmt.Println("Workspace:", workspace, "โœ—") + } + + if _, err := os.Stat(configPath); err == nil { + fmt.Printf("Model: %s\n", cfg.Agents.Defaults.Model) + + hasOpenRouter := cfg.Providers.OpenRouter.APIKey != "" + hasAnthropic := cfg.Providers.Anthropic.APIKey != "" + hasOpenAI := cfg.Providers.OpenAI.APIKey != "" + hasGemini := cfg.Providers.Gemini.APIKey != "" + hasZhipu := cfg.Providers.Zhipu.APIKey != "" + hasGroq := cfg.Providers.Groq.APIKey != "" + hasVLLM := cfg.Providers.VLLM.APIBase != "" + + status := func(enabled bool) string { + if enabled { + return "โœ“" + } + return "not set" + } + fmt.Println("OpenRouter API:", status(hasOpenRouter)) + fmt.Println("Anthropic API:", status(hasAnthropic)) + fmt.Println("OpenAI API:", status(hasOpenAI)) + fmt.Println("Gemini API:", status(hasGemini)) + fmt.Println("Zhipu API:", status(hasZhipu)) + fmt.Println("Groq API:", status(hasGroq)) + if hasVLLM { + fmt.Printf("vLLM/Local: โœ“ %s\n", cfg.Providers.VLLM.APIBase) + } else { + fmt.Println("vLLM/Local: not set") + } + } +} + +func getConfigPath() string { + return filepath.Join(config.GetConfigDir(), "config.json") +} + +func loadConfig() (*config.Config, error) { + return config.LoadConfig(getConfigPath()) +} + +func cronCmd() { + if len(os.Args) < 3 { + cronHelp() + return + } + + subcommand := os.Args[2] + + dataDir := filepath.Join(filepath.Dir(getConfigPath()), "cron") + cronStorePath := filepath.Join(dataDir, "jobs.json") + + switch subcommand { + case "list": + cronListCmd(cronStorePath) + case "add": + cronAddCmd(cronStorePath) + case "remove": + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw cron remove ") + return + } + cronRemoveCmd(cronStorePath, os.Args[3]) + case "enable": + cronEnableCmd(cronStorePath, false) + case "disable": + cronEnableCmd(cronStorePath, true) + default: + fmt.Printf("Unknown cron command: %s\n", subcommand) + cronHelp() + } +} + +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") +} + +func cronListCmd(storePath string) { + cs := cron.NewCronService(storePath, nil) + jobs := cs.ListJobs(false) + + if len(jobs) == 0 { + fmt.Println("No scheduled jobs.") + return + } + + fmt.Println("\nScheduled Jobs:") + fmt.Println("----------------") + for _, job := range jobs { + var schedule string + if job.Schedule.Kind == "every" && job.Schedule.EveryMS != nil { + schedule = fmt.Sprintf("every %ds", *job.Schedule.EveryMS/1000) + } else if job.Schedule.Kind == "cron" { + schedule = job.Schedule.Expr + } else { + schedule = "one-time" + } + + nextRun := "scheduled" + if job.State.NextRunAtMS != nil { + nextTime := time.UnixMilli(*job.State.NextRunAtMS) + nextRun = nextTime.Format("2006-01-02 15:04") + } + + status := "enabled" + if !job.Enabled { + status = "disabled" + } + + fmt.Printf(" %s (%s)\n", job.Name, job.ID) + fmt.Printf(" Schedule: %s\n", schedule) + fmt.Printf(" Status: %s\n", status) + fmt.Printf(" Next run: %s\n", nextRun) + } +} + +func cronAddCmd(storePath string) { + name := "" + message := "" + var everySec *int64 + cronExpr := "" + deliver := false + channel := "" + to := "" + + args := os.Args[3:] + for i := 0; i < len(args); i++ { + switch args[i] { + case "-n", "--name": + if i+1 < len(args) { + name = args[i+1] + i++ + } + case "-m", "--message": + if i+1 < len(args) { + message = args[i+1] + i++ + } + case "-e", "--every": + if i+1 < len(args) { + var sec int64 + fmt.Sscanf(args[i+1], "%d", &sec) + everySec = &sec + i++ + } + case "-c", "--cron": + if i+1 < len(args) { + cronExpr = args[i+1] + i++ + } + case "-d", "--deliver": + deliver = true + case "--to": + if i+1 < len(args) { + to = args[i+1] + i++ + } + case "--channel": + if i+1 < len(args) { + channel = args[i+1] + i++ + } + } + } + + if name == "" { + fmt.Println("Error: --name is required") + return + } + + if message == "" { + fmt.Println("Error: --message is required") + return + } + + if everySec == nil && cronExpr == "" { + fmt.Println("Error: Either --every or --cron must be specified") + return + } + + var schedule cron.CronSchedule + if everySec != nil { + everyMS := *everySec * 1000 + schedule = cron.CronSchedule{ + Kind: "every", + EveryMS: &everyMS, + } + } else { + schedule = cron.CronSchedule{ + Kind: "cron", + Expr: cronExpr, + } + } + + cs := cron.NewCronService(storePath, nil) + job, err := cs.AddJob(name, schedule, message, deliver, channel, to) + if err != nil { + fmt.Printf("Error adding job: %v\n", err) + return + } + + fmt.Printf("โœ“ Added job '%s' (%s)\n", job.Name, job.ID) +} + +func cronRemoveCmd(storePath, jobID string) { + cs := cron.NewCronService(storePath, nil) + if cs.RemoveJob(jobID) { + fmt.Printf("โœ“ Removed job %s\n", jobID) + } else { + fmt.Printf("โœ— Job %s not found\n", jobID) + } +} + +func cronEnableCmd(storePath string, disable bool) { + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw cron enable/disable ") + return + } + + jobID := os.Args[3] + cs := cron.NewCronService(storePath, nil) + enabled := !disable + + job := cs.EnableJob(jobID, enabled) + if job != nil { + status := "enabled" + if disable { + status = "disabled" + } + fmt.Printf("โœ“ Job '%s' %s\n", job.Name, status) + } else { + fmt.Printf("โœ— Job %s not found\n", jobID) + } +} + +func skillsCmd() { + if len(os.Args) < 3 { + skillsHelp() + return + } + + subcommand := os.Args[2] + + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + os.Exit(1) + } + + workspace := cfg.WorkspacePath() + installer := skills.NewSkillInstaller(workspace) + // ่Žทๅ–ๅ…จๅฑ€้…็ฝฎ็›ฎๅฝ•ๅ’Œๅ†…็ฝฎ skills ็›ฎๅฝ• + globalDir := filepath.Dir(getConfigPath()) + globalSkillsDir := filepath.Join(globalDir, "skills") + builtinSkillsDir := filepath.Join(globalDir, "picoclaw", "skills") + skillsLoader := skills.NewSkillsLoader(workspace, globalSkillsDir, builtinSkillsDir) + + switch subcommand { + case "list": + skillsListCmd(skillsLoader) + case "install": + skillsInstallCmd(installer) + case "remove", "uninstall": + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw skills remove ") + return + } + skillsRemoveCmd(installer, os.Args[3]) + case "search": + skillsSearchCmd(installer) + case "show": + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw skills show ") + return + } + skillsShowCmd(skillsLoader, os.Args[3]) + default: + fmt.Printf("Unknown skills command: %s\n", subcommand) + skillsHelp() + } +} + +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") +} + +func skillsListCmd(loader *skills.SkillsLoader) { + allSkills := loader.ListSkills() + + if len(allSkills) == 0 { + fmt.Println("No skills installed.") + return + } + + fmt.Println("\nInstalled Skills:") + fmt.Println("------------------") + for _, skill := range allSkills { + fmt.Printf(" โœ“ %s (%s)\n", skill.Name, skill.Source) + if skill.Description != "" { + fmt.Printf(" %s\n", skill.Description) + } + } +} + +func skillsInstallCmd(installer *skills.SkillInstaller) { + if len(os.Args) < 4 { + fmt.Println("Usage: picoclaw skills install ") + fmt.Println("Example: picoclaw skills install sipeed/picoclaw-skills/weather") + return + } + + repo := os.Args[3] + fmt.Printf("Installing skill from %s...\n", repo) + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + if err := installer.InstallFromGitHub(ctx, repo); err != nil { + fmt.Printf("โœ— Failed to install skill: %v\n", err) + os.Exit(1) + } + + fmt.Printf("โœ“ Skill '%s' installed successfully!\n", filepath.Base(repo)) +} + +func skillsRemoveCmd(installer *skills.SkillInstaller, skillName string) { + fmt.Printf("Removing skill '%s'...\n", skillName) + + if err := installer.Uninstall(skillName); err != nil { + fmt.Printf("โœ— Failed to remove skill: %v\n", err) + os.Exit(1) + } + + fmt.Printf("โœ“ Skill '%s' removed successfully!\n", skillName) +} + +func skillsInstallBuiltinCmd(workspace string) { + builtinSkillsDir := "./picoclaw/skills" + workspaceSkillsDir := filepath.Join(workspace, "skills") + + fmt.Printf("Copying builtin skills to workspace...\n") + + skillsToInstall := []string{ + "weather", + "news", + "stock", + "calculator", + } + + for _, skillName := range skillsToInstall { + builtinPath := filepath.Join(builtinSkillsDir, skillName) + workspacePath := filepath.Join(workspaceSkillsDir, skillName) + + if _, err := os.Stat(builtinPath); err != nil { + fmt.Printf("โŠ˜ Builtin skill '%s' not found: %v\n", skillName, err) + continue + } + + if err := os.MkdirAll(workspacePath, 0755); err != nil { + fmt.Printf("โœ— Failed to create directory for %s: %v\n", skillName, err) + continue + } + + if err := copyDirectory(builtinPath, workspacePath); err != nil { + fmt.Printf("โœ— Failed to copy %s: %v\n", skillName, err) + } + } + + fmt.Println("\nโœ“ All builtin skills installed!") + fmt.Println("Now you can use them in your workspace.") +} + +func skillsListBuiltinCmd() { + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + return + } + builtinSkillsDir := filepath.Join(filepath.Dir(cfg.WorkspacePath()), "picoclaw", "skills") + + fmt.Println("\nAvailable Builtin Skills:") + fmt.Println("-----------------------") + + entries, err := os.ReadDir(builtinSkillsDir) + if err != nil { + fmt.Printf("Error reading builtin skills: %v\n", err) + return + } + + if len(entries) == 0 { + fmt.Println("No builtin skills available.") + return + } + + for _, entry := range entries { + if entry.IsDir() { + skillName := entry.Name() + skillFile := filepath.Join(builtinSkillsDir, skillName, "SKILL.md") + + description := "No description" + if _, err := os.Stat(skillFile); err == nil { + data, err := os.ReadFile(skillFile) + if err == nil { + content := string(data) + if idx := strings.Index(content, "\n"); idx > 0 { + firstLine := content[:idx] + if strings.Contains(firstLine, "description:") { + descLine := strings.Index(content[idx:], "\n") + if descLine > 0 { + description = strings.TrimSpace(content[idx+descLine : idx+descLine]) + } + } + } + } + } + status := "โœ“" + fmt.Printf(" %s %s\n", status, entry.Name()) + if description != "" { + fmt.Printf(" %s\n", description) + } + } + } +} + +func skillsSearchCmd(installer *skills.SkillInstaller) { + fmt.Println("Searching for available skills...") + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + availableSkills, err := installer.ListAvailableSkills(ctx) + if err != nil { + fmt.Printf("โœ— Failed to fetch skills list: %v\n", err) + return + } + + if len(availableSkills) == 0 { + fmt.Println("No skills available.") + return + } + + fmt.Printf("\nAvailable Skills (%d):\n", len(availableSkills)) + fmt.Println("--------------------") + for _, skill := range availableSkills { + fmt.Printf(" ๐Ÿ“ฆ %s\n", skill.Name) + fmt.Printf(" %s\n", skill.Description) + fmt.Printf(" Repo: %s\n", skill.Repository) + if skill.Author != "" { + fmt.Printf(" Author: %s\n", skill.Author) + } + if len(skill.Tags) > 0 { + fmt.Printf(" Tags: %v\n", skill.Tags) + } + fmt.Println() + } +} + +func skillsShowCmd(loader *skills.SkillsLoader, skillName string) { + content, ok := loader.LoadSkill(skillName) + if !ok { + fmt.Printf("โœ— Skill '%s' not found\n", skillName) + return + } + + fmt.Printf("\n๐Ÿ“ฆ Skill: %s\n", skillName) + fmt.Println("----------------------") + fmt.Println(content) +} + +func loginCmd() { + cfg, err := loadConfig() + if err != nil { + fmt.Printf("Error loading config: %v\n", err) + os.Exit(1) + } + + providersList := []string{"OpenAI", "Anthropic", "Gemini", "OpenRouter", "Zhipu", "Groq", "VLLM"} + + if len(os.Args) > 2 { + provider := os.Args[2] + found := false + for _, p := range providersList { + if strings.EqualFold(p, provider) { + configureProvider(cfg, p) + found = true + break + } + } + if !found { + fmt.Printf("Unknown provider: %s\n", provider) + fmt.Println("Available providers:", strings.Join(providersList, ", ")) + } + return + } + + fmt.Println("Select provider to configure:") + for i, p := range providersList { + fmt.Printf("%d. %s\n", i+1, p) + } + + fmt.Print("\nEnter choice (number): ") + var choice int + _, err = fmt.Scanln(&choice) + if err != nil || choice < 1 || choice > len(providersList) { + fmt.Println("Invalid choice.") + return + } + + selected := providersList[choice-1] + configureProvider(cfg, selected) +} + +func configureProvider(cfg *config.Config, provider string) { + fmt.Printf("\nConfiguring %s...\n", provider) + + switch provider { + case "Gemini": + fmt.Println("Do you want to use gemini-cli authentication? (y/n): ") + var useCLI string + fmt.Scanln(&useCLI) + if strings.ToLower(useCLI) == "y" { + if err := exec.Command("gemini", "login").Run(); err != nil { + fmt.Printf("Error running gemini login: %v\n", err) + } + return // Config handled by gemini-cli + } + } + + fmt.Print("Enter API Key: ") + var apiKey string + fmt.Scanln(&apiKey) + + var apiBase string + // For VLLM or OpenAI, user might want custom base + if provider == "VLLM" || provider == "OpenAI" || provider == "OpenRouter" { + fmt.Print("Enter API Base URL (optional, press Enter to skip): ") + reader := bufio.NewReader(os.Stdin) + line, _ := reader.ReadString('\n') + apiBase = strings.TrimSpace(line) + } + + switch provider { + case "OpenAI": + fmt.Println("Do you want to use codex-cli authentication? (y/n): ") + var useCLI string + fmt.Scanln(&useCLI) + if strings.ToLower(useCLI) == "y" { + if err := exec.Command("codex", "login").Run(); err != nil { + fmt.Printf("Error running codex login: %v\n", err) + } + return // Config handled by codex-cli + } + + cfg.Providers.OpenAI.APIKey = apiKey + if apiBase != "" { + cfg.Providers.OpenAI.APIBase = apiBase + } + case "Anthropic": + cfg.Providers.Anthropic.APIKey = apiKey + if apiBase != "" { + cfg.Providers.Anthropic.APIBase = apiBase + } + case "Gemini": + cfg.Providers.Gemini.APIKey = apiKey + case "OpenRouter": + cfg.Providers.OpenRouter.APIKey = apiKey + if apiBase != "" { + cfg.Providers.OpenRouter.APIBase = apiBase + } + case "Zhipu": + cfg.Providers.Zhipu.APIKey = apiKey + case "Groq": + cfg.Providers.Groq.APIKey = apiKey + case "VLLM": + cfg.Providers.VLLM.APIKey = apiKey + if apiBase != "" { + cfg.Providers.VLLM.APIBase = apiBase + } + } + + if err := config.SaveConfig(getConfigPath(), cfg); err != nil { + fmt.Printf("Error saving config: %v\n", err) + os.Exit(1) + } + + fmt.Printf("โœ“ %s configuration saved.\n", provider) +} diff --git a/go.mod b/go.mod index c9c778f..8a5b0e0 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module gitea.kkkk.dev/DBT/clawgo +module clawgo go 1.25.5 diff --git a/pkg/agent/context.go b/pkg/agent/context.go index f0e3ea8..71ad6b4 100644 --- a/pkg/agent/context.go +++ b/pkg/agent/context.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" - "gitea.kkkk.dev/DBT/clawgo/pkg/providers" - "gitea.kkkk.dev/DBT/clawgo/pkg/skills" + "clawgo/pkg/logger" + "clawgo/pkg/providers" + "clawgo/pkg/skills" ) type ContextBuilder struct { @@ -159,8 +159,8 @@ func (cb *ContextBuilder) BuildMessages(history []providers.Message, summary str // Log system prompt summary for debugging (debug mode only) logger.DebugCF("agent", "System prompt built", map[string]interface{}{ - "total_chars": len(systemPrompt), - "total_lines": strings.Count(systemPrompt, "\n") + 1, + "total_chars": len(systemPrompt), + "total_lines": strings.Count(systemPrompt, "\n") + 1, "section_count": strings.Count(systemPrompt, "\n\n---\n\n") + 1, }) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 0d6e217..045b89d 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -15,13 +15,13 @@ import ( "regexp" "strings" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/cron" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" - "gitea.kkkk.dev/DBT/clawgo/pkg/providers" - "gitea.kkkk.dev/DBT/clawgo/pkg/session" - "gitea.kkkk.dev/DBT/clawgo/pkg/tools" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/cron" + "clawgo/pkg/logger" + "clawgo/pkg/providers" + "clawgo/pkg/session" + "clawgo/pkg/tools" ) type AgentLoop struct { @@ -45,7 +45,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers toolsRegistry.Register(&tools.WriteFileTool{}) toolsRegistry.Register(&tools.ListDirTool{}) toolsRegistry.Register(tools.NewExecTool(workspace)) - + if cs != nil { toolsRegistry.Register(tools.NewRemindTool(cs)) } @@ -214,12 +214,12 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage) // Log LLM request details logger.DebugCF("agent", "LLM request", map[string]interface{}{ - "iteration": iteration, - "model": al.model, - "messages_count": len(messages), - "tools_count": len(providerToolDefs), - "max_tokens": 8192, - "temperature": 0.7, + "iteration": iteration, + "model": al.model, + "messages_count": len(messages), + "tools_count": len(providerToolDefs), + "max_tokens": 8192, + "temperature": 0.7, "system_prompt_len": len(messages[0].Content), }) @@ -290,8 +290,8 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage) argsPreview := truncate(string(argsJSON), 200) logger.InfoCF("agent", fmt.Sprintf("Tool call: %s(%s)", tc.Name, argsPreview), map[string]interface{}{ - "tool": tc.Name, - "iteration": iteration, + "tool": tc.Name, + "iteration": iteration, }) result, err := al.tools.Execute(ctx, tc.Name, tc.Arguments) @@ -418,12 +418,12 @@ func (al *AgentLoop) processSystemMessage(ctx context.Context, msg bus.InboundMe // Log LLM request details logger.DebugCF("agent", "LLM request", map[string]interface{}{ - "iteration": iteration, - "model": al.model, - "messages_count": len(messages), - "tools_count": len(providerToolDefs), - "max_tokens": 8192, - "temperature": 0.7, + "iteration": iteration, + "model": al.model, + "messages_count": len(messages), + "tools_count": len(providerToolDefs), + "max_tokens": 8192, + "temperature": 0.7, "system_prompt_len": len(messages[0].Content), }) diff --git a/pkg/channels/base.go b/pkg/channels/base.go index 06bd71c..888d63c 100644 --- a/pkg/channels/base.go +++ b/pkg/channels/base.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" + "clawgo/pkg/bus" ) type Channel interface { diff --git a/pkg/channels/dingtalk.go b/pkg/channels/dingtalk.go index cf99cf7..b75f683 100644 --- a/pkg/channels/dingtalk.go +++ b/pkg/channels/dingtalk.go @@ -9,22 +9,22 @@ import ( "log" "sync" + "clawgo/pkg/bus" + "clawgo/pkg/config" "github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot" "github.com/open-dingtalk/dingtalk-stream-sdk-go/client" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" ) // DingTalkChannel implements the Channel interface for DingTalk (้’‰้’‰) // It uses WebSocket for receiving messages via stream mode and API for sending type DingTalkChannel struct { *BaseChannel - config config.DingTalkConfig - clientID string - clientSecret string - streamClient *client.StreamClient - ctx context.Context - cancel context.CancelFunc + config config.DingTalkConfig + clientID string + clientSecret string + streamClient *client.StreamClient + ctx context.Context + cancel context.CancelFunc // Map to store session webhooks for each chat sessionWebhooks sync.Map // chatID -> sessionWebhook } diff --git a/pkg/channels/discord.go b/pkg/channels/discord.go index 9756106..3e032e9 100644 --- a/pkg/channels/discord.go +++ b/pkg/channels/discord.go @@ -11,11 +11,11 @@ import ( "strings" "time" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/logger" + "clawgo/pkg/voice" "github.com/bwmarrin/discordgo" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" - "gitea.kkkk.dev/DBT/clawgo/pkg/voice" ) type DiscordChannel struct { diff --git a/pkg/channels/feishu.go b/pkg/channels/feishu.go index 6bfa4d0..9797e2e 100644 --- a/pkg/channels/feishu.go +++ b/pkg/channels/feishu.go @@ -12,9 +12,9 @@ import ( larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1" larkws "github.com/larksuite/oapi-sdk-go/v3/ws" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/logger" ) type FeishuChannel struct { diff --git a/pkg/channels/maixcam.go b/pkg/channels/maixcam.go index 80f367b..c00e726 100644 --- a/pkg/channels/maixcam.go +++ b/pkg/channels/maixcam.go @@ -7,9 +7,9 @@ import ( "net" "sync" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/logger" ) type MaixCamChannel struct { diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index 13dfe73..03add5f 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -11,9 +11,9 @@ import ( "fmt" "sync" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/logger" ) type Manager struct { diff --git a/pkg/channels/qq.go b/pkg/channels/qq.go index cbc88c8..290d4da 100644 --- a/pkg/channels/qq.go +++ b/pkg/channels/qq.go @@ -13,9 +13,9 @@ import ( "github.com/tencent-connect/botgo/token" "golang.org/x/oauth2" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/logger" ) type QQChannel struct { diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go index 0ee09c0..88d75e8 100644 --- a/pkg/channels/telegram.go +++ b/pkg/channels/telegram.go @@ -16,9 +16,9 @@ import ( "github.com/mymmrac/telego" "github.com/mymmrac/telego/telegoutil" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/voice" + "clawgo/pkg/bus" + "clawgo/pkg/config" + "clawgo/pkg/voice" ) type TelegramChannel struct { @@ -125,14 +125,14 @@ func (c *TelegramChannel) Send(ctx context.Context, msg bus.OutboundMessage) err // Try to edit placeholder if pID, ok := c.placeholders.Load(msg.ChatID); ok { c.placeholders.Delete(msg.ChatID) - + _, err := c.bot.EditMessageText(ctx, &telego.EditMessageTextParams{ ChatID: chatID, MessageID: pID.(int), Text: htmlContent, ParseMode: telego.ModeHTML, }) - + if err == nil { return nil } diff --git a/pkg/channels/whatsapp.go b/pkg/channels/whatsapp.go index 07bf1c1..24fb09f 100644 --- a/pkg/channels/whatsapp.go +++ b/pkg/channels/whatsapp.go @@ -10,8 +10,8 @@ import ( "github.com/gorilla/websocket" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" + "clawgo/pkg/bus" + "clawgo/pkg/config" ) type WhatsAppChannel struct { diff --git a/pkg/providers/http_provider.go b/pkg/providers/http_provider.go index 7968d83..0028777 100644 --- a/pkg/providers/http_provider.go +++ b/pkg/providers/http_provider.go @@ -15,7 +15,7 @@ import ( "net/http" "strings" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" + "clawgo/pkg/config" ) type HTTPProvider struct { diff --git a/pkg/server/server.go b/pkg/server/server.go index 7906ddd..48e8e53 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -6,8 +6,8 @@ import ( "net/http" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/config" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/config" + "clawgo/pkg/logger" ) type Server struct { @@ -35,7 +35,7 @@ func (s *Server) Start() error { logger.InfoCF("server", "Starting HTTP server", map[string]interface{}{ "addr": addr, }) - + // Check/log indicating it's ready for reverse proxying (per requirement) logger.InfoC("server", "Server ready for reverse proxying") diff --git a/pkg/session/manager.go b/pkg/session/manager.go index ba4ec03..e1aa0c7 100644 --- a/pkg/session/manager.go +++ b/pkg/session/manager.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/providers" + "clawgo/pkg/providers" ) type Session struct { diff --git a/pkg/tools/registry.go b/pkg/tools/registry.go index 3a14c9e..006333d 100644 --- a/pkg/tools/registry.go +++ b/pkg/tools/registry.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/logger" ) type ToolRegistry struct { diff --git a/pkg/tools/remind.go b/pkg/tools/remind.go index 0fff619..147b43d 100644 --- a/pkg/tools/remind.go +++ b/pkg/tools/remind.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/cron" + "clawgo/pkg/cron" ) type RemindTool struct { diff --git a/pkg/tools/subagent.go b/pkg/tools/subagent.go index 6146340..8f4bc97 100644 --- a/pkg/tools/subagent.go +++ b/pkg/tools/subagent.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/bus" - "gitea.kkkk.dev/DBT/clawgo/pkg/providers" + "clawgo/pkg/bus" + "clawgo/pkg/providers" ) type SubagentTask struct { diff --git a/pkg/voice/transcriber.go b/pkg/voice/transcriber.go index e6142eb..6fe23c8 100644 --- a/pkg/voice/transcriber.go +++ b/pkg/voice/transcriber.go @@ -12,7 +12,7 @@ import ( "path/filepath" "time" - "gitea.kkkk.dev/DBT/clawgo/pkg/logger" + "clawgo/pkg/logger" ) type GroqTranscriber struct {