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 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.21.1');
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
- .requiredOption('--name <name>', 'Endpoint name')
642
- .requiredOption('--prompt <prompt>', 'System prompt for the LLM')
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
- const body = {
655
- name: opts.name,
656
- prompt: opts.prompt,
657
- model: opts.model,
658
- usdcPrice: opts.price,
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 "${opts.name}"…`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apinow-sdk",
3
- "version": "0.21.1",
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",