@paynodelabs/paynode-402-cli 2.5.2 → 2.7.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/commands/check.ts CHANGED
@@ -24,7 +24,8 @@ export async function checkAction(options: CheckOptions) {
24
24
  const { provider, usdcAddress, chainId, networkName, isSandbox } = await resolveNetwork(
25
25
  options.rpc,
26
26
  options.network,
27
- options.rpcTimeout
27
+ options.rpcTimeout,
28
+ isJson
28
29
  );
29
30
 
30
31
  // Mainnet safety gate
@@ -43,7 +44,9 @@ export async function checkAction(options: CheckOptions) {
43
44
  provider
44
45
  ).balanceOf(address)
45
46
  ]),
46
- 'balanceCheck'
47
+ 'balanceCheck',
48
+ undefined,
49
+ isJson
47
50
  );
48
51
 
49
52
  // BigInt comparisons are used for logic (no precision loss).
@@ -32,18 +32,20 @@ function mergeHeaders(
32
32
  return merged;
33
33
  }
34
34
 
35
- function parsePayload(data?: string): any {
35
+ function parsePayload(data?: string, isJson?: boolean): any {
36
36
  if (!data) return undefined;
37
37
 
38
38
  try {
39
39
  return JSON.parse(data);
40
40
  } catch (err: any) {
41
41
  const isJsonLike = data.trim().startsWith('{') || data.trim().startsWith('[');
42
- if (isJsonLike) {
43
- console.warn(`⚠️ [Warning] Invocation data looks like JSON but failed to parse: ${err.message}`);
44
- console.warn(`Sending as raw string instead. Please verify your JSON syntax.`);
45
- } else {
46
- console.warn(`⚠️ [Warning] Invocation data is not valid JSON. Sending as raw string.`);
42
+ if (!isJson) {
43
+ if (isJsonLike) {
44
+ console.warn(`⚠️ [Warning] Invocation data looks like JSON but failed to parse: ${err.message}`);
45
+ console.warn(`Sending as raw string instead. Please verify your JSON syntax.`);
46
+ } else {
47
+ console.warn(`⚠️ [Warning] Invocation data is not valid JSON. Sending as raw string.`);
48
+ }
47
49
  }
48
50
  return data;
49
51
  }
@@ -60,7 +62,7 @@ export async function invokePaidApiAction(apiId: string, options: InvokePaidApiO
60
62
 
61
63
  const invoke = await client.prepareInvoke(apiId, {
62
64
  network: options.network,
63
- payload: parsePayload(options.data)
65
+ payload: parsePayload(options.data, isJson)
64
66
  });
65
67
 
66
68
  const requestHeaders = mergeHeaders(invoke.headers, options.header);
package/commands/mint.ts CHANGED
@@ -21,7 +21,8 @@ export async function mintAction(options: MintOptions) {
21
21
  const { provider, usdcAddress, chainId, networkName, isSandbox } = await resolveNetwork(
22
22
  options.rpc,
23
23
  options.network || 'testnet',
24
- options.rpcTimeout
24
+ options.rpcTimeout,
25
+ isJson
25
26
  );
26
27
 
27
28
  if (!isSandbox) {
@@ -57,13 +58,17 @@ export async function mintAction(options: MintOptions) {
57
58
  if (!isJson) console.error('⏳ Sending mint transaction...');
58
59
  const tx = await withRetry(
59
60
  () => usdc.mint(wallet.address, amount),
60
- 'mint'
61
+ 'mint',
62
+ undefined,
63
+ isJson
61
64
  );
62
65
 
63
66
  if (!isJson) console.error('⏳ Waiting for confirmation...');
64
67
  const receipt: any = await withRetry(
65
68
  () => tx.wait(),
66
- 'mintConfirm'
69
+ 'mintConfirm',
70
+ undefined,
71
+ isJson
67
72
  );
68
73
 
69
74
  if (!receipt || receipt.status !== 1) {
@@ -164,7 +164,7 @@ async function executeCore(url: string, args: string[], options: UnifiedRequestO
164
164
  throw new Error(`Invalid destination URL: '${url}'. Must start with 'http://' or 'https://'.`);
165
165
  }
166
166
 
167
- const { rpcUrls, networkName, isSandbox } = await resolveNetwork(options.rpc, options.network, options.rpcTimeout);
167
+ const { rpcUrls, networkName, isSandbox } = await resolveNetwork(options.rpc, options.network, options.rpcTimeout, isJson);
168
168
  requireMainnetConfirmation(isSandbox, !!options.confirmMainnet, isJson);
169
169
 
170
170
  // Handle params (k=v)
@@ -262,10 +262,12 @@ async function executeCore(url: string, args: string[], options: UnifiedRequestO
262
262
 
263
263
  const pk = getPrivateKey(isJson);
264
264
 
265
- const client = new PayNodeAgentClient(pk, rpcUrls);
265
+ const client = new PayNodeAgentClient(pk, { rpcUrls, quiet: isJson });
266
266
  const response = await withRetry(
267
267
  () => client.requestGate(targetUrl, requestOptions),
268
- 'x402:requestGate'
268
+ 'x402:requestGate',
269
+ undefined,
270
+ isJson
269
271
  );
270
272
 
271
273
  const contentType = response.headers.get('content-type') || 'application/octet-stream';
@@ -63,7 +63,7 @@ export interface RawCatalogApiItem {
63
63
 
64
64
  function normalizeCatalogItem(raw: RawCatalogApiItem): CatalogApiItem {
65
65
  return {
66
- id: raw.id || raw.api_id || '',
66
+ id: raw.api_id || raw.id?.replace(/-(testnet|mainnet)$/, '') || '',
67
67
  name: raw.name || raw.title || raw.api_name || raw.api_id || 'unnamed',
68
68
  description: raw.description,
69
69
  tags: Array.isArray(raw.tags) ? raw.tags : [],
@@ -80,9 +80,9 @@ function normalizeCatalogItem(raw: RawCatalogApiItem): CatalogApiItem {
80
80
  method: raw.method || raw.http_method,
81
81
  payable_url: raw.payable_url || raw.payment_url,
82
82
  invoke_url: raw.invoke_url,
83
- input_schema: raw.input_schema,
84
- sample_response: raw.sample_response,
85
- headers_template: raw.headers_template
83
+ input_schema: raw.input_schema || {},
84
+ sample_response: raw.sample_response || {},
85
+ headers_template: raw.headers_template || {}
86
86
  };
87
87
  }
88
88
 
@@ -99,7 +99,9 @@ export class MarketplaceClient {
99
99
  const url = joinUrl(this.baseUrl, path);
100
100
  const response = await withRetry(
101
101
  () => fetch(url, init),
102
- `marketplace:${path}`
102
+ `marketplace:${path}`,
103
+ undefined,
104
+ this.isJson
103
105
  );
104
106
 
105
107
  if (!response.ok) {
@@ -147,7 +149,12 @@ export class MarketplaceClient {
147
149
  if (network) params.set('network', network);
148
150
  const query = params.toString();
149
151
  const path = `/api/v1/paid-apis/${encodeURIComponent(apiId)}${query ? `?${query}` : ''}`;
150
- const raw = await this.request<any>(path);
152
+ const raw = await this.request<any>(path, {
153
+ method: 'POST',
154
+ headers: {
155
+ 'X-PayNode-Discovery': 'true'
156
+ }
157
+ });
151
158
  return normalizeCatalogItem(raw);
152
159
  }
153
160
 
@@ -170,7 +177,7 @@ export class MarketplaceClient {
170
177
 
171
178
  return preparation;
172
179
  } catch (err: any) {
173
- console.warn(`[Marketplace] /invoke preparation failed for ${apiId}, falling back to direct proxy. Error: ${err.message}`);
180
+ if (!this.isJson) console.warn(`[Marketplace] /invoke preparation failed for ${apiId}, falling back to direct proxy. Error: ${err.message}`);
174
181
  const detail = await this.getApiDetail(apiId, options.network);
175
182
  const invokeUrl = detail.payable_url || detail.invoke_url;
176
183
  if (!invokeUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paynodelabs/paynode-402-cli",
3
- "version": "2.5.2",
3
+ "version": "2.7.0",
4
4
  "description": "The official command-line interface for the PayNode protocol. Designed for AI Agents to execute stateless micro-payments via HTTP 402.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -23,7 +23,7 @@
23
23
  "build": "echo 'No build required for Bun' && exit 0"
24
24
  },
25
25
  "dependencies": {
26
- "@paynodelabs/sdk-js": "^2.4.0",
26
+ "@paynodelabs/sdk-js": "^2.5.0",
27
27
  "cac": "7.0.0"
28
28
  },
29
29
  "devDependencies": {
package/utils.ts CHANGED
@@ -134,7 +134,8 @@ const MAX_RETRIES = 3;
134
134
  export async function withRetry<T>(
135
135
  fn: () => Promise<T>,
136
136
  label: string,
137
- maxRetries = MAX_RETRIES
137
+ maxRetries = MAX_RETRIES,
138
+ quiet = false
138
139
  ): Promise<T> {
139
140
  let lastError: Error | null = null;
140
141
  for (let attempt = 0; attempt < maxRetries; attempt++) {
@@ -144,7 +145,7 @@ export async function withRetry<T>(
144
145
  lastError = error;
145
146
  if (!isTransientError(error) || attempt >= maxRetries - 1) throw error;
146
147
  const backoffMs = Math.pow(2, attempt) * 1000 * (0.5 + Math.random());
147
- console.error(`⚠️ [${label}] ${error.message}. Retry #${attempt + 1} (of ${maxRetries - 1}) in ${Math.round(backoffMs)}ms...`);
148
+ if (!quiet) console.error(`⚠️ [${label}] ${error.message}. Retry #${attempt + 1} (of ${maxRetries - 1}) in ${Math.round(backoffMs)}ms...`);
148
149
  await new Promise(resolve => setTimeout(resolve, backoffMs));
149
150
  }
150
151
  }
@@ -285,7 +286,7 @@ export function requireMainnetConfirmation(isSandbox: boolean, confirmMainnet: b
285
286
  /**
286
287
  * Resolves network configuration with multi-RPC failover.
287
288
  */
288
- export async function resolveNetwork(providedRpcUrl?: string, network?: string, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<NetworkConfig> {
289
+ export async function resolveNetwork(providedRpcUrl?: string, network?: string, timeoutMs = DEFAULT_TIMEOUT_MS, quiet = false): Promise<NetworkConfig> {
289
290
  const {
290
291
  PAYNODE_ROUTER_ADDRESS,
291
292
  PAYNODE_ROUTER_ADDRESS_SANDBOX,
@@ -324,7 +325,7 @@ export async function resolveNetwork(providedRpcUrl?: string, network?: string,
324
325
  break;
325
326
  } catch (error: any) {
326
327
  lastError = error;
327
- if (rpcUrls.length > 1) console.error(`⚠️ [resolveNetwork] RPC ${url} failed: ${error.message}.`);
328
+ if (rpcUrls.length > 1 && !quiet) console.error(`⚠️ [resolveNetwork] RPC ${url} failed: ${error.message}.`);
328
329
  }
329
330
  }
330
331