apinow-sdk 0.27.0 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.
@@ -65,7 +86,10 @@ export function createClient(config) {
65
86
  async function signAuthHeader() {
66
87
  const issuedAt = new Date().toISOString();
67
88
  const nonce = Math.random().toString(36).slice(2) + Date.now().toString(36);
68
- const message = `APINow auth\naddress: ${address}\nissuedAt: ${issuedAt}\nnonce: ${nonce}`;
89
+ // Single-line format: Chrome's fetch() rejects header values containing
90
+ // raw newlines ("Failed to execute 'fetch' on 'Window': Invalid value").
91
+ // Server parses via regex so space-separated works identically.
92
+ const message = `APINow auth | address: ${address} | issuedAt: ${issuedAt} | nonce: ${nonce}`;
69
93
  const signature = await signMessage(message);
70
94
  return {
71
95
  Authorization: `Bearer ${message}||${signature}||${address}`,
@@ -92,7 +116,14 @@ export function createClient(config) {
92
116
  const res = await authedFetch(url, init);
93
117
  if (!res.ok) {
94
118
  const text = await res.text();
95
- throw new Error(`APINow ${res.status}: ${text}`);
119
+ let parsed = text;
120
+ try {
121
+ parsed = JSON.parse(text);
122
+ }
123
+ catch {
124
+ // leave as text
125
+ }
126
+ throw new ApinowApiError(res.status, parsed);
96
127
  }
97
128
  return res.json();
98
129
  }
@@ -151,6 +182,13 @@ export function createClient(config) {
151
182
  return res.json();
152
183
  },
153
184
  // ─── Endpoint CRUD ───
185
+ /**
186
+ * Create an endpoint. If `splits` are provided they must sum to exactly
187
+ * `10000` basis points; the server deploys a 0xSplits V2 splitter after
188
+ * creation and stores `splitterAddress` on the endpoint. Non-fatal if
189
+ * the splitter deploy fails — endpoint will exist without one and can be
190
+ * retried via `updateEndpoint({ splits })`.
191
+ */
154
192
  async createEndpoint(config) {
155
193
  return authedJson(`${baseUrl}/api/endpoints`, {
156
194
  method: 'POST',
@@ -163,6 +201,12 @@ export function createClient(config) {
163
201
  throw new Error(`Failed to get endpoint: ${res.status}`);
164
202
  return res.json();
165
203
  },
204
+ /**
205
+ * Update an endpoint. Changing `splits` (structurally) redeploys the
206
+ * splitter contract (V2 is immutable); the old address is archived on
207
+ * `previousSplitters[]` and the platform drains leftover USDC from it.
208
+ * `splits` must sum to `10000` basis points when non-empty.
209
+ */
166
210
  async updateEndpoint(id, updates) {
167
211
  return authedJson(`${baseUrl}/api/endpoints/${id}`, {
168
212
  method: 'PUT',
@@ -219,6 +263,13 @@ export function createClient(config) {
219
263
  body: JSON.stringify(config),
220
264
  });
221
265
  },
266
+ /**
267
+ * Update a workflow. Changing `graph`, `totalPrice`, or `splits`
268
+ * auto-creates a new `WorkflowVersion` snapshot server-side; pass
269
+ * `changelog` to label it. `name`/`description` are subject to a 7-day
270
+ * cooldown — throws `ApinowApiError` with `status=429` and `retryAfterMs`
271
+ * when the cooldown is active.
272
+ */
222
273
  async updateWorkflow(workflowId, updates) {
223
274
  return authedJson(`${baseUrl}/api/workflows/${workflowId}`, {
224
275
  method: 'PUT',
@@ -254,8 +305,11 @@ export function createClient(config) {
254
305
  return res.json();
255
306
  },
256
307
  /**
257
- * Create a new workflow version (creator only). Defaults to setting it as default.
258
- * Omit fields to inherit from current workflow.
308
+ * Create a new workflow version (creator only). Defaults to setting it as
309
+ * default. Omit fields to inherit from current workflow. If `splits`
310
+ * differs from the current config, the server redeploys the 0xSplits V2
311
+ * contract and stores `splitterAddress` / `splitterTxHash` on the version.
312
+ * Splits basis points must sum to `10000`.
259
313
  */
260
314
  async createWorkflowVersion(workflowId, updates = {}) {
261
315
  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.1",
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",