Query Your Visitors From the CLI (No Dashboard Required)

Eduard CristeaFounder, Eyepup6 min read

The Eyepup CLI is a single binary you install once and run from any terminal. It exposes the same per-visitor AI dossier data as the dashboard — list visitors, fetch a dossier, list friction patterns, get the evidence video, watch live traffic — all as plain shell commands. It works inside any AI agent that can run shell (Claude Code, Cursor, Aider, Continue, plain ChatGPT-with-shell-access).

Key takeaways

  • The CLI is the lowest-friction surface for agent integration — anything that runs bash can run eyepup.
  • Same data as the dashboard. Same auth as the dashboard. Different surface.
  • Designed to be piped, scripted, and chained — --json output, exit codes, no decorations.
  • Free during private beta. Open-source CLI on GitHub.

Install

npm install -g eyepup

(The CLI is a single Node.js package — Node 18 or newer. Brew tap and one-line installer are coming.)

Verify:

$ eyepup --version
eyepup 0.4.2

Authenticate

$ eyepup login
✓ Open https://eyepup.com/cli-auth?code=ABCD-EFGH in your browser
✓ Logged in as eduard@eyepup.com (team: Eyepup)

The CLI uses device-flow auth — no manual API key needed. Tokens are stored in ~/.config/eyepup/credentials.json (mode 0600) and rotate automatically.

Command reference

eyepup whoami

$ eyepup whoami
team:    Eyepup (acme-prod)
user:    eduard@eyepup.com
role:    admin
sites:   eyepup.com, app.eyepup.com

eyepup visitors

List visitors with filters.

# Last 24h, filtered to "blocked by price"
$ eyepup visitors --recent 24h --filter blocked-by:price

# Filter by site
$ eyepup visitors --site eyepup.com

# Filter by email
$ eyepup visitors --filter email:sarah@acme.com

# JSON output for scripting
$ eyepup visitors --recent 7d --json | jq '.[] | .blocked_by'

# Pipe to fzf for interactive picking
$ eyepup visitors --recent 24h | fzf | awk '{print $1}' | xargs eyepup dossier

eyepup dossier <id>

Fetch one visitor's full AI dossier.

$ eyepup dossier 0xae1b
Visitor 0xae1b… landed on /pricing from a Reddit referral.
Spent 92s on the page, hovered the annual toggle 3× without
clicking it, opened the FAQ once, scrolled to the bottom, then
back to the hero CTA, then left.

  Blocked-by:    price uncertainty (medium confidence)
  Suggested fix: surface the annualized price next to the monthly
                 price by default
  Channel:       organic / Reddit
  Heat score:    72 / 100
  Sessions:      1
  Evidence:      eyepup evidence 0xae1b  (28s mp4)

eyepup patterns

Aggregated friction patterns across all visitors.

$ eyepup patterns --week-over-week
Top friction patterns this week:
  ▲ +12  price uncertainty   (47 visitors)
  ▼  -3  form length          (12 visitors)
  ▲  +5  mobile CTA hidden    (8 visitors)
  ↔   0  trust signal weak    (6 visitors)

# JSON output for scripting
$ eyepup patterns --since 30d --json

eyepup paths

Most-traveled friction paths.

$ eyepup paths --since 7d --top 5
1. /landing → /pricing → bounce            (124 visitors, 38% drop)
2. /signup → step 2 → bounce               ( 71 visitors, 22% drop)
3. /pricing → /pricing → /pricing → bounce ( 44 visitors, "thrash" pattern)
4. /demo → /pricing → bounce               ( 31 visitors, message mismatch)
5. /home → /features → /pricing → bounce   ( 28 visitors, FOMO bail)

eyepup evidence <session-id>

Print the URL of the rendered MP4 the AI watched.

$ eyepup evidence 0xae1b
https://evidence.eyepup.com/<presigned-url>.mp4

# Open in default video player
$ eyepup evidence 0xae1b --open

# Pipe through ffmpeg, mpv, etc.
$ eyepup evidence 0xae1b --json | jq -r .url | xargs mpv

eyepup live

Stream new visitors as they arrive.

$ eyepup live
[14:32:01] new visitor  0xc4e8  /pricing from /
[14:32:14] new visitor  0xa19a  /home from twitter.com
[14:33:02] friction     0xc4e8  hovered annual toggle, 4s dwell
[14:33:18] dossier      0xc4e8  blocked by price uncertainty
[14:34:01] conversion   0x99fb  signed up (sarah@acme.com)

