apinow-sdk 0.22.0 → 0.23.0

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
@@ -56,6 +56,13 @@ Returns:
56
56
  | `listWorkflows(opts?)` | List workflows (free) |
57
57
  | `getWorkflow(id)` | Get workflow details (free) |
58
58
  | `runWorkflow(id, input)` | Run a workflow with automatic x402 payment |
59
+ | `generateUI(opts)` | Start AI UI generation for an endpoint (async) |
60
+ | `generateUIAndWait(opts)` | Generate UI and poll until complete |
61
+ | `getGeneratedUI(id)` | Get a generated UI by ID |
62
+ | `listGeneratedUIs(key, sort?)` | List generated UIs for an endpoint |
63
+ | `checkFreeUI()` | Check free-tier generation eligibility |
64
+ | `reactToUI(id, action, comment?)` | Like/dislike/comment on a generated UI |
65
+ | `deleteGeneratedUI(id)` | Delete a generated UI |
59
66
  | `wallet` | Your wallet address |
60
67
  | `fetch` | Raw x402-wrapped `fetch` for advanced use |
61
68
 
@@ -77,6 +84,38 @@ const result = await apinow.runWorkflow('90931d9c8fb94df9', {
77
84
  });
78
85
  ```
79
86
 
87
+ ### AI UI Generation
88
+
89
+ Generate interactive Arrow JS sandbox UIs for any endpoint — ideal for AI agents that need a visual interface.
90
+
91
+ ```typescript
92
+ // generate a UI and wait for it to complete
93
+ const ui = await apinow.generateUIAndWait({
94
+ endpointName: 'horoscope',
95
+ namespace: 'gg402',
96
+ description: 'Get daily horoscope for a zodiac sign',
97
+ querySchema: { properties: { sign: { type: 'string' } } },
98
+ responseSchema: { properties: { horoscope: { type: 'string' } } },
99
+ customPrompt: 'Use a starry night theme',
100
+ });
101
+ console.log(ui.status); // 'complete'
102
+ console.log(ui.source); // { "main.ts": "...", "main.css": "..." }
103
+
104
+ // or fire-and-forget + poll yourself
105
+ const { id } = await apinow.generateUI({ endpointName: 'translate', namespace: 'apinowfun' });
106
+ const doc = await apinow.getGeneratedUI(id); // poll until doc.status !== 'generating'
107
+
108
+ // list existing UIs for an endpoint
109
+ const { uis } = await apinow.listGeneratedUIs('gg402/horoscope', 'popular');
110
+
111
+ // social
112
+ await apinow.reactToUI(ui._id, 'like');
113
+ await apinow.reactToUI(ui._id, 'comment', 'Great UI!');
114
+
115
+ // check free-tier
116
+ const { free, remaining } = await apinow.checkFreeUI();
117
+ ```
118
+
80
119
  ## CLI
81
120
 
82
121
  ```bash
@@ -144,6 +183,57 @@ APINOW_WALLET_PKEY=0x... npx apinow run-workflow 90931d9c8fb94df9 -d '{"query":"
144
183
  | `-d, --data <json>` | JSON input (default: `{"query":"hello world"}`) |
145
184
  | `-k, --key <privateKey>` | Wallet key (or set `APINOW_WALLET_PKEY`) |
146
185
 
