diff --git a/cmd/mcp/main.go b/cmd/mcp/main.go index ee0a275..c9d503c 100644 --- a/cmd/mcp/main.go +++ b/cmd/mcp/main.go @@ -13,6 +13,7 @@ import ( "github.com/lukaszraczylo/claude-mnemonic/internal/config" "github.com/lukaszraczylo/claude-mnemonic/internal/mcp" + "github.com/lukaszraczylo/claude-mnemonic/internal/telemetry" "github.com/lukaszraczylo/claude-mnemonic/internal/watcher" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -60,6 +61,8 @@ func main() { // Start file watchers for config changes startWatchers() + telemetry.Ping("claude-mnemonic", Version) + // Create and run MCP server server := mcp.NewServer(client, workerURL, *project, Version) log.Info().Str("project", *project).Str("version", Version).Str("worker", workerURL).Msg("Starting MCP server") diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go new file mode 100644 index 0000000..f74a33d --- /dev/null +++ b/internal/telemetry/telemetry.go @@ -0,0 +1,55 @@ +// Package telemetry sends anonymous OSS usage pings. +package telemetry + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "strings" + "time" + + "github.com/rs/zerolog/log" +) + +const endpoint = "https://oss.raczylo.com/v1/ping" + +type pingPayload struct { + Project string `json:"project"` + Version string `json:"version"` + Ts int64 `json:"ts"` +} + +// Ping fires an anonymous analytics ping asynchronously. Never blocks the caller. +// Silently skips dev/dirty builds; all errors are logged at debug level only. +func Ping(project, version string) { + if version == "dev" || strings.Contains(version, "dirty") { + return + } + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + body, err := json.Marshal(pingPayload{ + Project: project, + Version: strings.TrimPrefix(version, "v"), + Ts: time.Now().Unix(), + }) + if err != nil { + return + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body)) + if err != nil { + return + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Debug().Err(err).Msg("telemetry ping failed") + return + } + resp.Body.Close() + }() +}