@zeroxyz/cli 0.0.29 → 0.0.32
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/README.md +2 -2
- package/dist/index.js +629 -127
- package/package.json +1 -1
- package/skills/zero/SKILL.md +126 -19
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command as Command12 } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@zeroxyz/cli",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.32",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
12
12
|
zero: "dist/index.js",
|
|
@@ -119,10 +119,14 @@ var capabilityResponseSchema = z.object({
|
|
|
119
119
|
state: z.enum(["unrated", "rated"]).optional()
|
|
120
120
|
}),
|
|
121
121
|
priceObserved: z.object({
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
minCents: z.string().nullable(),
|
|
123
|
+
medianCents: z.string().nullable(),
|
|
124
|
+
maxCents: z.string().nullable(),
|
|
125
|
+
// Deprecated alias for maxCents; kept for backward compat.
|
|
126
|
+
p95Cents: z.string().nullable(),
|
|
124
127
|
sampleCount: z.number(),
|
|
125
|
-
varies: z.boolean()
|
|
128
|
+
varies: z.boolean(),
|
|
129
|
+
failureChargeRate: z.number().nullable()
|
|
126
130
|
}).nullable().optional(),
|
|
127
131
|
paymentMethods: z.array(
|
|
128
132
|
z.object({
|
|
@@ -149,7 +153,31 @@ var createRunResponseSchema = z.object({
|
|
|
149
153
|
});
|
|
150
154
|
var createReviewResponseSchema = z.object({
|
|
151
155
|
reviewId: z.string(),
|
|
152
|
-
recorded: z.boolean()
|
|
156
|
+
recorded: z.boolean(),
|
|
157
|
+
updated: z.boolean().optional()
|
|
158
|
+
});
|
|
159
|
+
var batchReviewResponseSchema = z.object({
|
|
160
|
+
results: z.array(
|
|
161
|
+
z.union([
|
|
162
|
+
z.object({
|
|
163
|
+
runId: z.string(),
|
|
164
|
+
ok: z.literal(true),
|
|
165
|
+
reviewId: z.string(),
|
|
166
|
+
updated: z.boolean()
|
|
167
|
+
}),
|
|
168
|
+
z.object({
|
|
169
|
+
runId: z.string(),
|
|
170
|
+
ok: z.literal(false),
|
|
171
|
+
status: z.number(),
|
|
172
|
+
error: z.string()
|
|
173
|
+
})
|
|
174
|
+
])
|
|
175
|
+
),
|
|
176
|
+
summary: z.object({
|
|
177
|
+
total: z.number(),
|
|
178
|
+
ok: z.number(),
|
|
179
|
+
failed: z.number()
|
|
180
|
+
})
|
|
153
181
|
});
|
|
154
182
|
var BUG_REPORT_CATEGORIES = [
|
|
155
183
|
"search_relevance",
|
|
@@ -275,6 +303,10 @@ var ApiService = class {
|
|
|
275
303
|
const json = await this.request("POST", "/v1/reviews", data);
|
|
276
304
|
return createReviewResponseSchema.parse(json);
|
|
277
305
|
};
|
|
306
|
+
createReviewsBatch = async (reviews) => {
|
|
307
|
+
const json = await this.request("POST", "/v1/reviews/batch", { reviews });
|
|
308
|
+
return batchReviewResponseSchema.parse(json);
|
|
309
|
+
};
|
|
278
310
|
createBugReport = async (data) => {
|
|
279
311
|
const json = await this.request("POST", "/v1/bug-reports", data);
|
|
280
312
|
return createBugReportResponseSchema.parse(json);
|
|
@@ -582,6 +614,8 @@ var configCommand = (_appContext) => new Command2("config").description("View or
|
|
|
582
614
|
});
|
|
583
615
|
|
|
584
616
|
// src/commands/fetch-command.ts
|
|
617
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
618
|
+
import { resolve as resolvePath } from "path";
|
|
585
619
|
import { Command as Command3 } from "commander";
|
|
586
620
|
import { formatUnits as formatUnits2 } from "viem";
|
|
587
621
|
|
|
@@ -595,7 +629,7 @@ import {
|
|
|
595
629
|
} from "@relayprotocol/relay-sdk";
|
|
596
630
|
import { x402Client as X402Client } from "@x402/core/client";
|
|
597
631
|
import { decodePaymentResponseHeader, x402HTTPClient } from "@x402/core/http";
|
|
598
|
-
import {
|
|
632
|
+
import { registerExactEvmScheme } from "@x402/evm/exact/client";
|
|
599
633
|
import { createSIWxClientHook } from "@x402/extensions/sign-in-with-x";
|
|
600
634
|
import { wrapFetchWithPayment } from "@x402/fetch";
|
|
601
635
|
import { Challenge, Receipt } from "mppx";
|
|
@@ -626,7 +660,9 @@ var PATHUSD_TEMPO = "0x20c0000000000000000000000000000000000000";
|
|
|
626
660
|
var BASE_CHAIN_ID = 8453;
|
|
627
661
|
var TEMPO_CHAIN_ID = 4217;
|
|
628
662
|
var TEMPO_TESTNET_CHAIN_ID = 42431;
|
|
629
|
-
var
|
|
663
|
+
var DEFAULT_PREFUND_NO_COST_INFO = "1";
|
|
664
|
+
var ADAPTIVE_PREFUND_MULTIPLIER = 10;
|
|
665
|
+
var ADAPTIVE_PREFUND_CEILING = "100";
|
|
630
666
|
var KNOWN_EIP712_DOMAINS = {
|
|
631
667
|
// USDC on Base
|
|
632
668
|
[USDC_BASE.toLowerCase()]: { name: "USDC", version: "2" },
|
|
@@ -643,6 +679,16 @@ var calculateBuffer = (baseBalance) => {
|
|
|
643
679
|
const twoDollars = 2000000n;
|
|
644
680
|
return twentyFivePercent < twoDollars ? twentyFivePercent : twoDollars;
|
|
645
681
|
};
|
|
682
|
+
var resolveDefaultPrefund = (displayCostAmount) => {
|
|
683
|
+
const floor = Number.parseFloat(DEFAULT_PREFUND_NO_COST_INFO);
|
|
684
|
+
const ceiling = Number.parseFloat(ADAPTIVE_PREFUND_CEILING);
|
|
685
|
+
if (!displayCostAmount) return DEFAULT_PREFUND_NO_COST_INFO;
|
|
686
|
+
const cost = Number.parseFloat(displayCostAmount);
|
|
687
|
+
if (!Number.isFinite(cost) || cost <= 0) return DEFAULT_PREFUND_NO_COST_INFO;
|
|
688
|
+
const scaled = cost * ADAPTIVE_PREFUND_MULTIPLIER;
|
|
689
|
+
const bounded = scaled < floor ? floor : scaled > ceiling ? ceiling : scaled;
|
|
690
|
+
return bounded.toFixed(6).replace(/\.?0+$/, "");
|
|
691
|
+
};
|
|
646
692
|
var tempoChain = {
|
|
647
693
|
id: TEMPO_CHAIN_ID,
|
|
648
694
|
name: "Tempo",
|
|
@@ -723,7 +769,7 @@ var PaymentService = class {
|
|
|
723
769
|
const bridgeAmount = requiredAmount + buffer;
|
|
724
770
|
if (baseBalance < bridgeAmount) {
|
|
725
771
|
throw new Error(
|
|
726
|
-
`Insufficient Base USDC to bridge: have ${formatUnits(baseBalance, 6)}, need ${formatUnits(bridgeAmount, 6)} (${formatUnits(requiredAmount, 6)} + ${formatUnits(buffer, 6)} buffer)
|
|
772
|
+
`Insufficient Base USDC to bridge: have ${formatUnits(baseBalance, 6)}, need ${formatUnits(bridgeAmount, 6)} (${formatUnits(requiredAmount, 6)} + ${formatUnits(buffer, 6)} buffer). This gateway uses an MPP payment channel, which is pre-funded up front. Either fund your wallet on Base, or pass \`--max-pay <amount>\` to use a smaller channel (e.g. \`--max-pay 0.05\` for a ~5-cent channel sized for a single cheap call).`
|
|
727
773
|
);
|
|
728
774
|
}
|
|
729
775
|
onProgress?.(
|
|
@@ -759,7 +805,7 @@ var PaymentService = class {
|
|
|
759
805
|
});
|
|
760
806
|
return bridgeTxHash;
|
|
761
807
|
};
|
|
762
|
-
handlePayment = async (url, request, paymentRequirement, maxPay, onProgress) => {
|
|
808
|
+
handlePayment = async (url, request, paymentRequirement, maxPay, onProgress, displayCostAmount) => {
|
|
763
809
|
if (!this.account) {
|
|
764
810
|
throw new Error(
|
|
765
811
|
"No wallet configured \u2014 run `zero init` or set ZERO_PRIVATE_KEY"
|
|
@@ -776,7 +822,8 @@ var PaymentService = class {
|
|
|
776
822
|
request,
|
|
777
823
|
paymentRequirement.raw,
|
|
778
824
|
maxPay,
|
|
779
|
-
onProgress
|
|
825
|
+
onProgress,
|
|
826
|
+
displayCostAmount
|
|
780
827
|
);
|
|
781
828
|
}
|
|
782
829
|
throw new Error("Unrecognized 402 payment protocol");
|
|
@@ -784,10 +831,9 @@ var PaymentService = class {
|
|
|
784
831
|
payX402 = async (url, request, _raw, maxPay) => {
|
|
785
832
|
if (!this.account) throw new Error("No wallet configured");
|
|
786
833
|
let capturedAmount = "0";
|
|
787
|
-
const client = new X402Client()
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
);
|
|
834
|
+
const client = registerExactEvmScheme(new X402Client(), {
|
|
835
|
+
signer: this.account
|
|
836
|
+
});
|
|
791
837
|
client.onBeforePaymentCreation(async (context) => {
|
|
792
838
|
const selected = context.selectedRequirements;
|
|
793
839
|
if (selected && (!selected.extra?.name || !selected.extra?.version)) {
|
|
@@ -798,7 +844,9 @@ var PaymentService = class {
|
|
|
798
844
|
}
|
|
799
845
|
const requirement = context.paymentRequired.accepts[0];
|
|
800
846
|
if (!requirement) return;
|
|
801
|
-
|
|
847
|
+
const rawAmount = requirement.amount ?? requirement.maxAmountRequired;
|
|
848
|
+
if (!rawAmount) return;
|
|
849
|
+
capturedAmount = formatUnits(BigInt(rawAmount), 6);
|
|
802
850
|
if (maxPay && Number.parseFloat(capturedAmount) > Number.parseFloat(maxPay)) {
|
|
803
851
|
return {
|
|
804
852
|
abort: true,
|
|
@@ -840,15 +888,14 @@ var PaymentService = class {
|
|
|
840
888
|
* BEFORE mppx signs a credential — so balance/bridge logic runs in-band
|
|
841
889
|
* without adding a pre-probe round-trip.
|
|
842
890
|
*/
|
|
843
|
-
prepareTempoFunds = async (challenge, maxPay, onProgress) => {
|
|
891
|
+
prepareTempoFunds = async (challenge, maxPay, onProgress, displayCostAmount) => {
|
|
844
892
|
const challengeRequest = challenge.request;
|
|
845
893
|
let requiredRaw;
|
|
846
894
|
if (challenge.intent === "session") {
|
|
847
895
|
const suggestedDeposit = challengeRequest.suggestedDeposit;
|
|
896
|
+
const defaultPrefund = resolveDefaultPrefund(displayCostAmount);
|
|
848
897
|
requiredRaw = suggestedDeposit ? BigInt(suggestedDeposit) : BigInt(
|
|
849
|
-
Math.floor(
|
|
850
|
-
Number.parseFloat(maxPay ?? DEFAULT_MAX_DEPOSIT) * 1e6
|
|
851
|
-
)
|
|
898
|
+
Math.floor(Number.parseFloat(maxPay ?? defaultPrefund) * 1e6)
|
|
852
899
|
);
|
|
853
900
|
} else {
|
|
854
901
|
requiredRaw = BigInt(challengeRequest.amount);
|
|
@@ -889,7 +936,7 @@ var PaymentService = class {
|
|
|
889
936
|
* the extra state and return `410 "channel not found"` on the close.
|
|
890
937
|
* This mirrors the working production v0.0.21 single-fetch flow.
|
|
891
938
|
*/
|
|
892
|
-
payMpp = async (url, request, _raw, maxPay, onProgress) => {
|
|
939
|
+
payMpp = async (url, request, _raw, maxPay, onProgress, displayCostAmount) => {
|
|
893
940
|
const account = this.account;
|
|
894
941
|
if (!account) throw new Error("No wallet configured");
|
|
895
942
|
let capturedAmount = "0";
|
|
@@ -901,7 +948,7 @@ var PaymentService = class {
|
|
|
901
948
|
methods: [
|
|
902
949
|
tempo({
|
|
903
950
|
account,
|
|
904
|
-
maxDeposit: maxPay ??
|
|
951
|
+
maxDeposit: maxPay ?? resolveDefaultPrefund(displayCostAmount),
|
|
905
952
|
onChannelUpdate: (entry) => {
|
|
906
953
|
channelEntry = {
|
|
907
954
|
channelId: entry.channelId,
|
|
@@ -917,7 +964,8 @@ var PaymentService = class {
|
|
|
917
964
|
capturedAmount = await this.prepareTempoFunds(
|
|
918
965
|
challenge,
|
|
919
966
|
maxPay,
|
|
920
|
-
onProgress
|
|
967
|
+
onProgress,
|
|
968
|
+
displayCostAmount
|
|
921
969
|
);
|
|
922
970
|
return void 0;
|
|
923
971
|
}
|
|
@@ -1167,6 +1215,40 @@ var isTextContentType = (contentType) => {
|
|
|
1167
1215
|
if (ct === "application/x-www-form-urlencoded") return true;
|
|
1168
1216
|
return false;
|
|
1169
1217
|
};
|
|
1218
|
+
var isJsonContentType = (contentType) => {
|
|
1219
|
+
if (!contentType) return false;
|
|
1220
|
+
const ct = contentType.toLowerCase().split(";")[0]?.trim() ?? "";
|
|
1221
|
+
return ct === "application/json" || ct.endsWith("+json");
|
|
1222
|
+
};
|
|
1223
|
+
var MAX_REQUEST_BODY_BYTES = 10 * 1024 * 1024;
|
|
1224
|
+
var resolveRequestBody = (rawData, readStdin) => {
|
|
1225
|
+
const fromFile = (spec) => {
|
|
1226
|
+
if (spec === "@-") return readFileSync3(0);
|
|
1227
|
+
return readFileSync3(resolvePath(spec.slice(1)));
|
|
1228
|
+
};
|
|
1229
|
+
if (readStdin && rawData !== void 0) {
|
|
1230
|
+
throw new Error(
|
|
1231
|
+
"Conflicting body sources: use either --data-stdin or -d, not both."
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
let body;
|
|
1235
|
+
if (readStdin) {
|
|
1236
|
+
body = readFileSync3(0);
|
|
1237
|
+
} else if (rawData?.startsWith("@")) {
|
|
1238
|
+
body = fromFile(rawData);
|
|
1239
|
+
} else {
|
|
1240
|
+
body = rawData;
|
|
1241
|
+
}
|
|
1242
|
+
if (body !== void 0) {
|
|
1243
|
+
const bytes = Buffer.isBuffer(body) ? body.length : Buffer.byteLength(body, "utf8");
|
|
1244
|
+
if (bytes > MAX_REQUEST_BODY_BYTES) {
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
`Request body is ${bytes} bytes \u2014 exceeds the ${MAX_REQUEST_BODY_BYTES} byte limit. Split the payload, compress it, or contact the capability owner about raising the cap.`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return body;
|
|
1251
|
+
};
|
|
1170
1252
|
var looksLikeX402V1Body = (body) => {
|
|
1171
1253
|
if (!body || typeof body !== "object") return false;
|
|
1172
1254
|
const b = body;
|
|
@@ -1201,15 +1283,26 @@ var detectPaymentRequirement = async (response) => {
|
|
|
1201
1283
|
}
|
|
1202
1284
|
return { protocol: "unknown", raw: {} };
|
|
1203
1285
|
};
|
|
1204
|
-
var fetchCommand = (appContext) => new Command3("fetch").description(
|
|
1286
|
+
var fetchCommand = (appContext) => new Command3("fetch").description(
|
|
1287
|
+
"Fetch a capability URL, handling 402 challenges automatically"
|
|
1288
|
+
).argument("<url>", "URL to fetch").option(
|
|
1205
1289
|
"-X, --method <method>",
|
|
1206
1290
|
"HTTP method (GET, POST, PUT, PATCH, DELETE). Defaults to POST when -d is set, otherwise GET"
|
|
1207
|
-
).option(
|
|
1291
|
+
).option(
|
|
1292
|
+
"-d, --data <body>",
|
|
1293
|
+
"Request body. Pass a literal JSON string, or `@path/to/file` to read from a file, or `@-` to read from stdin."
|
|
1294
|
+
).option(
|
|
1295
|
+
"--data-stdin",
|
|
1296
|
+
"Read the request body from stdin (equivalent to `-d @-`)."
|
|
1297
|
+
).option("-H, --header <header...>", "Headers in Key:Value format").option("--max-pay <amount>", "Maximum per-call spend limit (USDC)").option(
|
|
1208
1298
|
"--capability <id>",
|
|
1209
1299
|
"Bind this fetch to a capability (uid or slug) so a reviewable run is recorded even without a prior `zero search`"
|
|
1210
1300
|
).option(
|
|
1211
1301
|
"--json",
|
|
1212
|
-
"Emit {runId, status, latencyMs, payment, body} as JSON on stdout (for batch/non-TTY use)"
|
|
1302
|
+
"Emit {runId, ok, status, latencyMs, payment, body, bodyRaw} as JSON on stdout (for batch/non-TTY use)"
|
|
1303
|
+
).option(
|
|
1304
|
+
"--raw-body",
|
|
1305
|
+
"With --json: keep `body` as the raw response string instead of parsing JSON. `bodyRaw` still reflects the raw text."
|
|
1213
1306
|
).option(
|
|
1214
1307
|
"--agent <name>",
|
|
1215
1308
|
"Identify your agent host for this invocation (e.g. claude-web, codex). Overrides auto-detect for this call only."
|
|
@@ -1224,6 +1317,19 @@ var fetchCommand = (appContext) => new Command3("fetch").description("Fetch a ca
|
|
|
1224
1317
|
walletService
|
|
1225
1318
|
} = appContext.services;
|
|
1226
1319
|
const startTime = Date.now();
|
|
1320
|
+
let resolvedBody;
|
|
1321
|
+
try {
|
|
1322
|
+
resolvedBody = resolveRequestBody(
|
|
1323
|
+
options.data,
|
|
1324
|
+
options.dataStdin ?? false
|
|
1325
|
+
);
|
|
1326
|
+
} catch (err) {
|
|
1327
|
+
console.error(
|
|
1328
|
+
err instanceof Error ? err.message : "Failed to read request body"
|
|
1329
|
+
);
|
|
1330
|
+
process.exitCode = 1;
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1227
1333
|
const headers = {};
|
|
1228
1334
|
if (options.header) {
|
|
1229
1335
|
for (const h of options.header) {
|
|
@@ -1236,15 +1342,15 @@ var fetchCommand = (appContext) => new Command3("fetch").description("Fetch a ca
|
|
|
1236
1342
|
const hasContentType = Object.keys(headers).some(
|
|
1237
1343
|
(k) => k.toLowerCase() === "content-type"
|
|
1238
1344
|
);
|
|
1239
|
-
if (
|
|
1240
|
-
headers["content-type"] = "application/json";
|
|
1345
|
+
if (resolvedBody && !hasContentType) {
|
|
1346
|
+
headers["content-type"] = Buffer.isBuffer(resolvedBody) ? "application/octet-stream" : "application/json";
|
|
1241
1347
|
}
|
|
1242
1348
|
const log = (msg) => console.error(` ${msg}`);
|
|
1243
|
-
const method = options.method ? options.method.toUpperCase() :
|
|
1349
|
+
const method = options.method ? options.method.toUpperCase() : resolvedBody ? "POST" : "GET";
|
|
1244
1350
|
const requestInit = {
|
|
1245
1351
|
method,
|
|
1246
1352
|
headers,
|
|
1247
|
-
body:
|
|
1353
|
+
body: resolvedBody
|
|
1248
1354
|
};
|
|
1249
1355
|
const lastSearch = stateService.loadLastSearch();
|
|
1250
1356
|
const matchedCapability = lastSearch?.capabilities.find(
|
|
@@ -1283,7 +1389,8 @@ var fetchCommand = (appContext) => new Command3("fetch").description("Fetch a ca
|
|
|
1283
1389
|
requestInit,
|
|
1284
1390
|
paymentReq,
|
|
1285
1391
|
options.maxPay,
|
|
1286
|
-
log
|
|
1392
|
+
log,
|
|
1393
|
+
matchedCapability?.displayCostAmount
|
|
1287
1394
|
);
|
|
1288
1395
|
finalResponse = result.response;
|
|
1289
1396
|
paymentMeta = {
|
|
@@ -1384,8 +1491,8 @@ var fetchCommand = (appContext) => new Command3("fetch").description("Fetch a ca
|
|
|
1384
1491
|
if (capabilityId && apiService.walletAddress) {
|
|
1385
1492
|
let requestSchema;
|
|
1386
1493
|
let responseSchema;
|
|
1387
|
-
if (
|
|
1388
|
-
const parsedReq = tryParseJson(
|
|
1494
|
+
if (typeof resolvedBody === "string") {
|
|
1495
|
+
const parsedReq = tryParseJson(resolvedBody);
|
|
1389
1496
|
if (parsedReq !== null && typeof parsedReq === "object") {
|
|
1390
1497
|
requestSchema = inferSchema(parsedReq);
|
|
1391
1498
|
}
|
|
@@ -1440,14 +1547,30 @@ var fetchCommand = (appContext) => new Command3("fetch").description("Fetch a ca
|
|
|
1440
1547
|
console.error(` Fetch failed: ${fetchError.message}`);
|
|
1441
1548
|
}
|
|
1442
1549
|
if (options.json) {
|
|
1443
|
-
const
|
|
1550
|
+
const responseStatus = finalResponse?.status ?? null;
|
|
1551
|
+
const ok = responseStatus !== null && responseStatus >= 200 && responseStatus < 300;
|
|
1552
|
+
const responseCt = finalResponse?.headers.get("content-type") ?? null;
|
|
1553
|
+
const bodyString = bodyIsBinary ? (bodyBytes ?? Buffer.alloc(0)).toString("base64") : body;
|
|
1554
|
+
let parsedBody = null;
|
|
1555
|
+
if (finalResponse && !bodyIsBinary && !options.rawBody) {
|
|
1556
|
+
if (isJsonContentType(responseCt)) {
|
|
1557
|
+
const parsed = tryParseJson(body);
|
|
1558
|
+
parsedBody = parsed === null ? body : parsed;
|
|
1559
|
+
} else {
|
|
1560
|
+
parsedBody = body;
|
|
1561
|
+
}
|
|
1562
|
+
} else if (finalResponse) {
|
|
1563
|
+
parsedBody = bodyString;
|
|
1564
|
+
}
|
|
1444
1565
|
console.log(
|
|
1445
1566
|
JSON.stringify({
|
|
1446
1567
|
runId,
|
|
1447
|
-
|
|
1568
|
+
ok,
|
|
1569
|
+
status: responseStatus,
|
|
1448
1570
|
latencyMs,
|
|
1449
1571
|
payment: paymentMeta ?? null,
|
|
1450
|
-
body:
|
|
1572
|
+
body: parsedBody,
|
|
1573
|
+
bodyRaw: finalResponse ? bodyString : null,
|
|
1451
1574
|
...bodyIsBinary && { bodyEncoding: "base64" },
|
|
1452
1575
|
...fetchError && { error: fetchError.message },
|
|
1453
1576
|
...skipReasons.length > 0 && {
|
|
@@ -1508,6 +1631,30 @@ var formatTrustComponent = (label, value) => {
|
|
|
1508
1631
|
const display = value != null ? `${value}/100` : "--";
|
|
1509
1632
|
return ` ${label.padEnd(22)}${display}`;
|
|
1510
1633
|
};
|
|
1634
|
+
var centsToDollars = (cents) => {
|
|
1635
|
+
const value = Number.parseFloat(cents) / 100;
|
|
1636
|
+
if (value < 0.01) return value.toFixed(4);
|
|
1637
|
+
if (value < 1) return value.toFixed(3);
|
|
1638
|
+
return value.toFixed(2);
|
|
1639
|
+
};
|
|
1640
|
+
var formatCost = (capability) => {
|
|
1641
|
+
const lines = [];
|
|
1642
|
+
const observed = capability.priceObserved;
|
|
1643
|
+
if (observed && observed.sampleCount > 0 && observed.varies && observed.minCents && observed.maxCents) {
|
|
1644
|
+
const min = centsToDollars(observed.minCents);
|
|
1645
|
+
const max = centsToDollars(observed.maxCents);
|
|
1646
|
+
const median = observed.medianCents ? centsToDollars(observed.medianCents) : null;
|
|
1647
|
+
const detail = median ? `median $${median}, n=${observed.sampleCount}` : `n=${observed.sampleCount}`;
|
|
1648
|
+
lines.push(` Cost: $${min}\u2013$${max}/call (${detail})`);
|
|
1649
|
+
} else {
|
|
1650
|
+
lines.push(` Cost: $${capability.displayCostAmount}/call`);
|
|
1651
|
+
}
|
|
1652
|
+
if (observed?.failureChargeRate != null && observed.failureChargeRate > 0) {
|
|
1653
|
+
const pct = Math.round(observed.failureChargeRate * 100);
|
|
1654
|
+
lines.push(` \u26A0 ~${pct}% of failed runs still charged the wallet`);
|
|
1655
|
+
}
|
|
1656
|
+
return lines;
|
|
1657
|
+
};
|
|
1511
1658
|
var formatRating = (rating) => {
|
|
1512
1659
|
if (rating.state === "unrated") return "unrated";
|
|
1513
1660
|
const successPct = `${Math.round(Number.parseFloat(rating.successRate) * 100)}%`;
|
|
@@ -1517,6 +1664,81 @@ var formatRating = (rating) => {
|
|
|
1517
1664
|
}
|
|
1518
1665
|
return `${successPct} success, ${reviews} reviews`;
|
|
1519
1666
|
};
|
|
1667
|
+
var asNode = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
1668
|
+
var placeholderFor = (fieldName, propSchema) => {
|
|
1669
|
+
const node = asNode(propSchema);
|
|
1670
|
+
const example = node?.example ?? node?.default;
|
|
1671
|
+
if (typeof example === "string") return example;
|
|
1672
|
+
if (typeof example === "number" || typeof example === "boolean") {
|
|
1673
|
+
return String(example);
|
|
1674
|
+
}
|
|
1675
|
+
return `<${fieldName.toUpperCase()}>`;
|
|
1676
|
+
};
|
|
1677
|
+
var extractInputEnvelope = (bodySchema, method) => {
|
|
1678
|
+
if (!bodySchema) return {};
|
|
1679
|
+
const props = asNode(bodySchema.properties);
|
|
1680
|
+
if (!props) return {};
|
|
1681
|
+
const inputProps = asNode(asNode(props.input)?.properties);
|
|
1682
|
+
if (inputProps) {
|
|
1683
|
+
return {
|
|
1684
|
+
queryParams: asNode(asNode(inputProps.queryParams)?.properties) ?? void 0,
|
|
1685
|
+
body: asNode(asNode(inputProps.body)?.properties) ?? void 0
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
const upperMethod = method.toUpperCase();
|
|
1689
|
+
const isGetLike = upperMethod === "GET" || upperMethod === "DELETE";
|
|
1690
|
+
return isGetLike ? { queryParams: props } : { body: props };
|
|
1691
|
+
};
|
|
1692
|
+
var buildTryItExample = (capability) => {
|
|
1693
|
+
const method = capability.method.toUpperCase();
|
|
1694
|
+
const lines = ["", "Try it:"];
|
|
1695
|
+
const { queryParams, body } = extractInputEnvelope(
|
|
1696
|
+
capability.bodySchema,
|
|
1697
|
+
capability.method
|
|
1698
|
+
);
|
|
1699
|
+
const callerHeaders = Object.entries(capability.headers ?? {});
|
|
1700
|
+
const headerFlags = callerHeaders.map(
|
|
1701
|
+
([k, v]) => `-H "${k}: ${v}" # [caller-provided]`
|
|
1702
|
+
);
|
|
1703
|
+
if (method === "GET" || queryParams && !body) {
|
|
1704
|
+
const qs = queryParams ? Object.entries(queryParams).map(
|
|
1705
|
+
([k, schema]) => `${encodeURIComponent(k)}=${encodeURIComponent(placeholderFor(k, schema))}`
|
|
1706
|
+
).join("&") : "";
|
|
1707
|
+
const url = qs ? `${capability.url}?${qs}` : capability.url;
|
|
1708
|
+
const urlLine = ` zero fetch "${url}"`;
|
|
1709
|
+
if (headerFlags.length === 0) {
|
|
1710
|
+
lines.push(urlLine);
|
|
1711
|
+
} else {
|
|
1712
|
+
lines.push(` zero fetch \\`);
|
|
1713
|
+
for (const h of headerFlags) lines.push(` ${h} \\`);
|
|
1714
|
+
lines.push(` "${url}"`);
|
|
1715
|
+
}
|
|
1716
|
+
if (!queryParams && method === "GET") {
|
|
1717
|
+
lines.push(
|
|
1718
|
+
" # bodySchema did not expose queryParams \u2014 call the URL as-is or inspect the raw schema above."
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
return lines;
|
|
1722
|
+
}
|
|
1723
|
+
const samplePayload = body ? Object.fromEntries(
|
|
1724
|
+
Object.entries(body).map(([k, schema]) => [
|
|
1725
|
+
k,
|
|
1726
|
+
placeholderFor(k, schema)
|
|
1727
|
+
])
|
|
1728
|
+
) : null;
|
|
1729
|
+
const bodyJson = samplePayload ? JSON.stringify(samplePayload) : "<BODY_JSON>";
|
|
1730
|
+
lines.push(` zero fetch \\`);
|
|
1731
|
+
if (method !== "POST") lines.push(` -X ${method} \\`);
|
|
1732
|
+
for (const h of headerFlags) lines.push(` ${h} \\`);
|
|
1733
|
+
lines.push(` -d '${bodyJson}' \\`);
|
|
1734
|
+
lines.push(` ${capability.url}`);
|
|
1735
|
+
if (!body) {
|
|
1736
|
+
lines.push(
|
|
1737
|
+
" # bodySchema did not expose input.body \u2014 replace <BODY_JSON> with the exact shape shown above."
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
return lines;
|
|
1741
|
+
};
|
|
1520
1742
|
var formatCapability = (capability) => {
|
|
1521
1743
|
const lines = [];
|
|
1522
1744
|
lines.push(capability.name);
|
|
@@ -1543,9 +1765,10 @@ var formatCapability = (capability) => {
|
|
|
1543
1765
|
}
|
|
1544
1766
|
lines.push(` Rating: ${formatRating(capability.rating)}`);
|
|
1545
1767
|
lines.push(` Status: ${capability.availabilityStatus ?? "unknown"}`);
|
|
1546
|
-
lines.push(
|
|
1768
|
+
lines.push(...formatCost(capability));
|
|
1547
1769
|
lines.push(` URL: ${capability.url}`);
|
|
1548
1770
|
lines.push(` Method: ${capability.method}`);
|
|
1771
|
+
lines.push(...buildTryItExample(capability));
|
|
1549
1772
|
return lines.join("\n");
|
|
1550
1773
|
};
|
|
1551
1774
|
var getCommand = (appContext) => new Command4("get").description(
|
|
@@ -1609,21 +1832,153 @@ var getCommand = (appContext) => new Command4("get").description(
|
|
|
1609
1832
|
// src/commands/init-command.ts
|
|
1610
1833
|
import { createHash as createHash3 } from "crypto";
|
|
1611
1834
|
import {
|
|
1612
|
-
chmodSync,
|
|
1835
|
+
chmodSync as chmodSync2,
|
|
1613
1836
|
cpSync,
|
|
1614
1837
|
existsSync as existsSync2,
|
|
1615
|
-
mkdirSync as
|
|
1838
|
+
mkdirSync as mkdirSync3,
|
|
1616
1839
|
readdirSync,
|
|
1617
|
-
readFileSync as
|
|
1840
|
+
readFileSync as readFileSync4,
|
|
1618
1841
|
rmSync,
|
|
1619
1842
|
statSync,
|
|
1620
|
-
writeFileSync as
|
|
1843
|
+
writeFileSync as writeFileSync3
|
|
1621
1844
|
} from "fs";
|
|
1622
1845
|
import { homedir as homedir2 } from "os";
|
|
1623
1846
|
import { dirname, join as join2, relative } from "path";
|
|
1624
1847
|
import { fileURLToPath } from "url";
|
|
1625
1848
|
import { Command as Command5 } from "commander";
|
|
1626
1849
|
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
1850
|
+
|
|
1851
|
+
// src/util/install-banner.ts
|
|
1852
|
+
var ZEROMAN_ART = `
|
|
1853
|
+
:++-.
|
|
1854
|
+
.:--==+===--:. +@%@#+-
|
|
1855
|
+
.-+*%@@@@%%%%%%@@@@@%#*=.-# #%-=*=
|
|
1856
|
+
:+#@@@#+-:. .-+#@@@@@%#. %# =#:
|
|
1857
|
+
:+%@@#=: :+@%%@@%=+@: .#=
|
|
1858
|
+
-*@@#=. :%%%%@@%%+ #=
|
|
1859
|
+
:#@@#- .:-====--. :%%%%%@@+ %:
|
|
1860
|
+
+@@#- .-+#%@@@@@@@@@@%*- *@%%%%%+ =#
|
|
1861
|
+
.#@@= .. .-*%@@@@%%@%*+#%%%@@@#:=@%%%%@- %.
|
|
1862
|
+
.%@%: :#=:+%@@@%@%%@@+:-+*%%%%%%@%#%%%%%% %-
|
|
1863
|
+
#@%: .%%@@%%%%#%%*--+=%@@%%%%%%@@%%%%@- %-
|
|
1864
|
+
+@%: :#* :%%%%@==+=*= .%%%%%%%%%%%%%@* %:
|
|
1865
|
+
.%%+ :%@- %#.%%@==@# #@%%%%%%%%%%@# -#
|
|
1866
|
+
.::::: -@%: .%%%* .-%%%+ .. =%%%%%%%%%%%@# #+.
|
|
1867
|
+
.+=-::..-* -@% +@%%@*=-+%%%@@#-..-*@%%%%%%%%%@@+ -@%%%#+:
|
|
1868
|
+
%- .:::*=*%% #%%%%@@@@@@@%%%@@@@@%%%%%%%%%@%- :%%%%@@@@#-
|
|
1869
|
+
++ ..*- %@@- *@%%%%%%#-. .-#@%%%%%%%@%+. :@@@@@@%%%@@-
|
|
1870
|
+
:# .:--*:-%%%%. .%@%%%%%=:----: :#%%%%@@%+. -#-:.:-#@@@%@%
|
|
1871
|
+
.# *#+=@%@%- .#@@@@@%@@@@@@%*-.=%@@@%+. +* +%%%%%%=
|
|
1872
|
+
=* .====-*:#%%@@#=. .=*#%@%%%%%%%@@@@@%*- -#= -*:.. .:*:
|
|
1873
|
+
-*-. :%*##:-#@@@%#*+==+*#%%%@@@@@@%#+- :*+. .-*+:. .:#-
|
|
1874
|
+
.*+=--+#@@@@@%: .-+#%@@@@@@@@@@%%#+=:. -*+. =+: -*
|
|
1875
|
+
..:+*###*+=**. .:::::::. .-+=. =# - #:
|
|
1876
|
+
:*+. .=*%%: .+==*- #.
|
|
1877
|
+
-#@@#=:. .:=*#%@@%%%= %- .- .+-*=
|
|
1878
|
+
*@@%%@@@#+==-:::...:::--==+=-=#@@%%%%%@* .+=-=++-==-:.
|
|
1879
|
+
=@%%%%%%- .::---====---:. :#@%%%%%@*
|
|
1880
|
+
=@%%%%%%. *@%%%%%@+
|
|
1881
|
+
#%%%%%@%. #@%%%%%@:
|
|
1882
|
+
.-=++*%%%%%%@* :%%%%%%%#---:.
|
|
1883
|
+
.*%@@@@@%%%%%%%@: %%%%%%%@@@@@%*:
|
|
1884
|
+
%@@@@@@@@@@@@@%%. .%@@@@@@@@@@@@@@:
|
|
1885
|
+
-==++++====--:. -==+++++++++++=.
|
|
1886
|
+
`;
|
|
1887
|
+
var ZERO_BANNER = [
|
|
1888
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
1889
|
+
"\u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2588\u2588\u2588\u2588\u2557",
|
|
1890
|
+
" \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2551",
|
|
1891
|
+
" \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551",
|
|
1892
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
|
|
1893
|
+
"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "
|
|
1894
|
+
].join("\n");
|
|
1895
|
+
var colorsEnabled = () => {
|
|
1896
|
+
if (process.env.NO_COLOR) return false;
|
|
1897
|
+
if (process.env.FORCE_COLOR) return true;
|
|
1898
|
+
return Boolean(process.stdout.isTTY);
|
|
1899
|
+
};
|
|
1900
|
+
var wrap = (code, text) => colorsEnabled() ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
1901
|
+
var color = {
|
|
1902
|
+
bold: (s) => wrap("1", s),
|
|
1903
|
+
dim: (s) => wrap("2", s),
|
|
1904
|
+
cyan: (s) => wrap("36", s),
|
|
1905
|
+
magenta: (s) => wrap("35", s),
|
|
1906
|
+
green: (s) => wrap("32", s),
|
|
1907
|
+
yellow: (s) => wrap("33", s),
|
|
1908
|
+
gray: (s) => wrap("90", s),
|
|
1909
|
+
boldCyan: (s) => wrap("1;36", s),
|
|
1910
|
+
boldMagenta: (s) => wrap("1;35", s),
|
|
1911
|
+
boldGreen: (s) => wrap("1;32", s)
|
|
1912
|
+
};
|
|
1913
|
+
var printZeroBanner = () => {
|
|
1914
|
+
console.log("");
|
|
1915
|
+
console.log(ZEROMAN_ART);
|
|
1916
|
+
console.log(color.boldCyan(ZERO_BANNER));
|
|
1917
|
+
console.log("");
|
|
1918
|
+
console.log(` ${color.dim("The search engine for AI agents.")}`);
|
|
1919
|
+
console.log("");
|
|
1920
|
+
console.log(color.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1921
|
+
console.log("");
|
|
1922
|
+
};
|
|
1923
|
+
var supportsUnicode = () => {
|
|
1924
|
+
if (process.platform !== "win32") return true;
|
|
1925
|
+
return Boolean(
|
|
1926
|
+
process.env.WT_SESSION || process.env.TERM_PROGRAM === "vscode"
|
|
1927
|
+
);
|
|
1928
|
+
};
|
|
1929
|
+
var SYMBOLS = supportsUnicode() ? { check: "\u2713", arrow: "\u203A", warn: "!" } : { check: "OK", arrow: ">", warn: "!" };
|
|
1930
|
+
var stepSuccess = (label, detail) => {
|
|
1931
|
+
const line = detail ? ` ${color.boldGreen(SYMBOLS.check)} ${label} ${color.dim(detail)}` : ` ${color.boldGreen(SYMBOLS.check)} ${label}`;
|
|
1932
|
+
console.log(line);
|
|
1933
|
+
};
|
|
1934
|
+
var stepWarn = (label, detail) => {
|
|
1935
|
+
const line = detail ? ` ${color.yellow(SYMBOLS.warn)} ${label} ${color.dim(detail)}` : ` ${color.yellow(SYMBOLS.warn)} ${label}`;
|
|
1936
|
+
console.log(line);
|
|
1937
|
+
};
|
|
1938
|
+
var stepSkip = (label, detail) => {
|
|
1939
|
+
const line = detail ? ` ${color.gray(SYMBOLS.arrow)} ${color.dim(`${label} ${detail}`)}` : ` ${color.gray(SYMBOLS.arrow)} ${color.dim(label)}`;
|
|
1940
|
+
console.log(line);
|
|
1941
|
+
};
|
|
1942
|
+
var stepInfo = (message) => {
|
|
1943
|
+
console.log(` ${color.gray("\xB7")} ${color.dim(message)}`);
|
|
1944
|
+
};
|
|
1945
|
+
var sectionDivider = () => {
|
|
1946
|
+
console.log("");
|
|
1947
|
+
console.log(color.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1948
|
+
console.log("");
|
|
1949
|
+
};
|
|
1950
|
+
var printReadyFooter = () => {
|
|
1951
|
+
const lines = [
|
|
1952
|
+
"",
|
|
1953
|
+
` ${color.boldGreen("Zero is ready!")} Run ${color.cyan("`zero search`")} to find capabilities.`,
|
|
1954
|
+
"",
|
|
1955
|
+
` ${color.dim("Try:")}`,
|
|
1956
|
+
` ${color.cyan('zero search "translate text to Spanish"')}`,
|
|
1957
|
+
` ${color.cyan('zero search "generate an image"')}`,
|
|
1958
|
+
` ${color.cyan('zero search "weather forecast"')}`,
|
|
1959
|
+
"",
|
|
1960
|
+
` ${color.dim("By using Zero, you agree to our Terms of Service:")}`,
|
|
1961
|
+
` ${color.dim("https://zero.xyz/terms-of-service")}`,
|
|
1962
|
+
` ${color.dim("Run `zero terms` to view the full terms.")}`,
|
|
1963
|
+
""
|
|
1964
|
+
];
|
|
1965
|
+
return lines.join("\n");
|
|
1966
|
+
};
|
|
1967
|
+
|
|
1968
|
+
// src/util/secure-config.ts
|
|
1969
|
+
import { chmodSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1970
|
+
var SECURE_DIR_MODE = 448;
|
|
1971
|
+
var SECURE_FILE_MODE = 384;
|
|
1972
|
+
var ensureSecureDir = (path) => {
|
|
1973
|
+
mkdirSync2(path, { recursive: true, mode: SECURE_DIR_MODE });
|
|
1974
|
+
chmodSync(path, SECURE_DIR_MODE);
|
|
1975
|
+
};
|
|
1976
|
+
var writeSecureFile = (path, contents) => {
|
|
1977
|
+
writeFileSync2(path, contents, { mode: SECURE_FILE_MODE });
|
|
1978
|
+
chmodSync(path, SECURE_FILE_MODE);
|
|
1979
|
+
};
|
|
1980
|
+
|
|
1981
|
+
// src/commands/init-command.ts
|
|
1627
1982
|
var AGENT_TOOLS = [
|
|
1628
1983
|
{ name: "Claude Code", detectDir: ".claude", skillsDir: ".claude/skills" },
|
|
1629
1984
|
{ name: "Codex", detectDir: ".codex", skillsDir: ".agents/skills" },
|
|
@@ -1634,21 +1989,29 @@ var AGENT_TOOLS = [
|
|
|
1634
1989
|
},
|
|
1635
1990
|
{ name: "Cursor", detectDir: ".cursor", skillsDir: ".cursor/skills" }
|
|
1636
1991
|
];
|
|
1637
|
-
var
|
|
1638
|
-
let
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1992
|
+
var findResourceDir = (startDir, resourceName) => {
|
|
1993
|
+
let current = startDir;
|
|
1994
|
+
while (true) {
|
|
1995
|
+
const candidate = join2(current, resourceName);
|
|
1996
|
+
if (existsSync2(candidate)) {
|
|
1997
|
+
return candidate;
|
|
1998
|
+
}
|
|
1999
|
+
const parent = dirname(current);
|
|
2000
|
+
if (parent === current) {
|
|
2001
|
+
throw new Error(
|
|
2002
|
+
`Could not locate bundled '${resourceName}' directory from ${startDir}`
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
2005
|
+
current = parent;
|
|
1643
2006
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
2007
|
+
};
|
|
2008
|
+
var getCliModuleDir = () => {
|
|
2009
|
+
if (import.meta.url) {
|
|
2010
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
1648
2011
|
}
|
|
1649
|
-
return
|
|
2012
|
+
return __dirname;
|
|
1650
2013
|
};
|
|
1651
|
-
var sha256File = (filePath) => createHash3("sha256").update(
|
|
2014
|
+
var sha256File = (filePath) => createHash3("sha256").update(readFileSync4(filePath)).digest("hex");
|
|
1652
2015
|
var verifyFileCopy = (src, dest) => {
|
|
1653
2016
|
if (!existsSync2(dest)) return false;
|
|
1654
2017
|
return sha256File(src) === sha256File(dest);
|
|
@@ -1666,50 +2029,69 @@ var collectAllFiles = (dir) => {
|
|
|
1666
2029
|
return files;
|
|
1667
2030
|
};
|
|
1668
2031
|
var copyDirRecursive = (src, dest) => {
|
|
1669
|
-
|
|
2032
|
+
mkdirSync3(dest, { recursive: true });
|
|
1670
2033
|
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
1671
2034
|
const srcPath = join2(src, entry.name);
|
|
1672
2035
|
const destPath = join2(dest, entry.name);
|
|
1673
2036
|
if (entry.isDirectory()) {
|
|
1674
2037
|
copyDirRecursive(srcPath, destPath);
|
|
1675
2038
|
} else {
|
|
1676
|
-
const data =
|
|
1677
|
-
|
|
2039
|
+
const data = readFileSync4(srcPath);
|
|
2040
|
+
writeFileSync3(destPath, data);
|
|
1678
2041
|
try {
|
|
1679
2042
|
const mode = statSync(srcPath).mode;
|
|
1680
|
-
|
|
2043
|
+
chmodSync2(destPath, mode);
|
|
1681
2044
|
} catch {
|
|
1682
2045
|
}
|
|
1683
2046
|
}
|
|
1684
2047
|
}
|
|
1685
2048
|
};
|
|
1686
|
-
var installHook = (home) => {
|
|
2049
|
+
var installHook = (home, verbose = false) => {
|
|
1687
2050
|
const claudeDir = join2(home, ".claude");
|
|
1688
2051
|
if (!existsSync2(claudeDir)) {
|
|
2052
|
+
if (verbose) {
|
|
2053
|
+
stepInfo(`~/.claude not found \u2014 Claude Code not installed, skipping`);
|
|
2054
|
+
}
|
|
1689
2055
|
return false;
|
|
1690
2056
|
}
|
|
1691
2057
|
const zeroHooksDir = join2(home, ".zero", "hooks");
|
|
1692
|
-
|
|
2058
|
+
mkdirSync3(zeroHooksDir, { recursive: true });
|
|
2059
|
+
if (verbose) stepInfo(`staged hook dir at ${zeroHooksDir}`);
|
|
1693
2060
|
const hookFiles = ["auto-approve-zero.sh", "zero-context.sh"];
|
|
1694
2061
|
const hookDests = {};
|
|
2062
|
+
const hooksSourceDir = findResourceDir(getCliModuleDir(), "hooks");
|
|
1695
2063
|
for (const hookFile of hookFiles) {
|
|
1696
|
-
const hookSource = join2(
|
|
2064
|
+
const hookSource = join2(hooksSourceDir, hookFile);
|
|
1697
2065
|
const hookDest = join2(zeroHooksDir, hookFile);
|
|
1698
2066
|
cpSync(hookSource, hookDest);
|
|
1699
|
-
|
|
2067
|
+
chmodSync2(hookDest, 493);
|
|
1700
2068
|
if (!verifyFileCopy(hookSource, hookDest)) {
|
|
1701
2069
|
throw new Error(
|
|
1702
2070
|
`Integrity check failed: ${hookDest} does not match source`
|
|
1703
2071
|
);
|
|
1704
2072
|
}
|
|
1705
2073
|
hookDests[hookFile] = hookDest;
|
|
2074
|
+
if (verbose) stepInfo(`copied ${hookFile} \u2192 ${hookDest} (verified)`);
|
|
1706
2075
|
}
|
|
1707
2076
|
const settingsPath = join2(claudeDir, "settings.json");
|
|
1708
2077
|
let settings = {};
|
|
2078
|
+
let settingsExisted = false;
|
|
2079
|
+
let settingsCorrupted = false;
|
|
1709
2080
|
if (existsSync2(settingsPath)) {
|
|
2081
|
+
settingsExisted = true;
|
|
1710
2082
|
try {
|
|
1711
|
-
settings = JSON.parse(
|
|
2083
|
+
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
1712
2084
|
} catch {
|
|
2085
|
+
settingsCorrupted = true;
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (verbose) {
|
|
2089
|
+
if (!settingsExisted) {
|
|
2090
|
+
stepInfo(`${settingsPath} did not exist \u2014 creating`);
|
|
2091
|
+
} else if (settingsCorrupted) {
|
|
2092
|
+
stepInfo(`${settingsPath} was unparseable JSON \u2014 rewriting from scratch`);
|
|
2093
|
+
} else {
|
|
2094
|
+
stepInfo(`merging into existing ${settingsPath}`);
|
|
1713
2095
|
}
|
|
1714
2096
|
}
|
|
1715
2097
|
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
@@ -1738,8 +2120,10 @@ var installHook = (home) => {
|
|
|
1738
2120
|
});
|
|
1739
2121
|
if (existingIdx >= 0) {
|
|
1740
2122
|
preToolUse[existingIdx] = zeroHookEntry;
|
|
2123
|
+
if (verbose) stepInfo(`PreToolUse auto-approve-zero entry replaced`);
|
|
1741
2124
|
} else {
|
|
1742
2125
|
preToolUse.push(zeroHookEntry);
|
|
2126
|
+
if (verbose) stepInfo(`PreToolUse auto-approve-zero entry appended`);
|
|
1743
2127
|
}
|
|
1744
2128
|
if (!Array.isArray(hooks.UserPromptSubmit)) {
|
|
1745
2129
|
hooks.UserPromptSubmit = [];
|
|
@@ -1762,8 +2146,10 @@ var installHook = (home) => {
|
|
|
1762
2146
|
});
|
|
1763
2147
|
if (existingContextIdx >= 0) {
|
|
1764
2148
|
userPromptSubmit[existingContextIdx] = contextHookEntry;
|
|
2149
|
+
if (verbose) stepInfo(`UserPromptSubmit zero-context entry replaced`);
|
|
1765
2150
|
} else {
|
|
1766
2151
|
userPromptSubmit.push(contextHookEntry);
|
|
2152
|
+
if (verbose) stepInfo(`UserPromptSubmit zero-context entry appended`);
|
|
1767
2153
|
}
|
|
1768
2154
|
if (!settings.sandbox || typeof settings.sandbox !== "object") {
|
|
1769
2155
|
settings.sandbox = {};
|
|
@@ -1780,12 +2166,15 @@ var installHook = (home) => {
|
|
|
1780
2166
|
const zeroDomain = "*.zero.xyz";
|
|
1781
2167
|
if (!allowedDomains.includes(zeroDomain)) {
|
|
1782
2168
|
allowedDomains.push(zeroDomain);
|
|
2169
|
+
if (verbose) stepInfo(`sandbox.network.allowedDomains += ${zeroDomain}`);
|
|
2170
|
+
} else if (verbose) {
|
|
2171
|
+
stepInfo(`${zeroDomain} already in sandbox allowlist \u2014 not modified`);
|
|
1783
2172
|
}
|
|
1784
|
-
|
|
2173
|
+
writeFileSync3(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
1785
2174
|
`);
|
|
1786
2175
|
return true;
|
|
1787
2176
|
};
|
|
1788
|
-
var CONFLICTING_SKILL_PATTERNS = ["zam"
|
|
2177
|
+
var CONFLICTING_SKILL_PATTERNS = ["zam"];
|
|
1789
2178
|
var findConflictingSkills = (home) => {
|
|
1790
2179
|
const found = [];
|
|
1791
2180
|
for (const tool of AGENT_TOOLS) {
|
|
@@ -1815,22 +2204,38 @@ var removeConflictingSkills = (home) => {
|
|
|
1815
2204
|
}
|
|
1816
2205
|
return removed;
|
|
1817
2206
|
};
|
|
1818
|
-
var installSkills = (home) => {
|
|
1819
|
-
const skillsSourceDir =
|
|
2207
|
+
var installSkills = (home, verbose = false) => {
|
|
2208
|
+
const skillsSourceDir = findResourceDir(getCliModuleDir(), "skills");
|
|
1820
2209
|
const skillDirs = readdirSync(skillsSourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
2210
|
+
if (verbose) {
|
|
2211
|
+
stepInfo(
|
|
2212
|
+
`found ${skillDirs.length} bundled skill(s): ${skillDirs.join(", ")}`
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
1821
2215
|
const installed = [];
|
|
1822
2216
|
const errors = [];
|
|
1823
2217
|
for (const tool of AGENT_TOOLS) {
|
|
1824
2218
|
const toolDetectPath = join2(home, tool.detectDir);
|
|
1825
2219
|
if (!existsSync2(toolDetectPath)) {
|
|
2220
|
+
if (verbose) {
|
|
2221
|
+
stepInfo(
|
|
2222
|
+
`${tool.name}: ~/${tool.detectDir} not found \u2014 skipping install`
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
1826
2225
|
continue;
|
|
1827
2226
|
}
|
|
2227
|
+
if (verbose) {
|
|
2228
|
+
stepInfo(
|
|
2229
|
+
`${tool.name}: detected at ~/${tool.detectDir} \u2014 installing to ~/${tool.skillsDir}`
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
1828
2232
|
try {
|
|
1829
2233
|
const toolSkillsPath = join2(home, tool.skillsDir);
|
|
1830
|
-
|
|
2234
|
+
mkdirSync3(toolSkillsPath, { recursive: true });
|
|
1831
2235
|
for (const skillDir of skillDirs) {
|
|
1832
2236
|
const src = join2(skillsSourceDir, skillDir);
|
|
1833
2237
|
const dest = join2(toolSkillsPath, skillDir);
|
|
2238
|
+
const existed = existsSync2(dest);
|
|
1834
2239
|
copyDirRecursive(src, dest);
|
|
1835
2240
|
for (const srcFile of collectAllFiles(src)) {
|
|
1836
2241
|
const relPath = relative(src, srcFile);
|
|
@@ -1842,6 +2247,11 @@ var installSkills = (home) => {
|
|
|
1842
2247
|
}
|
|
1843
2248
|
}
|
|
1844
2249
|
installed.push(`${tool.name}: ${dest}`);
|
|
2250
|
+
if (verbose) {
|
|
2251
|
+
stepInfo(
|
|
2252
|
+
`${tool.name}: ${existed ? "updated" : "installed"} skill '${skillDir}'`
|
|
2253
|
+
);
|
|
2254
|
+
}
|
|
1845
2255
|
}
|
|
1846
2256
|
} catch (err) {
|
|
1847
2257
|
errors.push({
|
|
@@ -1853,11 +2263,13 @@ var installSkills = (home) => {
|
|
|
1853
2263
|
return { installed, errors };
|
|
1854
2264
|
};
|
|
1855
2265
|
var runInit = async (appContext, options = {}) => {
|
|
2266
|
+
const verbose = options.verbose ?? false;
|
|
1856
2267
|
appContext.services.analyticsService.capture("init_started", {
|
|
1857
2268
|
force: options.force ?? false
|
|
1858
2269
|
});
|
|
1859
2270
|
let currentStep = "wallet";
|
|
1860
2271
|
try {
|
|
2272
|
+
printZeroBanner();
|
|
1861
2273
|
const home = homedir2();
|
|
1862
2274
|
const zeroDir = join2(home, ".zero");
|
|
1863
2275
|
const configPath = join2(zeroDir, "config.json");
|
|
@@ -1866,7 +2278,7 @@ var runInit = async (appContext, options = {}) => {
|
|
|
1866
2278
|
const walletExists = (() => {
|
|
1867
2279
|
if (!existsSync2(configPath)) return false;
|
|
1868
2280
|
try {
|
|
1869
|
-
const existing = JSON.parse(
|
|
2281
|
+
const existing = JSON.parse(readFileSync4(configPath, "utf8"));
|
|
1870
2282
|
return !!existing.privateKey;
|
|
1871
2283
|
} catch {
|
|
1872
2284
|
return false;
|
|
@@ -1875,9 +2287,9 @@ var runInit = async (appContext, options = {}) => {
|
|
|
1875
2287
|
if (!walletExists || options.force) {
|
|
1876
2288
|
const privateKey = generatePrivateKey();
|
|
1877
2289
|
const account = privateKeyToAccount(privateKey);
|
|
1878
|
-
|
|
1879
|
-
const existing = existsSync2(configPath) ? JSON.parse(
|
|
1880
|
-
|
|
2290
|
+
ensureSecureDir(zeroDir);
|
|
2291
|
+
const existing = existsSync2(configPath) ? JSON.parse(readFileSync4(configPath, "utf8")) : {};
|
|
2292
|
+
writeSecureFile(
|
|
1881
2293
|
configPath,
|
|
1882
2294
|
JSON.stringify(
|
|
1883
2295
|
{ ...existing, privateKey, lowBalanceWarning: 1 },
|
|
@@ -1887,14 +2299,32 @@ var runInit = async (appContext, options = {}) => {
|
|
|
1887
2299
|
);
|
|
1888
2300
|
walletCreated = true;
|
|
1889
2301
|
walletAddress = account.address;
|
|
1890
|
-
|
|
2302
|
+
stepSuccess("Wallet created", account.address);
|
|
2303
|
+
if (verbose) {
|
|
2304
|
+
if (options.force) {
|
|
2305
|
+
stepInfo(
|
|
2306
|
+
`--force passed \u2014 generated a new key and overwrote ${configPath}`
|
|
2307
|
+
);
|
|
2308
|
+
} else {
|
|
2309
|
+
stepInfo(`no wallet found at ${configPath} \u2014 generated a new key`);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
1891
2312
|
} else {
|
|
1892
2313
|
try {
|
|
1893
|
-
const existing = JSON.parse(
|
|
2314
|
+
const existing = JSON.parse(readFileSync4(configPath, "utf8"));
|
|
1894
2315
|
const account = privateKeyToAccount(existing.privateKey);
|
|
1895
2316
|
walletAddress = account.address;
|
|
1896
2317
|
} catch {
|
|
1897
2318
|
}
|
|
2319
|
+
stepSkip(
|
|
2320
|
+
"Wallet already configured",
|
|
2321
|
+
walletAddress ?? "run `zero init --force` to reset"
|
|
2322
|
+
);
|
|
2323
|
+
if (verbose) {
|
|
2324
|
+
stepInfo(
|
|
2325
|
+
`existing wallet at ${configPath} was preserved \u2014 pass --force to regenerate`
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
1898
2328
|
}
|
|
1899
2329
|
const agentsDetected = [];
|
|
1900
2330
|
const agentsWithSkills = [];
|
|
@@ -1906,9 +2336,20 @@ var runInit = async (appContext, options = {}) => {
|
|
|
1906
2336
|
agentsDetected.push(tool.name);
|
|
1907
2337
|
}
|
|
1908
2338
|
}
|
|
2339
|
+
if (verbose) {
|
|
2340
|
+
const missing = AGENT_TOOLS.filter(
|
|
2341
|
+
(t) => !agentsDetected.includes(t.name)
|
|
2342
|
+
).map((t) => t.name);
|
|
2343
|
+
stepInfo(
|
|
2344
|
+
`agents detected: ${agentsDetected.length > 0 ? agentsDetected.join(", ") : "none"}`
|
|
2345
|
+
);
|
|
2346
|
+
if (missing.length > 0) {
|
|
2347
|
+
stepInfo(`agents not detected: ${missing.join(", ")}`);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
1909
2350
|
currentStep = "skills";
|
|
1910
2351
|
try {
|
|
1911
|
-
const { installed, errors } = installSkills(home);
|
|
2352
|
+
const { installed, errors } = installSkills(home, verbose);
|
|
1912
2353
|
for (const entry of installed) {
|
|
1913
2354
|
const toolName = entry.split(":")[0];
|
|
1914
2355
|
if (toolName && !agentsWithSkills.includes(toolName)) {
|
|
@@ -1927,27 +2368,45 @@ var runInit = async (appContext, options = {}) => {
|
|
|
1927
2368
|
skillsError = err instanceof Error ? err.message : "unknown skills error";
|
|
1928
2369
|
console.error(`Warning: skills install failed: ${skillsError}`);
|
|
1929
2370
|
}
|
|
2371
|
+
if (agentsWithSkills.length > 0) {
|
|
2372
|
+
stepSuccess("Skills installed");
|
|
2373
|
+
} else if (agentsDetected.length === 0) {
|
|
2374
|
+
stepSkip("No agent tools detected");
|
|
2375
|
+
} else if (skillsError) {
|
|
2376
|
+
stepWarn("Skills install had errors", skillsError);
|
|
2377
|
+
}
|
|
1930
2378
|
currentStep = "hook";
|
|
1931
2379
|
try {
|
|
1932
|
-
hookInstalled = installHook(home);
|
|
2380
|
+
hookInstalled = installHook(home, verbose);
|
|
1933
2381
|
} catch (err) {
|
|
1934
2382
|
hookError = err instanceof Error ? err.message : "unknown hook error";
|
|
1935
2383
|
}
|
|
2384
|
+
if (hookInstalled) {
|
|
2385
|
+
stepSuccess("Agents configured");
|
|
2386
|
+
} else if (hookError) {
|
|
2387
|
+
stepWarn("Agent config failed", hookError);
|
|
2388
|
+
}
|
|
1936
2389
|
currentStep = "cleanup_scan";
|
|
1937
2390
|
const conflictingSkills = findConflictingSkills(home);
|
|
2391
|
+
if (verbose) {
|
|
2392
|
+
stepInfo(
|
|
2393
|
+
`scanning agent skill dirs for deprecated skills matching [${CONFLICTING_SKILL_PATTERNS.join(", ")}]`
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
1938
2396
|
if (conflictingSkills.length > 0) {
|
|
1939
2397
|
const skillList = conflictingSkills.map((s) => ` - ${s.tool}: ${s.skillName}`).join("\n");
|
|
1940
2398
|
console.error(
|
|
1941
2399
|
`
|
|
1942
|
-
Found deprecated skills that may conflict with Zero:
|
|
2400
|
+
${color.yellow("Found deprecated skills that may conflict with Zero:")}
|
|
1943
2401
|
${skillList}
|
|
1944
2402
|
|
|
1945
|
-
To remove them, run: zero init cleanup`
|
|
2403
|
+
To remove them, run: ${color.cyan("zero init cleanup")}`
|
|
1946
2404
|
);
|
|
2405
|
+
} else if (verbose) {
|
|
2406
|
+
stepInfo(`no deprecated skills found`);
|
|
1947
2407
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
);
|
|
2408
|
+
sectionDivider();
|
|
2409
|
+
console.error(printReadyFooter());
|
|
1951
2410
|
currentStep = "complete";
|
|
1952
2411
|
appContext.services.analyticsService.capture("wallet_initialized", {
|
|
1953
2412
|
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
@@ -1982,12 +2441,13 @@ To remove them, run: zero init cleanup`
|
|
|
1982
2441
|
throw err;
|
|
1983
2442
|
}
|
|
1984
2443
|
};
|
|
1985
|
-
var initCommand = (appContext) => new Command5("init").description("Initialize Zero CLI for usage").option("--force", "Overwrite existing configuration").
|
|
2444
|
+
var initCommand = (appContext) => new Command5("init").description("Initialize Zero CLI for usage").option("--force", "Overwrite existing configuration").option(
|
|
2445
|
+
"-v, --verbose",
|
|
2446
|
+
"Explain why each install step was taken or skipped"
|
|
2447
|
+
).action(async (options) => {
|
|
1986
2448
|
await runInit(appContext, options);
|
|
1987
2449
|
}).addCommand(
|
|
1988
|
-
new Command5("cleanup").description(
|
|
1989
|
-
"Remove deprecated skills (zam, tempo) that conflict with Zero"
|
|
1990
|
-
).action(() => {
|
|
2450
|
+
new Command5("cleanup").description("Remove deprecated skills (zam) that conflict with Zero").action(() => {
|
|
1991
2451
|
const home = homedir2();
|
|
1992
2452
|
const removed = removeConflictingSkills(home);
|
|
1993
2453
|
if (removed.length === 0) {
|
|
@@ -2007,7 +2467,7 @@ ${removedList}`);
|
|
|
2007
2467
|
);
|
|
2008
2468
|
|
|
2009
2469
|
// src/commands/review-command.ts
|
|
2010
|
-
import { readFileSync as
|
|
2470
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
2011
2471
|
import { Command as Command6 } from "commander";
|
|
2012
2472
|
import { z as z3 } from "zod";
|
|
2013
2473
|
var bulkEntrySchema2 = z3.object({
|
|
@@ -2049,35 +2509,66 @@ Examples:
|
|
|
2049
2509
|
try {
|
|
2050
2510
|
const { analyticsService, apiService } = appContext.services;
|
|
2051
2511
|
if (options.fromFile) {
|
|
2052
|
-
const contents =
|
|
2512
|
+
const contents = readFileSync5(options.fromFile, "utf8");
|
|
2053
2513
|
const lines = contents.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
2054
|
-
|
|
2055
|
-
let
|
|
2514
|
+
const parsed = [];
|
|
2515
|
+
let parseFailures = 0;
|
|
2056
2516
|
for (const [idx, line] of lines.entries()) {
|
|
2057
2517
|
const lineNum = idx + 1;
|
|
2058
2518
|
try {
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
console.log(
|
|
2063
|
-
`[${lineNum}] ${parsed.runId} -> ${result2.reviewId}`
|
|
2064
|
-
);
|
|
2065
|
-
analyticsService.capture("review_submitted", {
|
|
2066
|
-
runId: parsed.runId,
|
|
2067
|
-
success: parsed.success,
|
|
2068
|
-
accuracy: parsed.accuracy,
|
|
2069
|
-
value: parsed.value,
|
|
2070
|
-
reliability: parsed.reliability,
|
|
2071
|
-
hasContent: !!parsed.content,
|
|
2072
|
-
bulk: true
|
|
2519
|
+
parsed.push({
|
|
2520
|
+
lineNum,
|
|
2521
|
+
entry: bulkEntrySchema2.parse(JSON.parse(line))
|
|
2073
2522
|
});
|
|
2074
2523
|
} catch (err) {
|
|
2075
|
-
|
|
2524
|
+
parseFailures += 1;
|
|
2076
2525
|
console.error(
|
|
2077
|
-
`[${lineNum}] FAILED: ${err instanceof Error ? err.message : String(err)}`
|
|
2526
|
+
`[${lineNum}] PARSE FAILED: ${err instanceof Error ? err.message : String(err)}`
|
|
2078
2527
|
);
|
|
2079
2528
|
}
|
|
2080
2529
|
}
|
|
2530
|
+
if (parsed.length === 0) {
|
|
2531
|
+
console.error("No valid review lines found in file.");
|
|
2532
|
+
process.exitCode = 1;
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const Chunk = 100;
|
|
2536
|
+
let ok = 0;
|
|
2537
|
+
let failed = parseFailures;
|
|
2538
|
+
for (let i = 0; i < parsed.length; i += Chunk) {
|
|
2539
|
+
const chunk = parsed.slice(i, i + Chunk);
|
|
2540
|
+
const response = await apiService.createReviewsBatch(
|
|
2541
|
+
chunk.map((p) => p.entry)
|
|
2542
|
+
);
|
|
2543
|
+
for (const [chunkIdx, item] of response.results.entries()) {
|
|
2544
|
+
const lineNum = chunk[chunkIdx]?.lineNum ?? i + chunkIdx + 1;
|
|
2545
|
+
if (item.ok) {
|
|
2546
|
+
ok += 1;
|
|
2547
|
+
const suffix = item.updated ? " (updated)" : "";
|
|
2548
|
+
console.log(
|
|
2549
|
+
`[${lineNum}] ${item.runId} -> ${item.reviewId}${suffix}`
|
|
2550
|
+
);
|
|
2551
|
+
const entry = chunk[chunkIdx]?.entry;
|
|
2552
|
+
if (entry) {
|
|
2553
|
+
analyticsService.capture("review_submitted", {
|
|
2554
|
+
runId: entry.runId,
|
|
2555
|
+
success: entry.success,
|
|
2556
|
+
accuracy: entry.accuracy,
|
|
2557
|
+
value: entry.value,
|
|
2558
|
+
reliability: entry.reliability,
|
|
2559
|
+
hasContent: !!entry.content,
|
|
2560
|
+
bulk: true,
|
|
2561
|
+
updated: item.updated
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
} else {
|
|
2565
|
+
failed += 1;
|
|
2566
|
+
console.error(
|
|
2567
|
+
`[${lineNum}] FAILED (${item.status}): ${item.error}`
|
|
2568
|
+
);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2081
2572
|
console.log(`
|
|
2082
2573
|
Bulk review complete: ${ok} ok, ${failed} failed`);
|
|
2083
2574
|
if (failed > 0) process.exitCode = 1;
|
|
@@ -2116,7 +2607,7 @@ Bulk review complete: ${ok} ok, ${failed} failed`);
|
|
|
2116
2607
|
}
|
|
2117
2608
|
if (list.runs.length > 1) {
|
|
2118
2609
|
console.error(
|
|
2119
|
-
`Multiple un-reviewed runs for "${options.capability}".
|
|
2610
|
+
`Multiple un-reviewed runs for "${options.capability}". Either pass a runId explicitly (see "zero runs --capability ${options.capability} --unreviewed"), or review them in one call via "zero review --from-file <reviews.jsonl>".`
|
|
2120
2611
|
);
|
|
2121
2612
|
process.exitCode = 1;
|
|
2122
2613
|
return;
|
|
@@ -2330,7 +2821,8 @@ var searchCommand = (appContext) => new Command8("search").description("Search f
|
|
|
2330
2821
|
capabilities: result.capabilities.map((c) => ({
|
|
2331
2822
|
position: c.position,
|
|
2332
2823
|
id: c.id,
|
|
2333
|
-
url: c.url
|
|
2824
|
+
url: c.url,
|
|
2825
|
+
displayCostAmount: c.cost.amount
|
|
2334
2826
|
}))
|
|
2335
2827
|
});
|
|
2336
2828
|
console.log(formatSearchResults(result.capabilities));
|
|
@@ -2373,7 +2865,7 @@ Read the full terms at: ${TERMS_URL}
|
|
|
2373
2865
|
});
|
|
2374
2866
|
|
|
2375
2867
|
// src/commands/wallet-command.ts
|
|
2376
|
-
import { existsSync as existsSync3,
|
|
2868
|
+
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
|
|
2377
2869
|
import { homedir as homedir3 } from "os";
|
|
2378
2870
|
import { join as join3 } from "path";
|
|
2379
2871
|
import { Command as Command10 } from "commander";
|
|
@@ -2395,6 +2887,9 @@ var walletBalanceCommand = (appContext) => new Command10("balance").description(
|
|
|
2395
2887
|
console.log(`${balance.amount} ${balance.asset}`);
|
|
2396
2888
|
});
|
|
2397
2889
|
var walletFundCommand = (appContext) => new Command10("fund").description("Fund your wallet").argument("[amount]", "Amount to fund in USDC").option("--manual", "Show wallet address for manual transfer").option(
|
|
2890
|
+
"--no-open",
|
|
2891
|
+
"Print the funding URL instead of opening a browser (for agents \u2014 funding links are one-time use, hand the URL to the user)"
|
|
2892
|
+
).option(
|
|
2398
2893
|
"--use <provider>",
|
|
2399
2894
|
"Onramp provider: coinbase or stripe",
|
|
2400
2895
|
"coinbase"
|
|
@@ -2422,12 +2917,19 @@ ${address}`);
|
|
|
2422
2917
|
provider
|
|
2423
2918
|
);
|
|
2424
2919
|
if (url) {
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2920
|
+
if (options.open) {
|
|
2921
|
+
await open(url);
|
|
2922
|
+
console.log("Opened funding page in your browser.");
|
|
2923
|
+
console.log(`If it didn't open, visit: ${url}`);
|
|
2924
|
+
} else {
|
|
2925
|
+
console.log(
|
|
2926
|
+
"Funding URL (one-time use \u2014 open it in a browser to fund):"
|
|
2927
|
+
);
|
|
2928
|
+
console.log(url);
|
|
2929
|
+
}
|
|
2428
2930
|
console.log(`Your wallet address: ${address}`);
|
|
2429
2931
|
analyticsService.capture("wallet_funded", {
|
|
2430
|
-
method: "browser",
|
|
2932
|
+
method: options.open ? "browser" : "url",
|
|
2431
2933
|
amount
|
|
2432
2934
|
});
|
|
2433
2935
|
} else {
|
|
@@ -2468,7 +2970,7 @@ var walletSetCommand = (appContext) => new Command10("set").description("Set wal
|
|
|
2468
2970
|
const configPath = join3(zeroDir, "config.json");
|
|
2469
2971
|
if (!options.force && existsSync3(configPath)) {
|
|
2470
2972
|
try {
|
|
2471
|
-
const existing2 = JSON.parse(
|
|
2973
|
+
const existing2 = JSON.parse(readFileSync6(configPath, "utf8"));
|
|
2472
2974
|
if (existing2.privateKey) {
|
|
2473
2975
|
console.error(
|
|
2474
2976
|
"Wallet already configured. Use --force to overwrite."
|
|
@@ -2479,9 +2981,9 @@ var walletSetCommand = (appContext) => new Command10("set").description("Set wal
|
|
|
2479
2981
|
} catch {
|
|
2480
2982
|
}
|
|
2481
2983
|
}
|
|
2482
|
-
|
|
2483
|
-
const existing = existsSync3(configPath) ? JSON.parse(
|
|
2484
|
-
|
|
2984
|
+
ensureSecureDir(zeroDir);
|
|
2985
|
+
const existing = existsSync3(configPath) ? JSON.parse(readFileSync6(configPath, "utf8")) : {};
|
|
2986
|
+
writeSecureFile(
|
|
2485
2987
|
configPath,
|
|
2486
2988
|
JSON.stringify(
|
|
2487
2989
|
{
|
|
@@ -2510,7 +3012,7 @@ var walletCommand = (appContext) => {
|
|
|
2510
3012
|
};
|
|
2511
3013
|
|
|
2512
3014
|
// src/commands/welcome-command.ts
|
|
2513
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3015
|
+
import { existsSync as existsSync4, readFileSync as readFileSync7 } from "fs";
|
|
2514
3016
|
import { homedir as homedir4 } from "os";
|
|
2515
3017
|
import { join as join4 } from "path";
|
|
2516
3018
|
import { Command as Command11 } from "commander";
|
|
@@ -2521,7 +3023,7 @@ var readPrivateKey = () => {
|
|
|
2521
3023
|
const configPath = join4(homedir4(), ".zero", "config.json");
|
|
2522
3024
|
if (!existsSync4(configPath)) return null;
|
|
2523
3025
|
try {
|
|
2524
|
-
const config = JSON.parse(
|
|
3026
|
+
const config = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
2525
3027
|
if (typeof config.privateKey === "string") {
|
|
2526
3028
|
return config.privateKey;
|
|
2527
3029
|
}
|
|
@@ -2573,7 +3075,7 @@ If your browser didn't open, paste the URL above.`
|
|
|
2573
3075
|
// src/app.ts
|
|
2574
3076
|
var createApp = (appContext) => {
|
|
2575
3077
|
const { analyticsService } = appContext.services;
|
|
2576
|
-
const program = new Command12().name("zero").description("Zero CLI \u2014 Search engine
|
|
3078
|
+
const program = new Command12().name("zero").description("Zero CLI \u2014 Search engine for AI agents").version(package_default.version, "-v, --version").exitOverride().hook("preAction", async (_thisCommand, actionCommand) => {
|
|
2577
3079
|
const agentFlag = actionCommand.opts().agent;
|
|
2578
3080
|
if (typeof agentFlag === "string" && agentFlag.trim().length > 0) {
|
|
2579
3081
|
analyticsService.setAgentHost(agentFlag.trim());
|
|
@@ -2620,14 +3122,14 @@ var getEnv = () => {
|
|
|
2620
3122
|
|
|
2621
3123
|
// src/app/app-services.ts
|
|
2622
3124
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2623
|
-
import { existsSync as existsSync7, readFileSync as
|
|
3125
|
+
import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
|
|
2624
3126
|
import { homedir as homedir5 } from "os";
|
|
2625
3127
|
import { join as join6 } from "path";
|
|
2626
3128
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
2627
3129
|
|
|
2628
3130
|
// src/services/analytics-service.ts
|
|
2629
3131
|
import { randomUUID } from "crypto";
|
|
2630
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as
|
|
3132
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
2631
3133
|
import { dirname as dirname2 } from "path";
|
|
2632
3134
|
import { PostHog } from "posthog-node";
|
|
2633
3135
|
var POSTHOG_API_KEY = "phc_B2vLyNxAf2mnqvdPQajf4d4b2iXc35dep2ZrvebMJLuX";
|
|
@@ -2650,7 +3152,7 @@ var AnalyticsService = class {
|
|
|
2650
3152
|
let persistedAnonId;
|
|
2651
3153
|
try {
|
|
2652
3154
|
if (existsSync5(opts.configPath)) {
|
|
2653
|
-
const config = JSON.parse(
|
|
3155
|
+
const config = JSON.parse(readFileSync8(opts.configPath, "utf8"));
|
|
2654
3156
|
if (config.telemetry === false) {
|
|
2655
3157
|
telemetryEnabled = false;
|
|
2656
3158
|
}
|
|
@@ -2675,7 +3177,7 @@ var AnalyticsService = class {
|
|
|
2675
3177
|
try {
|
|
2676
3178
|
const dir = dirname2(opts.configPath);
|
|
2677
3179
|
mkdirSync4(dir, { recursive: true });
|
|
2678
|
-
const existing = existsSync5(opts.configPath) ? JSON.parse(
|
|
3180
|
+
const existing = existsSync5(opts.configPath) ? JSON.parse(readFileSync8(opts.configPath, "utf8")) : {};
|
|
2679
3181
|
writeFileSync4(
|
|
2680
3182
|
opts.configPath,
|
|
2681
3183
|
JSON.stringify({ ...existing, anonId: newAnonId }, null, 2)
|
|
@@ -2711,7 +3213,7 @@ var AnalyticsService = class {
|
|
|
2711
3213
|
if (anonId === walletAddress) return;
|
|
2712
3214
|
let aliasedTo;
|
|
2713
3215
|
try {
|
|
2714
|
-
const config = JSON.parse(
|
|
3216
|
+
const config = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
2715
3217
|
if (typeof config.aliasedTo === "string") {
|
|
2716
3218
|
aliasedTo = config.aliasedTo;
|
|
2717
3219
|
}
|
|
@@ -2720,7 +3222,7 @@ var AnalyticsService = class {
|
|
|
2720
3222
|
if (aliasedTo === walletAddress) return;
|
|
2721
3223
|
this.posthog.alias({ distinctId: walletAddress, alias: anonId });
|
|
2722
3224
|
try {
|
|
2723
|
-
const config = existsSync5(configPath) ? JSON.parse(
|
|
3225
|
+
const config = existsSync5(configPath) ? JSON.parse(readFileSync8(configPath, "utf8")) : {};
|
|
2724
3226
|
writeFileSync4(
|
|
2725
3227
|
configPath,
|
|
2726
3228
|
JSON.stringify({ ...config, aliasedTo: walletAddress }, null, 2)
|
|
@@ -2763,7 +3265,7 @@ var AnalyticsService = class {
|
|
|
2763
3265
|
};
|
|
2764
3266
|
|
|
2765
3267
|
// src/services/state-service.ts
|
|
2766
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as
|
|
3268
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
2767
3269
|
import { join as join5 } from "path";
|
|
2768
3270
|
var StateService = class {
|
|
2769
3271
|
constructor(zeroDir) {
|
|
@@ -2778,7 +3280,7 @@ var StateService = class {
|
|
|
2778
3280
|
loadLastSearch = () => {
|
|
2779
3281
|
try {
|
|
2780
3282
|
if (!existsSync6(this.lastSearchPath)) return null;
|
|
2781
|
-
const raw =
|
|
3283
|
+
const raw = readFileSync9(this.lastSearchPath, "utf8");
|
|
2782
3284
|
return JSON.parse(raw);
|
|
2783
3285
|
} catch {
|
|
2784
3286
|
return null;
|
|
@@ -2831,7 +3333,7 @@ var getServices = (env) => {
|
|
|
2831
3333
|
if (!privateKey) {
|
|
2832
3334
|
try {
|
|
2833
3335
|
if (existsSync7(configPath)) {
|
|
2834
|
-
const config = JSON.parse(
|
|
3336
|
+
const config = JSON.parse(readFileSync10(configPath, "utf8"));
|
|
2835
3337
|
if (typeof config.privateKey === "string") {
|
|
2836
3338
|
privateKey = config.privateKey;
|
|
2837
3339
|
}
|
|
@@ -2843,7 +3345,7 @@ var getServices = (env) => {
|
|
|
2843
3345
|
let lowBalanceWarning = 1;
|
|
2844
3346
|
try {
|
|
2845
3347
|
if (existsSync7(configPath)) {
|
|
2846
|
-
const config = JSON.parse(
|
|
3348
|
+
const config = JSON.parse(readFileSync10(configPath, "utf8"));
|
|
2847
3349
|
if (typeof config.lowBalanceWarning === "number") {
|
|
2848
3350
|
lowBalanceWarning = config.lowBalanceWarning;
|
|
2849
3351
|
}
|