186
+ ### `ui-generate` — generate an AI UI for an endpoint
187
+
188
+ Generates an interactive Arrow JS sandbox UI. Automatically fetches the endpoint's schema and examples, sends the generation request, and polls until complete.
189
+
190
+ ```bash
191
+ APINOW_WALLET_PKEY=0x... npx apinow ui-generate gg402/horoscope
192
+ APINOW_WALLET_PKEY=0x... npx apinow ui-generate gg402/horoscope --prompt "dark theme with animations"
193
+ npx apinow ui-generate ns/endpoint --no-wait -k 0xKEY # returns immediately with ID
194
+ ```
195
+
196
+ | Flag | Description |
197
+ |------|-------------|
198
+ | `-p, --prompt <text>` | Custom instructions for the UI |
199
+ | `--no-wait` | Return immediately without polling |
200
+ | `--timeout <ms>` | Polling timeout (default: 120000) |
201
+ | `-k, --key <privateKey>` | Wallet key (or set `APINOW_WALLET_PKEY`) |
202
+
203
+ ### `ui-list` — list generated UIs
204
+
205
+ ```bash
206
+ npx apinow ui-list gg402/horoscope
207
+ npx apinow ui-list gg402/horoscope --sort recent
208
+ ```
209
+
210
+ ### `ui-get` — get a generated UI by ID
211
+
212
+ ```bash
213
+ npx apinow ui-get 665a1b2c3d4e5f6a7b8c9d0e
214
+ npx apinow ui-get 665a1b2c3d4e5f6a7b8c9d0e --source-only # just the Arrow JS source
215
+ ```
216
+
217
+ ### `ui-like` / `ui-dislike` / `ui-comment` — social actions
218
+
219
+ ```bash
220
+ APINOW_WALLET_PKEY=0x... npx apinow ui-like <id>
221
+ APINOW_WALLET_PKEY=0x... npx apinow ui-dislike <id>
222
+ APINOW_WALLET_PKEY=0x... npx apinow ui-comment <id> -m "Nice UI!"
223
+ ```
224
+
225
+ ### `ui-delete` — delete a generated UI
226
+
227
+ ```bash
228
+ APINOW_WALLET_PKEY=0x... npx apinow ui-delete <id>
229
+ ```
230
+
231
+ ### `ui-free-check` — check free-tier eligibility
232
+
233
+ ```bash
234
+ APINOW_WALLET_PKEY=0x... npx apinow ui-free-check
235
+ ```
236
+
147
237
  ### `discover` — check the x402 price of any URL (free)
148
238
 
