x402-bazaar 3.6.1 → 3.6.2
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/package.json +1 -1
- package/src/commands/list.js +85 -55
- package/src/commands/search.js +82 -52
- package/src/commands/status.js +29 -26
package/package.json
CHANGED
package/src/commands/list.js
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
-
import ora from
|
|
2
|
-
import chalk from
|
|
3
|
-
import { log } from
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { log } from "../utils/logger.js";
|
|
4
4
|
|
|
5
5
|
const CATEGORIES = [
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
"ai",
|
|
7
|
+
"data",
|
|
8
|
+
"automation",
|
|
9
|
+
"blockchain",
|
|
10
|
+
"weather",
|
|
11
|
+
"finance",
|
|
12
|
+
"social",
|
|
13
|
+
"image",
|
|
14
|
+
"video",
|
|
15
|
+
"audio",
|
|
16
|
+
"search",
|
|
17
|
+
"translation",
|
|
18
|
+
"other",
|
|
8
19
|
];
|
|
9
20
|
|
|
10
21
|
export async function listCommand(options) {
|
|
11
|
-
const serverUrl = options.serverUrl ||
|
|
22
|
+
const serverUrl = options.serverUrl || "https://x402-api.onrender.com";
|
|
12
23
|
|
|
13
24
|
log.banner();
|
|
14
|
-
log.info(`Fetching services from ${chalk.bold(
|
|
15
|
-
console.log(
|
|
25
|
+
log.info(`Fetching services from ${chalk.bold("x402 Bazaar")}`);
|
|
26
|
+
console.log("");
|
|
16
27
|
|
|
17
28
|
// Build query params
|
|
18
29
|
const params = new URLSearchParams();
|
|
19
|
-
if (options.chain) params.append(
|
|
20
|
-
if (options.category) params.append(
|
|
21
|
-
if (options.free) params.append(
|
|
30
|
+
if (options.chain) params.append("chain", options.chain);
|
|
31
|
+
if (options.category) params.append("category", options.category);
|
|
32
|
+
if (options.free) params.append("free", "true");
|
|
22
33
|
|
|
23
|
-
const url = `${serverUrl}/api/services${params.toString() ?
|
|
34
|
+
const url = `${serverUrl}/api/services${params.toString() ? "?" + params.toString() : ""}`;
|
|
24
35
|
|
|
25
|
-
const spinner = ora(
|
|
36
|
+
const spinner = ora("Loading services...").start();
|
|
26
37
|
|
|
27
38
|
try {
|
|
28
39
|
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
|
|
@@ -32,94 +43,113 @@ export async function listCommand(options) {
|
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
const data = await res.json();
|
|
35
|
-
const services =
|
|
46
|
+
const services =
|
|
47
|
+
data.data || data.services || (Array.isArray(data) ? data : []);
|
|
36
48
|
|
|
37
|
-
spinner.succeed(
|
|
38
|
-
|
|
49
|
+
spinner.succeed(
|
|
50
|
+
`Found ${chalk.bold(services.length)} service${services.length !== 1 ? "s" : ""}`,
|
|
51
|
+
);
|
|
52
|
+
console.log("");
|
|
39
53
|
|
|
40
54
|
if (services.length === 0) {
|
|
41
|
-
log.warn(
|
|
42
|
-
log.dim(
|
|
43
|
-
console.log(
|
|
55
|
+
log.warn("No services found with these filters.");
|
|
56
|
+
log.dim(" Try: x402-bazaar list --category ai");
|
|
57
|
+
console.log("");
|
|
44
58
|
return;
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
// Display services table
|
|
48
62
|
log.separator();
|
|
49
|
-
console.log(
|
|
63
|
+
console.log("");
|
|
50
64
|
|
|
51
65
|
services.forEach((service, idx) => {
|
|
52
|
-
const name = service.name || service.title ||
|
|
66
|
+
const name = service.name || service.title || "Unnamed Service";
|
|
53
67
|
const price = parseFloat(service.price || 0);
|
|
54
|
-
const priceLabel =
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
const priceLabel =
|
|
69
|
+
price === 0
|
|
70
|
+
? chalk.hex("#34D399").bold("FREE")
|
|
71
|
+
: chalk.cyan(`$${price.toFixed(3)} USDC`);
|
|
57
72
|
|
|
58
73
|
// Extract category from tags
|
|
59
|
-
let category =
|
|
74
|
+
let category = "other";
|
|
60
75
|
if (service.tags && Array.isArray(service.tags)) {
|
|
61
|
-
const realCategories = service.tags.filter(t =>
|
|
76
|
+
const realCategories = service.tags.filter((t) =>
|
|
77
|
+
CATEGORIES.includes(t.toLowerCase()),
|
|
78
|
+
);
|
|
62
79
|
if (realCategories.length > 0) {
|
|
63
80
|
category = realCategories[0];
|
|
64
81
|
}
|
|
65
82
|
}
|
|
66
83
|
|
|
67
|
-
const chain = service.chain ||
|
|
68
|
-
const chainLabel =
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
const chain = service.chain || "base";
|
|
85
|
+
const chainLabel =
|
|
86
|
+
chain === "skale"
|
|
87
|
+
? chalk.hex("#9333EA")("SKALE")
|
|
88
|
+
: chalk.hex("#0052FF")("Base");
|
|
71
89
|
|
|
72
90
|
console.log(
|
|
73
|
-
chalk
|
|
74
|
-
|
|
91
|
+
chalk
|
|
92
|
+
.hex("#FF9900")
|
|
93
|
+
.bold(`${(idx + 1).toString().padStart(2, " ")}. `) +
|
|
94
|
+
chalk.white.bold(name),
|
|
95
|
+
);
|
|
96
|
+
console.log(
|
|
97
|
+
` ${priceLabel} ${chalk.dim("|")} ${chalk.hex("#60A5FA")(category)} ${chalk.dim("|")} ${chainLabel}`,
|
|
75
98
|
);
|
|
76
|
-
console.log(` ${priceLabel} ${chalk.dim('|')} ${chalk.hex('#60A5FA')(category)} ${chalk.dim('|')} ${chainLabel}`);
|
|
77
99
|
|
|
78
100
|
if (service.description) {
|
|
79
|
-
const desc =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
101
|
+
const desc =
|
|
102
|
+
service.description.length > 80
|
|
103
|
+
? service.description.substring(0, 77) + "..."
|
|
104
|
+
: service.description;
|
|
105
|
+
console.log(` ${chalk.hex("#6B7280")(desc)}`);
|
|
83
106
|
}
|
|
84
107
|
|
|
85
108
|
if (service.endpoint) {
|
|
86
|
-
console.log(
|
|
109
|
+
console.log(
|
|
110
|
+
` ${chalk.dim("Endpoint:")} ${chalk.hex("#FBBF24")(service.endpoint)}`,
|
|
111
|
+
);
|
|
87
112
|
}
|
|
88
113
|
|
|
89
|
-
console.log(
|
|
114
|
+
console.log("");
|
|
90
115
|
});
|
|
91
116
|
|
|
92
117
|
log.separator();
|
|
93
|
-
console.log(
|
|
94
|
-
log.info(
|
|
118
|
+
console.log("");
|
|
119
|
+
log.info(
|
|
120
|
+
`Total: ${chalk.bold(services.length)} service${services.length !== 1 ? "s" : ""}`,
|
|
121
|
+
);
|
|
95
122
|
|
|
96
123
|
// Show filter info
|
|
97
124
|
if (options.chain || options.category || options.free) {
|
|
98
|
-
console.log(
|
|
99
|
-
log.dim(
|
|
125
|
+
console.log("");
|
|
126
|
+
log.dim(" Active filters:");
|
|
100
127
|
if (options.chain) log.dim(` Chain: ${options.chain}`);
|
|
101
128
|
if (options.category) log.dim(` Category: ${options.category}`);
|
|
102
|
-
if (options.free) log.dim(
|
|
103
|
-
log.dim(
|
|
129
|
+
if (options.free) log.dim(" Price: free only");
|
|
130
|
+
log.dim(" Run without options to see all services");
|
|
104
131
|
}
|
|
105
132
|
|
|
106
|
-
console.log(
|
|
107
|
-
|
|
133
|
+
console.log("");
|
|
108
134
|
} catch (err) {
|
|
109
|
-
spinner.fail(
|
|
135
|
+
spinner.fail("Failed to fetch services");
|
|
110
136
|
|
|
111
|
-
if (err.name ===
|
|
112
|
-
log.error(
|
|
113
|
-
log.dim(
|
|
114
|
-
|
|
115
|
-
|
|
137
|
+
if (err.name === "AbortError") {
|
|
138
|
+
log.error("Request timeout — server may be sleeping (Render free tier)");
|
|
139
|
+
log.dim(
|
|
140
|
+
" Try again in 30 seconds or check status: npx x402-bazaar status",
|
|
141
|
+
);
|
|
142
|
+
} else if (err.code === "ECONNREFUSED" || err.code === "ENOTFOUND") {
|
|
143
|
+
log.error("Cannot connect to server");
|
|
116
144
|
log.dim(` Server URL: ${serverUrl}`);
|
|
117
|
-
log.dim(
|
|
145
|
+
log.dim(
|
|
146
|
+
" Check your internet connection or try: npx x402-bazaar status",
|
|
147
|
+
);
|
|
118
148
|
} else {
|
|
119
149
|
log.error(err.message);
|
|
120
150
|
}
|
|
121
151
|
|
|
122
|
-
console.log(
|
|
152
|
+
console.log("");
|
|
123
153
|
process.exit(1);
|
|
124
154
|
}
|
|
125
155
|
}
|
package/src/commands/search.js
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
|
-
import ora from
|
|
2
|
-
import chalk from
|
|
3
|
-
import { log } from
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { log } from "../utils/logger.js";
|
|
4
4
|
|
|
5
5
|
const CATEGORIES = [
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
"ai",
|
|
7
|
+
"data",
|
|
8
|
+
"automation",
|
|
9
|
+
"blockchain",
|
|
10
|
+
"weather",
|
|
11
|
+
"finance",
|
|
12
|
+
"social",
|
|
13
|
+
"image",
|
|
14
|
+
"video",
|
|
15
|
+
"audio",
|
|
16
|
+
"search",
|
|
17
|
+
"translation",
|
|
18
|
+
"other",
|
|
8
19
|
];
|
|
9
20
|
|
|
10
21
|
export async function searchCommand(query, options) {
|
|
11
22
|
if (!query || query.trim().length === 0) {
|
|
12
|
-
log.error(
|
|
13
|
-
log.dim(
|
|
23
|
+
log.error("Search query is required");
|
|
24
|
+
log.dim(" Usage: x402-bazaar search <query>");
|
|
14
25
|
log.dim(' Example: x402-bazaar search "weather API"');
|
|
15
|
-
console.log(
|
|
26
|
+
console.log("");
|
|
16
27
|
process.exit(1);
|
|
17
28
|
}
|
|
18
29
|
|
|
19
|
-
const serverUrl = options.serverUrl ||
|
|
30
|
+
const serverUrl = options.serverUrl || "https://x402-api.onrender.com";
|
|
20
31
|
|
|
21
32
|
log.banner();
|
|
22
33
|
log.info(`Searching for: ${chalk.bold(query)}`);
|
|
23
|
-
console.log(
|
|
34
|
+
console.log("");
|
|
24
35
|
|
|
25
36
|
const url = `${serverUrl}/api/services?search=${encodeURIComponent(query)}`;
|
|
26
|
-
const spinner = ora(
|
|
37
|
+
const spinner = ora("Searching...").start();
|
|
27
38
|
|
|
28
39
|
try {
|
|
29
40
|
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
|
|
@@ -33,88 +44,107 @@ export async function searchCommand(query, options) {
|
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
const data = await res.json();
|
|
36
|
-
const services =
|
|
47
|
+
const services =
|
|
48
|
+
data.data || data.services || (Array.isArray(data) ? data : []);
|
|
37
49
|
|
|
38
50
|
if (services.length === 0) {
|
|
39
|
-
spinner.fail(
|
|
40
|
-
console.log(
|
|
51
|
+
spinner.fail("No results found");
|
|
52
|
+
console.log("");
|
|
41
53
|
log.warn(`No services match "${query}"`);
|
|
42
|
-
log.dim(
|
|
43
|
-
log.dim(
|
|
54
|
+
log.dim(" Tips:");
|
|
55
|
+
log.dim(" • Check your spelling");
|
|
44
56
|
log.dim(' • Try broader keywords (e.g., "AI" instead of "GPT-4")');
|
|
45
|
-
log.dim(
|
|
46
|
-
console.log(
|
|
57
|
+
log.dim(" • Browse all services: x402-bazaar list");
|
|
58
|
+
console.log("");
|
|
47
59
|
return;
|
|
48
60
|
}
|
|
49
61
|
|
|
50
|
-
spinner.succeed(
|
|
51
|
-
|
|
62
|
+
spinner.succeed(
|
|
63
|
+
`Found ${chalk.bold(services.length)} result${services.length !== 1 ? "s" : ""}`,
|
|
64
|
+
);
|
|
65
|
+
console.log("");
|
|
52
66
|
|
|
53
67
|
// Display results
|
|
54
68
|
log.separator();
|
|
55
|
-
console.log(
|
|
69
|
+
console.log("");
|
|
56
70
|
|
|
57
71
|
services.forEach((service, idx) => {
|
|
58
|
-
const name = service.name || service.title ||
|
|
72
|
+
const name = service.name || service.title || "Unnamed Service";
|
|
59
73
|
const price = parseFloat(service.price || 0);
|
|
60
|
-
const priceLabel =
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
const priceLabel =
|
|
75
|
+
price === 0
|
|
76
|
+
? chalk.hex("#34D399").bold("FREE")
|
|
77
|
+
: chalk.cyan(`$${price.toFixed(3)} USDC`);
|
|
63
78
|
|
|
64
79
|
// Extract category from tags
|
|
65
|
-
let category =
|
|
80
|
+
let category = "other";
|
|
66
81
|
if (service.tags && Array.isArray(service.tags)) {
|
|
67
|
-
const realCategories = service.tags.filter(t =>
|
|
82
|
+
const realCategories = service.tags.filter((t) =>
|
|
83
|
+
CATEGORIES.includes(t.toLowerCase()),
|
|
84
|
+
);
|
|
68
85
|
if (realCategories.length > 0) {
|
|
69
86
|
category = realCategories[0];
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
|
-
const chain = service.chain ||
|
|
74
|
-
const chainLabel =
|
|
75
|
-
|
|
76
|
-
|
|
90
|
+
const chain = service.chain || "base";
|
|
91
|
+
const chainLabel =
|
|
92
|
+
chain === "skale"
|
|
93
|
+
? chalk.hex("#9333EA")("SKALE")
|
|
94
|
+
: chalk.hex("#0052FF")("Base");
|
|
77
95
|
|
|
78
96
|
console.log(
|
|
79
|
-
chalk
|
|
80
|
-
|
|
97
|
+
chalk
|
|
98
|
+
.hex("#FF9900")
|
|
99
|
+
.bold(`${(idx + 1).toString().padStart(2, " ")}. `) +
|
|
100
|
+
chalk.white.bold(name),
|
|
101
|
+
);
|
|
102
|
+
console.log(
|
|
103
|
+
` ${priceLabel} ${chalk.dim("|")} ${chalk.hex("#60A5FA")(category)} ${chalk.dim("|")} ${chainLabel}`,
|
|
81
104
|
);
|
|
82
|
-
console.log(` ${priceLabel} ${chalk.dim('|')} ${chalk.hex('#60A5FA')(category)} ${chalk.dim('|')} ${chainLabel}`);
|
|
83
105
|
|
|
84
106
|
if (service.description) {
|
|
85
|
-
const desc =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
107
|
+
const desc =
|
|
108
|
+
service.description.length > 80
|
|
109
|
+
? service.description.substring(0, 77) + "..."
|
|
110
|
+
: service.description;
|
|
111
|
+
console.log(` ${chalk.hex("#6B7280")(desc)}`);
|
|
89
112
|
}
|
|
90
113
|
|
|
91
114
|
if (service.endpoint) {
|
|
92
|
-
console.log(
|
|
115
|
+
console.log(
|
|
116
|
+
` ${chalk.dim("Endpoint:")} ${chalk.hex("#FBBF24")(service.endpoint)}`,
|
|
117
|
+
);
|
|
93
118
|
}
|
|
94
119
|
|
|
95
|
-
console.log(
|
|
120
|
+
console.log("");
|
|
96
121
|
});
|
|
97
122
|
|
|
98
123
|
log.separator();
|
|
99
|
-
console.log(
|
|
100
|
-
log.info(
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
console.log("");
|
|
125
|
+
log.info(
|
|
126
|
+
`Total: ${chalk.bold(services.length)} result${services.length !== 1 ? "s" : ""}`,
|
|
127
|
+
);
|
|
128
|
+
console.log("");
|
|
103
129
|
} catch (err) {
|
|
104
|
-
spinner.fail(
|
|
130
|
+
spinner.fail("Search failed");
|
|
105
131
|
|
|
106
|
-
if (err.name ===
|
|
107
|
-
log.error(
|
|
108
|
-
log.dim(
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
if (err.name === "AbortError") {
|
|
133
|
+
log.error("Request timeout — server may be sleeping (Render free tier)");
|
|
134
|
+
log.dim(
|
|
135
|
+
" Try again in 30 seconds or check status: npx x402-bazaar status",
|
|
136
|
+
);
|
|
137
|
+
} else if (err.code === "ECONNREFUSED" || err.code === "ENOTFOUND") {
|
|
138
|
+
log.error("Cannot connect to server");
|
|
111
139
|
log.dim(` Server URL: ${serverUrl}`);
|
|
112
|
-
log.dim(
|
|
140
|
+
log.dim(
|
|
141
|
+
" Check your internet connection or try: npx x402-bazaar status",
|
|
142
|
+
);
|
|
113
143
|
} else {
|
|
114
144
|
log.error(err.message);
|
|
115
145
|
}
|
|
116
146
|
|
|
117
|
-
console.log(
|
|
147
|
+
console.log("");
|
|
118
148
|
process.exit(1);
|
|
119
149
|
}
|
|
120
150
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import ora from
|
|
2
|
-
import chalk from
|
|
3
|
-
import { log } from
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { log } from "../utils/logger.js";
|
|
4
4
|
|
|
5
5
|
export async function statusCommand(options) {
|
|
6
|
-
const serverUrl = options.serverUrl ||
|
|
6
|
+
const serverUrl = options.serverUrl || "https://x402-api.onrender.com";
|
|
7
7
|
|
|
8
8
|
log.banner();
|
|
9
9
|
log.info(`Checking connection to ${chalk.bold(serverUrl)}`);
|
|
10
10
|
log.separator();
|
|
11
|
-
console.log(
|
|
11
|
+
console.log("");
|
|
12
12
|
|
|
13
13
|
// 1. Health check
|
|
14
14
|
let healthOk = false;
|
|
15
|
-
const spinner = ora(
|
|
15
|
+
const spinner = ora("Checking /health endpoint...").start();
|
|
16
16
|
try {
|
|
17
17
|
const res = await fetch(`${serverUrl}/health`);
|
|
18
18
|
const data = await res.json();
|
|
@@ -21,43 +21,46 @@ export async function statusCommand(options) {
|
|
|
21
21
|
} catch (err) {
|
|
22
22
|
spinner.fail(`Cannot reach ${serverUrl}/health`);
|
|
23
23
|
log.error(` ${err.message}`);
|
|
24
|
-
log.dim(
|
|
25
|
-
console.log(
|
|
24
|
+
log.dim(" Make sure the server is running and the URL is correct.");
|
|
25
|
+
console.log("");
|
|
26
26
|
process.exit(1);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// 2. Root endpoint
|
|
30
|
-
const spinner2 = ora(
|
|
30
|
+
const spinner2 = ora("Fetching marketplace info...").start();
|
|
31
31
|
try {
|
|
32
32
|
const res = await fetch(serverUrl);
|
|
33
33
|
const data = await res.json();
|
|
34
|
-
spinner2.succeed(
|
|
34
|
+
spinner2.succeed(
|
|
35
|
+
`${data.name} — ${chalk.bold(data.total_services)} services`,
|
|
36
|
+
);
|
|
35
37
|
} catch (err) {
|
|
36
38
|
spinner2.fail(`Cannot fetch marketplace info: ${err.message}`);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
// 3. Stats
|
|
40
|
-
const spinner3 = ora(
|
|
41
|
+
// 3. Stats (from public endpoints — /api/stats requires admin auth)
|
|
42
|
+
const spinner3 = ora("Fetching stats...").start();
|
|
41
43
|
try {
|
|
42
|
-
const res = await fetch(`${serverUrl}/api/
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
log.dim(`
|
|
44
|
+
const res = await fetch(`${serverUrl}/api/services?limit=0`);
|
|
45
|
+
const svcData = await res.json();
|
|
46
|
+
const total = svcData.pagination?.total ?? svcData.data?.length ?? "?";
|
|
47
|
+
const online = Array.isArray(svcData.data)
|
|
48
|
+
? svcData.data.filter((s) => s.status === "online").length
|
|
49
|
+
: "?";
|
|
50
|
+
spinner3.succeed("Stats loaded");
|
|
51
|
+
console.log("");
|
|
52
|
+
log.dim(` Total services: ${total}`);
|
|
53
|
+
log.dim(` Online: ${online}`);
|
|
54
|
+
log.dim(` Free tier: 5 calls/day without wallet`);
|
|
55
|
+
log.dim(` Chains: SKALE (gas-free), Base, Polygon`);
|
|
53
56
|
} catch (err) {
|
|
54
57
|
spinner3.fail(`Cannot fetch stats: ${err.message}`);
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
console.log(
|
|
60
|
+
console.log("");
|
|
58
61
|
log.separator();
|
|
59
|
-
log.success(chalk.bold(
|
|
62
|
+
log.success(chalk.bold("x402 Bazaar is operational!"));
|
|
60
63
|
log.dim(` Dashboard: ${serverUrl}/dashboard`);
|
|
61
64
|
log.dim(` Website: https://x402bazaar.org`);
|
|
62
|
-
console.log(
|
|
65
|
+
console.log("");
|
|
63
66
|
}
|