npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

airanker

v0.1.5

Published

Measure how visible a brand is in AI/search results. Deterministic AI-brand-visibility scoring powered by Bright Data.

Readme

AIRanker

Measure how visible a brand is in AI and search results. Run a controlled set of queries across ChatGPT, Perplexity, Gemini, Google AI Mode and Google SERP, then get a deterministic, reproducible AI-visibility score for that brand — not a vibe, not one model's opinion: the same input always yields the same score.

npx airanker opencmo.site

What it does

  1. Analyzes the brand — fetches the website, uses an LLM to build a grounded BrandProfile (products, use-cases, keywords; competitors only if the site names them).
  2. Generates prompts — a configurable number (default 8) of realistic user queries covering 9 intent classes (best-of, comparison, alternatives, problem/solution, buyer, …). Deduplicated.
  3. Collects raw evidence — each query is run against every AI engine plus Google SERP via Bright Data. Raw responses are stored verbatim.
  4. Extracts evidence — an LLM converts each raw response into structured observations (mentioned? recommended? position? competitors?). Position is null unless the raw response states a real rank. The LLM never invents data — if extraction fails or the engine failed, the observation says so.
  5. Scores deterministically — a pure function of the observations. No LLM in the scoring path. Formula is documented below.
  6. Explains — an LLM summarizes weaknesses/opportunities/competitor insights grounded only in the structured observations.

The three layers stay strictly separated: raw evidence (Bright Data) → LLM interpretation (extraction/summarization) → AIRanker score (deterministic arithmetic). Nothing is blurred.

Install / run

npm install -g airanker        # or: npm i -D airanker
airanker opencmo.site

First run or any command checks your config. If Bright Data/LLM keys are missing, AIRanker walks you through setup interactively:

