@zeroxyz/cli 0.0.38 → 0.0.40
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 +567 -186
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { homedir as
|
|
5
|
-
import { join as
|
|
4
|
+
import { homedir as homedir8 } from "os";
|
|
5
|
+
import { join as join9 } from "path";
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@zeroxyz/cli",
|
|
10
|
-
version: "0.0.
|
|
10
|
+
version: "0.0.40",
|
|
11
11
|
type: "module",
|
|
12
12
|
bin: {
|
|
13
13
|
zero: "dist/index.js",
|
|
@@ -61,12 +61,124 @@ var package_default = {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
// src/app.ts
|
|
64
|
-
import { Command as
|
|
64
|
+
import { Command as Command13 } from "commander";
|
|
65
|
+
|
|
66
|
+
// src/commands/auth-command.ts
|
|
67
|
+
import { homedir } from "os";
|
|
68
|
+
import { join } from "path";
|
|
69
|
+
import { Command } from "commander";
|
|
70
|
+
import open from "open";
|
|
71
|
+
|
|
72
|
+
// src/util/secure-config.ts
|
|
73
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
74
|
+
var SECURE_DIR_MODE = 448;
|
|
75
|
+
var SECURE_FILE_MODE = 384;
|
|
76
|
+
var ensureSecureDir = (path) => {
|
|
77
|
+
mkdirSync(path, { recursive: true, mode: SECURE_DIR_MODE });
|
|
78
|
+
chmodSync(path, SECURE_DIR_MODE);
|
|
79
|
+
};
|
|
80
|
+
var writeSecureFile = (path, contents) => {
|
|
81
|
+
writeFileSync(path, contents, { mode: SECURE_FILE_MODE });
|
|
82
|
+
chmodSync(path, SECURE_FILE_MODE);
|
|
83
|
+
};
|
|
84
|
+
var readConfig = (path) => {
|
|
85
|
+
try {
|
|
86
|
+
const raw = readFileSync(path, "utf8");
|
|
87
|
+
return JSON.parse(raw);
|
|
88
|
+
} catch {
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/commands/auth-command.ts
|
|
94
|
+
var getConfigPath = () => join(homedir(), ".zero", "config.json");
|
|
95
|
+
var authLoginCommand = (appContext) => new Command("login").description("Sign in to Zero").option("--no-open", "Do not open the browser automatically").action(async (opts) => {
|
|
96
|
+
const { apiService } = appContext.services;
|
|
97
|
+
const start = await apiService.startDeviceLogin();
|
|
98
|
+
const url = `${start.verificationUri}?code=${start.userCode}`;
|
|
99
|
+
process.stdout.write(
|
|
100
|
+
`Open this URL to authorize:
|
|
101
|
+
${url}
|
|
102
|
+
User code: ${start.userCode}
|
|
103
|
+
`
|
|
104
|
+
);
|
|
105
|
+
if (opts.open) {
|
|
106
|
+
await open(url).catch(() => {
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
while (Date.now() < start.expiresAt) {
|
|
110
|
+
await new Promise(
|
|
111
|
+
(r) => setTimeout(r, start.pollInterval * 1e3)
|
|
112
|
+
);
|
|
113
|
+
const result = await apiService.pollDeviceLogin(start.deviceCode);
|
|
114
|
+
if (result.status === "pending") {
|
|
115
|
+
process.stdout.write(".");
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (result.status === "expired") {
|
|
119
|
+
process.stderr.write(
|
|
120
|
+
"\nDevice code expired. Run `zero auth login` again.\n"
|
|
121
|
+
);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const path = getConfigPath();
|
|
126
|
+
const current = readConfig(path);
|
|
127
|
+
const next = {
|
|
128
|
+
...current,
|
|
129
|
+
session: {
|
|
130
|
+
userId: result.user.id,
|
|
131
|
+
authMethod: "workos",
|
|
132
|
+
accessToken: result.accessToken,
|
|
133
|
+
refreshToken: result.refreshToken
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
writeSecureFile(path, JSON.stringify(next, null, 2));
|
|
137
|
+
process.stdout.write(
|
|
138
|
+
`
|
|
139
|
+
Signed in as ${result.user.email ?? result.user.id}
|
|
140
|
+
`
|
|
141
|
+
);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
process.stderr.write(
|
|
145
|
+
"\nDevice code expired. Run `zero auth login` again.\n"
|
|
146
|
+
);
|
|
147
|
+
process.exitCode = 1;
|
|
148
|
+
});
|
|
149
|
+
var authLogoutCommand = (appContext) => new Command("logout").description("Sign out of Zero").action(async () => {
|
|
150
|
+
const { apiService } = appContext.services;
|
|
151
|
+
const path = getConfigPath();
|
|
152
|
+
const config = readConfig(path);
|
|
153
|
+
if (config.session) {
|
|
154
|
+
await apiService.logout(config.session.refreshToken);
|
|
155
|
+
}
|
|
156
|
+
const { session: _drop, ...rest } = config;
|
|
157
|
+
writeSecureFile(path, JSON.stringify(rest, null, 2));
|
|
158
|
+
process.stdout.write("Signed out.\n");
|
|
159
|
+
});
|
|
160
|
+
var authWhoamiCommand = (appContext) => new Command("whoami").description("Print the current Zero identity").action(async () => {
|
|
161
|
+
const { apiService } = appContext.services;
|
|
162
|
+
const me = await apiService.getMe();
|
|
163
|
+
process.stdout.write(
|
|
164
|
+
`${me.email ?? me.id}
|
|
165
|
+
uid: ${me.id}
|
|
166
|
+
authMethod: ${me.authMethod}
|
|
167
|
+
`
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
var authCommand = (appContext) => {
|
|
171
|
+
const cmd = new Command("auth").description("Authentication commands");
|
|
172
|
+
cmd.addCommand(authLoginCommand(appContext));
|
|
173
|
+
cmd.addCommand(authLogoutCommand(appContext));
|
|
174
|
+
cmd.addCommand(authWhoamiCommand(appContext), { hidden: true });
|
|
175
|
+
return cmd;
|
|
176
|
+
};
|
|
65
177
|
|
|
66
178
|
// src/commands/bug-report-command.ts
|
|
67
179
|
import { createHash as createHash2 } from "crypto";
|
|
68
|
-
import { readFileSync } from "fs";
|
|
69
|
-
import { Command } from "commander";
|
|
180
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
181
|
+
import { Command as Command2 } from "commander";
|
|
70
182
|
import { z as z2 } from "zod";
|
|
71
183
|
|
|
72
184
|
// src/services/api-service.ts
|
|
@@ -93,7 +205,11 @@ var searchResultSchema = z.object({
|
|
|
93
205
|
reviewCount: z.number().optional(),
|
|
94
206
|
rating: ratingSchema,
|
|
95
207
|
availabilityStatus: z.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional(),
|
|
96
|
-
displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional()
|
|
208
|
+
displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
|
|
209
|
+
// Pass-through: API stamps this on Zero-published / withzero.{ai,xyz}
|
|
210
|
+
// services so `zero search --json` consumers can render their own
|
|
211
|
+
// provenance UI. CLI doesn't render a badge today.
|
|
212
|
+
isFirstParty: z.boolean().optional().default(false)
|
|
97
213
|
});
|
|
98
214
|
var searchResponseSchema = z.object({
|
|
99
215
|
searchId: z.string(),
|
|
@@ -146,7 +262,9 @@ var capabilityResponseSchema = z.object({
|
|
|
146
262
|
displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
|
|
147
263
|
activationCount: z.number().optional(),
|
|
148
264
|
lastUsedAt: z.string().nullable().optional(),
|
|
149
|
-
lastSuccessfullyRanAt: z.string().nullable().optional()
|
|
265
|
+
lastSuccessfullyRanAt: z.string().nullable().optional(),
|
|
266
|
+
// Pass-through (see searchResultSchema). Available on `zero get --json`.
|
|
267
|
+
isFirstParty: z.boolean().optional().default(false)
|
|
150
268
|
});
|
|
151
269
|
var createRunResponseSchema = z.object({
|
|
152
270
|
runId: z.string()
|
|
@@ -225,18 +343,64 @@ var listRunsResponseSchema = z.object({
|
|
|
225
343
|
runs: z.array(runListItemSchema),
|
|
226
344
|
nextCursor: z.string().nullable()
|
|
227
345
|
});
|
|
346
|
+
var userDtoSchema = z.object({
|
|
347
|
+
id: z.string(),
|
|
348
|
+
email: z.string().nullable(),
|
|
349
|
+
authMethod: z.string(),
|
|
350
|
+
createdAt: z.union([z.string(), z.date()]).optional(),
|
|
351
|
+
lastLoginAt: z.union([z.string(), z.date()]).nullable().optional()
|
|
352
|
+
});
|
|
353
|
+
var userWalletDtoSchema = z.object({
|
|
354
|
+
walletAddress: z.string(),
|
|
355
|
+
source: z.enum(["self_custody", "privy_embedded"]),
|
|
356
|
+
isPrimary: z.boolean(),
|
|
357
|
+
linkedAt: z.union([z.string(), z.date()])
|
|
358
|
+
});
|
|
359
|
+
var signResultSchema = z.object({
|
|
360
|
+
signature: z.string(),
|
|
361
|
+
walletAddress: z.string()
|
|
362
|
+
});
|
|
363
|
+
var deviceStartResultSchema = z.object({
|
|
364
|
+
deviceCode: z.string(),
|
|
365
|
+
userCode: z.string(),
|
|
366
|
+
verificationUri: z.string(),
|
|
367
|
+
pollInterval: z.number(),
|
|
368
|
+
expiresAt: z.number()
|
|
369
|
+
});
|
|
370
|
+
var devicePollResponseSchema = z.union([
|
|
371
|
+
z.object({ error: z.literal("authorization_pending") }),
|
|
372
|
+
z.object({ error: z.literal("expired_token") }),
|
|
373
|
+
z.object({
|
|
374
|
+
accessToken: z.string(),
|
|
375
|
+
refreshToken: z.string(),
|
|
376
|
+
expiresIn: z.number(),
|
|
377
|
+
user: userDtoSchema
|
|
378
|
+
})
|
|
379
|
+
]);
|
|
380
|
+
var jsonStringifyBigintSafe = (value) => JSON.stringify(
|
|
381
|
+
value,
|
|
382
|
+
(_key, v) => typeof v === "bigint" ? v.toString() : v
|
|
383
|
+
);
|
|
228
384
|
var buildCanonicalMessage = (method, path, body, timestamp, nonce) => {
|
|
229
385
|
const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
|
|
230
386
|
return `${method}:${path}:${bodyHash}:${timestamp}:${nonce}`;
|
|
231
387
|
};
|
|
232
388
|
var ApiService = class _ApiService {
|
|
233
|
-
constructor(baseUrl, account) {
|
|
389
|
+
constructor(baseUrl, account, credentials = { kind: "none" }, onSessionRefreshed = async () => {
|
|
390
|
+
}) {
|
|
234
391
|
this.baseUrl = baseUrl;
|
|
235
392
|
this.account = account;
|
|
236
393
|
this.walletAddress = this.account?.address ?? null;
|
|
394
|
+
this.credentials = credentials;
|
|
395
|
+
this.onSessionRefreshed = onSessionRefreshed;
|
|
237
396
|
}
|
|
238
397
|
walletAddress;
|
|
239
398
|
account;
|
|
399
|
+
credentials;
|
|
400
|
+
onSessionRefreshed;
|
|
401
|
+
setWalletAddress = (address) => {
|
|
402
|
+
this.walletAddress = address;
|
|
403
|
+
};
|
|
240
404
|
withAccount = (account) => new _ApiService(this.baseUrl, account);
|
|
241
405
|
signRequest = async (method, path, body) => {
|
|
242
406
|
if (!this.account) throw new Error("No private key configured");
|
|
@@ -251,21 +415,75 @@ var ApiService = class _ApiService {
|
|
|
251
415
|
"x-zero-signature": signature
|
|
252
416
|
};
|
|
253
417
|
};
|
|
254
|
-
request
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
418
|
+
// Auth modes per request. `this.account` is only ever the local private key
|
|
419
|
+
// (the managed Privy proxy lives on PaymentService, never here), so an
|
|
420
|
+
// account here means a BYO key is present.
|
|
421
|
+
// - "default": session JWT takes precedence over EIP-191. If signed in, the
|
|
422
|
+
// JWT is the identity the API trusts; otherwise fall back to a wallet
|
|
423
|
+
// signature, else anonymous. Used by session-scoped endpoints
|
|
424
|
+
// (/users/me, sign-*, wallets) and read endpoints.
|
|
425
|
+
// - "wallet-attributed": the action is attributed to a wallet (runs,
|
|
426
|
+
// reviews, bug reports). Prefer the local private key (EIP-191) so the run
|
|
427
|
+
// is attributed to the wallet that actually paid; when there's no local
|
|
428
|
+
// key (managed user) fall back to the session Bearer and let the server
|
|
429
|
+
// resolve the wallet from the authenticated user. We never send both — a
|
|
430
|
+
// present session would make the server skip EIP-191 verification, so a
|
|
431
|
+
// Bearer + wallet headers combo must never happen.
|
|
432
|
+
buildHeaders = async (method, path, bodyStr, auth) => {
|
|
433
|
+
const base2 = {
|
|
258
434
|
"content-type": "application/json"
|
|
259
435
|
};
|
|
436
|
+
if (auth === "wallet-attributed" && this.account) {
|
|
437
|
+
const walletHeaders = await this.signRequest(method, path, bodyStr);
|
|
438
|
+
return { ...base2, ...walletHeaders };
|
|
439
|
+
}
|
|
440
|
+
if (this.credentials.kind === "session") {
|
|
441
|
+
base2.authorization = `Bearer ${this.credentials.accessToken}`;
|
|
442
|
+
return base2;
|
|
443
|
+
}
|
|
260
444
|
if (this.account) {
|
|
261
445
|
const walletHeaders = await this.signRequest(method, path, bodyStr);
|
|
262
|
-
|
|
446
|
+
return { ...base2, ...walletHeaders };
|
|
263
447
|
}
|
|
264
|
-
|
|
448
|
+
return base2;
|
|
449
|
+
};
|
|
450
|
+
// Rotates the in-memory tokens and fires onSessionRefreshed so the
|
|
451
|
+
// caller can persist them. Returns false on any refresh failure — the
|
|
452
|
+
// outer request then propagates the original 401 to the caller.
|
|
453
|
+
tryRefreshSession = async () => {
|
|
454
|
+
if (this.credentials.kind !== "session") return false;
|
|
455
|
+
const { refreshToken } = this.credentials;
|
|
456
|
+
const resp = await fetch(`${this.baseUrl}/v1/auth/refresh`, {
|
|
457
|
+
method: "POST",
|
|
458
|
+
headers: { "content-type": "application/json" },
|
|
459
|
+
body: JSON.stringify({ refreshToken })
|
|
460
|
+
});
|
|
461
|
+
if (!resp.ok) return false;
|
|
462
|
+
const body = await resp.json();
|
|
463
|
+
this.credentials = {
|
|
464
|
+
...this.credentials,
|
|
465
|
+
accessToken: body.accessToken,
|
|
466
|
+
refreshToken: body.refreshToken
|
|
467
|
+
};
|
|
468
|
+
await this.onSessionRefreshed(body);
|
|
469
|
+
return true;
|
|
470
|
+
};
|
|
471
|
+
request = async (method, path, body, opts = {}) => {
|
|
472
|
+
const url = `${this.baseUrl}${path}`;
|
|
473
|
+
const bodyStr = body ? jsonStringifyBigintSafe(body) : void 0;
|
|
474
|
+
const auth = opts.auth ?? "default";
|
|
475
|
+
const makeInit = async () => ({
|
|
265
476
|
method,
|
|
266
|
-
headers,
|
|
477
|
+
headers: await this.buildHeaders(method, path, bodyStr, auth),
|
|
267
478
|
body: bodyStr
|
|
268
479
|
});
|
|
480
|
+
let response = await fetch(url, await makeInit());
|
|
481
|
+
if (response.status === 401 && this.credentials.kind === "session") {
|
|
482
|
+
const refreshed = await this.tryRefreshSession();
|
|
483
|
+
if (refreshed) {
|
|
484
|
+
response = await fetch(url, await makeInit());
|
|
485
|
+
}
|
|
486
|
+
}
|
|
269
487
|
if (!response.ok) {
|
|
270
488
|
const errorBody = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
271
489
|
throw new Error(
|
|
@@ -274,6 +492,44 @@ var ApiService = class _ApiService {
|
|
|
274
492
|
}
|
|
275
493
|
return response.json();
|
|
276
494
|
};
|
|
495
|
+
startDeviceLogin = async () => {
|
|
496
|
+
const resp = await fetch(`${this.baseUrl}/v1/auth/device/start`, {
|
|
497
|
+
method: "POST"
|
|
498
|
+
});
|
|
499
|
+
if (!resp.ok) throw new Error(`device_start_${resp.status}`);
|
|
500
|
+
return deviceStartResultSchema.parse(await resp.json());
|
|
501
|
+
};
|
|
502
|
+
pollDeviceLogin = async (deviceCode) => {
|
|
503
|
+
const resp = await fetch(`${this.baseUrl}/v1/auth/device/poll`, {
|
|
504
|
+
method: "POST",
|
|
505
|
+
headers: { "content-type": "application/json" },
|
|
506
|
+
body: JSON.stringify({ deviceCode })
|
|
507
|
+
});
|
|
508
|
+
if (!resp.ok) throw new Error(`device_poll_${resp.status}`);
|
|
509
|
+
const parsed = devicePollResponseSchema.parse(await resp.json());
|
|
510
|
+
if ("error" in parsed) {
|
|
511
|
+
return parsed.error === "authorization_pending" ? { status: "pending" } : { status: "expired" };
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
status: "ok",
|
|
515
|
+
accessToken: parsed.accessToken,
|
|
516
|
+
refreshToken: parsed.refreshToken,
|
|
517
|
+
expiresIn: parsed.expiresIn,
|
|
518
|
+
user: parsed.user
|
|
519
|
+
};
|
|
520
|
+
};
|
|
521
|
+
logout = async (refreshToken) => {
|
|
522
|
+
await fetch(`${this.baseUrl}/v1/auth/logout`, {
|
|
523
|
+
method: "POST",
|
|
524
|
+
headers: { "content-type": "application/json" },
|
|
525
|
+
body: JSON.stringify({ refreshToken })
|
|
526
|
+
}).catch(() => {
|
|
527
|
+
});
|
|
528
|
+
};
|
|
529
|
+
getMe = async () => {
|
|
530
|
+
const json = await this.request("GET", "/v1/users/me");
|
|
531
|
+
return userDtoSchema.parse(json);
|
|
532
|
+
};
|
|
277
533
|
search = async (options) => {
|
|
278
534
|
const json = await this.request("POST", "/v1/search", options);
|
|
279
535
|
return searchResponseSchema.parse(json);
|
|
@@ -287,7 +543,9 @@ var ApiService = class _ApiService {
|
|
|
287
543
|
return capabilityResponseSchema.parse(json);
|
|
288
544
|
};
|
|
289
545
|
createRun = async (data) => {
|
|
290
|
-
const json = await this.request("POST", "/v1/runs", data
|
|
546
|
+
const json = await this.request("POST", "/v1/runs", data, {
|
|
547
|
+
auth: "wallet-attributed"
|
|
548
|
+
});
|
|
291
549
|
return createRunResponseSchema.parse(json);
|
|
292
550
|
};
|
|
293
551
|
listRuns = async (params = {}) => {
|
|
@@ -297,19 +555,32 @@ var ApiService = class _ApiService {
|
|
|
297
555
|
if (params.limit) qs.set("limit", String(params.limit));
|
|
298
556
|
if (params.cursor) qs.set("cursor", params.cursor);
|
|
299
557
|
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
300
|
-
const json = await this.request("GET", `/v1/runs${suffix}
|
|
558
|
+
const json = await this.request("GET", `/v1/runs${suffix}`, void 0, {
|
|
559
|
+
auth: "wallet-attributed"
|
|
560
|
+
});
|
|
301
561
|
return listRunsResponseSchema.parse(json);
|
|
302
562
|
};
|
|
303
563
|
createReview = async (data) => {
|
|
304
|
-
const json = await this.request("POST", "/v1/reviews", data
|
|
564
|
+
const json = await this.request("POST", "/v1/reviews", data, {
|
|
565
|
+
auth: "wallet-attributed"
|
|
566
|
+
});
|
|
305
567
|
return createReviewResponseSchema.parse(json);
|
|
306
568
|
};
|
|
307
569
|
createReviewsBatch = async (reviews) => {
|
|
308
|
-
const json = await this.request(
|
|
570
|
+
const json = await this.request(
|
|
571
|
+
"POST",
|
|
572
|
+
"/v1/reviews/batch",
|
|
573
|
+
{ reviews },
|
|
574
|
+
{
|
|
575
|
+
auth: "wallet-attributed"
|
|
576
|
+
}
|
|
577
|
+
);
|
|
309
578
|
return batchReviewResponseSchema.parse(json);
|
|
310
579
|
};
|
|
311
580
|
createBugReport = async (data) => {
|
|
312
|
-
const json = await this.request("POST", "/v1/bug-reports", data
|
|
581
|
+
const json = await this.request("POST", "/v1/bug-reports", data, {
|
|
582
|
+
auth: "wallet-attributed"
|
|
583
|
+
});
|
|
313
584
|
return createBugReportResponseSchema.parse(json);
|
|
314
585
|
};
|
|
315
586
|
getFundingUrl = async (amount, provider = "coinbase") => {
|
|
@@ -326,6 +597,44 @@ var ApiService = class _ApiService {
|
|
|
326
597
|
return null;
|
|
327
598
|
}
|
|
328
599
|
};
|
|
600
|
+
getWallets = async () => {
|
|
601
|
+
const json = await this.request("GET", "/v1/users/me/wallets");
|
|
602
|
+
return z.array(userWalletDtoSchema).parse(json);
|
|
603
|
+
};
|
|
604
|
+
provisionWallet = async () => {
|
|
605
|
+
const json = await this.request("POST", "/v1/users/me/wallets/provision");
|
|
606
|
+
return userWalletDtoSchema.parse(json);
|
|
607
|
+
};
|
|
608
|
+
signTypedDataRemote = async (typedData) => {
|
|
609
|
+
const json = await this.request("POST", "/v1/users/me/sign-typed-data", {
|
|
610
|
+
typedData
|
|
611
|
+
});
|
|
612
|
+
const parsed = signResultSchema.parse(json);
|
|
613
|
+
return {
|
|
614
|
+
signature: parsed.signature,
|
|
615
|
+
walletAddress: parsed.walletAddress
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
signMessageRemote = async (message) => {
|
|
619
|
+
const json = await this.request("POST", "/v1/users/me/sign-message", {
|
|
620
|
+
message
|
|
621
|
+
});
|
|
622
|
+
const parsed = signResultSchema.parse(json);
|
|
623
|
+
return {
|
|
624
|
+
signature: parsed.signature,
|
|
625
|
+
walletAddress: parsed.walletAddress
|
|
626
|
+
};
|
|
627
|
+
};
|
|
628
|
+
signTransactionRemote = async (input) => {
|
|
629
|
+
const json = await this.request("POST", "/v1/users/me/sign-transaction", {
|
|
630
|
+
unsignedTransaction: input.unsignedTransaction
|
|
631
|
+
});
|
|
632
|
+
const parsed = signResultSchema.parse(json);
|
|
633
|
+
return {
|
|
634
|
+
signature: parsed.signature,
|
|
635
|
+
walletAddress: parsed.walletAddress
|
|
636
|
+
};
|
|
637
|
+
};
|
|
329
638
|
};
|
|
330
639
|
|
|
331
640
|
// src/commands/bug-report-command.ts
|
|
@@ -351,7 +660,7 @@ var autoIdempotencyKey = (description, contextSeed) => {
|
|
|
351
660
|
const seed = `${description}|${contextSeed ?? ""}|${bucket}`;
|
|
352
661
|
return `auto-${createHash2("sha256").update(seed).digest("hex").slice(0, 16)}`;
|
|
353
662
|
};
|
|
354
|
-
var bugReportCommand = (appContext) => new
|
|
663
|
+
var bugReportCommand = (appContext) => new Command2("bug-report").description(
|
|
355
664
|
"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."
|
|
356
665
|
).addHelpText(
|
|
357
666
|
"after",
|
|
@@ -428,7 +737,7 @@ Categories the classifier picks from:
|
|
|
428
737
|
return;
|
|
429
738
|
}
|
|
430
739
|
if (options.fromFile) {
|
|
431
|
-
const contents =
|
|
740
|
+
const contents = readFileSync2(options.fromFile, "utf8");
|
|
432
741
|
const lines = contents.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
433
742
|
let ok = 0;
|
|
434
743
|
let failed = 0;
|
|
@@ -565,21 +874,21 @@ Bulk bug-report complete: ${ok} ok, ${failed} failed`
|
|
|
565
874
|
);
|
|
566
875
|
|
|
567
876
|
// src/commands/config-command.ts
|
|
568
|
-
import { existsSync, mkdirSync, readFileSync as
|
|
569
|
-
import { homedir } from "os";
|
|
570
|
-
import { join } from "path";
|
|
571
|
-
import { Command as
|
|
877
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
878
|
+
import { homedir as homedir2 } from "os";
|
|
879
|
+
import { join as join2 } from "path";
|
|
880
|
+
import { Command as Command3 } from "commander";
|
|
572
881
|
var VALID_KEYS = ["lowBalanceWarning", "auth", "telemetry"];
|
|
573
882
|
var loadConfig = (configPath) => {
|
|
574
883
|
try {
|
|
575
884
|
if (!existsSync(configPath)) return {};
|
|
576
|
-
return JSON.parse(
|
|
885
|
+
return JSON.parse(readFileSync3(configPath, "utf8"));
|
|
577
886
|
} catch {
|
|
578
887
|
return {};
|
|
579
888
|
}
|
|
580
889
|
};
|
|
581
|
-
var configCommand = (_appContext) => new
|
|
582
|
-
const configPath =
|
|
890
|
+
var configCommand = (_appContext) => new Command3("config").description("View or update CLI configuration").option("--set <keyValue>", "Set a config value (key=value)").action((options) => {
|
|
891
|
+
const configPath = join2(homedir2(), ".zero", "config.json");
|
|
583
892
|
if (!options.set) {
|
|
584
893
|
const config2 = loadConfig(configPath);
|
|
585
894
|
console.log(JSON.stringify(config2, null, 2));
|
|
@@ -609,16 +918,16 @@ var configCommand = (_appContext) => new Command2("config").description("View or
|
|
|
609
918
|
}
|
|
610
919
|
const config = loadConfig(configPath);
|
|
611
920
|
config[key] = value;
|
|
612
|
-
|
|
613
|
-
|
|
921
|
+
mkdirSync2(join2(homedir2(), ".zero"), { recursive: true });
|
|
922
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2));
|
|
614
923
|
console.log(`Set ${key} = ${JSON.stringify(value)}`);
|
|
615
924
|
});
|
|
616
925
|
|
|
617
926
|
// src/commands/fetch-command.ts
|
|
618
927
|
import { createHash as createHash3 } from "crypto";
|
|
619
|
-
import { readFileSync as
|
|
928
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
620
929
|
import { resolve as resolvePath } from "path";
|
|
621
|
-
import { Command as
|
|
930
|
+
import { Command as Command4 } from "commander";
|
|
622
931
|
import { formatUnits as formatUnits2 } from "viem";
|
|
623
932
|
|
|
624
933
|
// src/services/payment-service.ts
|
|
@@ -922,6 +1231,11 @@ var PaymentService = class {
|
|
|
922
1231
|
`Insufficient pathUSD on Tempo testnet: have ${formatUnits(tempoBalance, 6)}, need ${capturedAmount}. Fund your wallet with the Tempo testnet faucet: https://docs.tempo.xyz/quickstart/faucet`
|
|
923
1232
|
);
|
|
924
1233
|
}
|
|
1234
|
+
if (this.config.managed) {
|
|
1235
|
+
throw new Error(
|
|
1236
|
+
`Insufficient USDC on Tempo: have ${formatUnits(tempoBalance, 6)}, need ${capturedAmount}. Managed wallets can't auto-bridge from Base yet \u2014 fund your Tempo wallet${this.account ? ` (${this.account.address})` : ""} directly, or set a local ZERO_PRIVATE_KEY to bridge.`
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
925
1239
|
await this.bridgeToTempo(requiredRaw, onProgress);
|
|
926
1240
|
}
|
|
927
1241
|
return capturedAmount;
|
|
@@ -1285,8 +1599,8 @@ var extractUpstreamErrorMessage = (body) => {
|
|
|
1285
1599
|
};
|
|
1286
1600
|
var resolveRequestBody = (rawData, readStdin2) => {
|
|
1287
1601
|
const fromFile = (spec) => {
|
|
1288
|
-
if (spec === "@-") return
|
|
1289
|
-
return
|
|
1602
|
+
if (spec === "@-") return readFileSync4(0);
|
|
1603
|
+
return readFileSync4(resolvePath(spec.slice(1)));
|
|
1290
1604
|
};
|
|
1291
1605
|
if (readStdin2 && rawData !== void 0) {
|
|
1292
1606
|
throw new Error(
|
|
@@ -1296,7 +1610,7 @@ var resolveRequestBody = (rawData, readStdin2) => {
|
|
|
1296
1610
|
let body;
|
|
1297
1611
|
let cap;
|
|
1298
1612
|
if (readStdin2) {
|
|
1299
|
-
body =
|
|
1613
|
+
body = readFileSync4(0);
|
|
1300
1614
|
cap = MAX_FILE_REQUEST_BODY_BYTES;
|
|
1301
1615
|
} else if (rawData?.startsWith("@")) {
|
|
1302
1616
|
body = fromFile(rawData);
|
|
@@ -1349,7 +1663,7 @@ var detectPaymentRequirement = async (response) => {
|
|
|
1349
1663
|
}
|
|
1350
1664
|
return { protocol: "unknown", raw: {} };
|
|
1351
1665
|
};
|
|
1352
|
-
var fetchCommand = (appContext) => new
|
|
1666
|
+
var fetchCommand = (appContext) => new Command4("fetch").description(
|
|
1353
1667
|
"Fetch a capability URL, handling 402 challenges automatically"
|
|
1354
1668
|
).argument(
|
|
1355
1669
|
"[url]",
|
|
@@ -1848,7 +2162,7 @@ var fetchCommand = (appContext) => new Command3("fetch").description(
|
|
|
1848
2162
|
);
|
|
1849
2163
|
|
|
1850
2164
|
// src/commands/get-command.ts
|
|
1851
|
-
import { Command as
|
|
2165
|
+
import { Command as Command5 } from "commander";
|
|
1852
2166
|
|
|
1853
2167
|
// src/util/format-price.ts
|
|
1854
2168
|
var centsToDollars = (cents) => {
|
|
@@ -2007,7 +2321,7 @@ var formatCapability = (capability) => {
|
|
|
2007
2321
|
lines.push(...buildTryItExample(capability));
|
|
2008
2322
|
return lines.join("\n");
|
|
2009
2323
|
};
|
|
2010
|
-
var getCommand = (appContext) => new
|
|
2324
|
+
var getCommand = (appContext) => new Command5("get").description(
|
|
2011
2325
|
"Get details for a capability by position from last search, or by slug"
|
|
2012
2326
|
).argument(
|
|
2013
2327
|
"<identifier>",
|
|
@@ -2082,15 +2396,15 @@ import {
|
|
|
2082
2396
|
existsSync as existsSync2,
|
|
2083
2397
|
mkdirSync as mkdirSync3,
|
|
2084
2398
|
readdirSync,
|
|
2085
|
-
readFileSync as
|
|
2399
|
+
readFileSync as readFileSync5,
|
|
2086
2400
|
rmSync,
|
|
2087
2401
|
statSync,
|
|
2088
2402
|
writeFileSync as writeFileSync3
|
|
2089
2403
|
} from "fs";
|
|
2090
|
-
import { homedir as
|
|
2091
|
-
import { dirname, join as
|
|
2404
|
+
import { homedir as homedir3 } from "os";
|
|
2405
|
+
import { dirname, join as join3, relative } from "path";
|
|
2092
2406
|
import { fileURLToPath } from "url";
|
|
2093
|
-
import { Command as
|
|
2407
|
+
import { Command as Command6 } from "commander";
|
|
2094
2408
|
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
2095
2409
|
|
|
2096
2410
|
// src/util/install-banner.ts
|
|
@@ -2222,19 +2536,6 @@ var printReadyFooter = () => {
|
|
|
2222
2536
|
return lines.join("\n");
|
|
2223
2537
|
};
|
|
2224
2538
|
|
|
2225
|
-
// src/util/secure-config.ts
|
|
2226
|
-
import { chmodSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2227
|
-
var SECURE_DIR_MODE = 448;
|
|
2228
|
-
var SECURE_FILE_MODE = 384;
|
|
2229
|
-
var ensureSecureDir = (path) => {
|
|
2230
|
-
mkdirSync2(path, { recursive: true, mode: SECURE_DIR_MODE });
|
|
2231
|
-
chmodSync(path, SECURE_DIR_MODE);
|
|
2232
|
-
};
|
|
2233
|
-
var writeSecureFile = (path, contents) => {
|
|
2234
|
-
writeFileSync2(path, contents, { mode: SECURE_FILE_MODE });
|
|
2235
|
-
chmodSync(path, SECURE_FILE_MODE);
|
|
2236
|
-
};
|
|
2237
|
-
|
|
2238
2539
|
// src/commands/init-command.ts
|
|
2239
2540
|
var AGENT_TOOLS = [
|
|
2240
2541
|
{ name: "Claude Code", detectDir: ".claude", skillsDir: ".claude/skills" },
|
|
@@ -2249,7 +2550,7 @@ var AGENT_TOOLS = [
|
|
|
2249
2550
|
var findResourceDir = (startDir, resourceName) => {
|
|
2250
2551
|
let current = startDir;
|
|
2251
2552
|
while (true) {
|
|
2252
|
-
const candidate =
|
|
2553
|
+
const candidate = join3(current, resourceName);
|
|
2253
2554
|
if (existsSync2(candidate)) {
|
|
2254
2555
|
return candidate;
|
|
2255
2556
|
}
|
|
@@ -2268,7 +2569,7 @@ var getCliModuleDir = () => {
|
|
|
2268
2569
|
}
|
|
2269
2570
|
return __dirname;
|
|
2270
2571
|
};
|
|
2271
|
-
var sha256File = (filePath) => createHash4("sha256").update(
|
|
2572
|
+
var sha256File = (filePath) => createHash4("sha256").update(readFileSync5(filePath)).digest("hex");
|
|
2272
2573
|
var verifyFileCopy = (src, dest) => {
|
|
2273
2574
|
if (!existsSync2(dest)) return false;
|
|
2274
2575
|
return sha256File(src) === sha256File(dest);
|
|
@@ -2276,7 +2577,7 @@ var verifyFileCopy = (src, dest) => {
|
|
|
2276
2577
|
var collectAllFiles = (dir) => {
|
|
2277
2578
|
const files = [];
|
|
2278
2579
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
2279
|
-
const fullPath =
|
|
2580
|
+
const fullPath = join3(dir, entry.name);
|
|
2280
2581
|
if (entry.isDirectory()) {
|
|
2281
2582
|
files.push(...collectAllFiles(fullPath));
|
|
2282
2583
|
} else {
|
|
@@ -2286,7 +2587,7 @@ var collectAllFiles = (dir) => {
|
|
|
2286
2587
|
return files;
|
|
2287
2588
|
};
|
|
2288
2589
|
var copyFile = (src, dest) => {
|
|
2289
|
-
writeFileSync3(dest,
|
|
2590
|
+
writeFileSync3(dest, readFileSync5(src));
|
|
2290
2591
|
try {
|
|
2291
2592
|
chmodSync2(dest, statSync(src).mode);
|
|
2292
2593
|
} catch {
|
|
@@ -2295,8 +2596,8 @@ var copyFile = (src, dest) => {
|
|
|
2295
2596
|
var copyDirRecursive = (src, dest) => {
|
|
2296
2597
|
mkdirSync3(dest, { recursive: true });
|
|
2297
2598
|
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
2298
|
-
const srcPath =
|
|
2299
|
-
const destPath =
|
|
2599
|
+
const srcPath = join3(src, entry.name);
|
|
2600
|
+
const destPath = join3(dest, entry.name);
|
|
2300
2601
|
if (entry.isDirectory()) {
|
|
2301
2602
|
copyDirRecursive(srcPath, destPath);
|
|
2302
2603
|
} else {
|
|
@@ -2305,22 +2606,22 @@ var copyDirRecursive = (src, dest) => {
|
|
|
2305
2606
|
}
|
|
2306
2607
|
};
|
|
2307
2608
|
var installHook = (home, verbose = false) => {
|
|
2308
|
-
const claudeDir =
|
|
2609
|
+
const claudeDir = join3(home, ".claude");
|
|
2309
2610
|
if (!existsSync2(claudeDir)) {
|
|
2310
2611
|
if (verbose) {
|
|
2311
2612
|
stepInfo(`~/.claude not found \u2014 Claude Code not installed, skipping`);
|
|
2312
2613
|
}
|
|
2313
2614
|
return false;
|
|
2314
2615
|
}
|
|
2315
|
-
const zeroHooksDir =
|
|
2616
|
+
const zeroHooksDir = join3(home, ".zero", "hooks");
|
|
2316
2617
|
mkdirSync3(zeroHooksDir, { recursive: true });
|
|
2317
2618
|
if (verbose) stepInfo(`staged hook dir at ${zeroHooksDir}`);
|
|
2318
2619
|
const hookFiles = ["auto-approve-zero.sh", "zero-context.sh"];
|
|
2319
2620
|
const hookDests = {};
|
|
2320
2621
|
const hooksSourceDir = findResourceDir(getCliModuleDir(), "hooks");
|
|
2321
2622
|
for (const hookFile of hookFiles) {
|
|
2322
|
-
const hookSource =
|
|
2323
|
-
const hookDest =
|
|
2623
|
+
const hookSource = join3(hooksSourceDir, hookFile);
|
|
2624
|
+
const hookDest = join3(zeroHooksDir, hookFile);
|
|
2324
2625
|
copyFile(hookSource, hookDest);
|
|
2325
2626
|
chmodSync2(hookDest, 493);
|
|
2326
2627
|
if (!verifyFileCopy(hookSource, hookDest)) {
|
|
@@ -2331,14 +2632,14 @@ var installHook = (home, verbose = false) => {
|
|
|
2331
2632
|
hookDests[hookFile] = hookDest;
|
|
2332
2633
|
if (verbose) stepInfo(`copied ${hookFile} \u2192 ${hookDest} (verified)`);
|
|
2333
2634
|
}
|
|
2334
|
-
const settingsPath =
|
|
2635
|
+
const settingsPath = join3(claudeDir, "settings.json");
|
|
2335
2636
|
let settings = {};
|
|
2336
2637
|
let settingsExisted = false;
|
|
2337
2638
|
let settingsCorrupted = false;
|
|
2338
2639
|
if (existsSync2(settingsPath)) {
|
|
2339
2640
|
settingsExisted = true;
|
|
2340
2641
|
try {
|
|
2341
|
-
settings = JSON.parse(
|
|
2642
|
+
settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
|
|
2342
2643
|
} catch {
|
|
2343
2644
|
settingsCorrupted = true;
|
|
2344
2645
|
}
|
|
@@ -2436,7 +2737,7 @@ var CONFLICTING_SKILL_PATTERNS = ["zam"];
|
|
|
2436
2737
|
var findConflictingSkills = (home) => {
|
|
2437
2738
|
const found = [];
|
|
2438
2739
|
for (const tool of AGENT_TOOLS) {
|
|
2439
|
-
const toolSkillsPath =
|
|
2740
|
+
const toolSkillsPath = join3(home, tool.skillsDir);
|
|
2440
2741
|
if (!existsSync2(toolSkillsPath)) continue;
|
|
2441
2742
|
const entries = readdirSync(toolSkillsPath, { withFileTypes: true });
|
|
2442
2743
|
for (const entry of entries) {
|
|
@@ -2445,7 +2746,7 @@ var findConflictingSkills = (home) => {
|
|
|
2445
2746
|
if (CONFLICTING_SKILL_PATTERNS.some((p) => lower.includes(p))) {
|
|
2446
2747
|
found.push({
|
|
2447
2748
|
tool: tool.name,
|
|
2448
|
-
skillPath:
|
|
2749
|
+
skillPath: join3(toolSkillsPath, entry.name),
|
|
2449
2750
|
skillName: entry.name
|
|
2450
2751
|
});
|
|
2451
2752
|
}
|
|
@@ -2473,7 +2774,7 @@ var installSkills = (home, verbose = false) => {
|
|
|
2473
2774
|
const installed = [];
|
|
2474
2775
|
const errors = [];
|
|
2475
2776
|
for (const tool of AGENT_TOOLS) {
|
|
2476
|
-
const toolDetectPath =
|
|
2777
|
+
const toolDetectPath = join3(home, tool.detectDir);
|
|
2477
2778
|
if (!existsSync2(toolDetectPath)) {
|
|
2478
2779
|
if (verbose) {
|
|
2479
2780
|
stepInfo(
|
|
@@ -2488,16 +2789,16 @@ var installSkills = (home, verbose = false) => {
|
|
|
2488
2789
|
);
|
|
2489
2790
|
}
|
|
2490
2791
|
try {
|
|
2491
|
-
const toolSkillsPath =
|
|
2792
|
+
const toolSkillsPath = join3(home, tool.skillsDir);
|
|
2492
2793
|
mkdirSync3(toolSkillsPath, { recursive: true });
|
|
2493
2794
|
for (const skillDir of skillDirs) {
|
|
2494
|
-
const src =
|
|
2495
|
-
const dest =
|
|
2795
|
+
const src = join3(skillsSourceDir, skillDir);
|
|
2796
|
+
const dest = join3(toolSkillsPath, skillDir);
|
|
2496
2797
|
const existed = existsSync2(dest);
|
|
2497
2798
|
copyDirRecursive(src, dest);
|
|
2498
2799
|
for (const srcFile of collectAllFiles(src)) {
|
|
2499
2800
|
const relPath = relative(src, srcFile);
|
|
2500
|
-
const destFile =
|
|
2801
|
+
const destFile = join3(dest, relPath);
|
|
2501
2802
|
if (!verifyFileCopy(srcFile, destFile)) {
|
|
2502
2803
|
throw new Error(
|
|
2503
2804
|
`Integrity check failed: ${destFile} does not match source`
|
|
@@ -2528,15 +2829,15 @@ var runInit = async (appContext, options = {}) => {
|
|
|
2528
2829
|
let currentStep = "wallet";
|
|
2529
2830
|
try {
|
|
2530
2831
|
printZeroBanner();
|
|
2531
|
-
const home =
|
|
2532
|
-
const zeroDir =
|
|
2533
|
-
const configPath =
|
|
2832
|
+
const home = homedir3();
|
|
2833
|
+
const zeroDir = join3(home, ".zero");
|
|
2834
|
+
const configPath = join3(zeroDir, "config.json");
|
|
2534
2835
|
let walletCreated = false;
|
|
2535
2836
|
let walletAddress = null;
|
|
2536
2837
|
const walletExists = (() => {
|
|
2537
2838
|
if (!existsSync2(configPath)) return false;
|
|
2538
2839
|
try {
|
|
2539
|
-
const existing = JSON.parse(
|
|
2840
|
+
const existing = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
2540
2841
|
return !!existing.privateKey;
|
|
2541
2842
|
} catch {
|
|
2542
2843
|
return false;
|
|
@@ -2546,7 +2847,7 @@ var runInit = async (appContext, options = {}) => {
|
|
|
2546
2847
|
const privateKey = generatePrivateKey();
|
|
2547
2848
|
const account = privateKeyToAccount(privateKey);
|
|
2548
2849
|
ensureSecureDir(zeroDir);
|
|
2549
|
-
const existing = existsSync2(configPath) ? JSON.parse(
|
|
2850
|
+
const existing = existsSync2(configPath) ? JSON.parse(readFileSync5(configPath, "utf8")) : {};
|
|
2550
2851
|
writeSecureFile(
|
|
2551
2852
|
configPath,
|
|
2552
2853
|
JSON.stringify(
|
|
@@ -2569,7 +2870,7 @@ var runInit = async (appContext, options = {}) => {
|
|
|
2569
2870
|
}
|
|
2570
2871
|
} else {
|
|
2571
2872
|
try {
|
|
2572
|
-
const existing = JSON.parse(
|
|
2873
|
+
const existing = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
2573
2874
|
const account = privateKeyToAccount(existing.privateKey);
|
|
2574
2875
|
walletAddress = account.address;
|
|
2575
2876
|
} catch {
|
|
@@ -2590,7 +2891,7 @@ var runInit = async (appContext, options = {}) => {
|
|
|
2590
2891
|
let hookInstalled = false;
|
|
2591
2892
|
let hookError = null;
|
|
2592
2893
|
for (const tool of AGENT_TOOLS) {
|
|
2593
|
-
if (existsSync2(
|
|
2894
|
+
if (existsSync2(join3(home, tool.detectDir))) {
|
|
2594
2895
|
agentsDetected.push(tool.name);
|
|
2595
2896
|
}
|
|
2596
2897
|
}
|
|
@@ -2700,14 +3001,14 @@ To remove them, run: ${color.cyan("zero init cleanup")}`
|
|
|
2700
3001
|
throw err;
|
|
2701
3002
|
}
|
|
2702
3003
|
};
|
|
2703
|
-
var initCommand = (appContext) => new
|
|
3004
|
+
var initCommand = (appContext) => new Command6("init").description("Initialize Zero CLI for usage").option("--force", "Overwrite existing configuration").option(
|
|
2704
3005
|
"-v, --verbose",
|
|
2705
3006
|
"Explain why each install step was taken or skipped"
|
|
2706
3007
|
).action(async (options) => {
|
|
2707
3008
|
await runInit(appContext, options);
|
|
2708
3009
|
}).addCommand(
|
|
2709
|
-
new
|
|
2710
|
-
const home =
|
|
3010
|
+
new Command6("cleanup").description("Remove deprecated skills (zam) that conflict with Zero").action(() => {
|
|
3011
|
+
const home = homedir3();
|
|
2711
3012
|
const removed = removeConflictingSkills(home);
|
|
2712
3013
|
if (removed.length === 0) {
|
|
2713
3014
|
console.error("No conflicting skills found. Nothing to remove.");
|
|
@@ -2726,8 +3027,8 @@ ${removedList}`);
|
|
|
2726
3027
|
);
|
|
2727
3028
|
|
|
2728
3029
|
// src/commands/review-command.ts
|
|
2729
|
-
import { readFileSync as
|
|
2730
|
-
import { Command as
|
|
3030
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
3031
|
+
import { Command as Command7 } from "commander";
|
|
2731
3032
|
import { z as z3 } from "zod";
|
|
2732
3033
|
var bulkEntrySchema2 = z3.object({
|
|
2733
3034
|
runId: z3.string(),
|
|
@@ -2737,7 +3038,7 @@ var bulkEntrySchema2 = z3.object({
|
|
|
2737
3038
|
reliability: z3.number().int().min(1).max(5),
|
|
2738
3039
|
content: z3.string().optional()
|
|
2739
3040
|
});
|
|
2740
|
-
var reviewCommand = (appContext) => new
|
|
3041
|
+
var reviewCommand = (appContext) => new Command7("review").description("Submit a review for a capability run").addHelpText(
|
|
2741
3042
|
"after",
|
|
2742
3043
|
`
|
|
2743
3044
|
Tips for a great review:
|
|
@@ -2768,7 +3069,7 @@ Examples:
|
|
|
2768
3069
|
try {
|
|
2769
3070
|
const { analyticsService, apiService } = appContext.services;
|
|
2770
3071
|
if (options.fromFile) {
|
|
2771
|
-
const contents =
|
|
3072
|
+
const contents = readFileSync6(options.fromFile, "utf8");
|
|
2772
3073
|
const lines = contents.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
2773
3074
|
const parsed = [];
|
|
2774
3075
|
let parseFailures = 0;
|
|
@@ -2916,7 +3217,7 @@ Bulk review complete: ${ok} ok, ${failed} failed`);
|
|
|
2916
3217
|
);
|
|
2917
3218
|
|
|
2918
3219
|
// src/commands/runs-command.ts
|
|
2919
|
-
import { Command as
|
|
3220
|
+
import { Command as Command8 } from "commander";
|
|
2920
3221
|
var USD_ASSETS = /* @__PURE__ */ new Set(["USD", "USDC"]);
|
|
2921
3222
|
var formatCost2 = (cost) => {
|
|
2922
3223
|
if (!cost) return "free";
|
|
@@ -2931,7 +3232,7 @@ var formatPayment = (payment) => {
|
|
|
2931
3232
|
const mode = payment.mode ? ` (${payment.mode})` : "";
|
|
2932
3233
|
return `${payment.protocol}${chain}${mode}`;
|
|
2933
3234
|
};
|
|
2934
|
-
var runsCommand = (appContext) => new
|
|
3235
|
+
var runsCommand = (appContext) => new Command8("runs").description("List your recent capability runs").addHelpText(
|
|
2935
3236
|
"after",
|
|
2936
3237
|
`
|
|
2937
3238
|
View your recent capability runs \u2014 status, latency, cost, and payment info.
|
|
@@ -2989,7 +3290,7 @@ Examples:
|
|
|
2989
3290
|
);
|
|
2990
3291
|
|
|
2991
3292
|
// src/commands/search-command.ts
|
|
2992
|
-
import { Command as
|
|
3293
|
+
import { Command as Command9 } from "commander";
|
|
2993
3294
|
var DEFAULT_MAX_COST_USD = "30";
|
|
2994
3295
|
var formatReviewCount2 = (count) => {
|
|
2995
3296
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
|
|
@@ -3021,12 +3322,12 @@ var formatSearchResults = (results) => {
|
|
|
3021
3322
|
"${displayDescription}"`;
|
|
3022
3323
|
}).join("\n");
|
|
3023
3324
|
};
|
|
3024
|
-
var searchCommand = (appContext) => new
|
|
3325
|
+
var searchCommand = (appContext) => new Command9("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(
|
|
3025
3326
|
"--max-cost <amount>",
|
|
3026
3327
|
`Maximum cost per call in USD (default: ${DEFAULT_MAX_COST_USD})`
|
|
3027
3328
|
).option("--protocol <protocol>", "Payment protocol (x402 or mpp)").option(
|
|
3028
3329
|
"--status <status>",
|
|
3029
|
-
"Filter by availability (healthy,
|
|
3330
|
+
"Filter by availability (healthy, unknown, down). Defaults to healthy when neither --status nor --all is set; pass --all to see everything."
|
|
3030
3331
|
).option("--all", "Disable default quality filtering").option(
|
|
3031
3332
|
"--source <source>",
|
|
3032
3333
|
"Only show results from this crawl source (e.g. mpp, bazaar)"
|
|
@@ -3053,14 +3354,20 @@ var searchCommand = (appContext) => new Command8("search").description("Search f
|
|
|
3053
3354
|
effectiveMaxCost = DEFAULT_MAX_COST_USD;
|
|
3054
3355
|
appliedDefaultMaxCost = true;
|
|
3055
3356
|
}
|
|
3056
|
-
const validStatuses = ["healthy", "degraded", "down"];
|
|
3357
|
+
const validStatuses = ["healthy", "unknown", "degraded", "down"];
|
|
3057
3358
|
if (options.status && !validStatuses.includes(options.status)) {
|
|
3058
3359
|
console.error(
|
|
3059
|
-
`Invalid status "${options.status}". Must be one of:
|
|
3360
|
+
`Invalid status "${options.status}". Must be one of: ${validStatuses.join(", ")}.`
|
|
3060
3361
|
);
|
|
3061
3362
|
process.exitCode = 1;
|
|
3062
3363
|
return;
|
|
3063
3364
|
}
|
|
3365
|
+
let effectiveStatus = options.status;
|
|
3366
|
+
let appliedDefaultStatus = false;
|
|
3367
|
+
if (effectiveStatus === void 0 && !options.all) {
|
|
3368
|
+
effectiveStatus = "healthy";
|
|
3369
|
+
appliedDefaultStatus = true;
|
|
3370
|
+
}
|
|
3064
3371
|
const result = await apiService.search({
|
|
3065
3372
|
query,
|
|
3066
3373
|
offset: options.offset,
|
|
@@ -3068,7 +3375,7 @@ var searchCommand = (appContext) => new Command8("search").description("Search f
|
|
|
3068
3375
|
freeOnly: options.free,
|
|
3069
3376
|
maxCost: effectiveMaxCost,
|
|
3070
3377
|
protocol: options.protocol,
|
|
3071
|
-
availabilityStatus:
|
|
3378
|
+
availabilityStatus: effectiveStatus,
|
|
3072
3379
|
includeAll: options.all,
|
|
3073
3380
|
source: options.source,
|
|
3074
3381
|
excludeSource: options.excludeSource
|
|
@@ -3086,7 +3393,8 @@ var searchCommand = (appContext) => new Command8("search").description("Search f
|
|
|
3086
3393
|
maxCost: effectiveMaxCost,
|
|
3087
3394
|
maxCostDefaulted: appliedDefaultMaxCost,
|
|
3088
3395
|
protocol: options.protocol,
|
|
3089
|
-
availabilityStatus:
|
|
3396
|
+
availabilityStatus: effectiveStatus,
|
|
3397
|
+
availabilityStatusDefaulted: appliedDefaultStatus,
|
|
3090
3398
|
includeAll: options.all ?? false,
|
|
3091
3399
|
listingSource: options.source,
|
|
3092
3400
|
excludeListingSource: options.excludeSource,
|
|
@@ -3137,9 +3445,9 @@ var searchCommand = (appContext) => new Command8("search").description("Search f
|
|
|
3137
3445
|
);
|
|
3138
3446
|
|
|
3139
3447
|
// src/commands/terms-command.ts
|
|
3140
|
-
import { Command as
|
|
3448
|
+
import { Command as Command10 } from "commander";
|
|
3141
3449
|
var TERMS_URL = "https://zero.xyz/terms-of-service";
|
|
3142
|
-
var termsCommand = (_appContext) => new
|
|
3450
|
+
var termsCommand = (_appContext) => new Command10("terms").description("View the ZeroClick Terms of Service").action(async () => {
|
|
3143
3451
|
console.log(
|
|
3144
3452
|
`ZeroClick Agentic Capability Search \u2014 Terms of Service
|
|
3145
3453
|
|
|
@@ -3157,11 +3465,11 @@ Read the full terms at: ${TERMS_URL}
|
|
|
3157
3465
|
});
|
|
3158
3466
|
|
|
3159
3467
|
// src/commands/wallet-command.ts
|
|
3160
|
-
import { existsSync as existsSync3, readFileSync as
|
|
3161
|
-
import { homedir as
|
|
3162
|
-
import { join as
|
|
3163
|
-
import { Command as
|
|
3164
|
-
import
|
|
3468
|
+
import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
|
|
3469
|
+
import { homedir as homedir4 } from "os";
|
|
3470
|
+
import { join as join4 } from "path";
|
|
3471
|
+
import { Command as Command11 } from "commander";
|
|
3472
|
+
import open2 from "open";
|
|
3165
3473
|
import { isAddress } from "viem";
|
|
3166
3474
|
import { generatePrivateKey as generatePrivateKey2, privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
3167
3475
|
|
|
@@ -3177,7 +3485,7 @@ var readStdin = async () => {
|
|
|
3177
3485
|
// src/commands/wallet-command.ts
|
|
3178
3486
|
var PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/;
|
|
3179
3487
|
var parseProvider = (raw) => raw === "stripe" ? "stripe" : "coinbase";
|
|
3180
|
-
var walletBalanceCommand = (appContext) => new
|
|
3488
|
+
var walletBalanceCommand = (appContext) => new Command11("balance").description("Show wallet balance").option(
|
|
3181
3489
|
"--address <address>",
|
|
3182
3490
|
"Check balance for an arbitrary address (no key needed). Useful with `zero wallet generate` \u2014 verify funds arrived without setting the wallet as your default."
|
|
3183
3491
|
).action(async (options) => {
|
|
@@ -3226,7 +3534,7 @@ var readPrivateKeyFromStdin = async () => {
|
|
|
3226
3534
|
}
|
|
3227
3535
|
return raw;
|
|
3228
3536
|
};
|
|
3229
|
-
var walletFundCommand = (appContext) => new
|
|
3537
|
+
var walletFundCommand = (appContext) => new Command11("fund").description("Fund your wallet").argument("[amount]", "Amount to fund in USDC").option("--manual", "Show wallet address for manual transfer").option(
|
|
3230
3538
|
"--no-open",
|
|
3231
3539
|
"Print the funding URL instead of opening a browser (for agents \u2014 funding links are one-time use, hand the URL to the user)"
|
|
3232
3540
|
).option(
|
|
@@ -3300,7 +3608,7 @@ ${address}`);
|
|
|
3300
3608
|
const url = await apiService.getFundingUrl(amount, provider);
|
|
3301
3609
|
if (url) {
|
|
3302
3610
|
if (options.open) {
|
|
3303
|
-
await
|
|
3611
|
+
await open2(url);
|
|
3304
3612
|
console.log("Opened funding page in your browser.");
|
|
3305
3613
|
console.log(`If it didn't open, visit: ${url}`);
|
|
3306
3614
|
} else {
|
|
@@ -3323,7 +3631,7 @@ ${address}`);
|
|
|
3323
3631
|
}
|
|
3324
3632
|
}
|
|
3325
3633
|
);
|
|
3326
|
-
var walletAddressCommand = (appContext) => new
|
|
3634
|
+
var walletAddressCommand = (appContext) => new Command11("address").description("Show wallet address").action(() => {
|
|
3327
3635
|
const { walletService } = appContext.services;
|
|
3328
3636
|
const address = walletService.getAddress();
|
|
3329
3637
|
if (!address) {
|
|
@@ -3333,7 +3641,7 @@ var walletAddressCommand = (appContext) => new Command10("address").description(
|
|
|
3333
3641
|
}
|
|
3334
3642
|
console.log(address);
|
|
3335
3643
|
});
|
|
3336
|
-
var walletSetCommand = (appContext) => new
|
|
3644
|
+
var walletSetCommand = (appContext) => new Command11("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) => {
|
|
3337
3645
|
const { analyticsService } = appContext.services;
|
|
3338
3646
|
if (!privateKey.startsWith("0x")) {
|
|
3339
3647
|
console.error("Private key must be 0x-prefixed hex string.");
|
|
@@ -3348,11 +3656,11 @@ var walletSetCommand = (appContext) => new Command10("set").description("Set wal
|
|
|
3348
3656
|
process.exitCode = 1;
|
|
3349
3657
|
return;
|
|
3350
3658
|
}
|
|
3351
|
-
const zeroDir =
|
|
3352
|
-
const configPath =
|
|
3659
|
+
const zeroDir = join4(homedir4(), ".zero");
|
|
3660
|
+
const configPath = join4(zeroDir, "config.json");
|
|
3353
3661
|
if (!options.force && existsSync3(configPath)) {
|
|
3354
3662
|
try {
|
|
3355
|
-
const existing2 = JSON.parse(
|
|
3663
|
+
const existing2 = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3356
3664
|
if (existing2.privateKey) {
|
|
3357
3665
|
console.error(
|
|
3358
3666
|
"Wallet already configured. Use --force to overwrite."
|
|
@@ -3364,7 +3672,7 @@ var walletSetCommand = (appContext) => new Command10("set").description("Set wal
|
|
|
3364
3672
|
}
|
|
3365
3673
|
}
|
|
3366
3674
|
ensureSecureDir(zeroDir);
|
|
3367
|
-
const existing = existsSync3(configPath) ? JSON.parse(
|
|
3675
|
+
const existing = existsSync3(configPath) ? JSON.parse(readFileSync7(configPath, "utf8")) : {};
|
|
3368
3676
|
writeSecureFile(
|
|
3369
3677
|
configPath,
|
|
3370
3678
|
JSON.stringify(
|
|
@@ -3383,7 +3691,7 @@ var walletSetCommand = (appContext) => new Command10("set").description("Set wal
|
|
|
3383
3691
|
force: options.force ?? false
|
|
3384
3692
|
});
|
|
3385
3693
|
});
|
|
3386
|
-
var walletGenerateCommand = (appContext) => new
|
|
3694
|
+
var walletGenerateCommand = (appContext) => new Command11("generate").description(
|
|
3387
3695
|
"Generate a fresh wallet (address + private key) without touching your configured wallet"
|
|
3388
3696
|
).option("--json", "Emit { address, privateKey } as JSON").option(
|
|
3389
3697
|
"--fund",
|
|
@@ -3461,7 +3769,7 @@ var walletGenerateCommand = (appContext) => new Command10("generate").descriptio
|
|
|
3461
3769
|
}
|
|
3462
3770
|
);
|
|
3463
3771
|
var walletCommand = (appContext) => {
|
|
3464
|
-
const cmd = new
|
|
3772
|
+
const cmd = new Command11("wallet").description("Manage your wallet");
|
|
3465
3773
|
cmd.addCommand(walletBalanceCommand(appContext));
|
|
3466
3774
|
cmd.addCommand(walletFundCommand(appContext));
|
|
3467
3775
|
cmd.addCommand(walletAddressCommand(appContext));
|
|
@@ -3471,18 +3779,18 @@ var walletCommand = (appContext) => {
|
|
|
3471
3779
|
};
|
|
3472
3780
|
|
|
3473
3781
|
// src/commands/welcome-command.ts
|
|
3474
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3475
|
-
import { homedir as
|
|
3476
|
-
import { join as
|
|
3477
|
-
import { Command as
|
|
3478
|
-
import
|
|
3782
|
+
import { existsSync as existsSync4, readFileSync as readFileSync8 } from "fs";
|
|
3783
|
+
import { homedir as homedir5 } from "os";
|
|
3784
|
+
import { join as join5 } from "path";
|
|
3785
|
+
import { Command as Command12 } from "commander";
|
|
3786
|
+
import open3 from "open";
|
|
3479
3787
|
import { getAddress } from "viem";
|
|
3480
3788
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
3481
3789
|
var readPrivateKey = () => {
|
|
3482
|
-
const configPath =
|
|
3790
|
+
const configPath = join5(homedir5(), ".zero", "config.json");
|
|
3483
3791
|
if (!existsSync4(configPath)) return null;
|
|
3484
3792
|
try {
|
|
3485
|
-
const config = JSON.parse(
|
|
3793
|
+
const config = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
3486
3794
|
if (typeof config.privateKey === "string") {
|
|
3487
3795
|
return config.privateKey;
|
|
3488
3796
|
}
|
|
@@ -3517,7 +3825,7 @@ var printManualFallback = (url) => {
|
|
|
3517
3825
|
}
|
|
3518
3826
|
console.log(lines.join("\n"));
|
|
3519
3827
|
};
|
|
3520
|
-
var welcomeCommand = (appContext) => new
|
|
3828
|
+
var welcomeCommand = (appContext) => new Command12("welcome").description("Claim your $5 welcome bonus.").action(async () => {
|
|
3521
3829
|
const { analyticsService } = appContext.services;
|
|
3522
3830
|
analyticsService.capture("welcome_started", {});
|
|
3523
3831
|
let walletAddress;
|
|
@@ -3550,7 +3858,7 @@ var welcomeCommand = (appContext) => new Command11("welcome").description("Claim
|
|
|
3550
3858
|
const urlString = url.toString();
|
|
3551
3859
|
analyticsService.setWalletAddress(walletAddress);
|
|
3552
3860
|
try {
|
|
3553
|
-
await
|
|
3861
|
+
await open3(urlString);
|
|
3554
3862
|
console.log(
|
|
3555
3863
|
`Opening ${urlString}
|
|
3556
3864
|
|
|
@@ -3577,7 +3885,7 @@ If your browser didn't open, paste the URL above.`
|
|
|
3577
3885
|
// src/app.ts
|
|
3578
3886
|
var createApp = (appContext) => {
|
|
3579
3887
|
const { analyticsService } = appContext.services;
|
|
3580
|
-
const program = new
|
|
3888
|
+
const program = new Command13().name("zero").description("Zero CLI \u2014 Search engine for AI agents").version(package_default.version, "-v, --version").exitOverride().hook("preAction", async (_thisCommand, actionCommand) => {
|
|
3581
3889
|
const agentFlag = actionCommand.opts().agent;
|
|
3582
3890
|
if (typeof agentFlag === "string" && agentFlag.trim().length > 0) {
|
|
3583
3891
|
analyticsService.setAgentHost(agentFlag.trim());
|
|
@@ -3601,6 +3909,7 @@ var createApp = (appContext) => {
|
|
|
3601
3909
|
program.addCommand(configCommand(appContext));
|
|
3602
3910
|
program.addCommand(termsCommand(appContext));
|
|
3603
3911
|
program.addCommand(welcomeCommand(appContext));
|
|
3912
|
+
program.addCommand(authCommand(appContext), { hidden: true });
|
|
3604
3913
|
return program;
|
|
3605
3914
|
};
|
|
3606
3915
|
|
|
@@ -3610,6 +3919,7 @@ var envSchema = z4.object({
|
|
|
3610
3919
|
ZERO_API_URL: z4.string().default("https://api.zero.xyz"),
|
|
3611
3920
|
ZERO_WEB_URL: z4.string().default("https://zero.xyz"),
|
|
3612
3921
|
ZERO_PRIVATE_KEY: z4.string().optional(),
|
|
3922
|
+
ZERO_SESSION_TOKEN: z4.string().optional(),
|
|
3613
3923
|
ZERO_ENV: z4.enum(["development", "production"]).default("production")
|
|
3614
3924
|
});
|
|
3615
3925
|
var getEnv = () => {
|
|
@@ -3624,14 +3934,14 @@ var getEnv = () => {
|
|
|
3624
3934
|
|
|
3625
3935
|
// src/app/app-services.ts
|
|
3626
3936
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3627
|
-
import { existsSync as existsSync7
|
|
3628
|
-
import { homedir as
|
|
3629
|
-
import { join as
|
|
3937
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3938
|
+
import { homedir as homedir6 } from "os";
|
|
3939
|
+
import { join as join7 } from "path";
|
|
3630
3940
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
3631
3941
|
|
|
3632
3942
|
// src/services/analytics-service.ts
|
|
3633
3943
|
import { randomUUID } from "crypto";
|
|
3634
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as
|
|
3944
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
3635
3945
|
import { dirname as dirname2 } from "path";
|
|
3636
3946
|
import { PostHog } from "posthog-node";
|
|
3637
3947
|
var POSTHOG_API_KEY = "phc_B2vLyNxAf2mnqvdPQajf4d4b2iXc35dep2ZrvebMJLuX";
|
|
@@ -3654,7 +3964,7 @@ var AnalyticsService = class {
|
|
|
3654
3964
|
let persistedAnonId;
|
|
3655
3965
|
try {
|
|
3656
3966
|
if (existsSync5(opts.configPath)) {
|
|
3657
|
-
const config = JSON.parse(
|
|
3967
|
+
const config = JSON.parse(readFileSync9(opts.configPath, "utf8"));
|
|
3658
3968
|
if (config.telemetry === false) {
|
|
3659
3969
|
telemetryEnabled = false;
|
|
3660
3970
|
}
|
|
@@ -3676,15 +3986,17 @@ var AnalyticsService = class {
|
|
|
3676
3986
|
} else {
|
|
3677
3987
|
const newAnonId = randomUUID();
|
|
3678
3988
|
this.distinctId = newAnonId;
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3989
|
+
if (!process.env.VITEST) {
|
|
3990
|
+
try {
|
|
3991
|
+
const dir = dirname2(opts.configPath);
|
|
3992
|
+
mkdirSync4(dir, { recursive: true });
|
|
3993
|
+
const existing = existsSync5(opts.configPath) ? JSON.parse(readFileSync9(opts.configPath, "utf8")) : {};
|
|
3994
|
+
writeFileSync4(
|
|
3995
|
+
opts.configPath,
|
|
3996
|
+
JSON.stringify({ ...existing, anonId: newAnonId }, null, 2)
|
|
3997
|
+
);
|
|
3998
|
+
} catch {
|
|
3999
|
+
}
|
|
3688
4000
|
}
|
|
3689
4001
|
}
|
|
3690
4002
|
const originalConsoleError = console.error;
|
|
@@ -3719,7 +4031,7 @@ var AnalyticsService = class {
|
|
|
3719
4031
|
if (anonId === walletAddress) return;
|
|
3720
4032
|
let aliasedTo;
|
|
3721
4033
|
try {
|
|
3722
|
-
const config = JSON.parse(
|
|
4034
|
+
const config = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
3723
4035
|
if (typeof config.aliasedTo === "string") {
|
|
3724
4036
|
aliasedTo = config.aliasedTo;
|
|
3725
4037
|
}
|
|
@@ -3727,8 +4039,9 @@ var AnalyticsService = class {
|
|
|
3727
4039
|
}
|
|
3728
4040
|
if (aliasedTo === walletAddress) return;
|
|
3729
4041
|
this.posthog.alias({ distinctId: walletAddress, alias: anonId });
|
|
4042
|
+
if (process.env.VITEST) return;
|
|
3730
4043
|
try {
|
|
3731
|
-
const config = existsSync5(configPath) ? JSON.parse(
|
|
4044
|
+
const config = existsSync5(configPath) ? JSON.parse(readFileSync9(configPath, "utf8")) : {};
|
|
3732
4045
|
writeFileSync4(
|
|
3733
4046
|
configPath,
|
|
3734
4047
|
JSON.stringify({ ...config, aliasedTo: walletAddress }, null, 2)
|
|
@@ -3803,15 +4116,43 @@ var AnalyticsService = class {
|
|
|
3803
4116
|
}
|
|
3804
4117
|
};
|
|
3805
4118
|
|
|
4119
|
+
// src/services/api-account.ts
|
|
4120
|
+
import {
|
|
4121
|
+
bytesToHex,
|
|
4122
|
+
parseSignature,
|
|
4123
|
+
serializeTransaction
|
|
4124
|
+
} from "viem";
|
|
4125
|
+
import { toAccount } from "viem/accounts";
|
|
4126
|
+
var createApiAccount = (walletAddress, api) => toAccount({
|
|
4127
|
+
address: walletAddress,
|
|
4128
|
+
async signMessage({ message }) {
|
|
4129
|
+
const asMessage = typeof message === "string" ? message : typeof message.raw === "string" ? message.raw : bytesToHex(message.raw);
|
|
4130
|
+
const { signature } = await api.signMessageRemote(asMessage);
|
|
4131
|
+
return signature;
|
|
4132
|
+
},
|
|
4133
|
+
async signTypedData(typedData) {
|
|
4134
|
+
const { signature } = await api.signTypedDataRemote(typedData);
|
|
4135
|
+
return signature;
|
|
4136
|
+
},
|
|
4137
|
+
async signTransaction(transaction, options) {
|
|
4138
|
+
const serialize = options?.serializer ?? serializeTransaction;
|
|
4139
|
+
const unsigned = await serialize(transaction);
|
|
4140
|
+
const { signature } = await api.signTransactionRemote({
|
|
4141
|
+
unsignedTransaction: unsigned
|
|
4142
|
+
});
|
|
4143
|
+
return await serialize(transaction, parseSignature(signature));
|
|
4144
|
+
}
|
|
4145
|
+
});
|
|
4146
|
+
|
|
3806
4147
|
// src/services/state-service.ts
|
|
3807
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as
|
|
3808
|
-
import { join as
|
|
4148
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
4149
|
+
import { join as join6 } from "path";
|
|
3809
4150
|
var RECENT_SEARCH_LIMIT = 10;
|
|
3810
4151
|
var StateService = class {
|
|
3811
4152
|
constructor(zeroDir) {
|
|
3812
4153
|
this.zeroDir = zeroDir;
|
|
3813
|
-
this.lastSearchPath =
|
|
3814
|
-
this.recentSearchesPath =
|
|
4154
|
+
this.lastSearchPath = join6(zeroDir, "last_search.json");
|
|
4155
|
+
this.recentSearchesPath = join6(zeroDir, "recent_searches.json");
|
|
3815
4156
|
}
|
|
3816
4157
|
lastSearchPath;
|
|
3817
4158
|
recentSearchesPath;
|
|
@@ -3831,7 +4172,7 @@ var StateService = class {
|
|
|
3831
4172
|
loadLastSearch = () => {
|
|
3832
4173
|
try {
|
|
3833
4174
|
if (!existsSync6(this.lastSearchPath)) return null;
|
|
3834
|
-
const raw =
|
|
4175
|
+
const raw = readFileSync10(this.lastSearchPath, "utf8");
|
|
3835
4176
|
return JSON.parse(raw);
|
|
3836
4177
|
} catch {
|
|
3837
4178
|
return null;
|
|
@@ -3843,7 +4184,7 @@ var StateService = class {
|
|
|
3843
4184
|
const last = this.loadLastSearch();
|
|
3844
4185
|
return { searches: last ? [last] : [] };
|
|
3845
4186
|
}
|
|
3846
|
-
const raw =
|
|
4187
|
+
const raw = readFileSync10(this.recentSearchesPath, "utf8");
|
|
3847
4188
|
const parsed = JSON.parse(raw);
|
|
3848
4189
|
return { searches: parsed.searches ?? [] };
|
|
3849
4190
|
} catch {
|
|
@@ -3941,40 +4282,67 @@ var detectAgentHost = (env = process.env) => {
|
|
|
3941
4282
|
|
|
3942
4283
|
// src/app/app-services.ts
|
|
3943
4284
|
var CLI_VERSION = package_default.version;
|
|
3944
|
-
var
|
|
3945
|
-
|
|
3946
|
-
const
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
4285
|
+
var resolveCredentials = (env, config) => {
|
|
4286
|
+
const injectedToken = env.ZERO_SESSION_TOKEN;
|
|
4287
|
+
const credentials = injectedToken ? {
|
|
4288
|
+
kind: "session",
|
|
4289
|
+
accessToken: injectedToken,
|
|
4290
|
+
refreshToken: "",
|
|
4291
|
+
userId: ""
|
|
4292
|
+
} : config.session ? {
|
|
4293
|
+
kind: "session",
|
|
4294
|
+
accessToken: config.session.accessToken,
|
|
4295
|
+
refreshToken: config.session.refreshToken,
|
|
4296
|
+
userId: config.session.userId
|
|
4297
|
+
} : { kind: "none" };
|
|
4298
|
+
const privateKey = env.ZERO_PRIVATE_KEY ?? config.privateKey ?? null;
|
|
4299
|
+
return { credentials, privateKey };
|
|
4300
|
+
};
|
|
4301
|
+
var buildOnSessionRefreshed = (configPath) => async (tokens) => {
|
|
4302
|
+
const current = readConfig(configPath);
|
|
4303
|
+
if (!current.session) return;
|
|
4304
|
+
const next = {
|
|
4305
|
+
...current,
|
|
4306
|
+
session: {
|
|
4307
|
+
...current.session,
|
|
4308
|
+
accessToken: tokens.accessToken,
|
|
4309
|
+
refreshToken: tokens.refreshToken
|
|
3957
4310
|
}
|
|
3958
|
-
}
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
4311
|
+
};
|
|
4312
|
+
writeSecureFile(configPath, JSON.stringify(next, null, 2));
|
|
4313
|
+
};
|
|
4314
|
+
var getServices = async (env) => {
|
|
4315
|
+
const zeroDir = join7(homedir6(), ".zero");
|
|
4316
|
+
const configPath = join7(zeroDir, "config.json");
|
|
4317
|
+
const config = existsSync7(configPath) ? readConfig(configPath) : {};
|
|
4318
|
+
const { credentials, privateKey } = resolveCredentials(env, config);
|
|
4319
|
+
const lowBalanceWarning = typeof config.lowBalanceWarning === "number" ? config.lowBalanceWarning : 1;
|
|
4320
|
+
const apiService = new ApiService(
|
|
4321
|
+
env.ZERO_API_URL,
|
|
4322
|
+
privateKey ? privateKeyToAccount4(privateKey) : null,
|
|
4323
|
+
credentials,
|
|
4324
|
+
buildOnSessionRefreshed(configPath)
|
|
4325
|
+
);
|
|
4326
|
+
let account = privateKey ? privateKeyToAccount4(privateKey) : null;
|
|
4327
|
+
let managed = false;
|
|
4328
|
+
if (!account && credentials.kind === "session") {
|
|
4329
|
+
const address = await resolveManagedWalletAddress(apiService);
|
|
4330
|
+
if (address) {
|
|
4331
|
+
account = createApiAccount(address, apiService);
|
|
4332
|
+
managed = true;
|
|
3967
4333
|
}
|
|
3968
|
-
} catch {
|
|
3969
4334
|
}
|
|
3970
|
-
|
|
3971
|
-
|
|
4335
|
+
if (account && !apiService.walletAddress) {
|
|
4336
|
+
apiService.setWalletAddress(account.address);
|
|
4337
|
+
}
|
|
4338
|
+
const paymentService = new PaymentService(account, {
|
|
4339
|
+
lowBalanceWarning,
|
|
4340
|
+
managed
|
|
4341
|
+
});
|
|
3972
4342
|
const stateService = new StateService(zeroDir);
|
|
3973
4343
|
const walletService = new WalletService(
|
|
3974
4344
|
apiService.walletAddress,
|
|
3975
|
-
{
|
|
3976
|
-
lowBalanceWarning
|
|
3977
|
-
},
|
|
4345
|
+
{ lowBalanceWarning },
|
|
3978
4346
|
paymentService.getTotalBalance
|
|
3979
4347
|
);
|
|
3980
4348
|
const analyticsService = new AnalyticsService({
|
|
@@ -3993,16 +4361,29 @@ var getServices = (env) => {
|
|
|
3993
4361
|
walletService
|
|
3994
4362
|
};
|
|
3995
4363
|
};
|
|
4364
|
+
var resolveManagedWalletAddress = async (api) => {
|
|
4365
|
+
try {
|
|
4366
|
+
const wallets = await api.getWallets();
|
|
4367
|
+
const primary = wallets.find(
|
|
4368
|
+
(w) => w.source === "privy_embedded" && w.isPrimary
|
|
4369
|
+
);
|
|
4370
|
+
if (primary) return primary.walletAddress;
|
|
4371
|
+
const provisioned = await api.provisionWallet();
|
|
4372
|
+
return provisioned.walletAddress ?? null;
|
|
4373
|
+
} catch {
|
|
4374
|
+
return null;
|
|
4375
|
+
}
|
|
4376
|
+
};
|
|
3996
4377
|
|
|
3997
4378
|
// src/app/app-context.ts
|
|
3998
|
-
var createAppContext = () => {
|
|
4379
|
+
var createAppContext = async () => {
|
|
3999
4380
|
const env = getEnv();
|
|
4000
4381
|
if (!env) {
|
|
4001
4382
|
return null;
|
|
4002
4383
|
}
|
|
4003
4384
|
return {
|
|
4004
4385
|
env,
|
|
4005
|
-
services: getServices(env),
|
|
4386
|
+
services: await getServices(env),
|
|
4006
4387
|
invocation: { current: null }
|
|
4007
4388
|
};
|
|
4008
4389
|
};
|
|
@@ -4016,8 +4397,8 @@ import {
|
|
|
4016
4397
|
readlinkSync,
|
|
4017
4398
|
writeFileSync as writeFileSync6
|
|
4018
4399
|
} from "fs";
|
|
4019
|
-
import { homedir as
|
|
4020
|
-
import { dirname as dirname3, join as
|
|
4400
|
+
import { homedir as homedir7 } from "os";
|
|
4401
|
+
import { dirname as dirname3, join as join8, resolve } from "path";
|
|
4021
4402
|
var CACHE_FILENAME = "update_check.json";
|
|
4022
4403
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@zeroxyz/cli/latest";
|
|
4023
4404
|
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -4041,10 +4422,10 @@ var resolveExecPath = (execPath) => {
|
|
|
4041
4422
|
var detectInstallMethod = (opts = {}) => {
|
|
4042
4423
|
const execPath = opts.execPath ?? process.execPath;
|
|
4043
4424
|
const pkg = opts.pkg ?? process.pkg;
|
|
4044
|
-
const home = opts.home ??
|
|
4425
|
+
const home = opts.home ?? homedir7();
|
|
4045
4426
|
if (pkg) return "binary";
|
|
4046
4427
|
const resolved = resolveExecPath(execPath);
|
|
4047
|
-
const zeroBin =
|
|
4428
|
+
const zeroBin = join8(home, ".zero", "bin");
|
|
4048
4429
|
if (resolved.startsWith(zeroBin)) return "binary";
|
|
4049
4430
|
return "npm";
|
|
4050
4431
|
};
|
|
@@ -4069,7 +4450,7 @@ var compareVersions = (a, b) => {
|
|
|
4069
4450
|
if (pb.pre === null) return -1;
|
|
4070
4451
|
return pa.pre < pb.pre ? -1 : 1;
|
|
4071
4452
|
};
|
|
4072
|
-
var cachePath = (zeroDir) =>
|
|
4453
|
+
var cachePath = (zeroDir) => join8(zeroDir, CACHE_FILENAME);
|
|
4073
4454
|
var readCache = (zeroDir) => {
|
|
4074
4455
|
try {
|
|
4075
4456
|
const path = cachePath(zeroDir);
|
|
@@ -4158,12 +4539,12 @@ var maybePrintUpdateBanner = (zeroDir, currentVersion) => {
|
|
|
4158
4539
|
|
|
4159
4540
|
// src/index.ts
|
|
4160
4541
|
var main = async () => {
|
|
4161
|
-
const appContext = createAppContext();
|
|
4542
|
+
const appContext = await createAppContext();
|
|
4162
4543
|
if (!appContext) {
|
|
4163
4544
|
console.error("Failed to create app context");
|
|
4164
4545
|
process.exit(1);
|
|
4165
4546
|
}
|
|
4166
|
-
const zeroDir =
|
|
4547
|
+
const zeroDir = join9(homedir8(), ".zero");
|
|
4167
4548
|
maybePrintUpdateBanner(zeroDir, package_default.version);
|
|
4168
4549
|
const app = createApp(appContext);
|
|
4169
4550
|
let caughtError = null;
|