Skip to content

Added whoami#42

Merged
ivpusic merged 5 commits into
mainfrom
tracking_source
Mar 23, 2026
Merged

Added whoami#42
ivpusic merged 5 commits into
mainfrom
tracking_source

Conversation

@ivpusic

@ivpusic ivpusic commented Mar 20, 2026

Copy link
Copy Markdown
Member

Added new cli command whoami and added tracking of user actions (when enabled) that are based on the actual user/team ids

@cursor

cursor Bot commented Mar 20, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches the root command pre-run path for all CLI invocations by adding a best-effort /whoami lookup and local disk cache writes; failures are swallowed but latency/permissions issues could affect startup behavior. Telemetry user identification semantics change from a persistent anon UUID to anonymous unless a customer ID is resolved.

Overview
Adds a new dune whoami command that calls /whoami and prints the current key’s handle, customer_id, and API key ID (text or JSON output).

Updates telemetry user identification: the tracker now defaults to anonymous and cli/root.go best-effort resolves the customer_id via WhoAmI() and sets it on the tracker, using a new authconfig YAML cache (user_identity.yaml) keyed by SHA-256 of the API key to avoid repeated API calls. Dependencies are updated to duneapi-client-go v0.4.4, and the old persisted anonymous UUID mechanism is removed.

Written by Cursor Bugbot for commit 04fc3f9. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Empty UserID causes Amplitude to silently drop events
    • Initialized tracker with default 'anonymous' userID to ensure all events meet Amplitude's 5-character MinIDLength requirement and are not silently dropped.

Create PR

Or push these changes by commenting:

@cursor push e2ae6dca7c
Preview (e2ae6dca7c)
diff --git a/tracking/tracking.go b/tracking/tracking.go
--- a/tracking/tracking.go
+++ b/tracking/tracking.go
@@ -41,12 +41,13 @@
 	return &Tracker{
 		client:  amplitude.NewClient(ampConfig),
 		version: cfg.CLIVersion,
+		userID:  "anonymous",
 		enabled: true,
 	}
 }
 
 // SetUserID sets the real user identity (e.g. "user_123") for all subsequent events.
-// If not called, events are sent with an empty UserID.
+// If not called, events are sent with UserID "anonymous".
 func (t *Tracker) SetUserID(id string) {
 	t.userID = id
 }

Comment thread tracking/tracking.go Outdated
@ivpusic ivpusic changed the title Tracking source Added whoami Mar 20, 2026
@ivpusic

ivpusic commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Sim commands never resolve user identity for tracking
    • Added customer ID resolution to simPreRun function to ensure sim commands properly identify users for analytics tracking since the sim command's PersistentPreRunE overrides the root command's hook.

Create PR

Or push these changes by commenting:

@cursor push 694a644423
Preview (694a644423)
diff --git a/cli/root.go b/cli/root.go
--- a/cli/root.go
+++ b/cli/root.go
@@ -80,7 +80,7 @@
 
 		// Resolve customer identity for analytics (best-effort, never blocks the CLI).
 		if tr := cmdutil.TrackerFromCmd(cmd); tr != nil {
-			if customerID := resolveCustomerID(client, env.APIKey); customerID != "" {
+			if customerID := ResolveCustomerID(client, env.APIKey); customerID != "" {
 				tr.SetUserID(customerID)
 			}
 		}
@@ -149,11 +149,11 @@
 	}
 }
 
-// resolveCustomerID returns the customer_id associated with the given API key.
+// ResolveCustomerID returns the customer_id associated with the given API key.
 // The customer_id may represent a user ("user_123") or a team ("team_456").
 // It uses a local cache to avoid calling /api/whoami on every invocation.
 // On any error it returns "" silently — analytics should never block the CLI.
-func resolveCustomerID(client dune.DuneClient, apiKey string) string {
+func ResolveCustomerID(client dune.DuneClient, apiKey string) string {
 	keyHash := authconfig.HashAPIKey(apiKey)
 
 	// Try the cache first.

diff --git a/cmd/sim/sim.go b/cmd/sim/sim.go
--- a/cmd/sim/sim.go
+++ b/cmd/sim/sim.go
@@ -8,6 +8,9 @@
 
 	"github.com/spf13/cobra"
 
+	"github.com/duneanalytics/duneapi-client-go/config"
+	"github.com/duneanalytics/duneapi-client-go/dune"
+
 	"github.com/duneanalytics/cli/authconfig"
 	"github.com/duneanalytics/cli/cmd/sim/evm"
 	"github.com/duneanalytics/cli/cmd/sim/svm"
@@ -50,6 +53,17 @@
 	// correct duration for telemetry.
 	cmdutil.SetStartTime(cmd, time.Now())
 
+	// Resolve customer identity for analytics (best-effort, never blocks the CLI).
+	// We try to resolve the Dune API key (not the Sim API key) to identify the user.
+	if tr := cmdutil.TrackerFromCmd(cmd); tr != nil {
+		if env := resolveDuneEnv(); env != nil {
+			client := dune.NewDuneClient(env)
+			if customerID := resolveCustomerIDForSim(client, env.APIKey); customerID != "" {
+				tr.SetUserID(customerID)
+			}
+		}
+	}
+
 	// Commands like `sim evm supported-chains` that hit public endpoints
 	// don't require an API key. Provide a bare (unauthenticated) client so
 	// they can still use the shared HTTP infrastructure and error handling.
@@ -103,3 +117,54 @@
 	}
 	return v.(*SimClient)
 }
+
+// resolveDuneEnv attempts to resolve the Dune API key from environment variables
+// or the config file, returning a Dune environment configuration.
+// Returns nil if no Dune API key is available (this is not an error for sim commands).
+func resolveDuneEnv() *config.Env {
+	// Try environment variable first.
+	env, err := config.FromEnvVars()
+	if err == nil {
+		return env
+	}
+
+	// Try config file.
+	cfg, err := authconfig.Load()
+	if err != nil || cfg == nil {
+		return nil
+	}
+
+	key := strings.TrimSpace(cfg.APIKey)
+	if key == "" {
+		return nil
+	}
+
+	return config.FromAPIKey(key)
+}
+
+// resolveCustomerIDForSim is a wrapper around cli.ResolveCustomerID that
+// can be called from the sim package. It must be declared here to avoid
+// a circular import between cli and cmd/sim.
+func resolveCustomerIDForSim(client dune.DuneClient, apiKey string) string {
+	keyHash := authconfig.HashAPIKey(apiKey)
+
+	// Try the cache first.
+	cached, err := authconfig.LoadIdentity()
+	if err == nil && cached != nil && cached.APIKeyHash == keyHash && cached.CustomerID != "" {
+		return cached.CustomerID
+	}
+
+	// Cache miss or stale — call the API.
+	resp, err := client.WhoAmI()
+	if err != nil || resp == nil || resp.CustomerID == "" {
+		return ""
+	}
+
+	// Persist for next time (best-effort).
+	_ = authconfig.SaveIdentity(&authconfig.UserIdentity{
+		CustomerID: resp.CustomerID,
+		APIKeyHash: keyHash,
+	})
+
+	return resp.CustomerID
+}

Comment @cursor review or bugbot run to trigger another review on this PR

Comment thread cli/root.go
Comment thread cli/root.go
Comment thread authconfig/identity.go
@ivpusic ivpusic merged commit acea182 into main Mar 23, 2026
2 checks passed
@ivpusic ivpusic deleted the tracking_source branch March 23, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants