#!/usr/bin/env node /** * Domain availability CLI for agents, over the Namechan API. No dependencies; Node 18+. * * node domains.mjs check acme.com acme.io getacme.com * node domains.mjs name acme --tlds com,io,ai * node domains.mjs whois example.com * node domains.mjs usage * * Works without a key, on the per-address limits. A key, if there is one, * comes from NAMECHAN_API_KEY or ~/.config/namechan/api-key and is sent as a * bearer token. Output is JSON on stdout; failures print a message on stderr * and exit 1. */ import { readFile } from 'node:fs/promises'; import { homedir } from 'node:os'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; const API = (process.env.NAMECHAN_API_URL || 'https://namechan.com/api/v1').replace(/\/$/, ''); const KEY_FILE = join(homedir(), '.config', 'namechan', 'api-key'); async function readKey() { const env = (process.env.NAMECHAN_API_KEY || '').trim(); if (env) return env; try { return (await readFile(KEY_FILE, 'utf8')).trim(); } catch { return ''; } } function fail(message) { process.stderr.write(`${message}\n`); process.exit(1); } /** Splits arguments that may themselves hold commas or newlines. */ export function names(args) { return args.flatMap((a) => a.split(/[\s,;]+/)).map((a) => a.trim()).filter(Boolean); } export function parseArgs(argv) { const [command, ...rest] = argv; const flags = {}; const positional = []; for (let i = 0; i < rest.length; i += 1) { if (rest[i] === '--tlds') flags.tlds = rest[(i += 1)] ?? ''; else if (rest[i].startsWith('--tlds=')) flags.tlds = rest[i].slice(7); else positional.push(rest[i]); } return { command, positional, flags }; } async function call(path, key, init = {}) { let res; try { res = await fetch(`${API}${path}`, { ...init, headers: { ...(key ? { Authorization: `Bearer ${key}` } : {}), 'Content-Type': 'application/json', 'User-Agent': 'domain-availability-skill/1.1', ...init.headers, }, }); } catch (error) { fail(`Could not reach Namechan: ${error.message}`); } const body = await res.json().catch(() => null); if (!res.ok) fail(body?.error?.message || `Namechan answered HTTP ${res.status}.`); return body; } async function main() { const { command, positional, flags } = parseArgs(process.argv.slice(2)); if (!command || command === 'help' || command === '--help') { process.stdout.write('Usage: domains.mjs check | name