149
239
  ```bash
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ const program = new Command();
7
7
  program
8
8
  .name('apinow')
9
9
  .description('CLI for APINow.fun — search, inspect, and call pay-per-request APIs')
10
- .version('0.22.0');
10
+ .version('0.23.0');
11
11
  // ─── Helpers ───
12
12
  function getPrivateKey(opts) {
13
13
  const raw = opts.key || process.env.APINOW_WALLET_PKEY || process.env.PRIVATE_KEY;
@@ -560,6 +560,231 @@ program
560
560
  process.exit(1);
561
561
  }
562
562
  });
563
+ // ─── ui-generate ───
564
+ program
565
+ .command('ui-generate <endpoint>')
566
+ .description('Generate an AI UI for an endpoint (namespace/name). Polls until complete.')
567
+ .option('-p, --prompt <prompt>', 'Custom prompt / instructions for the UI')
568
+ .option('--no-wait', 'Return immediately without polling')
569
+ .option('--timeout <ms>', 'Polling timeout in ms', '120000')
570
+ .option('-k, --key <privateKey>', 'Wallet private key')
571
+ .action(async (endpoint, opts) => {
572
+ try {
573
+ if (!endpoint.includes('/'))
574
+ throw new Error('Format: namespace/endpoint-name');
575
+ const [ns, ep] = endpoint.split('/');
576
+ const { address } = getWallet(opts);
577
+ const details = await fetchJson(`${API_BASE}/api/endpoints/${ns}/${ep}/details`);
578
+ const body = {
579
+ endpointName: ep,
580
+ namespace: ns,
581
+ description: details.description || '',
582
+ querySchema: details.querySchema || {},
583
+ responseSchema: details.responseSchema || {},
584
+ walletAddress: address,
585
+ };
586
+ if (details.exampleQuery || details.exampleOutput) {
587
+ body.examples = [{ input: details.exampleQuery, output: details.exampleOutput }];
588
+ }
589
+ if (opts.prompt)
590
+ body.customPrompt = opts.prompt;
591
+ console.error(`Generating UI for ${ns}/${ep}…`);
592
+ const { id } = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
593
+ method: 'POST',
594
+ headers: { 'Content-Type': 'application/json' },
595
+ body: JSON.stringify(body),
596
+ });
597
+ if (!opts.wait) {
598
+ console.log(JSON.stringify({ id, status: 'generating' }, null, 2));
599
+ return;
600
+ }
601
+ const timeout = Number(opts.timeout);
602
+ const deadline = Date.now() + timeout;
603
+ process.stderr.write(' Polling');
604
+ while (Date.now() < deadline) {
605
+ await new Promise((r) => setTimeout(r, 3000));
606
+ process.stderr.write('.');
607
+ const doc = await fetchJson(`${API_BASE}/api/ai/generate-ui?id=${encodeURIComponent(id)}`);
608
+ if (doc.status === 'complete') {
609
+ console.error(' done!');
610
+ console.log(JSON.stringify({
611
+ id: doc._id,
612
+ status: doc.status,
613
+ endpointKey: doc.endpointKey,
614
+ model: doc.model,
615
+ files: Object.keys(doc.source || {}),
616
+ source: doc.source,
617
+ viewUrl: `https://apinow.fun/try/${ns}/${ep}`,
618
+ }, null, 2));
619
+ return;
620
+ }
621
+ if (doc.status === 'error') {
622
+ console.error(' failed!');
623
+ console.error(`Error: ${doc.errorMessage}`);
624
+ process.exit(1);
625
+ }
626
+ }
627
+ console.error(` timed out after ${timeout / 1000}s`);
628
+ console.log(JSON.stringify({ id, status: 'generating', note: 'Still generating — poll with: apinow ui-get ' + id }));
629
+ }
630
+ catch (err) {
631
+ console.error(`Error: ${err.message}`);
632
+ process.exit(1);
633
+ }
634
+ });
635
+ // ─── ui-list ───
636
+ program
637
+ .command('ui-list <endpoint>')
638
+ .description('List generated UIs for an endpoint (namespace/name)')
639
+ .option('-s, --sort <sort>', 'Sort: popular | recent', 'popular')
640
+ .action(async (endpoint, opts) => {
641
+ try {
642
+ if (!endpoint.includes('/'))
643
+ throw new Error('Format: namespace/endpoint-name');
644
+ const params = new URLSearchParams({ endpointKey: endpoint, sort: opts.sort });
645
+ const data = await fetchJson(`${API_BASE}/api/ai/generate-ui?${params}`);
646
+ const uis = data.uis || [];
647
+ if (!uis.length) {
648
+ console.log('No generated UIs found.');
649
+ return;
650
+ }
651
+ const rows = [['ID', 'STATUS', 'MODEL', 'OPENS', 'LIKES', 'PROMPT', 'CREATED']];
652
+ for (const ui of uis) {
653
+ rows.push([
654
+ ui._id,
655
+ ui.status,
656
+ ui.model || '—',
657
+ String(ui.openCount || 0),
658
+ String((ui.likes || []).length),
659
+ truncate(ui.customPrompt || '(default)', 30),
660
+ new Date(ui.createdAt).toLocaleDateString(),
661
+ ]);
662
+ }
663
+ printTable(rows);
664
+ }
665
+ catch (err) {
666
+ console.error(`Error: ${err.message}`);
667
+ process.exit(1);
668
+ }
669
+ });
670
+ // ─── ui-get ───
671
+ program
672
+ .command('ui-get <id>')
673
+ .description('Get a generated UI by ID (includes source code)')
674
+ .option('--source-only', 'Print only the source JSON')
675
+ .action(async (id, opts) => {
676
+ try {
677
+ const doc = await fetchJson(`${API_BASE}/api/ai/generate-ui?id=${encodeURIComponent(id)}`);
678
+ if (opts.sourceOnly) {
679
+ console.log(JSON.stringify(doc.source, null, 2));
680
+ return;
681
+ }
682
+ console.log(JSON.stringify(doc, null, 2));
683
+ }
684
+ catch (err) {
685
+ console.error(`Error: ${err.message}`);
686
+ process.exit(1);
687
+ }
688
+ });
689
+ // ─── ui-delete ───
690
+ program
691
+ .command('ui-delete <id>')
692
+ .description('Delete a generated UI (must be creator)')
693
+ .option('-k, --key <privateKey>', 'Wallet private key')
694
+ .action(async (id, opts) => {
695
+ try {
696
+ const { address } = getWallet(opts);
697
+ const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
698
+ method: 'DELETE',
699
+ headers: { 'Content-Type': 'application/json' },
700
+ body: JSON.stringify({ id, wallet: address }),
701
+ });
702
+ console.log(JSON.stringify(data, null, 2));
703
+ }
704
+ catch (err) {
705
+ console.error(`Error: ${err.message}`);
706
+ process.exit(1);
707
+ }
708
+ });
709
+ // ─── ui-like / ui-dislike ───
710
+ program
711
+ .command('ui-like <id>')
712
+ .description('Like a generated UI')
713
+ .option('-k, --key <privateKey>', 'Wallet private key')
714
+ .action(async (id, opts) => {
715
+ try {
716
+ const { address } = getWallet(opts);
717
+ const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
718
+ method: 'PATCH',
719
+ headers: { 'Content-Type': 'application/json' },
720
+ body: JSON.stringify({ id, action: 'like', wallet: address }),
721
+ });
722
+ console.log(`Likes: ${data.likes} Dislikes: ${data.dislikes}`);
723
+ }
724
+ catch (err) {
725
+ console.error(`Error: ${err.message}`);
726
+ process.exit(1);
727
+ }
728
+ });
729
+ program
730
+ .command('ui-dislike <id>')
731
+ .description('Dislike a generated UI')
732
+ .option('-k, --key <privateKey>', 'Wallet private key')
733
+ .action(async (id, opts) => {
734
+ try {
735
+ const { address } = getWallet(opts);
736
+ const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
737
+ method: 'PATCH',
738
+ headers: { 'Content-Type': 'application/json' },
739
+ body: JSON.stringify({ id, action: 'dislike', wallet: address }),
740
+ });
741
+ console.log(`Likes: ${data.likes} Dislikes: ${data.dislikes}`);
742
+ }
743
+ catch (err) {
744
+ console.error(`Error: ${err.message}`);
745
+ process.exit(1);
746
+ }
747
+ });
748
+ // ─── ui-comment ───
749
+ program
750
+ .command('ui-comment <id>')
751
+ .description('Comment on a generated UI')
752
+ .requiredOption('-m, --message <text>', 'Comment text')
753
+ .option('-k, --key <privateKey>', 'Wallet private key')
754
+ .action(async (id, opts) => {
755
+ try {
756
+ const { address } = getWallet(opts);
757
+ const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
758
+ method: 'PATCH',
759
+ headers: { 'Content-Type': 'application/json' },
760
+ body: JSON.stringify({ id, action: 'comment', wallet: address, comment: opts.message }),
761
+ });
762
+ console.log(`Comments: ${(data.comments || []).length}`);
763
+ }
764
+ catch (err) {
765
+ console.error(`Error: ${err.message}`);
766
+ process.exit(1);
767
+ }
768
+ });
769
+ // ─── ui-free-check ───
770
+ program
771
+ .command('ui-free-check')
772
+ .description('Check free-tier UI generation eligibility')
773
+ .option('-k, --key <privateKey>', 'Wallet private key')
774
+ .action(async (opts) => {
775
+ try {
776
+ const { address } = getWallet(opts);
777
+ const params = new URLSearchParams({ checkFree: '1', wallet: address });
778
+ const data = await fetchJson(`${API_BASE}/api/ai/generate-ui?${params}`);
779
+ console.log(`\n Wallet: ${address}`);
780
+ console.log(` Free tier: ${data.free ? 'YES' : 'NO'}`);
781
+ console.log(` Remaining: ${data.remaining === Infinity ? '∞' : data.remaining}\n`);
782
+ }
783
+ catch (err) {
784
+ console.error(`Error: ${err.message}`);
785
+ process.exit(1);
786
+ }
787
+ });
563
788
  // ─── factory-balance ───