eyepup funnels

Funnel-level metrics.

$ eyepup funnels signup
Funnel: signup (last 7d)
  Step 1 — landing      : 1,247 visitors
  Step 2 — clicked CTA  :   894 (71.7%)
  Step 3 — form opened  :   612 (49.0%)
  Step 4 — submitted    :   328 (26.3%)
  Step 5 — verified     :   247 (19.8%)

Top friction at step 3 → 4: form length (47 visitors)

eyepup config

Manage local CLI settings.

$ eyepup config get default-site
$ eyepup config set default-site eyepup.com
$ eyepup config list

Workflows

Workflow 1 — agent-driven debugging

# Inside Claude Code
You: A customer says they couldn't sign up. Email: sarah@acme.com.

Claude: $ eyepup visitors --filter email:sarah@acme.com --json
        $ eyepup dossier <her-id>
        Found her: stuck on company-size dropdown, hidden below
        the fold on mobile. Bug at /signup line 84.

Workflow 2 — CI sanity check

A GitHub Action that fails the build if a regression-class friction pattern shows up after deploy:

# .github/workflows/post-deploy.yml
- name: Wait 10 minutes for traffic
  run: sleep 600

- name: Check for regression patterns
  run: |
    NEW_PATTERNS=$(eyepup patterns --since 30m --json \
      | jq '[.[] | select(.delta > 5)] | length')
    if [ "$NEW_PATTERNS" -gt 0 ]; then
      echo "::error::Detected $NEW_PATTERNS new friction patterns"
      eyepup patterns --since 30m
      exit 1
    fi

Workflow 3 — Slack standup digest

Cron job that posts the previous day's friction patterns to Slack:

#!/usr/bin/env bash
set -euo pipefail
DIGEST=$(eyepup patterns --since 24h --week-over-week)
curl -X POST -H 'Content-type: application/json' \
  --data "{\"text\":\"\`\`\`$DIGEST\`\`\`\"}" \
  "$SLACK_WEBHOOK_URL"

Workflow 4 — pipe into fzf for human review

$ eyepup visitors --recent 7d \
    | fzf --preview 'eyepup dossier {1}' \
    | awk '{print $1}' \
    | xargs eyepup evidence --open

Picks a visitor interactively, previews the dossier, and opens the evidence MP4.

Output formats

All commands support --json for machine output:

$ eyepup visitors --recent 24h --json | jq '.[] | select(.heat > 80)'
$ eyepup patterns --since 7d --json > patterns.json
$ eyepup dossier 0xae1b --json | jq -r '.suggested_fix'

Default output is human-readable, color-aware, and respects NO_COLOR=1 for CI logs.

Authentication and security

  • Tokens stored in ~/.config/eyepup/credentials.json, mode 0600
  • Tokens rotate automatically; manual rotate with eyepup login --force
  • Tokens inherit role of the user who created them
  • All traffic to api.eyepup.com over HTTPS
  • eyepup logout clears local credentials and revokes the token server-side

Frequently asked questions

Does the CLI need Eyepup-specific authentication?

The first time you run eyepup login, it opens a browser for a device-flow handshake — same OAuth pattern as vercel login or gh auth login. After that, every command uses the locally cached token. No manual API key copying.

Can I use it inside Docker / GitHub Actions?

Yes. Generate a long-lived API token from the dashboard (Settings → API tokens), set EYEPUP_API_KEY=<token> in the env, and the CLI uses it instead of the device-flow credentials. Works in CI.

Is there a Python or TypeScript SDK?

Not yet — the CLI plus a stable HTTP API covers most use cases. SDKs are on the roadmap.

Why a CLI instead of just a REST API?

Because every AI coding agent already knows how to run bash. None of them know how to call your custom REST API without you teaching them. A CLI is the lowest-friction agent surface on the planet.

Can I script live alerts?

Yes — eyepup live streams events as they arrive, one per line, machine-readable with --json. Pipe into awk, jq, or any alert system.

Can I use the CLI without an AI agent?

Of course. Plenty of humans run eyepup live in a terminal pane while debugging. Some teams use it as a literal "live ops feed" during launches.

Related reads