apinow-sdk 0.27.0 → 0.28.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/dist/cli.js CHANGED
@@ -210,6 +210,7 @@ program
210
210
  .requiredOption('--description <desc>', 'Description')
211
211
  .option('--method <method>', 'HTTP method', 'POST')
212
212
  .option('--price <usdc>', 'USDC price per call', '0.01')
213
+ .option('--splits <json>', 'Splits JSON array [{address, basisPoints, label?, tokenAddress?}] — basisPoints must sum to 10000')
213
214
  .option('-k, --key <privateKey>', 'Wallet private key')
214
215
  .action(async (opts) => {
215
216
  try {
@@ -222,6 +223,8 @@ program
222
223
  httpMethod: opts.method.toUpperCase(),
223
224
  paymentOptions: [{ usdAmount: opts.price, amount: opts.price }],
224
225
  };
226
+ if (opts.splits)
227
+ body.splits = JSON.parse(opts.splits);
225
228
  const data = await fetchJson(`${API_BASE}/api/endpoints`, {
226
229
  method: 'POST',
227
230
  headers: await walletHeaders(privateKey),
@@ -242,6 +245,7 @@ program
242
245
  .option('--url <url>', 'New URL')
243
246
  .option('--price <usdc>', 'New USDC price')
244
247
  .option('--status <status>', 'New status')
248
+ .option('--splits <json>', 'Splits JSON array — changing splits redeploys the splitter contract (V2 is immutable)')
245
249
  .option('-k, --key <privateKey>', 'Wallet private key')
246
250
  .action(async (id, opts) => {
247
251
  try {
@@ -255,6 +259,8 @@ program
255
259
  body.status = opts.status;
256
260
  if (opts.price)
257
261
  body.paymentOptions = [{ usdAmount: opts.price, amount: opts.price }];
262
+ if (opts.splits)
263
+ body.splits = JSON.parse(opts.splits);
258
264
  const data = await fetchJson(`${API_BASE}/api/endpoints/${id}`, {
259
265
  method: 'PUT',
260
266
  headers: await walletHeaders(privateKey),
@@ -518,11 +524,13 @@ program
518
524
  // ─── workflow-update ───
519
525
  program
520
526
  .command('workflow-update <id>')
521
- .description('Update a workflow by ID')
522
- .option('--name <name>', 'New name')
523
- .option('--description <desc>', 'New description')
527
+ .description('Update a workflow by ID (price/splits/graph changes auto-create a new version)')
528
+ .option('--name <name>', 'New name (7-day cooldown; 429 if on cooldown)')
529
+ .option('--description <desc>', 'New description (7-day cooldown)')
524
530
  .option('--status <status>', 'New status')
525
- .option('--price <usdc>', 'New total price')
531
+ .option('--price <usdc>', 'New total price — auto-bumps version')
532
+ .option('--splits <json>', 'Splits JSON array — auto-bumps version; redeploys splitter if structurally changed')
533
+ .option('--changelog <msg>', 'Label for the auto-created version snapshot')
526
534
  .option('-k, --key <privateKey>', 'Wallet private key')
527
535
  .action(async (id, opts) => {
528
536
  try {
@@ -536,6 +544,10 @@ program
536
544
  body.status = opts.status;
537
545
  if (opts.price)
538
546
  body.totalPrice = opts.price;
547
+ if (opts.splits)
548
+ body.splits = JSON.parse(opts.splits);
549
+ if (opts.changelog)
550
+ body.changelog = opts.changelog;
539
551
  const data = await fetchJson(`${API_BASE}/api/workflows/${id}`, {
540
552
  method: 'PUT',
541
553
  headers: await walletHeaders(privateKey),
package/dist/index.d.ts CHANGED
@@ -43,6 +43,25 @@ export interface ApinowBrowserConfig {
43
43
  paidFetch?: typeof globalThis.fetch;
44
44
  }
45
45
  export type ApinowConfig = ApinowServerConfig | ApinowBrowserConfig;
46
+ export interface SplitConfig {
47
+ address: string;
48
+ basisPoints: number;
49
+ label?: string;
50
+ tokenAddress?: string;
51
+ recipientAddress?: string;
52
+ }
53
+ /**
54
+ * Thrown by authed writes when the backend returns a non-2xx. Exposes status
55
+ * code and parsed JSON body so callers can branch on e.g. 429 cooldowns
56
+ * without re-parsing error text.
57
+ */
58
+ export declare class ApinowApiError extends Error {
59
+ readonly status: number;
60
+ readonly body: any;
61
+ readonly retryAfterMs?: number;
62
+ readonly retryAfterDays?: number;
63
+ constructor(status: number, body: any);
64
+ }
46
65
  export interface GenerateUIOptions {
47
66
  endpointName: string;
48
67
  namespace: string;
@@ -98,6 +117,13 @@ export declare function createClient(config: ApinowConfig): {
98
117
  * Get public endpoint info (free, no payment).
99
118
  */
100
119
  info(namespace: string, endpointName: string): Promise<any>;
120
+ /**
121
+ * Create an endpoint. If `splits` are provided they must sum to exactly
122
+ * `10000` basis points; the server deploys a 0xSplits V2 splitter after
123
+ * creation and stores `splitterAddress` on the endpoint. Non-fatal if
124
+ * the splitter deploy fails — endpoint will exist without one and can be
125
+ * retried via `updateEndpoint({ splits })`.
126
+ */
101
127
  createEndpoint(config: {
102
128
  namespace: string;
103
129
  endpointName: string;
@@ -116,9 +142,34 @@ export declare function createClient(config: ApinowConfig): {
116
142
  exampleQuery?: any;
117
143
  exampleOutput?: any;
118
144
  docsUrl?: string;
145
+ splits?: SplitConfig[];
119
146
  }): Promise<any>;
120
147
  getEndpoint(id: string): Promise<any>;
121
- updateEndpoint(id: string, updates: Record<string, any>): Promise<any>;
148
+ /**
149
+ * Update an endpoint. Changing `splits` (structurally) redeploys the
150
+ * splitter contract (V2 is immutable); the old address is archived on
151
+ * `previousSplitters[]` and the platform drains leftover USDC from it.
152
+ * `splits` must sum to `10000` basis points when non-empty.
153
+ */
154
+ updateEndpoint(id: string, updates: {
155
+ url?: string;
156
+ description?: string;
157
+ httpMethod?: "GET" | "POST";
158
+ paymentOptions?: Array<{
159
+ amount?: string;
160
+ usdAmount?: string;
161
+ tokenAddress?: string;
162
+ tokenSymbol?: string;
163
+ }>;
164
+ status?: string;
165
+ splits?: SplitConfig[];
166
+ querySchema?: any;
167
+ responseSchema?: any;
168
+ exampleQuery?: any;
169
+ exampleOutput?: any;
170
+ docsUrl?: string;
171
+ [key: string]: any;
172
+ }): Promise<any>;
122
173
  deleteEndpoint(id: string): Promise<any>;
123
174
  listEndpoints(opts?: {
124
175
  limit?: number;
@@ -153,15 +204,32 @@ export declare function createClient(config: ApinowConfig): {
153
204
  };
154
205
  prompt?: string;
155
206
  totalPrice?: string;
156
- splits?: Array<{
157
- address: string;
158
- basisPoints: number;
159
- label?: string;
160
- tokenAddress?: string;
161
- }>;
207
+ splits?: SplitConfig[];
162
208
  chain?: string;
163
209
  }): Promise<any>;
164
- updateWorkflow(workflowId: string, updates: Record<string, any>): Promise<any>;
210
+ /**
211
+ * Update a workflow. Changing `graph`, `totalPrice`, or `splits`
212
+ * auto-creates a new `WorkflowVersion` snapshot server-side; pass
213
+ * `changelog` to label it. `name`/`description` are subject to a 7-day
214
+ * cooldown — throws `ApinowApiError` with `status=429` and `retryAfterMs`
215
+ * when the cooldown is active.
216
+ */
217
+ updateWorkflow(workflowId: string, updates: {
218
+ name?: string;
219
+ description?: string;
220
+ status?: string;
221
+ totalPrice?: string;
222
+ splits?: SplitConfig[];
223
+ graph?: {
224
+ nodes: any[];
225
+ outputNode: string;
226
+ outputMapping?: any;
227
+ };
228
+ executionMode?: "balanced" | "optimistic" | "settle_first";
229
+ mermaidDiagram?: string;
230
+ changelog?: string;
231
+ [key: string]: any;
232
+ }): Promise<any>;
165
233
  deleteWorkflow(workflowId: string): Promise<any>;
166
234
  /**
167
235
  * List workflows you created (convenience for `listWorkflows({ creator: yourWallet })`).
@@ -181,8 +249,11 @@ export declare function createClient(config: ApinowConfig): {
181
249
  */
182
250
  getWorkflowVersion(workflowId: string, versionIdOrNumber: string | number): Promise<any>;
183
251
  /**
184
- * Create a new workflow version (creator only). Defaults to setting it as default.
185
- * Omit fields to inherit from current workflow.
252
+ * Create a new workflow version (creator only). Defaults to setting it as
253
+ * default. Omit fields to inherit from current workflow. If `splits`
254
+ * differs from the current config, the server redeploys the 0xSplits V2
255
+ * contract and stores `splitterAddress` / `splitterTxHash` on the version.
256
+ * Splits basis points must sum to `10000`.
186
257
  */
187
258
  createWorkflowVersion(workflowId: string, updates?: {
188
259
  graph?: {
@@ -191,12 +262,7 @@ export declare function createClient(config: ApinowConfig): {
191
262
  outputMapping?: any;
192
263
  };
193
264
  totalPrice?: string;
194
- splits?: Array<{
195
- address: string;
196
- basisPoints: number;
197
- label?: string;
198
- tokenAddress?: string;
199
- }>;
265
+ splits?: SplitConfig[];
200
266
  mermaidDiagram?: string;
201
267
  executionMode?: "balanced" | "optimistic" | "settle_first";
202
268
  changelog?: string;
package/dist/index.js CHANGED
@@ -5,6 +5,27 @@ const APINOW_BASE = 'https://apinow.fun';
5
5
  function isServerConfig(c) {
6
6
  return 'privateKey' in c && typeof c.privateKey === 'string';
7
7
  }
8
+ /**
9
+ * Thrown by authed writes when the backend returns a non-2xx. Exposes status
10
+ * code and parsed JSON body so callers can branch on e.g. 429 cooldowns
11
+ * without re-parsing error text.
12
+ */
13
+ export class ApinowApiError extends Error {
14
+ constructor(status, body) {
15
+ const msg = (body && typeof body === 'object' && (body.error || body.message)) ||
16
+ (typeof body === 'string' ? body : JSON.stringify(body));
17
+ super(`APINow ${status}: ${msg}`);
18
+ this.name = 'ApinowApiError';
19
+ this.status = status;
20
+ this.body = body;
21
+ if (body && typeof body === 'object') {
22
+ if (typeof body.retryAfterMs === 'number')
23
+ this.retryAfterMs = body.retryAfterMs;
24
+ if (typeof body.retryAfterDays === 'number')
25
+ this.retryAfterDays = body.retryAfterDays;
26
+ }
27
+ }
28
+ }
8
29
  // ─── SDK ───
9
30
  // Prevent undici (Node 20+ / Vercel) from crashing when @x402/fetch retries
10
31
  // a POST with a cloned Request body stream and the server returns a 3xx redirect.
@@ -92,7 +113,14 @@ export function createClient(config) {
92
113
  const res = await authedFetch(url, init);
93
114
  if (!res.ok) {
94
115
  const text = await res.text();
95
- throw new Error(`APINow ${res.status}: ${text}`);
116
+ let parsed = text;
117
+ try {
118
+ parsed = JSON.parse(text);
119
+ }
120
+ catch {
121
+ // leave as text
122
+ }
123
+ throw new ApinowApiError(res.status, parsed);
96
124
  }
97
125
  return res.json();
98
126
  }
@@ -151,6 +179,13 @@ export function createClient(config) {
151
179
  return res.json();
152
180
  },
153
181
  // ─── Endpoint CRUD ───
182
+ /**
183
+ * Create an endpoint. If `splits` are provided they must sum to exactly
184
+ * `10000` basis points; the server deploys a 0xSplits V2 splitter after
185
+ * creation and stores `splitterAddress` on the endpoint. Non-fatal if
186
+ * the splitter deploy fails — endpoint will exist without one and can be
187
+ * retried via `updateEndpoint({ splits })`.
188
+ */
154
189
  async createEndpoint(config) {
155
190
  return authedJson(`${baseUrl}/api/endpoints`, {
156
191
  method: 'POST',
@@ -163,6 +198,12 @@ export function createClient(config) {
163
198
  throw new Error(`Failed to get endpoint: ${res.status}`);
164
199
  return res.json();
165
200
  },
201
+ /**
202
+ * Update an endpoint. Changing `splits` (structurally) redeploys the
203
+ * splitter contract (V2 is immutable); the old address is archived on
204
+ * `previousSplitters[]` and the platform drains leftover USDC from it.
205
+ * `splits` must sum to `10000` basis points when non-empty.
206
+ */
166
207
  async updateEndpoint(id, updates) {
167
208
  return authedJson(`${baseUrl}/api/endpoints/${id}`, {
168
209
  method: 'PUT',
@@ -219,6 +260,13 @@ export function createClient(config) {
219
260
  body: JSON.stringify(config),
220
261
  });
221
262
  },
263
+ /**
264
+ * Update a workflow. Changing `graph`, `totalPrice`, or `splits`
265
+ * auto-creates a new `WorkflowVersion` snapshot server-side; pass
266
+ * `changelog` to label it. `name`/`description` are subject to a 7-day
267
+ * cooldown — throws `ApinowApiError` with `status=429` and `retryAfterMs`
268
+ * when the cooldown is active.
269
+ */
222
270
  async updateWorkflow(workflowId, updates) {
223
271
  return authedJson(`${baseUrl}/api/workflows/${workflowId}`, {
224
272
  method: 'PUT',
@@ -254,8 +302,11 @@ export function createClient(config) {
254
302
  return res.json();
255
303
  },
256
304
  /**
257
- * Create a new workflow version (creator only). Defaults to setting it as default.
258
- * Omit fields to inherit from current workflow.
305
+ * Create a new workflow version (creator only). Defaults to setting it as
306
+ * default. Omit fields to inherit from current workflow. If `splits`
307
+ * differs from the current config, the server redeploys the 0xSplits V2
308
+ * contract and stores `splitterAddress` / `splitterTxHash` on the version.
309
+ * Splits basis points must sum to `10000`.
259
310
  */
260
311
  async createWorkflowVersion(workflowId, updates = {}) {
261
312
  return authedJson(`${baseUrl}/api/workflows/${workflowId}/versions`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apinow-sdk",
3
- "version": "0.27.0",
3
+ "version": "0.28.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",