apinow-sdk 0.14.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,12 @@
1
1
  # apinow-sdk
2
2
 
3
- Pay-per-call API SDK for [APINow.fun](https://apinow.fun) — wraps [x402](https://www.x402.org/) so you don't have to.
4
-
5
- ## Install
3
+ Pay-per-call API SDK & CLI for [APINow.fun](https://apinow.fun) — wraps [x402](https://www.x402.org/) so you don't have to.
6
4
 
7
5
  ```bash
8
6
  npm install apinow-sdk
9
7
  ```
10
8
 
11
- ## Quick Start
9
+ ## SDK
12
10
 
13
11
  ```typescript
14
12
  import { createClient } from 'apinow-sdk';
@@ -17,133 +15,82 @@ const apinow = createClient({
17
15
  privateKey: process.env.PRIVATE_KEY as `0x${string}`,
18
16
  });
19
17
 
20
- const result = await apinow.call('/api/endpoints/apinowfun/translate-TRANSLATE', {
18
+ // call any endpoint x402 payment is handled automatically
19
+ const data = await apinow.call('/api/endpoints/apinowfun/translate', {
21
20
  method: 'POST',
22
- body: { text: 'Hello world', selectedLanguage: 'es' },
21
+ body: { text: 'Hello world', targetLanguage: 'es' },
23
22
  });
24
23
 
25
- console.log(result);
26
- ```
27
-
28
- That's it. If the endpoint requires payment, the SDK handles the full [x402 payment flow](https://www.x402.org/) automatically — no manual token handling, no extra steps.
29
-
30
- ## How It Works
31
-
32
- 1. You call an APINow endpoint via `apinow.call()`
33
- 2. If the server responds with `402 Payment Required`, the underlying `@x402/fetch` layer intercepts it
34
- 3. Payment is signed and submitted using your wallet
35
- 4. The original request is retried with proof of payment
36
- 5. You get the API response back
37
-
38
- ## API
39
-
40
- ### `createClient(config)`
41
-
42
- Creates an SDK client instance.
43
-
44
- ```typescript
45
- import { createClient } from 'apinow-sdk';
46
- // or
47
- import createClient from 'apinow-sdk';
24
+ // semantic search
25
+ const results = await apinow.search('weather forecast', 5);
48
26
 
49
- const apinow = createClient({
50
- privateKey: '0x...', // required — EVM private key
51
- baseUrl: 'https://apinow.fun', // optional — defaults to https://apinow.fun
52
- });
27
+ // free endpoint info (cost, schema, wallet)
28
+ const info = await apinow.info('gg402', 'horoscope');
53
29
  ```
54
30
 
55
- Returns an object with:
31
+ ### `createClient(config)`
56
32
 
57
- | Property | Description |
58
- |----------|-------------|
59
- | `wallet` | Your wallet address (derived from private key) |
60
- | `call()` | Call any APINow endpoint with automatic x402 payment |
61
- | `search()` | Semantic search across all APINow endpoints |
62
- | `info()` | Get public endpoint info (free, no payment) |
63
- | `fetch` | The underlying x402-wrapped `fetch` for advanced use |
33
+ | Option | Type | Default | Description |
34
+ |--------|------|---------|-------------|
35
+ | `privateKey` | `` `0x${string}` `` | | EVM private key (required) |
36
+ | `baseUrl` | `string` | `https://apinow.fun` | API base URL |
64
37
 
65
- ---
38
+ Returns:
66
39
 
67
- ### `apinow.call(endpoint, opts?)`
40
+ | Method | Description |
41
+ |--------|-------------|
42
+ | `call(endpoint, opts?)` | Call any endpoint with automatic x402 payment |
43
+ | `search(query, limit?)` | Semantic search across all endpoints |
44
+ | `info(ns, name)` | Get endpoint details (free) |
45
+ | `wallet` | Your wallet address |
46
+ | `fetch` | Raw x402-wrapped `fetch` for advanced use |
68
47
 
69
- Call any APINow endpoint. Handles x402 payment automatically.
48
+ ## CLI
70
49
 
71
- ```typescript
72
- const data = await apinow.call('/api/endpoints/apinowfun/translate-TRANSLATE', {
73
- method: 'POST',
74
- body: { text: 'Hello world', selectedLanguage: 'es' },
75
- });
50
+ ```bash
51
+ npx apinow <command>
76
52
  ```
77
53
 
78
- **Parameters:**
79
-
80
- | Param | Type | Default | Description |
81
- |-------|------|---------|-------------|
82
- | `endpoint` | `string` | — | Full URL or path (paths are resolved against `baseUrl`) |
83
- | `opts.method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` | `'POST'` | HTTP method |
84
- | `opts.body` | `Record<string, any>` | — | Request body (ignored for GET) |
85
- | `opts.headers` | `Record<string, string>` | — | Additional headers |
54
+ ### `search` — find endpoints
86
55
 
87
- **Returns:** `Promise<any>` — parsed JSON response.
88
-
89
- ---
90
-
91
- ### `apinow.search(query, limit?)`
92
-
93
- Semantic search across all APINow endpoints.
94
-
95
- ```typescript
96
- const results = await apinow.search('translate text', 5);
56
+ ```bash
57
+ npx apinow search "weather api" --limit 5
97
58
  ```
98
59
 
99
- | Param | Type | Default | Description |
100
- |-------|------|---------|-------------|
101
- | `query` | `string` | — | Search query |
102
- | `limit` | `number` | `10` | Max results |
103
-
104
- ---
105
-
106
- ### `apinow.info(namespace, endpointName)`
60
+ ### `list` browse endpoints
107
61
 
108
- Get public endpoint details. Free — no payment required.
109
-
110
- ```typescript
111
- const details = await apinow.info('apinowfun', 'translate-TRANSLATE');
62
+ ```bash
63
+ npx apinow list # popular endpoints
64
+ npx apinow list --sort newest --limit 10
65
+ npx apinow list --namespace gg402
66
+ npx apinow list --search translate
112
67
  ```
113
68
 
114
- ---
115
-
116
- ### `apinow.fetch`
69
+ ### `info` — endpoint details
117
70
 
118
- The raw x402-wrapped `fetch` function, if you need full control over requests.
71
+ Shows cost, wallet, chain, input/output schemas, and examples.
119
72
 
120
- ```typescript
121
- const res = await apinow.fetch('https://apinow.fun/api/endpoints/...', {
122
- method: 'POST',
123
- headers: { 'Content-Type': 'application/json' },
124
- body: JSON.stringify({ key: 'value' }),
125
- });
73
+ ```bash
74
+ npx apinow info gg402/horoscope
126
75
  ```
127
76
 
128
- ## Types
77
+ ### `call` — call an endpoint (paid)
129
78
 
130
- ```typescript
131
- interface ApinowConfig {
132
- privateKey: `0x${string}`;
133
- baseUrl?: string;
134
- }
135
-
136
- interface CallOptions {
137
- method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
138
- body?: Record<string, any>;
139
- headers?: Record<string, string>;
140
- }
79
+ ```bash
80
+ APINOW_WALLET_PKEY=0x... npx apinow call gg402/horoscope -d '{"sign":"aries"}'
81
+ npx apinow call ns/endpoint -m GET -k 0xYOUR_KEY
141
82
  ```
142
83
 
84
+ | Flag | Description |
85
+ |------|-------------|
86
+ | `-d, --data <json>` | JSON request body |
87
+ | `-m, --method <method>` | HTTP method (default: from endpoint) |
88
+ | `-k, --key <privateKey>` | Wallet key (or set `APINOW_WALLET_PKEY`) |
89
+
143
90
  ## Requirements
144
91
 
145
- - Node.js v18+ (uses native `fetch`)
146
- - An EVM wallet with funds on Base for paid endpoints
92
+ - Node.js v18+
93
+ - EVM wallet with funds on Base for paid endpoints
147
94
 
148
95
  ## License
149
96
 
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { createClient } from './index.js';
4
+ const API_BASE = 'https://apinow.fun';
5
+ const program = new Command();
6
+ program
7
+ .name('apinow')
8
+ .description('CLI for APINow.fun — search, inspect, and call pay-per-request APIs')
9
+ .version('0.15.0');
10
+ // ─── Helpers ───
11
+ function getPrivateKey(opts) {
12
+ const raw = opts.key || process.env.APINOW_WALLET_PKEY || process.env.PRIVATE_KEY;
13
+ if (!raw) {
14
+ console.error('Error: Private key required. Set APINOW_WALLET_PKEY or pass --key');
15
+ process.exit(1);
16
+ }
17
+ return (raw.startsWith('0x') ? raw : `0x${raw}`);
18
+ }
19
+ async function fetchJson(url, init) {
20
+ const res = await fetch(url, init);
21
+ if (!res.ok) {
22
+ const body = await res.text();
23
+ throw new Error(`${res.status} ${res.statusText}: ${body}`);
24
+ }
25
+ return res.json();
26
+ }
27
+ function formatUsd(paymentOptions) {
28
+ if (!paymentOptions?.length)
29
+ return 'free';
30
+ const p = paymentOptions[0];
31
+ const usd = p.usdAmount ?? p.amount ?? 0;
32
+ return `$${Number(usd).toFixed(4)}`;
33
+ }
34
+ function printTable(rows) {
35
+ if (!rows.length)
36
+ return;
37
+ const widths = rows[0].map((_, col) => Math.max(...rows.map((r) => (r[col] || '').length)));
38
+ for (const row of rows) {
39
+ console.log(row.map((cell, i) => (cell || '').padEnd(widths[i])).join(' '));
40
+ }
41
+ }
42
+ function truncate(s, max) {
43
+ return s.length > max ? s.slice(0, max - 1) + '…' : s;
44
+ }
45
+ // ─── search ───
46
+ program
47
+ .command('search <query>')
48
+ .description('Semantic search across all endpoints')
49
+ .option('-l, --limit <n>', 'Max results', '10')
50
+ .action(async (query, opts) => {
51
+ try {
52
+ const data = await fetchJson(`${API_BASE}/api/endpoints/semantic-search`, {
53
+ method: 'POST',
54
+ headers: { 'Content-Type': 'application/json' },
55
+ body: JSON.stringify({ query, limit: Number(opts.limit) }),
56
+ });
57
+ const endpoints = data.endpoints || data.results || data;
58
+ if (!Array.isArray(endpoints) || !endpoints.length) {
59
+ console.log('No results found.');
60
+ return;
61
+ }
62
+ const rows = [['ENDPOINT', 'METHOD', 'COST', 'DESCRIPTION']];
63
+ for (const ep of endpoints) {
64
+ const key = `${ep.namespace}/${ep.endpointName}`;
65
+ rows.push([
66
+ key,
67
+ ep.httpMethod || 'POST',
68
+ formatUsd(ep.paymentOptions),
69
+ truncate(ep.description || '', 60),
70
+ ]);
71
+ }
72
+ printTable(rows);
73
+ }
74
+ catch (err) {
75
+ console.error(`Error: ${err.message}`);
76
+ process.exit(1);
77
+ }
78
+ });
79
+ // ─── list ───
80
+ program
81
+ .command('list')
82
+ .description('List available endpoints')
83
+ .option('-l, --limit <n>', 'Max results', '20')
84
+ .option('-s, --sort <sortBy>', 'Sort by: popular | newest', 'popular')
85
+ .option('-n, --namespace <ns>', 'Filter by namespace')
86
+ .option('-q, --search <term>', 'Text search filter')
87
+ .action(async (opts) => {
88
+ try {
89
+ const params = new URLSearchParams({
90
+ limit: opts.limit,
91
+ sortBy: opts.sort,
92
+ });
93
+ if (opts.namespace)
94
+ params.set('namespace', opts.namespace);
95
+ if (opts.search)
96
+ params.set('search', opts.search);
97
+ const data = await fetchJson(`${API_BASE}/api/endpoints?${params}`);
98
+ const endpoints = data.endpoints || [];
99
+ if (!endpoints.length) {
100
+ console.log('No endpoints found.');
101
+ return;
102
+ }
103
+ const rows = [['ENDPOINT', 'METHOD', 'COST', 'CALLS', 'DESCRIPTION']];
104
+ for (const ep of endpoints) {
105
+ rows.push([
106
+ `${ep.namespace}/${ep.endpointName}`,
107
+ ep.httpMethod || 'POST',
108
+ formatUsd(ep.paymentOptions),
109
+ String(ep.callCount || 0),
110
+ truncate(ep.description || '', 50),
111
+ ]);
112
+ }
113
+ printTable(rows);
114
+ if (data.hasMore) {
115
+ console.log(`\n… more results available (showing ${endpoints.length})`);
116
+ }
117
+ }
118
+ catch (err) {
119
+ console.error(`Error: ${err.message}`);
120
+ process.exit(1);
121
+ }
122
+ });
123
+ // ─── info ───
124
+ program
125
+ .command('info <endpoint>')
126
+ .description('Get endpoint details (cost, wallet, schemas, examples)')
127
+ .action(async (endpoint) => {
128
+ try {
129
+ if (!endpoint.includes('/')) {
130
+ throw new Error('Format: namespace/endpoint-name');
131
+ }
132
+ const [ns, ep] = endpoint.split('/');
133
+ const data = await fetchJson(`${API_BASE}/api/endpoints/${ns}/${ep}/details`);
134
+ const cost = formatUsd(data.paymentOptions);
135
+ const payment = data.paymentOptions?.[0];
136
+ console.log(`\n ${data.namespace}/${data.endpointName}`);
137
+ console.log(` ${'─'.repeat(40)}`);
138
+ console.log(` ${data.description || '(no description)'}\n`);
139
+ console.log(` Method: ${data.httpMethod || 'POST'}`);
140
+ console.log(` Cost: ${cost}`);
141
+ console.log(` Chain: ${data.chain || 'base'}`);
142
+ console.log(` Wallet: ${data.walletAddress || '—'}`);
143
+ if (data.model)
144
+ console.log(` Model: ${data.model}`);
145
+ if (data.tags?.length)
146
+ console.log(` Tags: ${data.tags.join(', ')}`);
147
+ if (data.docsUrl)
148
+ console.log(` Docs: ${data.docsUrl}`);
149
+ if (payment) {
150
+ console.log(`\n Payment:`);
151
+ console.log(` Network: ${payment.network || 'base-sepolia'}`);
152
+ console.log(` Token: ${payment.resource || payment.token || 'USDC'}`);
153
+ console.log(` Amount: ${payment.amount ?? '—'}`);
154
+ if (payment.usdAmount != null)
155
+ console.log(` USD: $${payment.usdAmount}`);
156
+ }
157
+ if (data.querySchema) {
158
+ console.log(`\n Input Schema:`);
159
+ const schema = data.querySchema;
160
+ if (schema.properties) {
161
+ const required = new Set(schema.required || []);
162
+ for (const [key, val] of Object.entries(schema.properties)) {
163
+ const req = required.has(key) ? ' (required)' : '';
164
+ const desc = val.description ? ` — ${val.description}` : '';
165
+ const ex = val.example ? ` e.g. ${JSON.stringify(val.example)}` : '';
166
+ console.log(` ${key}: ${val.type || 'any'}${req}${desc}${ex}`);
167
+ }
168
+ }
169
+ else {
170
+ console.log(` ${JSON.stringify(schema, null, 4).replace(/\n/g, '\n ')}`);
171
+ }
172
+ }
173
+ if (data.responseSchema) {
174
+ console.log(`\n Output Schema:`);
175
+ console.log(` ${JSON.stringify(data.responseSchema, null, 4).replace(/\n/g, '\n ')}`);
176
+ }
177
+ if (data.exampleQuery) {
178
+ console.log(`\n Example Input:`);
179
+ console.log(` ${JSON.stringify(data.exampleQuery, null, 2).replace(/\n/g, '\n ')}`);
180
+ }
181
+ console.log('');
182
+ }
183
+ catch (err) {
184
+ console.error(`Error: ${err.message}`);
185
+ process.exit(1);
186
+ }
187
+ });
188
+ // ─── call ───
189
+ program
190
+ .command('call <endpoint>')
191
+ .description('Call an endpoint (x402 paid)')
192
+ .option('-d, --data <json>', 'JSON body')
193
+ .option('-m, --method <method>', 'HTTP method')
194
+ .option('-k, --key <privateKey>', 'Wallet private key')
195
+ .action(async (endpoint, opts) => {
196
+ try {
197
+ if (!endpoint.includes('/')) {
198
+ throw new Error('Format: namespace/endpoint-name');
199
+ }
200
+ const privateKey = getPrivateKey(opts);
201
+ const [ns, ep] = endpoint.split('/');
202
+ const details = await fetchJson(`${API_BASE}/api/endpoints/${ns}/${ep}/details`);
203
+ const method = (opts.method || details?.httpMethod || 'POST').toUpperCase();
204
+ const body = opts.data ? JSON.parse(opts.data) : undefined;
205
+ const cost = formatUsd(details?.paymentOptions);
206
+ console.error(`Calling ${ns}/${ep} [${method}] — cost: ${cost}`);
207
+ const apinow = createClient({ privateKey });
208
+ const result = await apinow.call(`/api/endpoints/${ns}/${ep}`, {
209
+ method: method,
210
+ ...(body ? { body } : {}),
211
+ });
212
+ console.log(JSON.stringify(result, null, 2));
213
+ }
214
+ catch (err) {
215
+ console.error(`Error: ${err.message}`);
216
+ process.exit(1);
217
+ }
218
+ });
219
+ program.parse();
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "apinow-sdk",
3
- "version": "0.14.0",
4
- "description": "Pay-per-call API SDK for APINow.fun — wraps x402 so you don't have to",
3
+ "version": "0.15.1",
4
+ "description": "Pay-per-call API SDK & CLI for APINow.fun — wraps x402 so you don't have to",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",
8
+ "bin": {
9
+ "apinow": "dist/cli.js"
10
+ },
8
11
  "files": [
9
12
  "dist"
10
13
  ],
@@ -20,7 +23,8 @@
20
23
  "base",
21
24
  "web3",
22
25
  "ai",
23
- "llm"
26
+ "llm",
27
+ "cli"
24
28
  ],
25
29
  "author": "ApiNow.fun",
26
30
  "license": "MIT",
@@ -30,8 +34,9 @@
30
34
  },
31
35
  "homepage": "https://apinow.fun",
32
36
  "dependencies": {
33
- "@x402/fetch": "^2.3.0",
34
37
  "@x402/evm": "^2.3.1",
38
+ "@x402/fetch": "^2.3.0",
39
+ "commander": "^13.1.0",
35
40
  "viem": "^2.38.3"
36
41
  },
37
42
  "devDependencies": {