@zeroxyz/cli 0.0.23 → 0.0.24
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 +580 -256
- package/package.json +1 -1
- package/skills/zero/SKILL.md +8 -0
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/app.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command10 } from "commander";
|
|
5
5
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@zeroxyz/cli",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.24",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
12
12
|
zero: "dist/index.js",
|
|
@@ -59,21 +59,477 @@ var package_default = {
|
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
// src/commands/bug-report-command.ts
|
|
63
|
+
import { createHash as createHash2 } from "crypto";
|
|
64
|
+
import { readFileSync } from "fs";
|
|
65
|
+
import { Command } from "commander";
|
|
66
|
+
import { z as z2 } from "zod";
|
|
67
|
+
|
|
68
|
+
// src/services/api-service.ts
|
|
69
|
+
import { createHash } from "crypto";
|
|
70
|
+
import z from "zod";
|
|
71
|
+
var searchResultSchema = z.object({
|
|
72
|
+
id: z.string(),
|
|
73
|
+
position: z.number(),
|
|
74
|
+
slug: z.string(),
|
|
75
|
+
name: z.string(),
|
|
76
|
+
canonicalName: z.string().nullable().optional(),
|
|
77
|
+
description: z.string(),
|
|
78
|
+
whatItDoes: z.string().nullable().optional(),
|
|
79
|
+
url: z.string(),
|
|
80
|
+
cost: z.object({ amount: z.string(), asset: z.string() }),
|
|
81
|
+
rating: z.object({
|
|
82
|
+
score: z.string(),
|
|
83
|
+
successRate: z.string(),
|
|
84
|
+
reviews: z.number(),
|
|
85
|
+
stars: z.string().nullable().optional(),
|
|
86
|
+
state: z.enum(["unrated", "rated"]).optional()
|
|
87
|
+
}),
|
|
88
|
+
trustScore: z.number().nullable().optional(),
|
|
89
|
+
trustSignalCount: z.number().optional(),
|
|
90
|
+
availabilityStatus: z.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional(),
|
|
91
|
+
relevanceScore: z.number().optional()
|
|
92
|
+
});
|
|
93
|
+
var searchResponseSchema = z.object({
|
|
94
|
+
searchId: z.string(),
|
|
95
|
+
total: z.number().optional().default(0),
|
|
96
|
+
offset: z.number().optional().default(0),
|
|
97
|
+
hasMore: z.boolean().optional().default(false),
|
|
98
|
+
capabilities: z.array(searchResultSchema)
|
|
99
|
+
});
|
|
100
|
+
var capabilityResponseSchema = z.object({
|
|
101
|
+
uid: z.string(),
|
|
102
|
+
slug: z.string(),
|
|
103
|
+
name: z.string(),
|
|
104
|
+
description: z.string(),
|
|
105
|
+
url: z.string(),
|
|
106
|
+
method: z.string(),
|
|
107
|
+
headers: z.record(z.string(), z.string()).nullable(),
|
|
108
|
+
bodySchema: z.record(z.string(), z.unknown()).nullable(),
|
|
109
|
+
responseSchema: z.record(z.string(), z.unknown()).nullable(),
|
|
110
|
+
example: z.object({ request: z.unknown(), response: z.unknown() }).nullable(),
|
|
111
|
+
tags: z.array(z.string()).nullable(),
|
|
112
|
+
displayCostAmount: z.string(),
|
|
113
|
+
displayCostAsset: z.string(),
|
|
114
|
+
rating: z.object({
|
|
115
|
+
score: z.string(),
|
|
116
|
+
successRate: z.string(),
|
|
117
|
+
reviews: z.number(),
|
|
118
|
+
stars: z.string().nullable().optional(),
|
|
119
|
+
state: z.enum(["unrated", "rated"]).optional()
|
|
120
|
+
}),
|
|
121
|
+
priceObserved: z.object({
|
|
122
|
+
medianCents: z.string(),
|
|
123
|
+
p95Cents: z.string(),
|
|
124
|
+
sampleCount: z.number(),
|
|
125
|
+
varies: z.boolean()
|
|
126
|
+
}).nullable().optional(),
|
|
127
|
+
paymentMethods: z.array(
|
|
128
|
+
z.object({
|
|
129
|
+
uid: z.string(),
|
|
130
|
+
protocol: z.string(),
|
|
131
|
+
methodType: z.string(),
|
|
132
|
+
chain: z.string().nullable(),
|
|
133
|
+
mode: z.string(),
|
|
134
|
+
costAmount: z.string(),
|
|
135
|
+
costPer: z.string(),
|
|
136
|
+
priority: z.number()
|
|
137
|
+
})
|
|
138
|
+
).nullable(),
|
|
139
|
+
trustScore: z.number().nullable().optional(),
|
|
140
|
+
trustComponents: z.object({
|
|
141
|
+
apiQuality: z.number().nullable(),
|
|
142
|
+
blockchainActivity: z.number().nullable(),
|
|
143
|
+
performance: z.number().nullable()
|
|
144
|
+
}).nullable().optional(),
|
|
145
|
+
availabilityStatus: z.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional()
|
|
146
|
+
});
|
|
147
|
+
var createRunResponseSchema = z.object({
|
|
148
|
+
runId: z.string()
|
|
149
|
+
});
|
|
150
|
+
var createReviewResponseSchema = z.object({
|
|
151
|
+
reviewId: z.string(),
|
|
152
|
+
recorded: z.boolean()
|
|
153
|
+
});
|
|
154
|
+
var BUG_REPORT_CATEGORIES = [
|
|
155
|
+
"search_relevance",
|
|
156
|
+
"ranking_issue",
|
|
157
|
+
"missing_capability",
|
|
158
|
+
"wrong_schema",
|
|
159
|
+
"misleading_description",
|
|
160
|
+
"broken_execution",
|
|
161
|
+
"payment_failure",
|
|
162
|
+
"billing_anomaly",
|
|
163
|
+
"cli_bug",
|
|
164
|
+
"security",
|
|
165
|
+
"other"
|
|
166
|
+
];
|
|
167
|
+
var createBugReportResponseSchema = z.object({
|
|
168
|
+
bugReportId: z.string(),
|
|
169
|
+
status: z.string(),
|
|
170
|
+
deduped: z.boolean(),
|
|
171
|
+
category: z.enum(BUG_REPORT_CATEGORIES).nullable(),
|
|
172
|
+
title: z.string().nullable(),
|
|
173
|
+
attached: z.object({
|
|
174
|
+
capabilityId: z.number().nullable(),
|
|
175
|
+
runId: z.number().nullable(),
|
|
176
|
+
searchId: z.number().nullable()
|
|
177
|
+
})
|
|
178
|
+
});
|
|
179
|
+
var runListItemSchema = z.object({
|
|
180
|
+
uid: z.string(),
|
|
181
|
+
capabilityUid: z.string(),
|
|
182
|
+
capabilitySlug: z.string(),
|
|
183
|
+
capabilityName: z.string(),
|
|
184
|
+
status: z.number().nullable(),
|
|
185
|
+
latencyMs: z.number().nullable(),
|
|
186
|
+
cost: z.object({ amount: z.string(), asset: z.string().nullable() }).nullable(),
|
|
187
|
+
payment: z.object({
|
|
188
|
+
protocol: z.string(),
|
|
189
|
+
chain: z.string().nullable(),
|
|
190
|
+
txHash: z.string().nullable(),
|
|
191
|
+
mode: z.string().nullable()
|
|
192
|
+
}).nullable(),
|
|
193
|
+
createdAt: z.coerce.date(),
|
|
194
|
+
reviewed: z.boolean()
|
|
195
|
+
});
|
|
196
|
+
var listRunsResponseSchema = z.object({
|
|
197
|
+
runs: z.array(runListItemSchema),
|
|
198
|
+
nextCursor: z.string().nullable()
|
|
199
|
+
});
|
|
200
|
+
var buildCanonicalMessage = (method, path, body, timestamp, nonce) => {
|
|
201
|
+
const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
|
|
202
|
+
return `${method}:${path}:${bodyHash}:${timestamp}:${nonce}`;
|
|
203
|
+
};
|
|
204
|
+
var ApiService = class {
|
|
205
|
+
constructor(baseUrl, account) {
|
|
206
|
+
this.baseUrl = baseUrl;
|
|
207
|
+
this.account = account;
|
|
208
|
+
this.walletAddress = this.account?.address ?? null;
|
|
209
|
+
}
|
|
210
|
+
walletAddress;
|
|
211
|
+
account;
|
|
212
|
+
signRequest = async (method, path, body) => {
|
|
213
|
+
if (!this.account) throw new Error("No private key configured");
|
|
214
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
215
|
+
const nonce = crypto.randomUUID();
|
|
216
|
+
const message = buildCanonicalMessage(method, path, body, timestamp, nonce);
|
|
217
|
+
const signature = await this.account.signMessage({ message });
|
|
218
|
+
return {
|
|
219
|
+
"x-zero-address": this.account.address,
|
|
220
|
+
"x-zero-timestamp": timestamp,
|
|
221
|
+
"x-zero-nonce": nonce,
|
|
222
|
+
"x-zero-signature": signature
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
request = async (method, path, body) => {
|
|
226
|
+
const url = `${this.baseUrl}${path}`;
|
|
227
|
+
const bodyStr = body ? JSON.stringify(body) : void 0;
|
|
228
|
+
const headers = {
|
|
229
|
+
"content-type": "application/json"
|
|
230
|
+
};
|
|
231
|
+
if (this.account) {
|
|
232
|
+
const walletHeaders = await this.signRequest(method, path, bodyStr);
|
|
233
|
+
Object.assign(headers, walletHeaders);
|
|
234
|
+
}
|
|
235
|
+
const response = await fetch(url, {
|
|
236
|
+
method,
|
|
237
|
+
headers,
|
|
238
|
+
body: bodyStr
|
|
239
|
+
});
|
|
240
|
+
if (!response.ok) {
|
|
241
|
+
const errorBody = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
242
|
+
throw new Error(
|
|
243
|
+
errorBody.error ?? `HTTP ${response.status}`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return response.json();
|
|
247
|
+
};
|
|
248
|
+
search = async (options) => {
|
|
249
|
+
const json = await this.request("POST", "/v1/search", options);
|
|
250
|
+
return searchResponseSchema.parse(json);
|
|
251
|
+
};
|
|
252
|
+
getCapability = async (id, searchId) => {
|
|
253
|
+
const qs = searchId ? `?searchId=${encodeURIComponent(searchId)}` : "";
|
|
254
|
+
const json = await this.request(
|
|
255
|
+
"GET",
|
|
256
|
+
`/v1/capabilities/${encodeURIComponent(id)}${qs}`
|
|
257
|
+
);
|
|
258
|
+
return capabilityResponseSchema.parse(json);
|
|
259
|
+
};
|
|
260
|
+
createRun = async (data) => {
|
|
261
|
+
const json = await this.request("POST", "/v1/runs", data);
|
|
262
|
+
return createRunResponseSchema.parse(json);
|
|
263
|
+
};
|
|
264
|
+
listRuns = async (params = {}) => {
|
|
265
|
+
const qs = new URLSearchParams();
|
|
266
|
+
if (params.capabilityId) qs.set("capabilityId", params.capabilityId);
|
|
267
|
+
if (params.unreviewed) qs.set("unreviewed", "true");
|
|
268
|
+
if (params.limit) qs.set("limit", String(params.limit));
|
|
269
|
+
if (params.cursor) qs.set("cursor", params.cursor);
|
|
270
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
271
|
+
const json = await this.request("GET", `/v1/runs${suffix}`);
|
|
272
|
+
return listRunsResponseSchema.parse(json);
|
|
273
|
+
};
|
|
274
|
+
createReview = async (data) => {
|
|
275
|
+
const json = await this.request("POST", "/v1/reviews", data);
|
|
276
|
+
return createReviewResponseSchema.parse(json);
|
|
277
|
+
};
|
|
278
|
+
createBugReport = async (data) => {
|
|
279
|
+
const json = await this.request("POST", "/v1/bug-reports", data);
|
|
280
|
+
return createBugReportResponseSchema.parse(json);
|
|
281
|
+
};
|
|
282
|
+
getFundingUrl = async (amount) => {
|
|
283
|
+
try {
|
|
284
|
+
const qs = amount ? `?amount=${encodeURIComponent(amount)}` : "";
|
|
285
|
+
const json = await this.request("GET", `/v1/wallet/fund-url${qs}`);
|
|
286
|
+
const parsed = z.object({ url: z.string() }).parse(json);
|
|
287
|
+
return parsed.url;
|
|
288
|
+
} catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// src/commands/bug-report-command.ts
|
|
295
|
+
var bulkEntrySchema = z2.object({
|
|
296
|
+
description: z2.string().min(1),
|
|
297
|
+
category: z2.enum(BUG_REPORT_CATEGORIES).optional(),
|
|
298
|
+
title: z2.string().min(1).max(200).optional(),
|
|
299
|
+
severity: z2.number().int().min(1).max(3).optional(),
|
|
300
|
+
reproduction: z2.string().optional(),
|
|
301
|
+
capabilityId: z2.string().optional(),
|
|
302
|
+
runId: z2.string().optional(),
|
|
303
|
+
searchId: z2.string().optional(),
|
|
304
|
+
idempotencyKey: z2.string().min(1).max(200).optional()
|
|
305
|
+
});
|
|
306
|
+
var buildCliContext = () => ({
|
|
307
|
+
cliVersion: package_default.version,
|
|
308
|
+
os: process.platform,
|
|
309
|
+
node: process.version
|
|
310
|
+
});
|
|
311
|
+
var TEN_MIN_MS = 10 * 60 * 1e3;
|
|
312
|
+
var autoIdempotencyKey = (description, contextSeed) => {
|
|
313
|
+
const bucket = Math.floor(Date.now() / TEN_MIN_MS);
|
|
314
|
+
const seed = `${description}|${contextSeed ?? ""}|${bucket}`;
|
|
315
|
+
return `auto-${createHash2("sha256").update(seed).digest("hex").slice(0, 16)}`;
|
|
316
|
+
};
|
|
317
|
+
var bugReportCommand = (appContext) => new Command("bug-report").description(
|
|
318
|
+
"Report a Zero platform bug \u2014 bad search ranking, wrong indexed URL, CLI bugs, billing issues. The CLI auto-attaches your most recent context (last search + last run), auto-derives a title, and lets the server classify the category. For 'this capability returned a bad result', use `zero review` instead."
|
|
319
|
+
).addHelpText(
|
|
320
|
+
"after",
|
|
321
|
+
`
|
|
322
|
+
Minimum surface \u2014 just describe the bug:
|
|
323
|
+
zero bug-report "Search ranked generic crypto API above exact match for BTC price"
|
|
324
|
+
|
|
325
|
+
The CLI will:
|
|
326
|
+
- Attach your most recent search + run as context (use --no-context to skip)
|
|
327
|
+
- Auto-derive a title from your description
|
|
328
|
+
- Generate an idempotency key so accidental retries dedupe within 10 minutes
|
|
329
|
+
- Capture cliContext (cli version, os, node)
|
|
330
|
+
|
|
331
|
+
The server will:
|
|
332
|
+
- Pick a category from your description (override with --category if it gets it wrong)
|
|
333
|
+
- Default severity to 2 (override with --severity)
|
|
334
|
+
- Echo back what it picked + attached so you can verify
|
|
335
|
+
|
|
336
|
+
Overrides (only when needed):
|
|
337
|
+
--capability cap_xyz Attach a specific capability (overrides auto-context)
|
|
338
|
+
--run run_xyz Attach a specific run
|
|
339
|
+
--search sea_xyz Attach a specific search
|
|
340
|
+
--no-context Skip auto-attached context (file a standalone platform bug)
|
|
341
|
+
--category <c> Force a category (otherwise classifier picks)
|
|
342
|
+
--severity 1|2|3 Override default severity 2
|
|
343
|
+
--title "..." Override auto-derived title
|
|
344
|
+
--reproduction "..." Add reproduction notes
|
|
345
|
+
--idempotency-key <k> Override auto-key (e.g. for explicit retries)
|
|
346
|
+
--json Machine-readable output
|
|
347
|
+
--from-file <path> Bulk submit from JSONL (each line: {description, ...overrides})
|
|
348
|
+
|
|
349
|
+
Categories the classifier picks from:
|
|
350
|
+
search & discovery: search_relevance, ranking_issue, missing_capability
|
|
351
|
+
indexed-data quality: wrong_schema, misleading_description, broken_execution
|
|
352
|
+
platform infra: payment_failure, billing_anomaly, cli_bug
|
|
353
|
+
cross-cutting: security, other`
|
|
354
|
+
).argument(
|
|
355
|
+
"[description]",
|
|
356
|
+
"What broke (free-form text). Required unless --from-file is used."
|
|
357
|
+
).option(
|
|
358
|
+
"--capability <id>",
|
|
359
|
+
"Attach this capability uid (cap_*) or slug as context, overriding auto-context"
|
|
360
|
+
).option(
|
|
361
|
+
"--run <runId>",
|
|
362
|
+
"Attach this run uid (run_*) as context, overriding auto-context"
|
|
363
|
+
).option(
|
|
364
|
+
"--search <searchId>",
|
|
365
|
+
"Attach this search uid (sea_*) as context, overriding auto-context"
|
|
366
|
+
).option("--no-context", "Skip auto-attached context entirely").option(
|
|
367
|
+
"--category <category>",
|
|
368
|
+
`Force a category (skips classifier). One of: ${BUG_REPORT_CATEGORIES.join(", ")}`
|
|
369
|
+
).option(
|
|
370
|
+
"--severity <n>",
|
|
371
|
+
"Severity 1 (low), 2 (med, default), 3 (high)",
|
|
372
|
+
Number.parseInt
|
|
373
|
+
).option("--title <text>", "Override auto-derived title (\u2264200 chars)").option(
|
|
374
|
+
"--reproduction <text>",
|
|
375
|
+
"Optional reproduction steps (e.g. equivalent curl)"
|
|
376
|
+
).option("--idempotency-key <key>", "Override the auto-generated dedup key").option(
|
|
377
|
+
"--from-file <path>",
|
|
378
|
+
"Submit bug reports in bulk from a JSONL file (one report per line)"
|
|
379
|
+
).option("--json", "Emit the API result as JSON on stdout (for batch use)").action(
|
|
380
|
+
async (description, options) => {
|
|
381
|
+
try {
|
|
382
|
+
const { analyticsService, apiService, stateService } = appContext.services;
|
|
383
|
+
if (!apiService.walletAddress) {
|
|
384
|
+
console.error(
|
|
385
|
+
"Wallet auth required \u2014 run `zero wallet import` or set ZERO_PRIVATE_KEY."
|
|
386
|
+
);
|
|
387
|
+
process.exitCode = 1;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (options.fromFile) {
|
|
391
|
+
const contents = readFileSync(options.fromFile, "utf8");
|
|
392
|
+
const lines = contents.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
393
|
+
let ok = 0;
|
|
394
|
+
let failed = 0;
|
|
395
|
+
const cliContext = buildCliContext();
|
|
396
|
+
for (const [idx, line] of lines.entries()) {
|
|
397
|
+
const lineNum = idx + 1;
|
|
398
|
+
try {
|
|
399
|
+
const parsed = bulkEntrySchema.parse(JSON.parse(line));
|
|
400
|
+
const result2 = await apiService.createBugReport({
|
|
401
|
+
description: parsed.description,
|
|
402
|
+
category: parsed.category,
|
|
403
|
+
severity: parsed.severity,
|
|
404
|
+
title: parsed.title,
|
|
405
|
+
reproduction: parsed.reproduction,
|
|
406
|
+
capabilityId: parsed.capabilityId,
|
|
407
|
+
runId: parsed.runId,
|
|
408
|
+
searchId: parsed.searchId,
|
|
409
|
+
cliContext,
|
|
410
|
+
idempotencyKey: parsed.idempotencyKey ?? autoIdempotencyKey(parsed.description, parsed.runId)
|
|
411
|
+
});
|
|
412
|
+
ok += 1;
|
|
413
|
+
console.log(
|
|
414
|
+
`[${lineNum}] ${result2.bugReportId} category=${result2.category ?? "unclassified"}${result2.deduped ? " (deduped)" : ""}`
|
|
415
|
+
);
|
|
416
|
+
analyticsService.capture("bug_report_submitted", {
|
|
417
|
+
category: result2.category,
|
|
418
|
+
severity: parsed.severity ?? 2,
|
|
419
|
+
bulk: true,
|
|
420
|
+
deduped: result2.deduped
|
|
421
|
+
});
|
|
422
|
+
} catch (err) {
|
|
423
|
+
failed += 1;
|
|
424
|
+
console.error(
|
|
425
|
+
`[${lineNum}] FAILED: ${err instanceof Error ? err.message : String(err)}`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
console.log(
|
|
430
|
+
`
|
|
431
|
+
Bulk bug-report complete: ${ok} ok, ${failed} failed`
|
|
432
|
+
);
|
|
433
|
+
if (failed > 0) process.exitCode = 1;
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (!description || description.trim().length === 0) {
|
|
437
|
+
console.error(
|
|
438
|
+
'Provide a description: zero bug-report "what broke"'
|
|
439
|
+
);
|
|
440
|
+
process.exitCode = 1;
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (options.category && !BUG_REPORT_CATEGORIES.includes(
|
|
444
|
+
options.category
|
|
445
|
+
)) {
|
|
446
|
+
console.error(
|
|
447
|
+
`Invalid --category. Valid: ${BUG_REPORT_CATEGORIES.join(", ")}`
|
|
448
|
+
);
|
|
449
|
+
process.exitCode = 1;
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (options.severity !== void 0 && (Number.isNaN(options.severity) || options.severity < 1 || options.severity > 3)) {
|
|
453
|
+
console.error("--severity must be 1, 2, or 3");
|
|
454
|
+
process.exitCode = 1;
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
let { capability, run, search } = options;
|
|
458
|
+
const useAutoContext = options.context !== false;
|
|
459
|
+
if (useAutoContext) {
|
|
460
|
+
const lastSearch = stateService.loadLastSearch();
|
|
461
|
+
if (lastSearch) {
|
|
462
|
+
search = search ?? lastSearch.searchId;
|
|
463
|
+
const recent = lastSearch.capabilities[0];
|
|
464
|
+
capability = capability ?? recent?.id;
|
|
465
|
+
}
|
|
466
|
+
if (!run) {
|
|
467
|
+
try {
|
|
468
|
+
const list = await apiService.listRuns({ limit: 1 });
|
|
469
|
+
const [latest] = list.runs;
|
|
470
|
+
if (latest) {
|
|
471
|
+
run = latest.uid;
|
|
472
|
+
capability = capability ?? latest.capabilityUid;
|
|
473
|
+
}
|
|
474
|
+
} catch {
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const idempotencyKey = options.idempotencyKey ?? autoIdempotencyKey(description, run ?? capability ?? null);
|
|
479
|
+
const result = await apiService.createBugReport({
|
|
480
|
+
description,
|
|
481
|
+
category: options.category,
|
|
482
|
+
severity: options.severity,
|
|
483
|
+
title: options.title,
|
|
484
|
+
reproduction: options.reproduction,
|
|
485
|
+
capabilityId: capability,
|
|
486
|
+
runId: run,
|
|
487
|
+
searchId: search,
|
|
488
|
+
cliContext: buildCliContext(),
|
|
489
|
+
idempotencyKey
|
|
490
|
+
});
|
|
491
|
+
if (options.json) {
|
|
492
|
+
console.log(JSON.stringify(result));
|
|
493
|
+
} else {
|
|
494
|
+
const cat = result.category ?? "unclassified";
|
|
495
|
+
const dedupeNote = result.deduped ? " (deduped \u2014 same description within 10 min)" : "";
|
|
496
|
+
console.log(
|
|
497
|
+
`Bug report filed: ${result.bugReportId}${dedupeNote}
|
|
498
|
+
category: ${cat}
|
|
499
|
+
title: ${result.title ?? "(none)"}
|
|
500
|
+
attached: capability=${result.attached.capabilityId ?? "\u2014"} run=${result.attached.runId ?? "\u2014"} search=${result.attached.searchId ?? "\u2014"}`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
analyticsService.capture("bug_report_submitted", {
|
|
504
|
+
category: result.category,
|
|
505
|
+
severity: options.severity ?? 2,
|
|
506
|
+
deduped: result.deduped,
|
|
507
|
+
autoContext: useAutoContext
|
|
508
|
+
});
|
|
509
|
+
} catch (err) {
|
|
510
|
+
console.error(
|
|
511
|
+
err instanceof Error ? err.message : "Bug report failed"
|
|
512
|
+
);
|
|
513
|
+
process.exitCode = 1;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
);
|
|
517
|
+
|
|
62
518
|
// src/commands/config-command.ts
|
|
63
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
519
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
64
520
|
import { homedir } from "os";
|
|
65
521
|
import { join } from "path";
|
|
66
|
-
import { Command } from "commander";
|
|
522
|
+
import { Command as Command2 } from "commander";
|
|
67
523
|
var VALID_KEYS = ["lowBalanceWarning", "auth", "telemetry"];
|
|
68
524
|
var loadConfig = (configPath) => {
|
|
69
525
|
try {
|
|
70
526
|
if (!existsSync(configPath)) return {};
|
|
71
|
-
return JSON.parse(
|
|
527
|
+
return JSON.parse(readFileSync2(configPath, "utf8"));
|
|
72
528
|
} catch {
|
|
73
529
|
return {};
|
|
74
530
|
}
|
|
75
531
|
};
|
|
76
|
-
var configCommand = (_appContext) => new
|
|
532
|
+
var configCommand = (_appContext) => new Command2("config").description("View or update CLI configuration").option("--set <keyValue>", "Set a config value (key=value)").action((options) => {
|
|
77
533
|
const configPath = join(homedir(), ".zero", "config.json");
|
|
78
534
|
if (!options.set) {
|
|
79
535
|
const config2 = loadConfig(configPath);
|
|
@@ -110,7 +566,7 @@ var configCommand = (_appContext) => new Command("config").description("View or
|
|
|
110
566
|
});
|
|
111
567
|
|
|
112
568
|
// src/commands/fetch-command.ts
|
|
113
|
-
import { Command as
|
|
569
|
+
import { Command as Command3 } from "commander";
|
|
114
570
|
|
|
115
571
|
// src/util/infer-schema.ts
|
|
116
572
|
var inferSchema = (value, depth = 0) => {
|
|
@@ -168,7 +624,7 @@ var detectPaymentRequirement = (headers, status) => {
|
|
|
168
624
|
}
|
|
169
625
|
return { protocol: "unknown", raw: {} };
|
|
170
626
|
};
|
|
171
|
-
var fetchCommand = (appContext) => new
|
|
627
|
+
var fetchCommand = (appContext) => new Command3("fetch").description("Fetch a capability URL with automatic payment handling").argument("<url>", "URL to fetch").option(
|
|
172
628
|
"-X, --method <method>",
|
|
173
629
|
"HTTP method (GET, POST, PUT, PATCH, DELETE). Defaults to POST when -d is set, otherwise GET"
|
|
174
630
|
).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(
|
|
@@ -391,7 +847,7 @@ var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a ca
|
|
|
391
847
|
);
|
|
392
848
|
|
|
393
849
|
// src/commands/get-command.ts
|
|
394
|
-
import { Command as
|
|
850
|
+
import { Command as Command4 } from "commander";
|
|
395
851
|
var formatReviewCount = (count) => {
|
|
396
852
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
|
|
397
853
|
return count.toString();
|
|
@@ -446,7 +902,7 @@ var formatCapability = (capability) => {
|
|
|
446
902
|
lines.push(` Method: ${capability.method}`);
|
|
447
903
|
return lines.join("\n");
|
|
448
904
|
};
|
|
449
|
-
var getCommand = (appContext) => new
|
|
905
|
+
var getCommand = (appContext) => new Command4("get").description(
|
|
450
906
|
"Get details for a capability by position from last search, or by slug"
|
|
451
907
|
).argument(
|
|
452
908
|
"<identifier>",
|
|
@@ -500,20 +956,21 @@ var getCommand = (appContext) => new Command3("get").description(
|
|
|
500
956
|
});
|
|
501
957
|
|
|
502
958
|
// src/commands/init-command.ts
|
|
503
|
-
import { createHash } from "crypto";
|
|
959
|
+
import { createHash as createHash3 } from "crypto";
|
|
504
960
|
import {
|
|
505
961
|
chmodSync,
|
|
506
962
|
cpSync,
|
|
507
963
|
existsSync as existsSync2,
|
|
508
964
|
mkdirSync as mkdirSync2,
|
|
509
965
|
readdirSync,
|
|
510
|
-
readFileSync as
|
|
966
|
+
readFileSync as readFileSync3,
|
|
967
|
+
rmSync,
|
|
511
968
|
writeFileSync as writeFileSync2
|
|
512
969
|
} from "fs";
|
|
513
970
|
import { homedir as homedir2 } from "os";
|
|
514
971
|
import { dirname, join as join2, relative } from "path";
|
|
515
972
|
import { fileURLToPath } from "url";
|
|
516
|
-
import { Command as
|
|
973
|
+
import { Command as Command5 } from "commander";
|
|
517
974
|
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
518
975
|
var AGENT_TOOLS = [
|
|
519
976
|
{ name: "Claude Code", configDir: ".claude" },
|
|
@@ -535,7 +992,7 @@ var getPackageRoot = () => {
|
|
|
535
992
|
}
|
|
536
993
|
return dir;
|
|
537
994
|
};
|
|
538
|
-
var sha256File = (filePath) =>
|
|
995
|
+
var sha256File = (filePath) => createHash3("sha256").update(readFileSync3(filePath)).digest("hex");
|
|
539
996
|
var verifyFileCopy = (src, dest) => {
|
|
540
997
|
if (!existsSync2(dest)) return false;
|
|
541
998
|
return sha256File(src) === sha256File(dest);
|
|
@@ -577,7 +1034,7 @@ var installHook = (home) => {
|
|
|
577
1034
|
let settings = {};
|
|
578
1035
|
if (existsSync2(settingsPath)) {
|
|
579
1036
|
try {
|
|
580
|
-
settings = JSON.parse(
|
|
1037
|
+
settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
581
1038
|
} catch {
|
|
582
1039
|
}
|
|
583
1040
|
}
|
|
@@ -654,6 +1111,36 @@ var installHook = (home) => {
|
|
|
654
1111
|
`);
|
|
655
1112
|
return true;
|
|
656
1113
|
};
|
|
1114
|
+
var CONFLICTING_SKILL_PATTERNS = ["zam", "tempo"];
|
|
1115
|
+
var findConflictingSkills = (home) => {
|
|
1116
|
+
const found = [];
|
|
1117
|
+
for (const tool of AGENT_TOOLS) {
|
|
1118
|
+
const toolSkillsPath = join2(home, tool.configDir, "skills");
|
|
1119
|
+
if (!existsSync2(toolSkillsPath)) continue;
|
|
1120
|
+
const entries = readdirSync(toolSkillsPath, { withFileTypes: true });
|
|
1121
|
+
for (const entry of entries) {
|
|
1122
|
+
if (!entry.isDirectory()) continue;
|
|
1123
|
+
const lower = entry.name.toLowerCase();
|
|
1124
|
+
if (CONFLICTING_SKILL_PATTERNS.some((p) => lower.includes(p))) {
|
|
1125
|
+
found.push({
|
|
1126
|
+
tool: tool.name,
|
|
1127
|
+
skillPath: join2(toolSkillsPath, entry.name),
|
|
1128
|
+
skillName: entry.name
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return found;
|
|
1134
|
+
};
|
|
1135
|
+
var removeConflictingSkills = (home) => {
|
|
1136
|
+
const conflicts = findConflictingSkills(home);
|
|
1137
|
+
const removed = [];
|
|
1138
|
+
for (const { skillPath, tool, skillName } of conflicts) {
|
|
1139
|
+
rmSync(skillPath, { recursive: true, force: true });
|
|
1140
|
+
removed.push(`${tool}: ${skillName}`);
|
|
1141
|
+
}
|
|
1142
|
+
return removed;
|
|
1143
|
+
};
|
|
657
1144
|
var installSkills = (home) => {
|
|
658
1145
|
const skillsSourceDir = join2(getPackageRoot(), "skills");
|
|
659
1146
|
const skillDirs = readdirSync(skillsSourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -683,7 +1170,7 @@ var installSkills = (home) => {
|
|
|
683
1170
|
}
|
|
684
1171
|
return installed;
|
|
685
1172
|
};
|
|
686
|
-
var initCommand = (appContext) => new
|
|
1173
|
+
var initCommand = (appContext) => new Command5("init").description("Initialize Zero CLI for usage").option("--force", "Overwrite existing configuration").action(async (options) => {
|
|
687
1174
|
const home = homedir2();
|
|
688
1175
|
const zeroDir = join2(home, ".zero");
|
|
689
1176
|
const configPath = join2(zeroDir, "config.json");
|
|
@@ -692,7 +1179,7 @@ var initCommand = (appContext) => new Command4("init").description("Initialize Z
|
|
|
692
1179
|
const walletExists = (() => {
|
|
693
1180
|
if (!existsSync2(configPath)) return false;
|
|
694
1181
|
try {
|
|
695
|
-
const existing = JSON.parse(
|
|
1182
|
+
const existing = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
696
1183
|
return !!existing.privateKey;
|
|
697
1184
|
} catch {
|
|
698
1185
|
return false;
|
|
@@ -702,7 +1189,7 @@ var initCommand = (appContext) => new Command4("init").description("Initialize Z
|
|
|
702
1189
|
const privateKey = generatePrivateKey();
|
|
703
1190
|
const account = privateKeyToAccount(privateKey);
|
|
704
1191
|
mkdirSync2(zeroDir, { recursive: true });
|
|
705
|
-
const existing = existsSync2(configPath) ? JSON.parse(
|
|
1192
|
+
const existing = existsSync2(configPath) ? JSON.parse(readFileSync3(configPath, "utf8")) : {};
|
|
706
1193
|
writeFileSync2(
|
|
707
1194
|
configPath,
|
|
708
1195
|
JSON.stringify(
|
|
@@ -716,7 +1203,7 @@ var initCommand = (appContext) => new Command4("init").description("Initialize Z
|
|
|
716
1203
|
console.log(`Wallet address: ${account.address}`);
|
|
717
1204
|
} else {
|
|
718
1205
|
try {
|
|
719
|
-
const existing = JSON.parse(
|
|
1206
|
+
const existing = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
720
1207
|
const account = privateKeyToAccount(existing.privateKey);
|
|
721
1208
|
walletAddress = account.address;
|
|
722
1209
|
} catch {
|
|
@@ -748,6 +1235,17 @@ var initCommand = (appContext) => new Command4("init").description("Initialize Z
|
|
|
748
1235
|
} catch (err) {
|
|
749
1236
|
hookError = err instanceof Error ? err.message : "unknown hook error";
|
|
750
1237
|
}
|
|
1238
|
+
const conflictingSkills = findConflictingSkills(home);
|
|
1239
|
+
if (conflictingSkills.length > 0) {
|
|
1240
|
+
const skillList = conflictingSkills.map((s) => ` - ${s.tool}: ${s.skillName}`).join("\n");
|
|
1241
|
+
console.error(
|
|
1242
|
+
`
|
|
1243
|
+
Found deprecated skills that may conflict with Zero:
|
|
1244
|
+
${skillList}
|
|
1245
|
+
|
|
1246
|
+
To remove them, run: zero init cleanup`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
751
1249
|
console.error(
|
|
752
1250
|
'Zero is ready! Run `zero search` to find capabilities.\n\nTry:\n zero search "translate text to Spanish"\n zero search "generate an image"\n zero search "weather forecast"'
|
|
753
1251
|
);
|
|
@@ -770,23 +1268,45 @@ var initCommand = (appContext) => new Command4("init").description("Initialize Z
|
|
|
770
1268
|
hook_installed: hookInstalled,
|
|
771
1269
|
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
772
1270
|
hook_error: hookError,
|
|
1271
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
1272
|
+
conflicting_skills_found: conflictingSkills.length,
|
|
773
1273
|
force: options.force ?? false
|
|
774
1274
|
});
|
|
775
|
-
})
|
|
1275
|
+
}).addCommand(
|
|
1276
|
+
new Command5("cleanup").description(
|
|
1277
|
+
"Remove deprecated skills (zam, tempo) that conflict with Zero"
|
|
1278
|
+
).action(() => {
|
|
1279
|
+
const home = homedir2();
|
|
1280
|
+
const removed = removeConflictingSkills(home);
|
|
1281
|
+
if (removed.length === 0) {
|
|
1282
|
+
console.error("No conflicting skills found. Nothing to remove.");
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
const removedList = removed.map((s) => ` - ${s}`).join("\n");
|
|
1286
|
+
console.error(`Removed deprecated skills:
|
|
1287
|
+
${removedList}`);
|
|
1288
|
+
appContext.services.analyticsService.capture("skills_cleanup", {
|
|
1289
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
1290
|
+
skills_removed: removed,
|
|
1291
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
1292
|
+
skills_removed_count: removed.length
|
|
1293
|
+
});
|
|
1294
|
+
})
|
|
1295
|
+
);
|
|
776
1296
|
|
|
777
1297
|
// src/commands/review-command.ts
|
|
778
|
-
import { readFileSync as
|
|
779
|
-
import { Command as
|
|
780
|
-
import { z } from "zod";
|
|
781
|
-
var
|
|
782
|
-
runId:
|
|
783
|
-
success:
|
|
784
|
-
accuracy:
|
|
785
|
-
value:
|
|
786
|
-
reliability:
|
|
787
|
-
content:
|
|
1298
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
1299
|
+
import { Command as Command6 } from "commander";
|
|
1300
|
+
import { z as z3 } from "zod";
|
|
1301
|
+
var bulkEntrySchema2 = z3.object({
|
|
1302
|
+
runId: z3.string(),
|
|
1303
|
+
success: z3.boolean(),
|
|
1304
|
+
accuracy: z3.number().int().min(1).max(5),
|
|
1305
|
+
value: z3.number().int().min(1).max(5),
|
|
1306
|
+
reliability: z3.number().int().min(1).max(5),
|
|
1307
|
+
content: z3.string().optional()
|
|
788
1308
|
});
|
|
789
|
-
var reviewCommand = (appContext) => new
|
|
1309
|
+
var reviewCommand = (appContext) => new Command6("review").description("Submit a review for a capability run").addHelpText(
|
|
790
1310
|
"after",
|
|
791
1311
|
`
|
|
792
1312
|
Tips for a great review:
|
|
@@ -814,14 +1334,14 @@ Examples:
|
|
|
814
1334
|
try {
|
|
815
1335
|
const { analyticsService, apiService } = appContext.services;
|
|
816
1336
|
if (options.fromFile) {
|
|
817
|
-
const contents =
|
|
1337
|
+
const contents = readFileSync4(options.fromFile, "utf8");
|
|
818
1338
|
const lines = contents.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
819
1339
|
let ok = 0;
|
|
820
1340
|
let failed = 0;
|
|
821
1341
|
for (const [idx, line] of lines.entries()) {
|
|
822
1342
|
const lineNum = idx + 1;
|
|
823
1343
|
try {
|
|
824
|
-
const parsed =
|
|
1344
|
+
const parsed = bulkEntrySchema2.parse(JSON.parse(line));
|
|
825
1345
|
const result2 = await apiService.createReview(parsed);
|
|
826
1346
|
ok += 1;
|
|
827
1347
|
console.log(
|
|
@@ -922,8 +1442,8 @@ Bulk review complete: ${ok} ok, ${failed} failed`);
|
|
|
922
1442
|
);
|
|
923
1443
|
|
|
924
1444
|
// src/commands/runs-command.ts
|
|
925
|
-
import { Command as
|
|
926
|
-
var runsCommand = (appContext) => new
|
|
1445
|
+
import { Command as Command7 } from "commander";
|
|
1446
|
+
var runsCommand = (appContext) => new Command7("runs").description("List your recent capability runs").addHelpText(
|
|
927
1447
|
"after",
|
|
928
1448
|
`
|
|
929
1449
|
View your recent capability runs \u2014 status, latency, cost, and payment info.
|
|
@@ -971,7 +1491,7 @@ Examples:
|
|
|
971
1491
|
);
|
|
972
1492
|
|
|
973
1493
|
// src/commands/search-command.ts
|
|
974
|
-
import { Command as
|
|
1494
|
+
import { Command as Command8 } from "commander";
|
|
975
1495
|
var formatReviewCount2 = (count) => {
|
|
976
1496
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
|
|
977
1497
|
return count.toString();
|
|
@@ -1009,7 +1529,7 @@ var formatSearchResults = (results) => {
|
|
|
1009
1529
|
"${displayDescription}"`;
|
|
1010
1530
|
}).join("\n");
|
|
1011
1531
|
};
|
|
1012
|
-
var searchCommand = (appContext) => new
|
|
1532
|
+
var searchCommand = (appContext) => new Command8("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(
|
|
1013
1533
|
"--status <status>",
|
|
1014
1534
|
"Filter by availability (healthy, degraded, down)"
|
|
1015
1535
|
).option("--all", "Show all results (no trust or health filtering)").option(
|
|
@@ -1087,13 +1607,13 @@ var searchCommand = (appContext) => new Command7("search").description("Search f
|
|
|
1087
1607
|
);
|
|
1088
1608
|
|
|
1089
1609
|
// src/commands/wallet-command.ts
|
|
1090
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as
|
|
1610
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
1091
1611
|
import { homedir as homedir3 } from "os";
|
|
1092
1612
|
import { join as join3 } from "path";
|
|
1093
|
-
import { Command as
|
|
1613
|
+
import { Command as Command9 } from "commander";
|
|
1094
1614
|
import open from "open";
|
|
1095
1615
|
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
1096
|
-
var walletBalanceCommand = (appContext) => new
|
|
1616
|
+
var walletBalanceCommand = (appContext) => new Command9("balance").description("Show wallet balance").action(async () => {
|
|
1097
1617
|
const { walletService } = appContext.services;
|
|
1098
1618
|
const balance = await walletService.getBalance();
|
|
1099
1619
|
if (balance === null) {
|
|
@@ -1108,7 +1628,7 @@ var walletBalanceCommand = (appContext) => new Command8("balance").description("
|
|
|
1108
1628
|
}
|
|
1109
1629
|
console.log(`${balance.amount} ${balance.asset}`);
|
|
1110
1630
|
});
|
|
1111
|
-
var walletFundCommand = (appContext) => new
|
|
1631
|
+
var walletFundCommand = (appContext) => new Command9("fund").description("Fund your wallet").argument("[amount]", "Amount to fund in USDC").option("--manual", "Show wallet address for manual transfer").action(
|
|
1112
1632
|
async (amount, options) => {
|
|
1113
1633
|
const { analyticsService, walletService } = appContext.services;
|
|
1114
1634
|
const address = walletService.getAddress();
|
|
@@ -1145,7 +1665,7 @@ ${address}`);
|
|
|
1145
1665
|
}
|
|
1146
1666
|
}
|
|
1147
1667
|
);
|
|
1148
|
-
var walletAddressCommand = (appContext) => new
|
|
1668
|
+
var walletAddressCommand = (appContext) => new Command9("address").description("Show wallet address").action(() => {
|
|
1149
1669
|
const { walletService } = appContext.services;
|
|
1150
1670
|
const address = walletService.getAddress();
|
|
1151
1671
|
if (!address) {
|
|
@@ -1155,7 +1675,7 @@ var walletAddressCommand = (appContext) => new Command8("address").description("
|
|
|
1155
1675
|
}
|
|
1156
1676
|
console.log(address);
|
|
1157
1677
|
});
|
|
1158
|
-
var walletSetCommand = (appContext) => new
|
|
1678
|
+
var walletSetCommand = (appContext) => new Command9("set").description("Set wallet from an existing private key").argument("<privateKey>", "Hex-encoded private key (0x-prefixed)").option("--force", "Overwrite existing wallet without prompting").action(async (privateKey, options) => {
|
|
1159
1679
|
const { analyticsService } = appContext.services;
|
|
1160
1680
|
if (!privateKey.startsWith("0x")) {
|
|
1161
1681
|
console.error("Private key must be 0x-prefixed hex string.");
|
|
@@ -1174,7 +1694,7 @@ var walletSetCommand = (appContext) => new Command8("set").description("Set wall
|
|
|
1174
1694
|
const configPath = join3(zeroDir, "config.json");
|
|
1175
1695
|
if (!options.force && existsSync3(configPath)) {
|
|
1176
1696
|
try {
|
|
1177
|
-
const existing2 = JSON.parse(
|
|
1697
|
+
const existing2 = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
1178
1698
|
if (existing2.privateKey) {
|
|
1179
1699
|
console.error(
|
|
1180
1700
|
"Wallet already configured. Use --force to overwrite."
|
|
@@ -1186,7 +1706,7 @@ var walletSetCommand = (appContext) => new Command8("set").description("Set wall
|
|
|
1186
1706
|
}
|
|
1187
1707
|
}
|
|
1188
1708
|
mkdirSync3(zeroDir, { recursive: true });
|
|
1189
|
-
const existing = existsSync3(configPath) ? JSON.parse(
|
|
1709
|
+
const existing = existsSync3(configPath) ? JSON.parse(readFileSync5(configPath, "utf8")) : {};
|
|
1190
1710
|
writeFileSync3(
|
|
1191
1711
|
configPath,
|
|
1192
1712
|
JSON.stringify(
|
|
@@ -1207,7 +1727,7 @@ var walletSetCommand = (appContext) => new Command8("set").description("Set wall
|
|
|
1207
1727
|
});
|
|
1208
1728
|
});
|
|
1209
1729
|
var walletCommand = (appContext) => {
|
|
1210
|
-
const cmd = new
|
|
1730
|
+
const cmd = new Command9("wallet").description("Manage your wallet");
|
|
1211
1731
|
cmd.addCommand(walletBalanceCommand(appContext));
|
|
1212
1732
|
cmd.addCommand(walletFundCommand(appContext));
|
|
1213
1733
|
cmd.addCommand(walletAddressCommand(appContext));
|
|
@@ -1218,7 +1738,7 @@ var walletCommand = (appContext) => {
|
|
|
1218
1738
|
// src/app.ts
|
|
1219
1739
|
var createApp = (appContext) => {
|
|
1220
1740
|
const { analyticsService } = appContext.services;
|
|
1221
|
-
const program = new
|
|
1741
|
+
const program = new Command10().name("zero").description("Zero CLI \u2014 Search engine and payment platform for AI agents").version(package_default.version, "-v, --version").exitOverride().hook("preAction", (_thisCommand, actionCommand) => {
|
|
1222
1742
|
analyticsService.capture("command_executed", {
|
|
1223
1743
|
command: actionCommand.name()
|
|
1224
1744
|
});
|
|
@@ -1229,16 +1749,17 @@ var createApp = (appContext) => {
|
|
|
1229
1749
|
program.addCommand(fetchCommand(appContext));
|
|
1230
1750
|
program.addCommand(reviewCommand(appContext));
|
|
1231
1751
|
program.addCommand(runsCommand(appContext));
|
|
1752
|
+
program.addCommand(bugReportCommand(appContext));
|
|
1232
1753
|
program.addCommand(walletCommand(appContext));
|
|
1233
1754
|
program.addCommand(configCommand(appContext));
|
|
1234
1755
|
return program;
|
|
1235
1756
|
};
|
|
1236
1757
|
|
|
1237
1758
|
// src/app/app-env.ts
|
|
1238
|
-
import
|
|
1239
|
-
var envSchema =
|
|
1240
|
-
ZERO_API_URL:
|
|
1241
|
-
ZERO_PRIVATE_KEY:
|
|
1759
|
+
import z4 from "zod";
|
|
1760
|
+
var envSchema = z4.object({
|
|
1761
|
+
ZERO_API_URL: z4.string().default("https://api.zero.xyz"),
|
|
1762
|
+
ZERO_PRIVATE_KEY: z4.string().optional()
|
|
1242
1763
|
});
|
|
1243
1764
|
var getEnv = () => {
|
|
1244
1765
|
try {
|
|
@@ -1251,14 +1772,14 @@ var getEnv = () => {
|
|
|
1251
1772
|
};
|
|
1252
1773
|
|
|
1253
1774
|
// src/app/app-services.ts
|
|
1254
|
-
import { existsSync as existsSync6, readFileSync as
|
|
1775
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
|
|
1255
1776
|
import { homedir as homedir4 } from "os";
|
|
1256
1777
|
import { join as join5 } from "path";
|
|
1257
1778
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
1258
1779
|
|
|
1259
1780
|
// src/services/analytics-service.ts
|
|
1260
1781
|
import { randomUUID } from "crypto";
|
|
1261
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as
|
|
1782
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1262
1783
|
import { dirname as dirname2 } from "path";
|
|
1263
1784
|
import { PostHog } from "posthog-node";
|
|
1264
1785
|
var POSTHOG_API_KEY = "phc_B2vLyNxAf2mnqvdPQajf4d4b2iXc35dep2ZrvebMJLuX";
|
|
@@ -1273,7 +1794,7 @@ var AnalyticsService = class {
|
|
|
1273
1794
|
let persistedAnonId;
|
|
1274
1795
|
try {
|
|
1275
1796
|
if (existsSync4(opts.configPath)) {
|
|
1276
|
-
const config = JSON.parse(
|
|
1797
|
+
const config = JSON.parse(readFileSync6(opts.configPath, "utf8"));
|
|
1277
1798
|
if (config.telemetry === false) {
|
|
1278
1799
|
telemetryEnabled = false;
|
|
1279
1800
|
}
|
|
@@ -1298,7 +1819,7 @@ var AnalyticsService = class {
|
|
|
1298
1819
|
try {
|
|
1299
1820
|
const dir = dirname2(opts.configPath);
|
|
1300
1821
|
mkdirSync4(dir, { recursive: true });
|
|
1301
|
-
const existing = existsSync4(opts.configPath) ? JSON.parse(
|
|
1822
|
+
const existing = existsSync4(opts.configPath) ? JSON.parse(readFileSync6(opts.configPath, "utf8")) : {};
|
|
1302
1823
|
writeFileSync4(
|
|
1303
1824
|
opts.configPath,
|
|
1304
1825
|
JSON.stringify({ ...existing, anonId: newAnonId }, null, 2)
|
|
@@ -1344,203 +1865,6 @@ var AnalyticsService = class {
|
|
|
1344
1865
|
}
|
|
1345
1866
|
};
|
|
1346
1867
|
|
|
1347
|
-
// src/services/api-service.ts
|
|
1348
|
-
import { createHash as createHash2 } from "crypto";
|
|
1349
|
-
import z3 from "zod";
|
|
1350
|
-
var searchResultSchema = z3.object({
|
|
1351
|
-
id: z3.string(),
|
|
1352
|
-
position: z3.number(),
|
|
1353
|
-
slug: z3.string(),
|
|
1354
|
-
name: z3.string(),
|
|
1355
|
-
canonicalName: z3.string().nullable().optional(),
|
|
1356
|
-
description: z3.string(),
|
|
1357
|
-
whatItDoes: z3.string().nullable().optional(),
|
|
1358
|
-
url: z3.string(),
|
|
1359
|
-
cost: z3.object({ amount: z3.string(), asset: z3.string() }),
|
|
1360
|
-
rating: z3.object({
|
|
1361
|
-
score: z3.string(),
|
|
1362
|
-
successRate: z3.string(),
|
|
1363
|
-
reviews: z3.number(),
|
|
1364
|
-
stars: z3.string().nullable().optional(),
|
|
1365
|
-
state: z3.enum(["unrated", "rated"]).optional()
|
|
1366
|
-
}),
|
|
1367
|
-
trustScore: z3.number().nullable().optional(),
|
|
1368
|
-
trustSignalCount: z3.number().optional(),
|
|
1369
|
-
availabilityStatus: z3.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional(),
|
|
1370
|
-
relevanceScore: z3.number().optional()
|
|
1371
|
-
});
|
|
1372
|
-
var searchResponseSchema = z3.object({
|
|
1373
|
-
searchId: z3.string(),
|
|
1374
|
-
total: z3.number().optional().default(0),
|
|
1375
|
-
offset: z3.number().optional().default(0),
|
|
1376
|
-
hasMore: z3.boolean().optional().default(false),
|
|
1377
|
-
capabilities: z3.array(searchResultSchema)
|
|
1378
|
-
});
|
|
1379
|
-
var capabilityResponseSchema = z3.object({
|
|
1380
|
-
uid: z3.string(),
|
|
1381
|
-
slug: z3.string(),
|
|
1382
|
-
name: z3.string(),
|
|
1383
|
-
description: z3.string(),
|
|
1384
|
-
url: z3.string(),
|
|
1385
|
-
method: z3.string(),
|
|
1386
|
-
headers: z3.record(z3.string(), z3.string()).nullable(),
|
|
1387
|
-
bodySchema: z3.record(z3.string(), z3.unknown()).nullable(),
|
|
1388
|
-
responseSchema: z3.record(z3.string(), z3.unknown()).nullable(),
|
|
1389
|
-
example: z3.object({ request: z3.unknown(), response: z3.unknown() }).nullable(),
|
|
1390
|
-
tags: z3.array(z3.string()).nullable(),
|
|
1391
|
-
displayCostAmount: z3.string(),
|
|
1392
|
-
displayCostAsset: z3.string(),
|
|
1393
|
-
rating: z3.object({
|
|
1394
|
-
score: z3.string(),
|
|
1395
|
-
successRate: z3.string(),
|
|
1396
|
-
reviews: z3.number(),
|
|
1397
|
-
stars: z3.string().nullable().optional(),
|
|
1398
|
-
state: z3.enum(["unrated", "rated"]).optional()
|
|
1399
|
-
}),
|
|
1400
|
-
priceObserved: z3.object({
|
|
1401
|
-
medianCents: z3.string(),
|
|
1402
|
-
p95Cents: z3.string(),
|
|
1403
|
-
sampleCount: z3.number(),
|
|
1404
|
-
varies: z3.boolean()
|
|
1405
|
-
}).nullable().optional(),
|
|
1406
|
-
paymentMethods: z3.array(
|
|
1407
|
-
z3.object({
|
|
1408
|
-
uid: z3.string(),
|
|
1409
|
-
protocol: z3.string(),
|
|
1410
|
-
methodType: z3.string(),
|
|
1411
|
-
chain: z3.string().nullable(),
|
|
1412
|
-
mode: z3.string(),
|
|
1413
|
-
costAmount: z3.string(),
|
|
1414
|
-
costPer: z3.string(),
|
|
1415
|
-
priority: z3.number()
|
|
1416
|
-
})
|
|
1417
|
-
).nullable(),
|
|
1418
|
-
trustScore: z3.number().nullable().optional(),
|
|
1419
|
-
trustComponents: z3.object({
|
|
1420
|
-
apiQuality: z3.number().nullable(),
|
|
1421
|
-
blockchainActivity: z3.number().nullable(),
|
|
1422
|
-
performance: z3.number().nullable()
|
|
1423
|
-
}).nullable().optional(),
|
|
1424
|
-
availabilityStatus: z3.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional()
|
|
1425
|
-
});
|
|
1426
|
-
var createRunResponseSchema = z3.object({
|
|
1427
|
-
runId: z3.string()
|
|
1428
|
-
});
|
|
1429
|
-
var createReviewResponseSchema = z3.object({
|
|
1430
|
-
reviewId: z3.string(),
|
|
1431
|
-
recorded: z3.boolean()
|
|
1432
|
-
});
|
|
1433
|
-
var runListItemSchema = z3.object({
|
|
1434
|
-
uid: z3.string(),
|
|
1435
|
-
capabilityUid: z3.string(),
|
|
1436
|
-
capabilitySlug: z3.string(),
|
|
1437
|
-
capabilityName: z3.string(),
|
|
1438
|
-
status: z3.number().nullable(),
|
|
1439
|
-
latencyMs: z3.number().nullable(),
|
|
1440
|
-
cost: z3.object({ amount: z3.string(), asset: z3.string().nullable() }).nullable(),
|
|
1441
|
-
payment: z3.object({
|
|
1442
|
-
protocol: z3.string(),
|
|
1443
|
-
chain: z3.string().nullable(),
|
|
1444
|
-
txHash: z3.string().nullable(),
|
|
1445
|
-
mode: z3.string().nullable()
|
|
1446
|
-
}).nullable(),
|
|
1447
|
-
createdAt: z3.coerce.date(),
|
|
1448
|
-
reviewed: z3.boolean()
|
|
1449
|
-
});
|
|
1450
|
-
var listRunsResponseSchema = z3.object({
|
|
1451
|
-
runs: z3.array(runListItemSchema),
|
|
1452
|
-
nextCursor: z3.string().nullable()
|
|
1453
|
-
});
|
|
1454
|
-
var buildCanonicalMessage = (method, path, body, timestamp, nonce) => {
|
|
1455
|
-
const bodyHash = createHash2("sha256").update(body ?? "").digest("hex");
|
|
1456
|
-
return `${method}:${path}:${bodyHash}:${timestamp}:${nonce}`;
|
|
1457
|
-
};
|
|
1458
|
-
var ApiService = class {
|
|
1459
|
-
constructor(baseUrl, account) {
|
|
1460
|
-
this.baseUrl = baseUrl;
|
|
1461
|
-
this.account = account;
|
|
1462
|
-
this.walletAddress = this.account?.address ?? null;
|
|
1463
|
-
}
|
|
1464
|
-
walletAddress;
|
|
1465
|
-
account;
|
|
1466
|
-
signRequest = async (method, path, body) => {
|
|
1467
|
-
if (!this.account) throw new Error("No private key configured");
|
|
1468
|
-
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
1469
|
-
const nonce = crypto.randomUUID();
|
|
1470
|
-
const message = buildCanonicalMessage(method, path, body, timestamp, nonce);
|
|
1471
|
-
const signature = await this.account.signMessage({ message });
|
|
1472
|
-
return {
|
|
1473
|
-
"x-zero-address": this.account.address,
|
|
1474
|
-
"x-zero-timestamp": timestamp,
|
|
1475
|
-
"x-zero-nonce": nonce,
|
|
1476
|
-
"x-zero-signature": signature
|
|
1477
|
-
};
|
|
1478
|
-
};
|
|
1479
|
-
request = async (method, path, body) => {
|
|
1480
|
-
const url = `${this.baseUrl}${path}`;
|
|
1481
|
-
const bodyStr = body ? JSON.stringify(body) : void 0;
|
|
1482
|
-
const headers = {
|
|
1483
|
-
"content-type": "application/json"
|
|
1484
|
-
};
|
|
1485
|
-
if (this.account) {
|
|
1486
|
-
const walletHeaders = await this.signRequest(method, path, bodyStr);
|
|
1487
|
-
Object.assign(headers, walletHeaders);
|
|
1488
|
-
}
|
|
1489
|
-
const response = await fetch(url, {
|
|
1490
|
-
method,
|
|
1491
|
-
headers,
|
|
1492
|
-
body: bodyStr
|
|
1493
|
-
});
|
|
1494
|
-
if (!response.ok) {
|
|
1495
|
-
const errorBody = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
1496
|
-
throw new Error(
|
|
1497
|
-
errorBody.error ?? `HTTP ${response.status}`
|
|
1498
|
-
);
|
|
1499
|
-
}
|
|
1500
|
-
return response.json();
|
|
1501
|
-
};
|
|
1502
|
-
search = async (options) => {
|
|
1503
|
-
const json = await this.request("POST", "/v1/search", options);
|
|
1504
|
-
return searchResponseSchema.parse(json);
|
|
1505
|
-
};
|
|
1506
|
-
getCapability = async (id, searchId) => {
|
|
1507
|
-
const qs = searchId ? `?searchId=${encodeURIComponent(searchId)}` : "";
|
|
1508
|
-
const json = await this.request(
|
|
1509
|
-
"GET",
|
|
1510
|
-
`/v1/capabilities/${encodeURIComponent(id)}${qs}`
|
|
1511
|
-
);
|
|
1512
|
-
return capabilityResponseSchema.parse(json);
|
|
1513
|
-
};
|
|
1514
|
-
createRun = async (data) => {
|
|
1515
|
-
const json = await this.request("POST", "/v1/runs", data);
|
|
1516
|
-
return createRunResponseSchema.parse(json);
|
|
1517
|
-
};
|
|
1518
|
-
listRuns = async (params = {}) => {
|
|
1519
|
-
const qs = new URLSearchParams();
|
|
1520
|
-
if (params.capabilityId) qs.set("capabilityId", params.capabilityId);
|
|
1521
|
-
if (params.unreviewed) qs.set("unreviewed", "true");
|
|
1522
|
-
if (params.limit) qs.set("limit", String(params.limit));
|
|
1523
|
-
if (params.cursor) qs.set("cursor", params.cursor);
|
|
1524
|
-
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
1525
|
-
const json = await this.request("GET", `/v1/runs${suffix}`);
|
|
1526
|
-
return listRunsResponseSchema.parse(json);
|
|
1527
|
-
};
|
|
1528
|
-
createReview = async (data) => {
|
|
1529
|
-
const json = await this.request("POST", "/v1/reviews", data);
|
|
1530
|
-
return createReviewResponseSchema.parse(json);
|
|
1531
|
-
};
|
|
1532
|
-
getFundingUrl = async (amount) => {
|
|
1533
|
-
try {
|
|
1534
|
-
const qs = amount ? `?amount=${encodeURIComponent(amount)}` : "";
|
|
1535
|
-
const json = await this.request("GET", `/v1/wallet/fund-url${qs}`);
|
|
1536
|
-
const parsed = z3.object({ url: z3.string() }).parse(json);
|
|
1537
|
-
return parsed.url;
|
|
1538
|
-
} catch {
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1541
|
-
};
|
|
1542
|
-
};
|
|
1543
|
-
|
|
1544
1868
|
// src/services/payment-service.ts
|
|
1545
1869
|
import {
|
|
1546
1870
|
adaptViemWallet,
|
|
@@ -1847,7 +2171,7 @@ var PaymentService = class {
|
|
|
1847
2171
|
};
|
|
1848
2172
|
|
|
1849
2173
|
// src/services/state-service.ts
|
|
1850
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as
|
|
2174
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
1851
2175
|
import { join as join4 } from "path";
|
|
1852
2176
|
var StateService = class {
|
|
1853
2177
|
constructor(zeroDir) {
|
|
@@ -1862,7 +2186,7 @@ var StateService = class {
|
|
|
1862
2186
|
loadLastSearch = () => {
|
|
1863
2187
|
try {
|
|
1864
2188
|
if (!existsSync5(this.lastSearchPath)) return null;
|
|
1865
|
-
const raw =
|
|
2189
|
+
const raw = readFileSync7(this.lastSearchPath, "utf8");
|
|
1866
2190
|
return JSON.parse(raw);
|
|
1867
2191
|
} catch {
|
|
1868
2192
|
return null;
|
|
@@ -1906,7 +2230,7 @@ var getServices = (env) => {
|
|
|
1906
2230
|
if (!privateKey) {
|
|
1907
2231
|
try {
|
|
1908
2232
|
if (existsSync6(configPath)) {
|
|
1909
|
-
const config = JSON.parse(
|
|
2233
|
+
const config = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
1910
2234
|
if (typeof config.privateKey === "string") {
|
|
1911
2235
|
privateKey = config.privateKey;
|
|
1912
2236
|
}
|
|
@@ -1918,7 +2242,7 @@ var getServices = (env) => {
|
|
|
1918
2242
|
let lowBalanceWarning = 1;
|
|
1919
2243
|
try {
|
|
1920
2244
|
if (existsSync6(configPath)) {
|
|
1921
|
-
const config = JSON.parse(
|
|
2245
|
+
const config = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
1922
2246
|
if (typeof config.lowBalanceWarning === "number") {
|
|
1923
2247
|
lowBalanceWarning = config.lowBalanceWarning;
|
|
1924
2248
|
}
|
package/package.json
CHANGED
package/skills/zero/SKILL.md
CHANGED
|
@@ -125,6 +125,14 @@ zero config # view current config
|
|
|
125
125
|
zero config --set lowBalanceWarning=2.0 # warn when balance drops below $2
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
+
## Reporting a Zero Platform Bug
|
|
129
|
+
|
|
130
|
+
`zero bug-report "<what broke>"` exists for **Zero platform issues** — bad search ranking, indexed URL is wrong, billing was off, CLI itself misbehaved. **Only use it when the user explicitly asks** ("file a bug", "report a bug to Zero"). Never substitute it for `zero review` — capability quality (good/bad result, accuracy, value) always belongs in a review.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
zero bug-report "Search ranked the wrong API first for BTC price"
|
|
134
|
+
```
|
|
135
|
+
|
|
128
136
|
## Examples
|
|
129
137
|
|
|
130
138
|
### Translate text
|