@bankofai/x402-cli 1.0.1-beta.8 → 1.0.1-beta.9

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.
@@ -0,0 +1,94 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import YAML from "yaml";
4
+ import { normalizeNetwork } from "./tokens.js";
5
+ function readYaml(file) {
6
+ return YAML.parse(fs.readFileSync(file, "utf8"));
7
+ }
8
+ function expandEnv(value) {
9
+ return value.replace(/\$\{([^}]+)\}/g, (_, name) => {
10
+ if (!(name in process.env))
11
+ throw new Error(`environment variable \${${name}} is not set`);
12
+ return process.env[name] ?? "";
13
+ });
14
+ }
15
+ function expandDeep(value) {
16
+ if (typeof value === "string")
17
+ return expandEnv(value);
18
+ if (Array.isArray(value))
19
+ return value.map(expandDeep);
20
+ if (value && typeof value === "object")
21
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, expandDeep(item)]));
22
+ return value;
23
+ }
24
+ export function providerFiles(root) {
25
+ const stat = fs.statSync(root);
26
+ if (stat.isFile())
27
+ return [root];
28
+ const out = [];
29
+ for (const entry of fs.readdirSync(root, { recursive: true })) {
30
+ const file = path.join(root, String(entry));
31
+ if (file.endsWith("provider.yml") || file.endsWith("provider.yaml"))
32
+ out.push(file);
33
+ }
34
+ return out.sort();
35
+ }
36
+ export function providerPrice(endpoint) {
37
+ return endpoint?.metering?.dimensions?.[0]?.tiers?.[0]?.price_usd ?? 0;
38
+ }
39
+ export function validateProvider(provider, file = "provider.yml") {
40
+ const required = [["name", provider?.name], ["forward_url", provider?.forward_url], ["operator.network", provider?.operator?.network], ["operator.recipient", provider?.operator?.recipient]];
41
+ for (const [name, value] of required)
42
+ if (typeof value !== "string" || !value.trim())
43
+ throw new Error(`${file}: ${name} is required`);
44
+ try {
45
+ const url = new URL(provider.forward_url);
46
+ if (!["http:", "https:"].includes(url.protocol))
47
+ throw new Error("unsupported protocol");
48
+ }
49
+ catch {
50
+ throw new Error(`${file}: forward_url must be a valid http(s) URL`);
51
+ }
52
+ if (!Array.isArray(provider.endpoints) || !provider.endpoints.length)
53
+ throw new Error(`${file}: endpoints must contain at least one endpoint`);
54
+ const seen = new Set();
55
+ for (const endpoint of provider.endpoints) {
56
+ if (typeof endpoint.method !== "string" || typeof endpoint.path !== "string")
57
+ throw new Error(`${file}: each endpoint needs method and path`);
58
+ if (!endpoint.method.trim() || !endpoint.path.trim() || !endpoint.path.startsWith("/"))
59
+ throw new Error(`${file}: endpoint method/path must be non-empty and path must start with /`);
60
+ const price = providerPrice(endpoint);
61
+ if (!Number.isFinite(price) || price < 0)
62
+ throw new Error(`${file}: endpoint price_usd must be a finite number >= 0`);
63
+ const key = `${endpoint.method.toUpperCase()} ${endpoint.path}`;
64
+ if (seen.has(key))
65
+ throw new Error(`${file}: duplicate endpoint ${key}`);
66
+ seen.add(key);
67
+ }
68
+ }
69
+ export function loadProviderFile(file) {
70
+ const provider = expandDeep(readYaml(file));
71
+ validateProvider(provider, file);
72
+ provider.operator.network = normalizeNetwork(provider.operator.network);
73
+ provider.operator.scheme = provider.operator.scheme ?? "exact";
74
+ return provider;
75
+ }
76
+ export function providerAssetTransferMethod(provider) {
77
+ return provider.operator?.asset_transfer_method ?? provider.operator?.assetTransferMethod ?? "permit2";
78
+ }
79
+ export function providerScheme(provider) {
80
+ return provider.operator?.scheme ?? "exact";
81
+ }
82
+ export function providerCatalog(provider) {
83
+ return {
84
+ name: provider.name, title: provider.title ?? provider.name, description: provider.description ?? "",
85
+ category: provider.category ?? "other", service_url: provider.display?.service_url, tags: provider.display?.tags ?? [],
86
+ network: normalizeNetwork(provider.operator.network), currency: provider.operator.currencies?.usd?.[0] ?? "USDT",
87
+ endpoints: (provider.endpoints ?? []).map((endpoint) => ({
88
+ method: endpoint.method.toUpperCase(), path: `/providers/${provider.name}${endpoint.path}`, upstream_path: endpoint.path,
89
+ description: endpoint.description ?? "",
90
+ paid: providerPrice(endpoint) > 0 ? { scheme: providerScheme(provider), network: normalizeNetwork(provider.operator.network), currency: provider.operator.currencies?.usd?.[0] ?? "USDT", price_usd: providerPrice(endpoint) } : null,
91
+ x402_routes: providerPrice(endpoint) > 0 ? [{ provider: provider.name, network: normalizeNetwork(provider.operator.network), scheme: providerScheme(provider), assetTransferMethod: providerAssetTransferMethod(provider), url: `/providers/${provider.name}${endpoint.path}` }] : [],
92
+ })),
93
+ };
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bankofai/x402-cli",
3
- "version": "1.0.1-beta.8",
3
+ "version": "1.0.1-beta.9",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -23,11 +23,10 @@
23
23
  "dependencies": {
24
24
  "@bankofai/x402-core": "1.0.1",
25
25
  "@bankofai/x402-evm": "1.0.1",
26
- "@bankofai/x402-gateway": "1.0.1-beta.7",
26
+ "@bankofai/x402-gateway": "1.0.1-beta.10",
27
27
  "@bankofai/x402-tron": "1.0.1",
28
28
  "tronweb": "6.4.0",
29
- "viem": "^2.55.0",
30
- "yaml": "^2.8.2"
29
+ "viem": "^2.55.0"
31
30
  },
32
31
  "devDependencies": {
33
32
  "@types/node": "^24.10.1",