#!/usr/bin/env node const API_KEY = process.env.RANKPARSE_API_KEY const BASE_URL = 'https://api.rankparse.com/v1' if (!API_KEY) { console.error(JSON.stringify({ error: 'RANKPARSE_API_KEY environment variable required' })) process.exit(1) } async function api(method, path, body) { if (args['dry-run']) { return { _dry_run: true, method, url: `${BASE_URL}${path}`, headers: { 'X-API-Key': '***', 'Content-Type': 'application/json' }, body } } const init = { method, headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json', }, } if (body) init.body = JSON.stringify(body) const res = await fetch(`${BASE_URL}${path}`, init) const text = await res.text() try { return JSON.parse(text) } catch { return { status: res.status, body: text } } } function parseArgs(args) { const result = { _: [] } for (let i = 0; i < args.length; i++) { const arg = args[i] if (arg.startsWith('--')) { const key = arg.slice(2) const next = args[i + 1] if (next && !next.startsWith('--')) { result[key] = next i++ } else { result[key] = true } } else { result._.push(arg) } } return result } const args = parseArgs(process.argv.slice(2)) const [cmd, sub, ...rest] = args._ function requireDomain() { if (!args.domain) return { error: '--domain required' } return null } function requireUrl() { if (!args.url) return { error: '--url required' } return null } async function main() { let result switch (cmd) { case 'domain-authority': case 'domain-rank': case 'tech-stack': case 'site-health': case 'similar-domains': case 'link-audit': case 'site-explorer': case 'crawl-history': { const err = requireDomain(); if (err) { result = err; break } result = await api('GET', `/${cmd}?domain=${encodeURIComponent(args.domain)}`) break } case 'backlinks': { const err = requireDomain(); if (err) { result = err; break } const params = new URLSearchParams({ domain: args.domain }) if (args.limit) params.set('limit', args.limit) if (args.sort) params.set('sort', args.sort) if (args['from-domain']) params.set('from_domain', args['from-domain']) if (args['link-type']) params.set('link_type', args['link-type']) if (args.score) params.set('score', 'true') result = await api('GET', `/backlinks?${params}`) break } case 'referring-domains': case 'outbound-links': case 'anchor-text': case 'top-pages': case 'sitemap': { const err = requireDomain(); if (err) { result = err; break } const params = new URLSearchParams({ domain: args.domain }) if (args.limit) params.set('limit', args.limit) result = await api('GET', `/${cmd}?${params}`) break } case 'domain-overlap': { if (!args.domains) { result = { error: '--domains required (comma-separated, 2-5 domains)' }; break } const params = new URLSearchParams({ domains: args.domains }) if (args.limit) params.set('limit', args.limit) result = await api('GET', `/domain-overlap?${params}`) break } case 'link-intersect': { if (!args['domain-a'] || !args['domain-b']) { result = { error: '--domain-a and --domain-b required' }; break } const params = new URLSearchParams({ domain_a: args['domain-a'], domain_b: args['domain-b'] }) if (args.limit) params.set('limit', args.limit) result = await api('GET', `/link-intersect?${params}`) break } case 'competitor-gap': { const err = requireDomain(); if (err) { result = err; break } if (!args.vs) { result = { error: '--vs required (competitor domain)' }; break } const params = new URLSearchParams({ domain: args.domain, vs: args.vs }) if (args.limit) params.set('limit', args.limit) result = await api('GET', `/competitor-gap?${params}`) break } case 'page-seo': { const err = requireUrl(); if (err) { result = err; break } result = await api('GET', `/page-seo?url=${encodeURIComponent(args.url)}`) break } case 'page-performance': { const err = requireUrl(); if (err) { result = err; break } const params = new URLSearchParams({ url: args.url }) if (args.strategy) params.set('strategy', args.strategy) result = await api('GET', `/page-performance?${params}`) break } case 'batch': { if (!args.domains) { result = { error: '--domains required (comma-separated)' }; break } const domains = args.domains.split(',').map(d => d.trim()).filter(Boolean) result = await api('POST', '/batch', { domains }) break } case 'me': result = await api('GET', '/me') break case 'credits': result = await api('GET', '/credits') break default: result = { error: 'Unknown command', usage: { 'domain-authority': 'domain-authority --domain ', 'domain-rank': 'domain-rank --domain ', 'backlinks': 'backlinks --domain [--limit ] [--sort importance|recent] [--from-domain ] [--link-type ] [--score]', 'referring-domains': 'referring-domains --domain [--limit ]', 'outbound-links': 'outbound-links --domain [--limit ]', 'anchor-text': 'anchor-text --domain [--limit ]', 'top-pages': 'top-pages --domain [--limit ]', 'domain-overlap': 'domain-overlap --domains [--limit ]', 'link-intersect': 'link-intersect --domain-a --domain-b [--limit ]', 'competitor-gap': 'competitor-gap --domain --vs [--limit ]', 'similar-domains': 'similar-domains --domain ', 'tech-stack': 'tech-stack --domain ', 'site-health': 'site-health --domain ', 'sitemap': 'sitemap --domain [--limit ]', 'crawl-history': 'crawl-history --domain ', 'page-seo': 'page-seo --url ', 'page-performance': 'page-performance --url [--strategy mobile|desktop]', 'link-audit': 'link-audit --domain ', 'site-explorer': 'site-explorer --domain ', 'batch': 'batch --domains ', 'me': 'me (account info + credit balance)', 'credits': 'credits (credit balance)', } } } console.log(JSON.stringify(result, null, 2)) } main().catch(err => { console.error(JSON.stringify({ error: err.message })) process.exit(1) })