@zeroxyz/cli 0.0.21 → 0.0.23
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/index.js +150 -58
- package/package.json +1 -1
- package/skills/zero/SKILL.md +11 -3
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command as Command9 } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@zeroxyz/cli",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.23",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
12
12
|
zero: "dist/index.js",
|
|
@@ -111,6 +111,44 @@ var configCommand = (_appContext) => new Command("config").description("View or
|
|
|
111
111
|
|
|
112
112
|
// src/commands/fetch-command.ts
|
|
113
113
|
import { Command as Command2 } from "commander";
|
|
114
|
+
|
|
115
|
+
// src/util/infer-schema.ts
|
|
116
|
+
var inferSchema = (value, depth = 0) => {
|
|
117
|
+
if (depth > 6) return { type: typeOf(value) };
|
|
118
|
+
if (value === null) return { type: "null" };
|
|
119
|
+
if (Array.isArray(value)) {
|
|
120
|
+
const itemSchemas = value.slice(0, 3).map((v) => inferSchema(v, depth + 1));
|
|
121
|
+
return {
|
|
122
|
+
type: "array",
|
|
123
|
+
items: itemSchemas[0] ?? { type: "unknown" }
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (typeof value === "object") {
|
|
127
|
+
const obj = value;
|
|
128
|
+
const properties = {};
|
|
129
|
+
const required = [];
|
|
130
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
131
|
+
properties[k] = inferSchema(v, depth + 1);
|
|
132
|
+
required.push(k);
|
|
133
|
+
}
|
|
134
|
+
return { type: "object", properties, required };
|
|
135
|
+
}
|
|
136
|
+
return { type: typeOf(value) };
|
|
137
|
+
};
|
|
138
|
+
var typeOf = (v) => {
|
|
139
|
+
if (v === null) return "null";
|
|
140
|
+
if (Array.isArray(v)) return "array";
|
|
141
|
+
return typeof v;
|
|
142
|
+
};
|
|
143
|
+
var tryParseJson = (text) => {
|
|
144
|
+
try {
|
|
145
|
+
return JSON.parse(text);
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/commands/fetch-command.ts
|
|
114
152
|
var detectPaymentRequirement = (headers, status) => {
|
|
115
153
|
if (status !== 402) return null;
|
|
116
154
|
const x402Header = headers.get("payment-required") ?? headers.get("x-payment-required");
|
|
@@ -130,7 +168,10 @@ var detectPaymentRequirement = (headers, status) => {
|
|
|
130
168
|
}
|
|
131
169
|
return { protocol: "unknown", raw: {} };
|
|
132
170
|
};
|
|
133
|
-
var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a capability URL with automatic payment handling").argument("<url>", "URL to fetch").option(
|
|
171
|
+
var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a capability URL with automatic payment handling").argument("<url>", "URL to fetch").option(
|
|
172
|
+
"-X, --method <method>",
|
|
173
|
+
"HTTP method (GET, POST, PUT, PATCH, DELETE). Defaults to POST when -d is set, otherwise GET"
|
|
174
|
+
).option("-d, --data <body>", "Request body (JSON string)").option("-H, --header <header...>", "Headers in Key:Value format").option("--max-pay <amount>", "Maximum amount willing to pay (USDC)").option(
|
|
134
175
|
"--capability <id>",
|
|
135
176
|
"Bind this fetch to a capability (uid or slug) so a reviewable run is recorded even without a prior `zero search`"
|
|
136
177
|
).option(
|
|
@@ -163,55 +204,80 @@ var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a ca
|
|
|
163
204
|
headers["content-type"] = "application/json";
|
|
164
205
|
}
|
|
165
206
|
const log = (msg) => console.error(` ${msg}`);
|
|
207
|
+
const method = options.method ? options.method.toUpperCase() : options.data ? "POST" : "GET";
|
|
166
208
|
const requestInit = {
|
|
167
|
-
method
|
|
209
|
+
method,
|
|
168
210
|
headers,
|
|
169
211
|
body: options.data
|
|
170
212
|
};
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
response.headers,
|
|
175
|
-
response.status
|
|
213
|
+
const lastSearch = stateService.loadLastSearch();
|
|
214
|
+
const matchedCapability = lastSearch?.capabilities.find(
|
|
215
|
+
(c) => url.startsWith(c.url)
|
|
176
216
|
);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
217
|
+
const capabilityId = options.capability ?? matchedCapability?.id ?? null;
|
|
218
|
+
const searchId = matchedCapability ? lastSearch?.searchId : void 0;
|
|
219
|
+
const skipReasons = [];
|
|
220
|
+
if (!apiService.walletAddress) {
|
|
221
|
+
skipReasons.push(
|
|
222
|
+
"no wallet configured (run `zero wallet import` / set ZERO_PRIVATE_KEY)"
|
|
182
223
|
);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
options.maxPay,
|
|
188
|
-
log
|
|
224
|
+
}
|
|
225
|
+
if (!capabilityId) {
|
|
226
|
+
skipReasons.push(
|
|
227
|
+
"no capability resolved \u2014 pass --capability <uid|slug> or run `zero search` first so the URL can be matched"
|
|
189
228
|
);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
229
|
+
}
|
|
230
|
+
let finalResponse;
|
|
231
|
+
let body = "";
|
|
232
|
+
let paymentMeta;
|
|
233
|
+
let fetchError;
|
|
234
|
+
try {
|
|
235
|
+
log(`Calling ${url}...`);
|
|
236
|
+
const response = await fetch(url, requestInit);
|
|
237
|
+
const paymentReq = detectPaymentRequirement(
|
|
238
|
+
response.headers,
|
|
239
|
+
response.status
|
|
200
240
|
);
|
|
201
|
-
|
|
202
|
-
|
|
241
|
+
if (paymentReq) {
|
|
242
|
+
log(
|
|
243
|
+
`Payment required (${paymentReq.protocol}) \u2014 preparing payment...`
|
|
244
|
+
);
|
|
245
|
+
const result = await paymentService.handlePayment(
|
|
246
|
+
url,
|
|
247
|
+
requestInit,
|
|
248
|
+
paymentReq,
|
|
249
|
+
options.maxPay,
|
|
250
|
+
log
|
|
251
|
+
);
|
|
252
|
+
finalResponse = result.response;
|
|
253
|
+
paymentMeta = {
|
|
254
|
+
protocol: result.protocol,
|
|
255
|
+
chain: result.chain,
|
|
256
|
+
txHash: result.txHash,
|
|
257
|
+
amount: result.amount,
|
|
258
|
+
asset: result.asset
|
|
259
|
+
};
|
|
260
|
+
log(
|
|
261
|
+
`Paid ${result.amount} ${result.asset} via ${result.protocol} on ${result.chain}`
|
|
262
|
+
);
|
|
263
|
+
} else {
|
|
264
|
+
finalResponse = response;
|
|
265
|
+
}
|
|
266
|
+
body = await finalResponse.text();
|
|
267
|
+
} catch (err) {
|
|
268
|
+
fetchError = err instanceof Error ? err : new Error(String(err));
|
|
203
269
|
}
|
|
204
270
|
const latencyMs = Date.now() - startTime;
|
|
205
|
-
|
|
206
|
-
if (!options.json) {
|
|
271
|
+
if (finalResponse && !options.json) {
|
|
207
272
|
console.log(body);
|
|
208
273
|
}
|
|
209
274
|
analyticsService.capture("fetch_executed", {
|
|
210
275
|
url,
|
|
211
|
-
status: finalResponse
|
|
276
|
+
status: finalResponse?.status,
|
|
212
277
|
hasPayment: !!paymentMeta,
|
|
213
278
|
paymentProtocol: paymentMeta?.protocol,
|
|
214
|
-
paymentAmount: paymentMeta?.amount
|
|
279
|
+
paymentAmount: paymentMeta?.amount,
|
|
280
|
+
...fetchError && { error: fetchError.message }
|
|
215
281
|
});
|
|
216
282
|
if (paymentMeta) {
|
|
217
283
|
try {
|
|
@@ -230,31 +296,30 @@ var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a ca
|
|
|
230
296
|
} catch {
|
|
231
297
|
}
|
|
232
298
|
}
|
|
233
|
-
const lastSearch = stateService.loadLastSearch();
|
|
234
|
-
const matchedCapability = lastSearch?.capabilities.find(
|
|
235
|
-
(c) => url.startsWith(c.url)
|
|
236
|
-
);
|
|
237
|
-
const capabilityId = options.capability ?? matchedCapability?.id ?? null;
|
|
238
|
-
const searchId = matchedCapability ? lastSearch?.searchId : void 0;
|
|
239
299
|
let runId = null;
|
|
240
|
-
const skipReasons = [];
|
|
241
|
-
if (!apiService.walletAddress) {
|
|
242
|
-
skipReasons.push(
|
|
243
|
-
"no wallet configured (run `zero wallet import` / set ZERO_PRIVATE_KEY)"
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
if (!capabilityId) {
|
|
247
|
-
skipReasons.push(
|
|
248
|
-
"no capability resolved \u2014 pass --capability <uid|slug> or run `zero search` first so the URL can be matched"
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
300
|
if (capabilityId && apiService.walletAddress) {
|
|
301
|
+
let requestSchema;
|
|
302
|
+
let responseSchema;
|
|
303
|
+
if (options.data) {
|
|
304
|
+
const parsedReq = tryParseJson(options.data);
|
|
305
|
+
if (parsedReq !== null && typeof parsedReq === "object") {
|
|
306
|
+
requestSchema = inferSchema(parsedReq);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (body) {
|
|
310
|
+
const parsedResp = tryParseJson(body);
|
|
311
|
+
if (parsedResp !== null && typeof parsedResp === "object") {
|
|
312
|
+
responseSchema = inferSchema(parsedResp);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
252
315
|
try {
|
|
253
316
|
const runResult = await apiService.createRun({
|
|
254
317
|
capabilityId,
|
|
255
318
|
searchId,
|
|
256
|
-
status: finalResponse
|
|
319
|
+
status: finalResponse?.status,
|
|
257
320
|
latencyMs,
|
|
321
|
+
requestSchema,
|
|
322
|
+
responseSchema,
|
|
258
323
|
...paymentMeta && {
|
|
259
324
|
costAmount: paymentMeta.amount,
|
|
260
325
|
costAsset: paymentMeta.asset,
|
|
@@ -271,14 +336,18 @@ var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a ca
|
|
|
271
336
|
);
|
|
272
337
|
}
|
|
273
338
|
}
|
|
339
|
+
if (fetchError && !options.json) {
|
|
340
|
+
console.error(` Fetch failed: ${fetchError.message}`);
|
|
341
|
+
}
|
|
274
342
|
if (options.json) {
|
|
275
343
|
console.log(
|
|
276
344
|
JSON.stringify({
|
|
277
345
|
runId,
|
|
278
|
-
status: finalResponse
|
|
346
|
+
status: finalResponse?.status ?? null,
|
|
279
347
|
latencyMs,
|
|
280
348
|
payment: paymentMeta ?? null,
|
|
281
|
-
body,
|
|
349
|
+
body: finalResponse ? body : null,
|
|
350
|
+
...fetchError && { error: fetchError.message },
|
|
282
351
|
...skipReasons.length > 0 && {
|
|
283
352
|
runTrackingSkipped: skipReasons
|
|
284
353
|
}
|
|
@@ -311,6 +380,9 @@ var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a ca
|
|
|
311
380
|
Note: this run was NOT recorded for review \u2014 ${skipReasons.join("; ")}.`
|
|
312
381
|
);
|
|
313
382
|
}
|
|
383
|
+
if (fetchError) {
|
|
384
|
+
process.exitCode = 1;
|
|
385
|
+
}
|
|
314
386
|
} catch (err) {
|
|
315
387
|
console.error(err instanceof Error ? err.message : "Fetch failed");
|
|
316
388
|
process.exitCode = 1;
|
|
@@ -810,7 +882,13 @@ Bulk review complete: ${ok} ok, ${failed} failed`);
|
|
|
810
882
|
process.exitCode = 1;
|
|
811
883
|
return;
|
|
812
884
|
}
|
|
813
|
-
|
|
885
|
+
const [only] = list.runs;
|
|
886
|
+
if (!only) {
|
|
887
|
+
console.error("No run found for capability");
|
|
888
|
+
process.exitCode = 1;
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
runId = only.uid;
|
|
814
892
|
console.log(`Resolved to run ${runId}`);
|
|
815
893
|
}
|
|
816
894
|
const accuracy = requireRating("accuracy", options.accuracy);
|
|
@@ -934,7 +1012,13 @@ var formatSearchResults = (results) => {
|
|
|
934
1012
|
var searchCommand = (appContext) => new Command7("search").description("Search for capabilities").argument("<query>", "Search query").option("--json", "Output raw JSON to stdout").option("--offset <n>", "Pagination offset", Number).option("--limit <n>", "Results per page", Number).option("--free", "Only show free capabilities").option("--max-cost <amount>", "Maximum cost per call").option("--min-rating <stars>", "Minimum star rating (1-5)", Number).option("--protocol <protocol>", "Payment protocol (x402 or mpp)").option("--min-trust <n>", "Minimum trust score (0-100)", Number).option(
|
|
935
1013
|
"--status <status>",
|
|
936
1014
|
"Filter by availability (healthy, degraded, down)"
|
|
937
|
-
).option("--all", "Show all results (no trust or health filtering)").
|
|
1015
|
+
).option("--all", "Show all results (no trust or health filtering)").option(
|
|
1016
|
+
"--source <source>",
|
|
1017
|
+
"Only show results from this crawl source (e.g. mpp, bazaar)"
|
|
1018
|
+
).option(
|
|
1019
|
+
"--exclude-source <source>",
|
|
1020
|
+
"Exclude results from this crawl source"
|
|
1021
|
+
).action(
|
|
938
1022
|
async (query, options) => {
|
|
939
1023
|
try {
|
|
940
1024
|
const { analyticsService, apiService, stateService } = appContext.services;
|
|
@@ -963,7 +1047,9 @@ var searchCommand = (appContext) => new Command7("search").description("Search f
|
|
|
963
1047
|
protocol: options.protocol,
|
|
964
1048
|
minTrust: options.minTrust,
|
|
965
1049
|
availabilityStatus: options.status,
|
|
966
|
-
includeAll: options.all
|
|
1050
|
+
includeAll: options.all,
|
|
1051
|
+
source: options.source,
|
|
1052
|
+
excludeSource: options.excludeSource
|
|
967
1053
|
});
|
|
968
1054
|
analyticsService.capture("search_executed", {
|
|
969
1055
|
query,
|
|
@@ -1311,6 +1397,12 @@ var capabilityResponseSchema = z3.object({
|
|
|
1311
1397
|
stars: z3.string().nullable().optional(),
|
|
1312
1398
|
state: z3.enum(["unrated", "rated"]).optional()
|
|
1313
1399
|
}),
|
|
1400
|
+
priceObserved: z3.object({
|
|
1401
|
+
medianCents: z3.string(),
|
|
1402
|
+
p95Cents: z3.string(),
|
|
1403
|
+
sampleCount: z3.number(),
|
|
1404
|
+
varies: z3.boolean()
|
|
1405
|
+
}).nullable().optional(),
|
|
1314
1406
|
paymentMethods: z3.array(
|
|
1315
1407
|
z3.object({
|
|
1316
1408
|
uid: z3.string(),
|
package/package.json
CHANGED
package/skills/zero/SKILL.md
CHANGED
|
@@ -33,7 +33,13 @@ Run these commands in order. Do not skip steps.
|
|
|
33
33
|
|
|
34
34
|
**Step 1 — Install:** `npm i -g @zeroxyz/cli`
|
|
35
35
|
|
|
36
|
-
**Step 2 —
|
|
36
|
+
**Step 2 — Provide a wallet.** Pick one path:
|
|
37
|
+
|
|
38
|
+
- **New wallet (persistent):** `zero init` — generates a wallet and writes it to `~/.zero/config.json`. Save the printed address.
|
|
39
|
+
- **Existing wallet (persistent):** `zero wallet set <privateKey>` — 0x-prefixed hex. Writes to `~/.zero/config.json`. Refuses to overwrite an existing wallet without `--force`.
|
|
40
|
+
- **Existing wallet (ephemeral, e.g. CI):** set `ZERO_PRIVATE_KEY=0x...` in the environment. The CLI reads it at runtime and writes nothing to disk.
|
|
41
|
+
|
|
42
|
+
**Prefer `zero init` or `zero wallet set` for local/interactive use** so the wallet persists across shells. Use `ZERO_PRIVATE_KEY` only when persisting to disk is undesirable — CI jobs, containers, one-off scripts, or secrets-manager-injected environments.
|
|
37
43
|
|
|
38
44
|
**Step 3 — Fund wallet:** `zero wallet fund` — opens browser to add USDC (Base). For manual transfer: `zero wallet fund --manual`.
|
|
39
45
|
|
|
@@ -41,7 +47,7 @@ Run these commands in order. Do not skip steps.
|
|
|
41
47
|
|
|
42
48
|
### Setup Rules
|
|
43
49
|
|
|
44
|
-
-
|
|
50
|
+
- Precedence: `ZERO_PRIVATE_KEY` (env) > `~/.zero/config.json`. If the env var is set, it wins even when a config file exists — useful for overriding on a single invocation (`ZERO_PRIVATE_KEY=0x... zero wallet balance`).
|
|
45
51
|
- Wallet must be funded with USDC on Base before calling paid capabilities.
|
|
46
52
|
|
|
47
53
|
## After Setup
|
|
@@ -105,7 +111,9 @@ zero fetch https://api.example.com/expensive --max-pay 0.50
|
|
|
105
111
|
|
|
106
112
|
### Rules
|
|
107
113
|
|
|
108
|
-
- Always
|
|
114
|
+
- **Always `zero search` fresh, every time.** Never reuse a capability URL, slug, schema, or price from an earlier turn, prior conversation, training data, or memory. Capabilities churn constantly — endpoints go offline, prices change, schemas evolve, and rankings shift as reviews accumulate. A capability that worked yesterday may be dead, repriced, or outranked today. Searching again costs nothing and is the only way to get current trust scores and availability.
|
|
115
|
+
- **Always `zero get` before `zero fetch`.** Even if you "know" the URL, re-fetch the full details to confirm the URL, method, required headers, body schema, and current price. Do not reconstruct a fetch call from memory.
|
|
116
|
+
- Never guess endpoint URLs or schemas.
|
|
109
117
|
- Use `--max-pay` before potentially expensive requests.
|
|
110
118
|
- Review capabilities after every paid call. The review signal feeds search ranking — skipping reviews means your next search is less informed.
|
|
111
119
|
- Before ending a multi-call task, run `zero runs --unreviewed` and review anything you missed.
|