apinow-sdk 0.21.1 → 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 +90 -0
- package/dist/cli.js +396 -11
- package/dist/index.d.ts +103 -0
- package/dist/index.js +149 -0
- package/package.json +1 -1
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.
|
|
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')
|
|
@@ -637,9 +862,9 @@ program
|
|
|
637
862
|
// ─── factory-create ───
|
|
638
863
|
program
|
|
639
864
|
.command('factory-create')
|
|
640
|
-
.description('Create an LLM endpoint via user-factory')
|
|
641
|
-
.
|
|
642
|
-
.
|
|
865
|
+
.description('Create an LLM endpoint via user-factory (flags or --from-json)')
|
|
866
|
+
.option('--name <name>', 'Endpoint name')
|
|
867
|
+
.option('--prompt <prompt>', 'System prompt for the LLM')
|
|
643
868
|
.option('--namespace <ns>', 'Namespace (defaults to u-<wallet>)')
|
|
644
869
|
.option('--description <desc>', 'Endpoint description')
|
|
645
870
|
.option('--model <model>', 'LLM model', 'google/gemini-2.0-flash-001')
|
|
@@ -647,16 +872,54 @@ program
|
|
|
647
872
|
.option('--recipient <wallet>', 'Payment recipient wallet')
|
|
648
873
|
.option('--input-params <json>', 'Input params JSON array')
|
|
649
874
|
.option('--output-params <json>', 'Output params JSON array')
|
|
875
|
+
.option('--from-json <path>', 'JSON file with config (use "-" for stdin). Fields: name, prompt, description, model, suggestedPrice, inputParams, outputParams')
|
|
650
876
|
.option('-k, --key <privateKey>', 'Wallet private key')
|
|
651
877
|
.action(async (opts) => {
|
|
652
878
|
try {
|
|
653
879
|
const { address } = getWallet(opts);
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
880
|
+
let body;
|
|
881
|
+
if (opts.fromJson) {
|
|
882
|
+
const { readFileSync } = await import('fs');
|
|
883
|
+
let raw;
|
|
884
|
+
if (opts.fromJson === '-') {
|
|
885
|
+
const chunks = [];
|
|
886
|
+
for await (const chunk of process.stdin)
|
|
887
|
+
chunks.push(chunk);
|
|
888
|
+
raw = Buffer.concat(chunks).toString('utf8');
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
raw = readFileSync(opts.fromJson, 'utf8');
|
|
892
|
+
}
|
|
893
|
+
const json = JSON.parse(raw);
|
|
894
|
+
body = {
|
|
895
|
+
name: json.name,
|
|
896
|
+
prompt: json.prompt,
|
|
897
|
+
model: json.model || opts.model,
|
|
898
|
+
usdcPrice: json.suggestedPrice || json.usdcPrice || opts.price,
|
|
899
|
+
};
|
|
900
|
+
if (json.namespace)
|
|
901
|
+
body.namespace = json.namespace;
|
|
902
|
+
if (json.description)
|
|
903
|
+
body.description = json.description;
|
|
904
|
+
if (json.recipientWallet)
|
|
905
|
+
body.recipientWallet = json.recipientWallet;
|
|
906
|
+
if (json.inputParams)
|
|
907
|
+
body.inputParams = json.inputParams;
|
|
908
|
+
if (json.outputParams)
|
|
909
|
+
body.outputParams = json.outputParams;
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
body = {
|
|
913
|
+
name: opts.name,
|
|
914
|
+
prompt: opts.prompt,
|
|
915
|
+
model: opts.model,
|
|
916
|
+
usdcPrice: opts.price,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
if (!body.name || !body.prompt) {
|
|
920
|
+
console.error('Error: --name and --prompt are required (or provide --from-json)');
|
|
921
|
+
process.exit(1);
|
|
922
|
+
}
|
|
660
923
|
if (opts.namespace)
|
|
661
924
|
body.namespace = opts.namespace;
|
|
662
925
|
if (opts.description)
|
|
@@ -667,7 +930,7 @@ program
|
|
|
667
930
|
body.inputParams = JSON.parse(opts.inputParams);
|
|
668
931
|
if (opts.outputParams)
|
|
669
932
|
body.outputParams = JSON.parse(opts.outputParams);
|
|
670
|
-
console.error(`Creating endpoint "${
|
|
933
|
+
console.error(`Creating endpoint "${body.name}"…`);
|
|
671
934
|
const data = await fetchJson(`${API_BASE}/api/user-factory`, {
|
|
672
935
|
method: 'POST',
|
|
673
936
|
headers: walletHeaders(address),
|
|
@@ -765,4 +1028,126 @@ program
|
|
|
765
1028
|
process.exit(1);
|
|
766
1029
|
}
|
|
767
1030
|
});
|
|
1031
|
+
// ─── factory-pipeline ───
|
|
1032
|
+
program
|
|
1033
|
+
.command('factory-pipeline <idea>')
|
|
1034
|
+
.description('Full pipeline: generate → create → test → optional markup workflow')
|
|
1035
|
+
.option('--markup <percent>', 'Markup percent (creates a markup workflow after endpoint creation)')
|
|
1036
|
+
.option('--markup-recipient <wallet>', 'Wallet to receive markup USDC')
|
|
1037
|
+
.option('--token-buy-percent <percent>', 'Percent of base price for token buy')
|
|
1038
|
+
.option('--token-buy-recipient <wallet>', 'Wallet to receive token buy portion')
|
|
1039
|
+
.option('--token-buy-ca <address>', 'Token contract address for buy (default: $APINOW)')
|
|
1040
|
+
.option('--recipient <wallet>', 'Endpoint payment recipient wallet')
|
|
1041
|
+
.option('--price <usdc>', 'Override suggested USDC price')
|
|
1042
|
+
.option('--model <model>', 'Override suggested model')
|
|
1043
|
+
.option('--dry-run', 'Generate and print config without creating')
|
|
1044
|
+
.option('-k, --key <privateKey>', 'Wallet private key')
|
|
1045
|
+
.action(async (idea, opts) => {
|
|
1046
|
+
try {
|
|
1047
|
+
const { address } = getWallet(opts);
|
|
1048
|
+
// Step 1: Generate
|
|
1049
|
+
console.error('Step 1/4: Generating endpoint config from idea…');
|
|
1050
|
+
const draft = await fetchJson(`${API_BASE}/api/user-factory/generate`, {
|
|
1051
|
+
method: 'POST',
|
|
1052
|
+
headers: walletHeaders(address),
|
|
1053
|
+
body: JSON.stringify({ idea }),
|
|
1054
|
+
});
|
|
1055
|
+
console.error(` → ${draft.name}: ${draft.description}`);
|
|
1056
|
+
console.error(` → Model: ${draft.model}, Price: $${draft.suggestedPrice}`);
|
|
1057
|
+
console.error(` → Input params: ${(draft.inputParams || []).map((p) => p.name).join(', ') || '(none)'}`);
|
|
1058
|
+
console.error(` → Output params: ${(draft.outputParams || []).map((p) => p.name).join(', ') || '(none)'}`);
|
|
1059
|
+
if (opts.dryRun) {
|
|
1060
|
+
console.log(JSON.stringify(draft, null, 2));
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
// Step 2: Create
|
|
1064
|
+
console.error('\nStep 2/4: Creating endpoint…');
|
|
1065
|
+
const createBody = {
|
|
1066
|
+
name: draft.name,
|
|
1067
|
+
prompt: draft.prompt,
|
|
1068
|
+
description: draft.description,
|
|
1069
|
+
model: opts.model || draft.model || 'google/gemini-2.0-flash-001',
|
|
1070
|
+
usdcPrice: opts.price || draft.suggestedPrice || '0.01',
|
|
1071
|
+
inputParams: draft.inputParams,
|
|
1072
|
+
outputParams: draft.outputParams,
|
|
1073
|
+
};
|
|
1074
|
+
if (opts.recipient)
|
|
1075
|
+
createBody.recipientWallet = opts.recipient;
|
|
1076
|
+
const createData = await fetchJson(`${API_BASE}/api/user-factory`, {
|
|
1077
|
+
method: 'POST',
|
|
1078
|
+
headers: walletHeaders(address),
|
|
1079
|
+
body: JSON.stringify(createBody),
|
|
1080
|
+
});
|
|
1081
|
+
const ep = createData.endpoint;
|
|
1082
|
+
console.error(` → Created: ${ep.namespace}/${ep.endpointName}`);
|
|
1083
|
+
// Step 3: Test
|
|
1084
|
+
console.error('\nStep 3/4: Running test call…');
|
|
1085
|
+
const testInput = draft.exampleInput || { prompt: 'test' };
|
|
1086
|
+
const testData = await fetchJson(`${API_BASE}/api/user-factory/test-call`, {
|
|
1087
|
+
method: 'POST',
|
|
1088
|
+
headers: walletHeaders(address),
|
|
1089
|
+
body: JSON.stringify({
|
|
1090
|
+
namespace: ep.namespace,
|
|
1091
|
+
endpointName: ep.endpointName,
|
|
1092
|
+
input: testInput,
|
|
1093
|
+
saveExample: true,
|
|
1094
|
+
}),
|
|
1095
|
+
});
|
|
1096
|
+
console.error(' → Test passed, example saved');
|
|
1097
|
+
// Step 4: Markup (optional)
|
|
1098
|
+
let workflow = null;
|
|
1099
|
+
if (opts.markup) {
|
|
1100
|
+
console.error(`\nStep 4/4: Creating markup workflow (${opts.markup}%)…`);
|
|
1101
|
+
const markupBody = {
|
|
1102
|
+
endpointId: ep.id,
|
|
1103
|
+
markupPercent: Number(opts.markup),
|
|
1104
|
+
};
|
|
1105
|
+
if (opts.markupRecipient)
|
|
1106
|
+
markupBody.markupRecipient = opts.markupRecipient;
|
|
1107
|
+
if (opts.tokenBuyPercent)
|
|
1108
|
+
markupBody.tokenBuyPercent = Number(opts.tokenBuyPercent);
|
|
1109
|
+
if (opts.tokenBuyRecipient)
|
|
1110
|
+
markupBody.tokenBuyRecipient = opts.tokenBuyRecipient;
|
|
1111
|
+
if (opts.tokenBuyCa)
|
|
1112
|
+
markupBody.tokenBuyCA = opts.tokenBuyCa;
|
|
1113
|
+
const markupData = await fetchJson(`${API_BASE}/api/user-factory/markup`, {
|
|
1114
|
+
method: 'POST',
|
|
1115
|
+
headers: walletHeaders(address),
|
|
1116
|
+
body: JSON.stringify(markupBody),
|
|
1117
|
+
});
|
|
1118
|
+
workflow = markupData.workflow;
|
|
1119
|
+
console.error(` → Workflow: ${workflow.name} ($${workflow.totalPrice} USDC)`);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
console.error('\nStep 4/4: Skipped (no --markup flag)');
|
|
1123
|
+
}
|
|
1124
|
+
// Final output
|
|
1125
|
+
const result = {
|
|
1126
|
+
endpoint: {
|
|
1127
|
+
id: ep.id,
|
|
1128
|
+
namespace: ep.namespace,
|
|
1129
|
+
name: ep.endpointName,
|
|
1130
|
+
model: ep.model,
|
|
1131
|
+
price: ep.price,
|
|
1132
|
+
tryUrl: `https://apinow.fun${ep.tryUrl}`,
|
|
1133
|
+
apiUrl: `https://apinow.fun${ep.apiUrl}`,
|
|
1134
|
+
},
|
|
1135
|
+
testOutput: testData.output,
|
|
1136
|
+
};
|
|
1137
|
+
if (workflow) {
|
|
1138
|
+
result.workflow = {
|
|
1139
|
+
id: workflow.workflowId,
|
|
1140
|
+
name: workflow.name,
|
|
1141
|
+
totalPrice: workflow.totalPrice,
|
|
1142
|
+
markupPercent: workflow.markupPercent,
|
|
1143
|
+
viewUrl: `https://apinow.fun${workflow.viewUrl}`,
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1147
|
+
}
|
|
1148
|
+
catch (err) {
|
|
1149
|
+
console.error(`Error: ${err.message}`);
|
|
1150
|
+
process.exit(1);
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
768
1153
|
program.parse();
|
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
|
/**
|
|
@@ -171,6 +201,79 @@ export declare function createClient(config: ApinowConfig): {
|
|
|
171
201
|
input?: any;
|
|
172
202
|
saveExample?: boolean;
|
|
173
203
|
}): Promise<any>;
|
|
204
|
+
/**
|
|
205
|
+
* Full pipeline: generate config from idea → create endpoint → test → optional markup workflow.
|
|
206
|
+
* Returns { draft, endpoint, testOutput, workflow? }.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* const result = await apinow.factoryPipeline('Score startup pitches on 8 criteria', {
|
|
210
|
+
* recipientWallet: '0x...',
|
|
211
|
+
* markup: { markupPercent: 30, markupRecipient: '0x...' },
|
|
212
|
+
* });
|
|
213
|
+
* console.log(result.endpoint.namespace + '/' + result.endpoint.endpointName);
|
|
214
|
+
* console.log(result.workflow?.viewUrl);
|
|
215
|
+
*/
|
|
216
|
+
factoryPipeline(idea: string, opts?: {
|
|
217
|
+
recipientWallet?: string;
|
|
218
|
+
model?: string;
|
|
219
|
+
usdcPrice?: string;
|
|
220
|
+
markup?: {
|
|
221
|
+
markupPercent?: number;
|
|
222
|
+
markupAmount?: number;
|
|
223
|
+
markupRecipient?: string;
|
|
224
|
+
tokenBuyPercent?: number;
|
|
225
|
+
tokenBuyRecipient?: string;
|
|
226
|
+
tokenBuyCA?: string;
|
|
227
|
+
};
|
|
228
|
+
skipTest?: boolean;
|
|
229
|
+
}): Promise<{
|
|
230
|
+
draft: any;
|
|
231
|
+
endpoint: any;
|
|
232
|
+
testOutput: any;
|
|
233
|
+
workflow: any;
|
|
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>;
|
|
174
277
|
/**
|
|
175
278
|
* Discover the x402 price for any external URL without executing it.
|
|
176
279
|
* Free — no payment required.
|
package/dist/index.js
CHANGED
|
@@ -294,6 +294,155 @@ export function createClient(config) {
|
|
|
294
294
|
}
|
|
295
295
|
return res.json();
|
|
296
296
|
},
|
|
297
|
+
// ─── Factory Pipeline ───
|
|
298
|
+
/**
|
|
299
|
+
* Full pipeline: generate config from idea → create endpoint → test → optional markup workflow.
|
|
300
|
+
* Returns { draft, endpoint, testOutput, workflow? }.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* const result = await apinow.factoryPipeline('Score startup pitches on 8 criteria', {
|
|
304
|
+
* recipientWallet: '0x...',
|
|
305
|
+
* markup: { markupPercent: 30, markupRecipient: '0x...' },
|
|
306
|
+
* });
|
|
307
|
+
* console.log(result.endpoint.namespace + '/' + result.endpoint.endpointName);
|
|
308
|
+
* console.log(result.workflow?.viewUrl);
|
|
309
|
+
*/
|
|
310
|
+
async factoryPipeline(idea, opts = {}) {
|
|
311
|
+
const draft = await this.factoryGenerate(idea);
|
|
312
|
+
const createConfig = {
|
|
313
|
+
name: draft.name,
|
|
314
|
+
prompt: draft.prompt,
|
|
315
|
+
description: draft.description,
|
|
316
|
+
model: opts.model || draft.model || 'google/gemini-2.0-flash-001',
|
|
317
|
+
usdcPrice: opts.usdcPrice || draft.suggestedPrice || '0.01',
|
|
318
|
+
inputParams: draft.inputParams,
|
|
319
|
+
outputParams: draft.outputParams,
|
|
320
|
+
};
|
|
321
|
+
if (opts.recipientWallet)
|
|
322
|
+
createConfig.recipientWallet = opts.recipientWallet;
|
|
323
|
+
const createData = await this.factoryCreate(createConfig);
|
|
324
|
+
const endpoint = createData.endpoint;
|
|
325
|
+
let testOutput = null;
|
|
326
|
+
if (!opts.skipTest) {
|
|
327
|
+
const testInput = draft.exampleInput || { prompt: 'test' };
|
|
328
|
+
const testData = await this.factoryTestCall({
|
|
329
|
+
namespace: endpoint.namespace,
|
|
330
|
+
endpointName: endpoint.endpointName,
|
|
331
|
+
input: testInput,
|
|
332
|
+
saveExample: true,
|
|
333
|
+
});
|
|
334
|
+
testOutput = testData.output;
|
|
335
|
+
}
|
|
336
|
+
let workflow = null;
|
|
337
|
+
if (opts.markup) {
|
|
338
|
+
const markupData = await this.factoryMarkup({
|
|
339
|
+
endpointId: endpoint.id,
|
|
340
|
+
...opts.markup,
|
|
341
|
+
});
|
|
342
|
+
workflow = markupData.workflow;
|
|
343
|
+
}
|
|
344
|
+
return { draft, endpoint, testOutput, workflow };
|
|
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
|
+
},
|
|
297
446
|
// ─── External x402 Proxy ───
|
|
298
447
|
/**
|
|
299
448
|
* Discover the x402 price for any external URL without executing it.
|
package/package.json
CHANGED