✓ AIRanker setup
  Bright Data API key (won't be echoed): ••••••••••
  Which LLM provider?  ▸ Gemini API (Google AI Studio)
                        OpenAI API
                        Anthropic API
                        claude CLI (not detected)

Values are written to ~/.airanker/.env (never printed to the terminal).

Commands

airanker opencmo.site                # bare scan (equivalent to scan)
airanker scan opencmo.site           # full scan
airanker scan opencmo.site --queries 100
airanker scan opencmo.site --queries 100 --output report.json
airanker scan opencmo.site --no-google
airanker scan opencmo.site --engines chatgpt,perplexity
airanker scan opencmo.site --raw     # embed raw provider responses in JSON report
airanker report report.json          # render a saved report
airanker setup                       # re-run interactive configuration
airanker --help

Offline demo (no keys, mock stack — tests/demo only):

airanker --demo opencmo.site

--mock uses the mock Bright Data provider only (real LLM still required); --demo uses mock Bright Data + mock LLM.

Required environment

| Variable | Purpose | | --- | --- | | BRIGHTDATA_API_KEY | Bright Data API key (required) | | AIRANKER_LLM_PROVIDER | openai · anthropic · gemini · claude · codex · opencode | | AIRANKER_OPENCODE_MODEL | opencode model, default opencode/deepseek-v4-flash-free | | OPENAI_API_KEY | used when provider is openai | | ANTHROPIC_API_KEY | used when provider is anthropic | | GEMINI_API_KEY | Google AI Studio key (AIza...) |

If AIRANKER_LLM_PROVIDER is unset, the first available API key wins. Local CLI providers (claude, codex, opencode) auto-detected on PATH; never required, never assumed.

Copy .env.example to ~/.airanker/.env (or project .env) for the full list, including dataset-id overrides, concurrency, timeouts and debug flags.

Bright Data setup

  1. Create an account at brightdata.com.
  2. SERP API zone (serp_api1 default) for Google results.
  3. The AI-engine scrapers need no zone — they use the Scraper API (POST /datasets/v3/scrape) with dataset ids:
    • ChatGPT gd_m7aof0k82r803d5bjm
    • Google AI Mode gd_mcswdt6z2elth3zqr2
    • Perplexity / Gemini / Copilot: auto-discovered from your account via GET /datasets/v3/scrapers, or set BRIGHTDATA_PERPLEXITY_DATASET_ID / BRIGHTDATA_GEMINI_DATASET_ID / BRIGHTDATA_COPILOT_DATASET_ID.
  4. Grab the key at Settings → Users → API key (get.brightdata.com/ybqohltzjud2), then set it:
export BRIGHTDATA_API_KEY=your-key
airanker opencmo.site

LLM setup

Pick any one API provider. Keys are never logged; setup uses hidden prompts.

export OPENAI_API_KEY=sk-...
export GEMINI_API_KEY=AIza...          # Google AI Studio
export ANTHROPIC_API_KEY=sk-ant-...
airanker scan opencmo.site

Or set AIRANKER_LLM_PROVIDER=gemini etc. You may also use a locally installed claude / codex / opencode CLI — set AIRANKER_LLM_PROVIDER=claude.

Scoring methodology

Every observation contributes to four deterministic components. All are pure functions of the observations array.

mentionRate   = mentioned queries      / total queries
recommendRate = recommended queries    / total queries
positionComp  = 1 - clamp((avgPosition - 1) / 9, 0, 1)     (positions known only)
                 → falls back to mentionRate when no positions are known
competitComp  = 1 - competitorShare

competitorShare = competitor mentions / (competitor mentions + brand recommendations)

AI Visibility = round(100 × (0.30·mentionRate + 0.30·recommendRate +
                            0.20·positionComp + 0.20·competitComp))
  • Position 1 → full position credit; position 10+ → zero; linear between.
  • Avg. position uses only observations where the raw response stated a position. Unknown positions are null, never guessed.
  • competitorShare measures how often competitors are recommended instead of the brand. No recommendations at all → competitors effectively "won" (share 1), which is the honest reading of an unseen brand.
  • Formula version is stored in every report (airanker-v1), so scores stay comparable across releases.

JSON report

airanker scan opencmo.site --output report.json

Writes a versioned, Zod-validated report:

{
  "schemaVersion": 1,
  "format": "airanker-report",
  "report": {
    "version": 1,
    "generatedAt": "2026-08-17T00:00:00.000Z",
    "brandProfile": { /* BrandProfile */ },
    "queries": [ /* TestQuery */ ],
    "observations": [ /* QueryObservation */ ],
    "rawEvidence": [ /* only with --raw */ ],
    "aggregateMetrics": { /* mentionRate, byEngine, byCategory, ... */ },
    "score": { "overall": 74, "components": { ... }, "formulaVersion": "airanker-v1" },
    "recommendations": { "summary": "...", "strengths": [], "weaknesses": [], ... }
  }
}

Render it later: airanker report report.json.

Architecture

DOMAIN
  ↓
Fetch/analyze website ──► LLM: BrandProfile (Zod)
  ↓
LLM: generate search queries (dedup)
  ↓
Bright Data: collect AI + SERP evidence        (retries · timeouts · partial failure)
  ↓
LLM: extract structured observations          (positions = null unless stated)
  ↓
Deterministic scoring engine                  (pure function, formula versioned)
  ↓
LLM: recommendations (grounded in observations only)
  ↓
CLI report + optional JSON

src/

cli/            commander CLI, interactive setup, progress, Ctrl+C
providers/
  brightnessdata/  BrightDataClient (Scraper + SERP APIs), mock provider
  llm/             LLMProvider interface, OpenAi/Anthropic/Gemini, local CLIs
brand/          website fetch + BrandProfile
queries/        query generation + semantic-ish dedup
analysis/       evidence extraction (LLM as extractor, never oracle)
scoring/        pure deterministic scoring
recommendations/ grounded summarization
report/         stable versioned JSON + terminal renderer
config/         env loading (~/.airanker/.env + project .env)

Providers are interchangeable: LLMProvider has one method (generateStructured<T>({system, user, schema})) and BrightDataProvider has collectAi / collectSerp. Swap in your own in under 20 lines.

Reliability & security

  • Strict Zod validation on every structured LLM output; null position rather than fabrication.
  • Retries with exponential backoff, request timeouts, concurrency limits, structured debug logging (--debug).
  • Partial scans: a failing engine becomes a failed RawEvidence entry; only a total provider outage aborts the scan.
  • Domain input sanitized (no URLs, paths or shell metachars reach fetch or prompts).
  • Scraped content is treated as untrusted text — never executed.
  • Subprocess execution restricted to an explicit allow-list (claude/codex/opencode) with fixed args, timeouts, and non-shell spawn.
  • API keys are never printed or logged; the logger scrubs key-shaped strings.

Testing

npm test        # vitest · 38 tests, fixture-driven

Covers BrandProfile validation, query dedup, evidence extraction (incl. invalid/missing LLM JSON), scoring formulas, competitor aggregation, failed Bright Data requests, missing position, and partial scans.

Limitations

  • AI/search responses are not deterministic universal rankings. Every engine personalizes answers by account, region, session and model. A scan is a sample at a point in time, not a ground-truth ranking. Re-run to see variance; that variance is itself information.
  • Attempts bounded — a scan is an approximation over N queries × M engines.
  • No dashboard, no accounts, no database — single-binary, local JSON only.

License

MIT. Open source by design — fork it, audit it, trust the score you computed yourself.