564
789
  program
565
790
  .command('factory-balance')
package/dist/index.d.ts CHANGED
@@ -23,6 +23,36 @@ export interface ApinowConfig {
23
23
  baseUrl?: string;
24
24
  fetch?: typeof globalThis.fetch;
25
25
  }
26
+ export interface GenerateUIOptions {
27
+ endpointName: string;
28
+ namespace: string;
29
+ description?: string;
30
+ querySchema?: any;
31
+ responseSchema?: any;
32
+ examples?: any[];
33
+ customPrompt?: string;
34
+ }
35
+ export interface GeneratedUI {
36
+ _id: string;
37
+ endpointKey: string;
38
+ endpointName: string;
39
+ namespace: string;
40
+ source: Record<string, string> | null;
41
+ status: 'generating' | 'complete' | 'error';
42
+ errorMessage?: string;
43
+ customPrompt: string;
44
+ model: string;
45
+ generatedBy: string | null;
46
+ openCount: number;
47
+ likes: string[];
48
+ dislikes: string[];
49
+ comments: Array<{
50
+ wallet: string;
51
+ text: string;
52
+ createdAt: string;
53
+ }>;
54
+ createdAt: string;
55
+ }
26
56
  export declare function createClient(config: ApinowConfig): {
27
57
  wallet: `0x${string}`;
28
58
  /**
@@ -202,6 +232,48 @@ export declare function createClient(config: ApinowConfig): {
202
232
  testOutput: any;
203
233
  workflow: any;
204
234
  }>;
235
+ /**
236
+ * Start generating an AI UI for an endpoint. Returns immediately with a doc ID.
237
+ * Poll with getGeneratedUI() or use generateUIAndWait() for convenience.
238
+ */
239
+ generateUI(opts: GenerateUIOptions): Promise<{
240
+ id: string;
241
+ status: string;
242
+ }>;
243
+ /**
244
+ * Get a generated UI by ID (use to poll status after generateUI).
245
+ */
246
+ getGeneratedUI(id: string): Promise<GeneratedUI>;
247
+ /**
248
+ * List generated UIs for an endpoint.
249
+ */
250
+ listGeneratedUIs(endpointKey: string, sort?: "popular" | "recent"): Promise<{
251
+ uis: GeneratedUI[];
252
+ }>;
253
+ /**
254
+ * Check free-tier UI generation eligibility for a wallet.
255
+ */
256
+ checkFreeUI(): Promise<{
257
+ free: boolean;
258
+ remaining: number;
259
+ }>;
260
+ /**
261
+ * Like, dislike, or comment on a generated UI.
262
+ */
263
+ reactToUI(id: string, action: "like" | "dislike" | "comment", comment?: string): Promise<any>;
264
+ /**
265
+ * Delete a generated UI (must be creator or admin).
266
+ */
267
+ deleteGeneratedUI(id: string): Promise<{
268
+ deleted: boolean;
269
+ }>;
270
+ /**
271
+ * Generate a UI and poll until complete or error. Returns the final GeneratedUI doc.
272
+ * @param opts - generation params
273
+ * @param pollIntervalMs - polling interval (default 3000ms)
274
+ * @param timeoutMs - max wait time (default 120000ms)
275
+ */
276
+ generateUIAndWait(opts: GenerateUIOptions, pollIntervalMs?: number, timeoutMs?: number): Promise<GeneratedUI>;
205
277
  /**
206
278
  * Discover the x402 price for any external URL without executing it.
207
279
  * Free — no payment required.
package/dist/index.js CHANGED
@@ -343,6 +343,106 @@ export function createClient(config) {
343
343
  }
344
344
  return { draft, endpoint, testOutput, workflow };
345
345
  },
346
+ // ─── AI UI Generation ───
347
+ /**
348
+ * Start generating an AI UI for an endpoint. Returns immediately with a doc ID.
349
+ * Poll with getGeneratedUI() or use generateUIAndWait() for convenience.
350
+ */
351
+ async generateUI(opts) {
352
+ const res = await fetch(`${baseUrl}/api/ai/generate-ui`, {
353
+ method: 'POST',
354
+ headers: { 'Content-Type': 'application/json' },
355
+ body: JSON.stringify({ ...opts, walletAddress: account.address }),
356
+ });
357
+ if (!res.ok) {
358
+ const text = await res.text();
359
+ throw new Error(`Failed to generate UI: ${res.status} ${text}`);
360
+ }
361
+ return res.json();
362
+ },
363
+ /**
364
+ * Get a generated UI by ID (use to poll status after generateUI).
365
+ */
366
+ async getGeneratedUI(id) {
367
+ const res = await fetch(`${baseUrl}/api/ai/generate-ui?id=${encodeURIComponent(id)}`);
368
+ if (!res.ok) {
369
+ const text = await res.text();
370
+ throw new Error(`Failed to get generated UI: ${res.status} ${text}`);
371
+ }
372
+ return res.json();
373
+ },
374
+ /**
375
+ * List generated UIs for an endpoint.
376
+ */
377
+ async listGeneratedUIs(endpointKey, sort = 'popular') {
378
+ const params = new URLSearchParams({ endpointKey, sort });
379
+ const res = await fetch(`${baseUrl}/api/ai/generate-ui?${params}`);
380
+ if (!res.ok) {
381
+ const text = await res.text();
382
+ throw new Error(`Failed to list generated UIs: ${res.status} ${text}`);
383
+ }
384
+ return res.json();
385
+ },
386
+ /**
387
+ * Check free-tier UI generation eligibility for a wallet.
388
+ */
389
+ async checkFreeUI() {
390
+ const params = new URLSearchParams({ checkFree: '1', wallet: account.address });
391
+ const res = await fetch(`${baseUrl}/api/ai/generate-ui?${params}`);
392
+ if (!res.ok)
393
+ throw new Error(`Failed to check free tier: ${res.status}`);
394
+ return res.json();
395
+ },
396
+ /**
397
+ * Like, dislike, or comment on a generated UI.
398
+ */
399
+ async reactToUI(id, action, comment) {
400
+ const body = { id, action, wallet: account.address };
401
+ if (comment)
402
+ body.comment = comment;
403
+ const res = await fetch(`${baseUrl}/api/ai/generate-ui`, {
404
+ method: 'PATCH',
405
+ headers: { 'Content-Type': 'application/json' },
406
+ body: JSON.stringify(body),
407
+ });
408
+ if (!res.ok) {
409
+ const text = await res.text();
410
+ throw new Error(`Failed to react to UI: ${res.status} ${text}`);
411
+ }
412
+ return res.json();
413
+ },
414
+ /**
415
+ * Delete a generated UI (must be creator or admin).
416
+ */
417
+ async deleteGeneratedUI(id) {
418
+ const res = await fetch(`${baseUrl}/api/ai/generate-ui`, {
419
+ method: 'DELETE',
420
+ headers: { 'Content-Type': 'application/json' },
421
+ body: JSON.stringify({ id, wallet: account.address }),
422
+ });
423
+ if (!res.ok) {
424
+ const text = await res.text();
425
+ throw new Error(`Failed to delete UI: ${res.status} ${text}`);
426
+ }
427
+ return res.json();
428
+ },
429
+ /**
430
+ * Generate a UI and poll until complete or error. Returns the final GeneratedUI doc.
431
+ * @param opts - generation params
432
+ * @param pollIntervalMs - polling interval (default 3000ms)
433
+ * @param timeoutMs - max wait time (default 120000ms)
434
+ */
435
+ async generateUIAndWait(opts, pollIntervalMs = 3000, timeoutMs = 120000) {
436
+ const { id } = await this.generateUI(opts);
437
+ const deadline = Date.now() + timeoutMs;
438
+ while (Date.now() < deadline) {
439
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
440
+ const doc = await this.getGeneratedUI(id);
441
+ if (doc.status === 'complete' || doc.status === 'error')
442
+ return doc;
443
+ }
444
+ throw new Error(`UI generation timed out after ${timeoutMs / 1000}s (id: ${id})`);
445
+ },
346
446
  // ─── External x402 Proxy ───
347
447
  /**
348
448
  * Discover the x402 price for any external URL without executing it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apinow-sdk",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Pay-per-call API SDK & CLI for APINow.fun — endpoints + workflows, wraps x402 so you don't have to",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",