Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Query detailed token information and metadata from Binance via natural language through the Binance Skills Hub.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/cli.mjs
1#!/usr/bin/env node2// query-token-info CLI — self-contained, zero-dep, Node >= 223// Usage: node cli.mjs <command> '<json_params>'4//5// Commands:6// search token search by keyword7// meta static token metadata8// dynamic real-time market data (price, volume, holders)9// kline candlestick data10//11// All 4 commands use the SAME parameter shape: { chainId, contractAddress, ... }.12// Internally, kline hits a different host and uses different upstream field names13// (platform/address) — this CLI handles the translation so the LLM caller sees14// one consistent interface.1516// ---- inline HTTP helper (self-contained, zero dependency) ----17const TIMEOUT_MS = 10_000;18const UA = { 'Accept-Encoding': 'identity', 'User-Agent': 'binance-web3/2.0 (Skill)' };1920// ---- chainId → upstream kline platform name ----21// Single source of truth for chain identity. Used only by kline (other commands22// pass chainId through as-is to the Binance Web3 API which accepts chainId directly).23const CHAIN_ID_TO_PLATFORM = {24'1': 'ethereum',25'56': 'bsc',26'8453': 'base',27'CT_501': 'solana',28};2930const qs = (p) => Object.entries(p)31.filter(([, v]) => v != null)32.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)33.join('&');3435async function call({ url, method = 'GET', body, headers = {} }) {36const ctrl = new AbortController();37const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);38const opts = { method, headers: { ...UA, ...headers }, signal: ctrl.signal };39if (method === 'POST') { opts.headers['content-type'] = 'application/json'; opts.body = JSON.stringify(body || {}); }40let res;41try { res = await fetch(url, opts); }42catch { clearTimeout(timer); throw Object.assign(new Error('Network request failed'), { exitCode: 3 }); }43clearTimeout(timer);44const data = await res.json();45if (res.status >= 400) throw Object.assign(new Error(`HTTP ${res.status}`), { exitCode: 1, body: data });46return data;47}4849// ---- commands: (params) => { url, method?, body?, headers? } ----50const COMMANDS = {51search: (p) => ({52url: `https://web3.binance.com/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search/ai?${qs(p)}`,53}),54meta: (p) => ({55url: `https://web3.binance.com/bapi/defi/v1/public/wallet-direct/buw/wallet/dex/market/token/meta/info/ai?${qs(p)}`,56}),57dynamic: (p) => ({58url: `https://web3.binance.com/bapi/defi/v4/public/wallet-direct/buw/wallet/market/token/dynamic/info/ai?${qs(p)}`,59}),60kline: (p) => {61// Translate unified interface → upstream kline field names.62// { chainId, contractAddress, interval, ... } → { platform, address, interval, ... }63const { chainId, contractAddress, ...rest } = p;64const platform = chainId == null ? undefined : CHAIN_ID_TO_PLATFORM[chainId];65if (chainId != null && platform === undefined) {66const supported = Object.keys(CHAIN_ID_TO_PLATFORM).map((k) => `"${k}"`).join(', ');67throw Object.assign(68new Error(`kline: unsupported chainId "${chainId}". Supported: ${supported}`),69{ exitCode: 1 },70);71}72const upstream = { ...rest };73if (platform !== undefined) upstream.platform = platform;74if (contractAddress !== undefined) upstream.address = contractAddress;75return {76url: `https://dquery.sintral.io/u-kline/v1/k-line/candles?${qs(upstream)}`,77};78},79};8081// ---- exports (for unit testing; direct execution still works — see dispatch below) ----82export { COMMANDS, call, qs, UA, TIMEOUT_MS, CHAIN_ID_TO_PLATFORM };8384// ---- CLI dispatch (only runs when executed directly, not when imported) ----85if (import.meta.url === `file://${process.argv[1]}`) {86const [cmd, paramsStr] = process.argv.slice(2);8788if (!cmd || cmd === '--help' || cmd === '-h') {89console.log("Usage: node cli.mjs <command> '<json_params>'\n\nCommands:");90for (const name of Object.keys(COMMANDS)) console.log(` ${name}`);91process.exit(0);92}9394const builder = COMMANDS[cmd];95if (!builder) { console.error(`Unknown command: ${cmd}\nRun with --help to see available commands.`); process.exit(1); }9697let params = {};98if (paramsStr) {99try { params = JSON.parse(paramsStr); }100catch { console.error('Invalid JSON params'); process.exit(1); }101}102103try {104const result = await call(builder(params));105console.log(JSON.stringify(result, null, 2));106} catch (err) {107console.error(err.message);108if (err.body) console.log(JSON.stringify(err.body, null, 2));109process.exit(err.exitCode || 1);110}111}112