diff --git a/Makefile b/Makefile index c54be1555..dd4914868 100644 --- a/Makefile +++ b/Makefile @@ -223,6 +223,25 @@ install: build @echo "Installed uninstaller script to $(INSTALL_BIN_DIR)/$(BINARY_NAME)-uninstall" @echo "Installation complete!" +## install-pico: Build core, web and install picoclaw and picoclaw-launcher to system +install-pico: build build-launcher + @echo "Installing $(BINARY_NAME) and picoclaw-launcher..." + @mkdir -p $(INSTALL_BIN_DIR) + # Copy core binary with temporary suffix to ensure atomic update + @cp $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_BIN_DIR)/$(BINARY_NAME)$(INSTALL_TMP_SUFFIX) + @chmod +x $(INSTALL_BIN_DIR)/$(BINARY_NAME)$(INSTALL_TMP_SUFFIX) + @mv -f $(INSTALL_BIN_DIR)/$(BINARY_NAME)$(INSTALL_TMP_SUFFIX) $(INSTALL_BIN_DIR)/$(BINARY_NAME) + @echo "Installed binary to $(INSTALL_BIN_DIR)/$(BINARY_NAME)" + # Copy launcher binary with temporary suffix + @cp $(BUILD_DIR)/picoclaw-launcher $(INSTALL_BIN_DIR)/picoclaw-launcher$(INSTALL_TMP_SUFFIX) + @chmod +x $(INSTALL_BIN_DIR)/picoclaw-launcher$(INSTALL_TMP_SUFFIX) + @mv -f $(INSTALL_BIN_DIR)/picoclaw-launcher$(INSTALL_TMP_SUFFIX) $(INSTALL_BIN_DIR)/picoclaw-launcher + @echo "Installed launcher to $(INSTALL_BIN_DIR)/picoclaw-launcher" + @cp scripts/uninstall.sh $(INSTALL_BIN_DIR)/$(BINARY_NAME)-uninstall + @chmod +x $(INSTALL_BIN_DIR)/$(BINARY_NAME)-uninstall + @echo "Installed uninstaller script to $(INSTALL_BIN_DIR)/$(BINARY_NAME)-uninstall" + @echo "Installation complete!" + ## uninstall: Remove picoclaw from system uninstall: @echo "Uninstalling $(BINARY_NAME)..." diff --git a/pkg/config/config.go b/pkg/config/config.go index 3a4c0f6a6..b6a2c3ef0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1048,10 +1048,10 @@ type SearXNGConfig struct { } type GLMSearchConfig struct { - Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"` + Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"` apiKey string secDirty bool - BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"` + BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"` // SearchEngine specifies the search backend: "search_std" (default), // "search_pro", "search_pro_sogou", or "search_pro_quark". SearchEngine string `json:"search_engine" env:"PICOCLAW_TOOLS_WEB_GLM_SEARCH_ENGINE"` @@ -1279,7 +1279,10 @@ func LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { - logger.WarnF("config file not found, using default config", map[string]any{"path": path}) + logger.WarnF( + "config file not found, using default config", + map[string]any{"path": path}, + ) return DefaultConfig(), nil } logger.Errorf("failed to read config file: %v", err) @@ -1302,7 +1305,10 @@ func LoadConfig(path string) (*Config, error) { var cfg *Config switch versionInfo.Version { case 0: - logger.InfoF("config migrate start", map[string]any{"from": versionInfo.Version, "to": CurrentVersion}) + logger.InfoF( + "config migrate start", + map[string]any{"from": versionInfo.Version, "to": CurrentVersion}, + ) // Legacy config (no version field) v, e := loadConfigV0(data) if e != nil { @@ -1310,10 +1316,16 @@ func LoadConfig(path string) (*Config, error) { } cfg, e = v.Migrate() if e != nil { - logger.ErrorF("config migrate fail", map[string]any{"from": versionInfo.Version, "to": CurrentVersion}) + logger.ErrorF( + "config migrate fail", + map[string]any{"from": versionInfo.Version, "to": CurrentVersion}, + ) return nil, e } - logger.InfoF("config migrate success", map[string]any{"from": versionInfo.Version, "to": CurrentVersion}) + logger.InfoF( + "config migrate success", + map[string]any{"from": versionInfo.Version, "to": CurrentVersion}, + ) err = makeBackup(path) if err != nil { return nil, err @@ -1321,13 +1333,19 @@ func LoadConfig(path string) (*Config, error) { // Load existing security config and merge with migrated one to prevent data loss existingSec, secErr := loadSecurityConfig(securityPath(path)) if secErr != nil { - logger.WarnF("failed to load existing security config during migration", map[string]any{"error": secErr}) + logger.WarnF( + "failed to load existing security config during migration", + map[string]any{"error": secErr}, + ) } if existingSec != nil && cfg.security != nil { cfg.security = mergeSecurityConfig(existingSec, cfg.security) // Re-apply the merged security config to update all channels and models if err = applySecurityConfig(cfg, cfg.security); err != nil { - logger.WarnF("failed to re-apply merged security config during migration", map[string]any{"error": err}) + logger.WarnF( + "failed to re-apply merged security config during migration", + map[string]any{"error": err}, + ) } } defer func(cfg *Config) { @@ -1348,7 +1366,10 @@ func LoadConfig(path string) (*Config, error) { tmpCfgMigrated, e := tmpCfg.Migrate() if e != nil { - logger.ErrorF("config migrate fail", map[string]any{"from": versionInfo.Version, "to": CurrentVersion}) + logger.ErrorF( + "config migrate fail", + map[string]any{"from": versionInfo.Version, "to": CurrentVersion}, + ) return nil, e } @@ -1371,9 +1392,11 @@ func LoadConfig(path string) (*Config, error) { for _, m := range cfg.ModelList { for _, k := range m.apiKeys { if k != "" && !strings.HasPrefix(k, "enc://") && !strings.HasPrefix(k, "file://") { - fmt.Fprintf(os.Stderr, + fmt.Fprintf( + os.Stderr, "picoclaw: warning: model %q has a plaintext api_key; call SaveConfig to encrypt it\n", - m.ModelName) + m.ModelName, + ) break // Only warn once per model } } diff --git a/pkg/providers/vertex/provider.go b/pkg/providers/vertex/provider.go index 9c1ea41dc..6da3c4a57 100644 --- a/pkg/providers/vertex/provider.go +++ b/pkg/providers/vertex/provider.go @@ -87,7 +87,14 @@ func (p *Provider) buildURL(model string, action string) string { if region == "" { region = "us-central1" } - baseURL = fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:%s", region, p.projectID, region, model, action) + baseURL = fmt.Sprintf( + "https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:%s", + region, + p.projectID, + region, + model, + action, + ) } // Only append ?key= for custom apiBase endpoints @@ -438,7 +445,11 @@ func (p *Provider) ChatStream( if part.FunctionCall != nil { argsJSON, _ := json.Marshal(part.FunctionCall.Args) toolCall := ToolCall{ - ID: fmt.Sprintf("call_%s_%d", part.FunctionCall.Name, time.Now().UnixNano()), + ID: fmt.Sprintf( + "call_%s_%d", + part.FunctionCall.Name, + time.Now().UnixNano(), + ), Name: part.FunctionCall.Name, Arguments: part.FunctionCall.Args, Function: &FunctionCall{ diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index 3e17f74ec..a0822c8d0 100755 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -20,6 +20,14 @@ else echo "Executable ${INSTALL_BIN_DIR}/${BINARY_NAME} not found. Skipping." fi +# Remove the launcher binary +if [ -f "${INSTALL_BIN_DIR}/picoclaw-launcher" ]; then + rm -f "${INSTALL_BIN_DIR}/picoclaw-launcher" + echo "Removed executable: ${INSTALL_BIN_DIR}/picoclaw-launcher" +else + echo "Executable ${INSTALL_BIN_DIR}/picoclaw-launcher not found. Skipping." +fi + # Remove the workspace and configurations if [ -d "${PICOCLAW_HOME}" ]; then rm -rf "${PICOCLAW_HOME}"