@zeroxyz/cli 0.0.41 → 0.0.43

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.
Files changed (2) hide show
  1. package/dist/index.js +432 -110
  2. 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 homedir8 } from "os";
5
- import { join as join10 } from "path";
4
+ import { homedir as homedir9 } from "os";
5
+ import { join as join11 } from "path";
6
6
 
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@zeroxyz/cli",
10
- version: "0.0.41",
10
+ version: "0.0.43",
11
11
  type: "module",
12
12
  bin: {
13
13
  zero: "dist/index.js",
@@ -61,7 +61,7 @@ var package_default = {
61
61
  };
62
62
 
63
63
  // src/app.ts
64
- import { Command as Command13 } from "commander";
64
+ import { Command as Command14 } from "commander";
65
65
 
66
66
  // src/commands/auth-command.ts
67
67
  import { homedir } from "os";
@@ -204,8 +204,7 @@ var searchResultSchema = z.object({
204
204
  cost: z.object({ amount: z.string(), asset: z.string() }),
205
205
  reviewCount: z.number().optional(),
206
206
  rating: ratingSchema,
207
- availabilityStatus: z.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional(),
208
- displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
207
+ availabilityStatus: z.enum(["healthy", "unknown", "down"]).nullable().optional(),
209
208
  // Pass-through: API stamps this on Zero-published / withzero.{ai,xyz}
210
209
  // services so `zero search --json` consumers can render their own
211
210
  // provenance UI. CLI doesn't render a badge today.
@@ -258,8 +257,7 @@ var capabilityResponseSchema = z.object({
258
257
  priority: z.number()
259
258
  })
260
259
  ).nullable(),
261
- availabilityStatus: z.enum(["healthy", "degraded", "down", "unknown"]).nullable().optional(),
262
- displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
260
+ availabilityStatus: z.enum(["healthy", "unknown", "down"]).nullable().optional(),
263
261
  activationCount: z.number().optional(),
264
262
  lastUsedAt: z.string().nullable().optional(),
265
263
  lastSuccessfullyRanAt: z.string().nullable().optional(),
@@ -586,16 +584,37 @@ var ApiService = class _ApiService {
586
584
  });
587
585
  return createBugReportResponseSchema.parse(json);
588
586
  };
587
+ // Mints a one-time onramp URL for the caller's wallet. Two paths, picked by
588
+ // which identity is available:
589
+ // • BYO key present (this.account) → /v1/wallet/fund-url, proving ownership
590
+ // with an EIP-191 signature.
591
+ // • No key but a session (managed/runner) → /v1/users/me/fund-url, which
592
+ // resolves the user's managed wallet server-side from the Bearer.
593
+ // Both honor the `provider` choice. Returns null on any failure so callers
594
+ // can fall back to manual transfer.
589
595
  getFundingUrl = async (amount, provider = "coinbase") => {
590
596
  try {
591
- const params = new URLSearchParams({ provider });
592
- if (amount) params.set("amount", amount);
593
- const json = await this.request(
594
- "GET",
595
- `/v1/wallet/fund-url?${params.toString()}`
596
- );
597
- const parsed = z.object({ url: z.string() }).parse(json);
598
- return parsed.url;
597
+ if (this.account) {
598
+ const params = new URLSearchParams({ provider });
599
+ if (amount) params.set("amount", amount);
600
+ const json = await this.request(
601
+ "GET",
602
+ `/v1/wallet/fund-url?${params.toString()}`,
603
+ void 0,
604
+ { auth: "wallet-attributed" }
605
+ );
606
+ return z.object({ url: z.string() }).parse(json).url;
607
+ }
608
+ if (this.credentials.kind === "session") {
609
+ const params = new URLSearchParams({ provider });
610
+ if (amount) params.set("amount", amount);
611
+ const json = await this.request(
612
+ "GET",
613
+ `/v1/users/me/fund-url?${params.toString()}`
614
+ );
615
+ return z.object({ url: z.string(), walletAddress: z.string() }).parse(json).url;
616
+ }
617
+ return null;
599
618
  } catch {
600
619
  return null;
601
620
  }
@@ -965,6 +984,61 @@ import {
965
984
  http
966
985
  } from "viem";
967
986
  import { base, baseSepolia, tempo as viemTempoChain } from "viem/chains";
987
+
988
+ // src/services/x402-challenge.ts
989
+ import { encodePaymentRequiredHeader } from "@x402/core/http";
990
+ var looksLikeX402Body = (body) => {
991
+ if (!body || typeof body !== "object") return false;
992
+ const b = body;
993
+ return typeof b.x402Version === "number" && Array.isArray(b.accepts) && b.accepts.length > 0;
994
+ };
995
+ var isV1ShapedAccept = (accept) => {
996
+ if (!accept || typeof accept !== "object") return false;
997
+ const a = accept;
998
+ const network = typeof a.network === "string" ? a.network : "";
999
+ const bareNetwork = network !== "" && !network.includes(":");
1000
+ const v1AmountField = a.maxAmountRequired != null && a.amount == null;
1001
+ return bareNetwork || v1AmountField;
1002
+ };
1003
+ var coerceX402BodyVersion = (challenge) => {
1004
+ const accepts = Array.isArray(challenge.accepts) ? challenge.accepts : [];
1005
+ return { ...challenge, x402Version: accepts.some(isV1ShapedAccept) ? 1 : 2 };
1006
+ };
1007
+ var withSynthesizedX402Header = (baseFetch) => async (input, init) => {
1008
+ const response = await baseFetch(input, init);
1009
+ if (response.status !== 402) return response;
1010
+ if (response.headers.get("payment-required") ?? response.headers.get("x-payment-required")) {
1011
+ return response;
1012
+ }
1013
+ const text = await response.clone().text();
1014
+ let parsed;
1015
+ try {
1016
+ parsed = JSON.parse(text);
1017
+ } catch {
1018
+ return response;
1019
+ }
1020
+ if (!looksLikeX402Body(parsed) || parsed.x402Version === 1) {
1021
+ return response;
1022
+ }
1023
+ const challenge = coerceX402BodyVersion(parsed);
1024
+ const headers = new Headers(response.headers);
1025
+ headers.set(
1026
+ "PAYMENT-REQUIRED",
1027
+ // Validated as an x402 body above; encoder wants the concrete type.
1028
+ encodePaymentRequiredHeader(
1029
+ challenge
1030
+ )
1031
+ );
1032
+ headers.delete("content-length");
1033
+ headers.delete("content-encoding");
1034
+ return new Response(text, {
1035
+ status: response.status,
1036
+ statusText: response.statusText,
1037
+ headers
1038
+ });
1039
+ };
1040
+
1041
+ // src/services/payment-service.ts
968
1042
  var SessionCloseFailedError = class extends Error {
969
1043
  session;
970
1044
  response;
@@ -1208,7 +1282,10 @@ var PaymentService = class {
1208
1282
  const httpClient = new x402HTTPClient(client).onPaymentRequired(
1209
1283
  createSIWxClientHook(this.account)
1210
1284
  );
1211
- const wrappedFetch = wrapFetchWithPayment(fetch, httpClient);
1285
+ const wrappedFetch = wrapFetchWithPayment(
1286
+ withSynthesizedX402Header(fetch),
1287
+ httpClient
1288
+ );
1212
1289
  const response = await wrappedFetch(url, {
1213
1290
  method: request.method,
1214
1291
  headers: request.headers,
@@ -1772,11 +1849,6 @@ var resolveRequestBody = (rawData, readStdin2) => {
1772
1849
  }
1773
1850
  return body;
1774
1851
  };
1775
- var looksLikeX402V1Body = (body) => {
1776
- if (!body || typeof body !== "object") return false;
1777
- const b = body;
1778
- return b.x402Version === 1 && Array.isArray(b.accepts) && b.accepts.length > 0;
1779
- };
1780
1852
  var detectPaymentRequirement = async (response) => {
1781
1853
  if (response.status !== 402) return null;
1782
1854
  const x402Header = response.headers.get("payment-required") ?? response.headers.get("x-payment-required");
@@ -1798,7 +1870,7 @@ var detectPaymentRequirement = async (response) => {
1798
1870
  const text = await response.clone().text();
1799
1871
  if (text) {
1800
1872
  const parsed = JSON.parse(text);
1801
- if (looksLikeX402V1Body(parsed)) {
1873
+ if (looksLikeX402Body(parsed)) {
1802
1874
  return { protocol: "x402", raw: parsed };
1803
1875
  }
1804
1876
  }
@@ -2403,7 +2475,7 @@ var formatCapability = (capability) => {
2403
2475
  const lines = [];
2404
2476
  lines.push(capability.name);
2405
2477
  lines.push(` Rating: ${formatRating(capability.rating)}`);
2406
- lines.push(` Status: ${capability.displayStatus ?? "unknown"}`);
2478
+ lines.push(` Status: ${capability.availabilityStatus ?? "unknown"}`);
2407
2479
  lines.push(...formatCost(capability));
2408
2480
  lines.push(` URL: ${capability.url}`);
2409
2481
  lines.push(` Method: ${capability.method}`);
@@ -2645,6 +2717,8 @@ var AGENT_TOOLS = [
2645
2717
  },
2646
2718
  { name: "Cursor", detectDir: ".cursor", skillsDir: ".cursor/skills" }
2647
2719
  ];
2720
+ var HOOK_FILES = ["auto-approve-zero.sh", "zero-context.sh"];
2721
+ var ZERO_SANDBOX_DOMAIN = "*.zero.xyz";
2648
2722
  var findResourceDir = (startDir, resourceName) => {
2649
2723
  let current = startDir;
2650
2724
  while (true) {
@@ -2714,10 +2788,9 @@ var installHook = (home, verbose = false) => {
2714
2788
  const zeroHooksDir = join3(home, ".zero", "hooks");
2715
2789
  mkdirSync3(zeroHooksDir, { recursive: true });
2716
2790
  if (verbose) stepInfo(`staged hook dir at ${zeroHooksDir}`);
2717
- const hookFiles = ["auto-approve-zero.sh", "zero-context.sh"];
2718
2791
  const hookDests = {};
2719
2792
  const hooksSourceDir = findResourceDir(getCliModuleDir(), "hooks");
2720
- for (const hookFile of hookFiles) {
2793
+ for (const hookFile of HOOK_FILES) {
2721
2794
  const hookSource = join3(hooksSourceDir, hookFile);
2722
2795
  const hookDest = join3(zeroHooksDir, hookFile);
2723
2796
  copyFile(hookSource, hookDest);
@@ -2820,12 +2893,15 @@ var installHook = (home, verbose = false) => {
2820
2893
  network.allowedDomains = [];
2821
2894
  }
2822
2895
  const allowedDomains = network.allowedDomains;
2823
- const zeroDomain = "*.zero.xyz";
2824
- if (!allowedDomains.includes(zeroDomain)) {
2825
- allowedDomains.push(zeroDomain);
2826
- if (verbose) stepInfo(`sandbox.network.allowedDomains += ${zeroDomain}`);
2896
+ if (!allowedDomains.includes(ZERO_SANDBOX_DOMAIN)) {
2897
+ allowedDomains.push(ZERO_SANDBOX_DOMAIN);
2898
+ if (verbose) {
2899
+ stepInfo(`sandbox.network.allowedDomains += ${ZERO_SANDBOX_DOMAIN}`);
2900
+ }
2827
2901
  } else if (verbose) {
2828
- stepInfo(`${zeroDomain} already in sandbox allowlist \u2014 not modified`);
2902
+ stepInfo(
2903
+ `${ZERO_SANDBOX_DOMAIN} already in sandbox allowlist \u2014 not modified`
2904
+ );
2829
2905
  }
2830
2906
  writeFileSync3(settingsPath, `${JSON.stringify(settings, null, 2)}
2831
2907
  `);
@@ -2861,6 +2937,14 @@ var removeConflictingSkills = (home) => {
2861
2937
  }
2862
2938
  return removed;
2863
2939
  };
2940
+ var getBundledSkillNames = () => {
2941
+ try {
2942
+ const skillsSourceDir = findResourceDir(getCliModuleDir(), "skills");
2943
+ return readdirSync(skillsSourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2944
+ } catch {
2945
+ return ["zero"];
2946
+ }
2947
+ };
2864
2948
  var installSkills = (home, verbose = false) => {
2865
2949
  const skillsSourceDir = findResourceDir(getCliModuleDir(), "skills");
2866
2950
  const skillDirs = readdirSync(skillsSourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -3404,8 +3488,16 @@ var formatRatingBadge = (rating) => {
3404
3488
  return `${successPct} success \xB7 ${reviews} reviews`;
3405
3489
  };
3406
3490
  var formatStatusBadge = (status) => {
3407
- if (!status || status === "unknown") return "";
3408
- return ` \u2014 ${status}`;
3491
+ switch (status) {
3492
+ case "healthy":
3493
+ return ` \u2014 ${color.green("\u2713 healthy")}`;
3494
+ case "down":
3495
+ return ` \u2014 ${color.red("\u2717 down")}`;
3496
+ case "unknown":
3497
+ return ` \u2014 ${color.yellow("? unknown")}`;
3498
+ default:
3499
+ return "";
3500
+ }
3409
3501
  };
3410
3502
  var formatSearchResults = (results) => {
3411
3503
  if (results.length === 0) return "No capabilities found.";
@@ -3414,7 +3506,7 @@ var formatSearchResults = (results) => {
3414
3506
  const displayName = r.brandName ? `${r.brandName} ${baseName}` : baseName;
3415
3507
  const displayDescription = r.whatItDoes ?? r.description;
3416
3508
  const ratingBadge = formatRatingBadge(r.rating);
3417
- const statusBadge = formatStatusBadge(r.displayStatus);
3509
+ const statusBadge = formatStatusBadge(r.availabilityStatus);
3418
3510
  const costLabel = r.cost.amount === "0" ? "Free" : r.cost.amount === "unknown" ? "variable pricing" : `$${r.cost.amount}/call`;
3419
3511
  return ` ${r.position}. ${displayName} \u2014 ${costLabel} \u2014 ${ratingBadge}${statusBadge}
3420
3512
  "${displayDescription}"`;
@@ -3425,7 +3517,7 @@ var searchCommand = (appContext) => new Command9("search").description("Search f
3425
3517
  `Maximum cost per call in USD (default: ${DEFAULT_MAX_COST_USD})`
3426
3518
  ).option("--protocol <protocol>", "Payment protocol (x402 or mpp)").option(
3427
3519
  "--status <status>",
3428
- "Filter by availability (healthy, unknown, down). Defaults to healthy when neither --status nor --all is set; pass --all to see everything."
3520
+ "Filter by availability (healthy, unknown, down). Default (unset) returns healthy + unknown with healthy first; pass 'healthy' for only-healthy, or --all to include everything."
3429
3521
  ).option("--all", "Disable default quality filtering").option(
3430
3522
  "--source <source>",
3431
3523
  "Only show results from this crawl source (e.g. mpp, bazaar)"
@@ -3460,12 +3552,8 @@ var searchCommand = (appContext) => new Command9("search").description("Search f
3460
3552
  process.exitCode = 1;
3461
3553
  return;
3462
3554
  }
3463
- let effectiveStatus = options.status;
3464
- let appliedDefaultStatus = false;
3465
- if (effectiveStatus === void 0 && !options.all) {
3466
- effectiveStatus = "healthy";
3467
- appliedDefaultStatus = true;
3468
- }
3555
+ const effectiveStatus = options.status;
3556
+ const appliedDefaultStatus = effectiveStatus === void 0 && !options.all;
3469
3557
  const result = await apiService.search({
3470
3558
  query,
3471
3559
  offset: options.offset,
@@ -3562,23 +3650,256 @@ Read the full terms at: ${TERMS_URL}
3562
3650
  }
3563
3651
  });
3564
3652
 
3565
- // src/commands/wallet-command.ts
3566
- import { existsSync as existsSync3, readFileSync as readFileSync8 } from "fs";
3653
+ // src/commands/uninstall-command.ts
3654
+ import {
3655
+ existsSync as existsSync3,
3656
+ readdirSync as readdirSync2,
3657
+ readFileSync as readFileSync7,
3658
+ rmSync as rmSync2,
3659
+ writeFileSync as writeFileSync4
3660
+ } from "fs";
3567
3661
  import { homedir as homedir4 } from "os";
3568
- import { join as join5 } from "path";
3569
- import { confirm, isCancel } from "@clack/prompts";
3662
+ import { join as join4 } from "path";
3570
3663
  import { Command as Command11 } from "commander";
3664
+ var removeSkills = (home, verbose = false) => {
3665
+ const skillNames = getBundledSkillNames();
3666
+ const removed = [];
3667
+ for (const tool of AGENT_TOOLS) {
3668
+ const toolSkillsPath = join4(home, tool.skillsDir);
3669
+ if (!existsSync3(toolSkillsPath)) {
3670
+ if (verbose) {
3671
+ stepInfo(
3672
+ `${tool.name}: ~/${tool.skillsDir} not found \u2014 nothing to remove`
3673
+ );
3674
+ }
3675
+ continue;
3676
+ }
3677
+ for (const skillName of skillNames) {
3678
+ const skillPath = join4(toolSkillsPath, skillName);
3679
+ if (!existsSync3(skillPath)) continue;
3680
+ rmSync2(skillPath, { recursive: true, force: true });
3681
+ removed.push(`${tool.name}: ${skillPath}`);
3682
+ if (verbose) stepInfo(`${tool.name}: removed skill '${skillName}'`);
3683
+ }
3684
+ }
3685
+ return removed;
3686
+ };
3687
+ var removeHooks = (home, verbose = false) => {
3688
+ let settingsChanged = false;
3689
+ const settingsPath = join4(home, ".claude", "settings.json");
3690
+ if (!existsSync3(settingsPath)) {
3691
+ if (verbose) stepInfo(`${settingsPath} not found \u2014 no settings to clean`);
3692
+ } else {
3693
+ let settings = null;
3694
+ try {
3695
+ settings = JSON.parse(readFileSync7(settingsPath, "utf-8"));
3696
+ } catch {
3697
+ if (verbose) {
3698
+ stepInfo(`${settingsPath} was unparseable JSON \u2014 leaving it untouched`);
3699
+ }
3700
+ }
3701
+ if (settings) {
3702
+ settingsChanged = stripZeroFromSettings(settings, verbose);
3703
+ if (settingsChanged) {
3704
+ writeFileSync4(settingsPath, `${JSON.stringify(settings, null, 2)}
3705
+ `);
3706
+ if (verbose) stepInfo(`rewrote ${settingsPath} without Zero entries`);
3707
+ } else if (verbose) {
3708
+ stepInfo(`no Zero entries found in ${settingsPath}`);
3709
+ }
3710
+ }
3711
+ }
3712
+ let scriptsRemoved = 0;
3713
+ const zeroHooksDir = join4(home, ".zero", "hooks");
3714
+ if (existsSync3(zeroHooksDir)) {
3715
+ for (const hookFile of HOOK_FILES) {
3716
+ const hookPath = join4(zeroHooksDir, hookFile);
3717
+ if (!existsSync3(hookPath)) continue;
3718
+ rmSync2(hookPath, { force: true });
3719
+ scriptsRemoved++;
3720
+ if (verbose) stepInfo(`removed ${hookPath}`);
3721
+ }
3722
+ if (readdirSync2(zeroHooksDir).length === 0) {
3723
+ rmSync2(zeroHooksDir, { recursive: true, force: true });
3724
+ if (verbose) stepInfo(`removed empty ${zeroHooksDir}`);
3725
+ }
3726
+ } else if (verbose) {
3727
+ stepInfo(`${zeroHooksDir} not found \u2014 no hook scripts to remove`);
3728
+ }
3729
+ return { settingsChanged, scriptsRemoved };
3730
+ };
3731
+ var stripZeroFromSettings = (settings, verbose) => {
3732
+ let changed = false;
3733
+ const hooks = settings.hooks && typeof settings.hooks === "object" ? settings.hooks : null;
3734
+ if (hooks) {
3735
+ changed = removeHookEntries(hooks, "PreToolUse", "auto-approve-zero", verbose) || changed;
3736
+ changed = removeHookEntries(hooks, "UserPromptSubmit", "zero-context", verbose) || changed;
3737
+ for (const key of ["PreToolUse", "UserPromptSubmit"]) {
3738
+ const arr = hooks[key];
3739
+ if (Array.isArray(arr) && arr.length === 0) delete hooks[key];
3740
+ }
3741
+ if (Object.keys(hooks).length === 0) delete settings.hooks;
3742
+ }
3743
+ const sandbox = settings.sandbox && typeof settings.sandbox === "object" ? settings.sandbox : null;
3744
+ const network = sandbox?.network && typeof sandbox.network === "object" ? sandbox.network : null;
3745
+ if (network && Array.isArray(network.allowedDomains)) {
3746
+ const domains = network.allowedDomains;
3747
+ const next = domains.filter((d) => d !== ZERO_SANDBOX_DOMAIN);
3748
+ if (next.length !== domains.length) {
3749
+ changed = true;
3750
+ if (verbose) {
3751
+ stepInfo(`sandbox.network.allowedDomains -= ${ZERO_SANDBOX_DOMAIN}`);
3752
+ }
3753
+ if (next.length === 0) {
3754
+ delete network.allowedDomains;
3755
+ if (sandbox && Object.keys(network).length === 0)
3756
+ delete sandbox.network;
3757
+ if (sandbox && Object.keys(sandbox).length === 0)
3758
+ delete settings.sandbox;
3759
+ } else {
3760
+ network.allowedDomains = next;
3761
+ }
3762
+ }
3763
+ }
3764
+ return changed;
3765
+ };
3766
+ var removeHookEntries = (hooks, hookType, commandSubstring, verbose) => {
3767
+ const entries = hooks[hookType];
3768
+ if (!Array.isArray(entries)) return false;
3769
+ const next = entries.filter((entry) => {
3770
+ const entryHooks = entry?.hooks;
3771
+ if (!Array.isArray(entryHooks)) return true;
3772
+ return !entryHooks.some(
3773
+ (h) => typeof h.command === "string" && h.command.includes(commandSubstring)
3774
+ );
3775
+ });
3776
+ if (next.length === entries.length) return false;
3777
+ hooks[hookType] = next;
3778
+ if (verbose) stepInfo(`removed ${hookType} entry (${commandSubstring})`);
3779
+ return true;
3780
+ };
3781
+ var removeWallet = (home, verbose = false) => {
3782
+ const zeroDir = join4(home, ".zero");
3783
+ const configPath = join4(zeroDir, "config.json");
3784
+ let removed = false;
3785
+ if (existsSync3(configPath)) {
3786
+ rmSync2(configPath, { force: true });
3787
+ removed = true;
3788
+ if (verbose) stepInfo(`removed ${configPath}`);
3789
+ }
3790
+ if (existsSync3(zeroDir) && readdirSync2(zeroDir).length === 0) {
3791
+ rmSync2(zeroDir, { recursive: true, force: true });
3792
+ if (verbose) stepInfo(`removed empty ${zeroDir}`);
3793
+ }
3794
+ return removed;
3795
+ };
3796
+ var runUninstall = async (appContext, options = {}) => {
3797
+ const verbose = options.verbose ?? false;
3798
+ const purge = options.purge ?? false;
3799
+ appContext.services.analyticsService.capture("uninstall_started", { purge });
3800
+ let currentStep = "skills";
3801
+ try {
3802
+ console.log("");
3803
+ console.log(` ${color.boldCyan("Uninstalling Zero")}`);
3804
+ console.log("");
3805
+ 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"));
3806
+ console.log("");
3807
+ const home = homedir4();
3808
+ currentStep = "skills";
3809
+ const skillsRemoved = removeSkills(home, verbose);
3810
+ if (skillsRemoved.length > 0) {
3811
+ stepSuccess("Skills removed", `${skillsRemoved.length} removed`);
3812
+ } else {
3813
+ stepSkip("No Zero skills found");
3814
+ }
3815
+ currentStep = "hooks";
3816
+ const { settingsChanged, scriptsRemoved } = removeHooks(home, verbose);
3817
+ if (settingsChanged || scriptsRemoved > 0) {
3818
+ stepSuccess("Hooks removed");
3819
+ } else {
3820
+ stepSkip("No Zero hooks found");
3821
+ }
3822
+ currentStep = "wallet";
3823
+ let walletRemoved = false;
3824
+ if (purge) {
3825
+ walletRemoved = removeWallet(home, verbose);
3826
+ if (walletRemoved) {
3827
+ stepWarn("Wallet config deleted", "~/.zero/config.json");
3828
+ } else {
3829
+ stepSkip("No wallet config to delete");
3830
+ }
3831
+ } else {
3832
+ stepSkip(
3833
+ "Wallet preserved",
3834
+ "run `zero uninstall --purge` to also delete ~/.zero"
3835
+ );
3836
+ }
3837
+ currentStep = "complete";
3838
+ appContext.services.analyticsService.capture("uninstall_completed", {
3839
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
3840
+ skills_removed: skillsRemoved,
3841
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
3842
+ skills_removed_count: skillsRemoved.length,
3843
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
3844
+ hooks_removed: settingsChanged,
3845
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
3846
+ hook_scripts_removed: scriptsRemoved,
3847
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
3848
+ wallet_removed: walletRemoved,
3849
+ purge
3850
+ });
3851
+ sectionDivider();
3852
+ console.log(` ${color.boldGreen("Zero uninstalled.")}`);
3853
+ if (!purge) {
3854
+ console.log(
3855
+ ` ${color.dim("Your wallet is kept at ~/.zero \u2014 reinstall anytime with `zero init`.")}`
3856
+ );
3857
+ }
3858
+ console.log("");
3859
+ return {
3860
+ skillsRemoved,
3861
+ hooksRemoved: settingsChanged,
3862
+ hookScriptsRemoved: scriptsRemoved,
3863
+ walletRemoved
3864
+ };
3865
+ } catch (err) {
3866
+ appContext.services.analyticsService.capture("uninstall_failed", {
3867
+ step: currentStep,
3868
+ error: truncateError(err instanceof Error ? err.message : String(err)),
3869
+ purge
3870
+ });
3871
+ throw err;
3872
+ }
3873
+ };
3874
+ var uninstallCommand = (appContext) => new Command11("uninstall").description(
3875
+ "Remove Zero skills and hooks installed by `zero init` (keeps your wallet)"
3876
+ ).option(
3877
+ "--purge",
3878
+ "Also delete the wallet config at ~/.zero (irreversible \u2014 destroys the private key)"
3879
+ ).option(
3880
+ "-v, --verbose",
3881
+ "Explain why each removal step was taken or skipped"
3882
+ ).action(async (options) => {
3883
+ await runUninstall(appContext, options);
3884
+ });
3885
+
3886
+ // src/commands/wallet-command.ts
3887
+ import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
3888
+ import { homedir as homedir5 } from "os";
3889
+ import { join as join6 } from "path";
3890
+ import { confirm, isCancel } from "@clack/prompts";
3891
+ import { Command as Command12 } from "commander";
3571
3892
  import open2 from "open";
3572
3893
  import { isAddress } from "viem";
3573
3894
  import { generatePrivateKey as generatePrivateKey2, privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
3574
3895
 
3575
3896
  // src/util/migrate-config.ts
3576
- import { readFileSync as readFileSync7 } from "fs";
3577
- import { join as join4 } from "path";
3897
+ import { readFileSync as readFileSync8 } from "fs";
3898
+ import { join as join5 } from "path";
3578
3899
  var backupAndStripPrivateKey = (zeroDir) => {
3579
- const configPath = join4(zeroDir, "config.json");
3580
- const backupPath = join4(zeroDir, "config.backup.json");
3581
- const raw = readFileSync7(configPath, "utf8");
3900
+ const configPath = join5(zeroDir, "config.json");
3901
+ const backupPath = join5(zeroDir, "config.backup.json");
3902
+ const raw = readFileSync8(configPath, "utf8");
3582
3903
  ensureSecureDir(zeroDir);
3583
3904
  writeSecureFile(backupPath, raw);
3584
3905
  const parsed = JSON.parse(raw);
@@ -3599,7 +3920,7 @@ var readStdin = async () => {
3599
3920
  // src/commands/wallet-command.ts
3600
3921
  var PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/;
3601
3922
  var parseProvider = (raw) => raw === "stripe" ? "stripe" : "coinbase";
3602
- var walletBalanceCommand = (appContext) => new Command11("balance").description("Show wallet balance").option(
3923
+ var walletBalanceCommand = (appContext) => new Command12("balance").description("Show wallet balance").option(
3603
3924
  "--address <address>",
3604
3925
  "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."
3605
3926
  ).action(async (options) => {
@@ -3648,7 +3969,7 @@ var readPrivateKeyFromStdin = async () => {
3648
3969
  }
3649
3970
  return raw;
3650
3971
  };
3651
- 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(
3972
+ var walletFundCommand = (appContext) => new Command12("fund").description("Fund your wallet").argument("[amount]", "Amount to fund in USDC").option("--manual", "Show wallet address for manual transfer").option(
3652
3973
  "--no-open",
3653
3974
  "Print the funding URL instead of opening a browser (for agents \u2014 funding links are one-time use, hand the URL to the user)"
3654
3975
  ).option(
@@ -3745,7 +4066,7 @@ ${address}`);
3745
4066
  }
3746
4067
  }
3747
4068
  );
3748
- var walletAddressCommand = (appContext) => new Command11("address").description("Show wallet address").action(() => {
4069
+ var walletAddressCommand = (appContext) => new Command12("address").description("Show wallet address").action(() => {
3749
4070
  const { walletService } = appContext.services;
3750
4071
  const address = walletService.getAddress();
3751
4072
  if (!address) {
@@ -3755,7 +4076,7 @@ var walletAddressCommand = (appContext) => new Command11("address").description(
3755
4076
  }
3756
4077
  console.log(address);
3757
4078
  });
3758
- 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) => {
4079
+ var walletSetCommand = (appContext) => new Command12("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) => {
3759
4080
  const { analyticsService } = appContext.services;
3760
4081
  if (!privateKey.startsWith("0x")) {
3761
4082
  console.error("Private key must be 0x-prefixed hex string.");
@@ -3770,11 +4091,11 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
3770
4091
  process.exitCode = 1;
3771
4092
  return;
3772
4093
  }
3773
- const zeroDir = join5(homedir4(), ".zero");
3774
- const configPath = join5(zeroDir, "config.json");
3775
- if (!options.force && existsSync3(configPath)) {
4094
+ const zeroDir = join6(homedir5(), ".zero");
4095
+ const configPath = join6(zeroDir, "config.json");
4096
+ if (!options.force && existsSync4(configPath)) {
3776
4097
  try {
3777
- const existing2 = JSON.parse(readFileSync8(configPath, "utf8"));
4098
+ const existing2 = JSON.parse(readFileSync9(configPath, "utf8"));
3778
4099
  if (existing2.privateKey) {
3779
4100
  console.error(
3780
4101
  "Wallet already configured. Use --force to overwrite."
@@ -3786,7 +4107,7 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
3786
4107
  }
3787
4108
  }
3788
4109
  ensureSecureDir(zeroDir);
3789
- const existing = existsSync3(configPath) ? JSON.parse(readFileSync8(configPath, "utf8")) : {};
4110
+ const existing = existsSync4(configPath) ? JSON.parse(readFileSync9(configPath, "utf8")) : {};
3790
4111
  writeSecureFile(
3791
4112
  configPath,
3792
4113
  JSON.stringify(
@@ -3805,7 +4126,7 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
3805
4126
  force: options.force ?? false
3806
4127
  });
3807
4128
  });
3808
- var walletGenerateCommand = (appContext) => new Command11("generate").description(
4129
+ var walletGenerateCommand = (appContext) => new Command12("generate").description(
3809
4130
  "Generate a fresh wallet (address + private key) without touching your configured wallet"
3810
4131
  ).option("--json", "Emit { address, privateKey } as JSON").option(
3811
4132
  "--fund",
@@ -3895,13 +4216,13 @@ var resolveZeroWalletAddress = async (apiService) => {
3895
4216
  return null;
3896
4217
  }
3897
4218
  };
3898
- var walletMigrateCommand = (appContext) => new Command11("migrate").description(
4219
+ var walletMigrateCommand = (appContext) => new Command12("migrate").description(
3899
4220
  "Sweep all USDC from your private-key wallet into your Zero wallet"
3900
4221
  ).option("--json", "Emit the migration result as JSON").option("-y, --yes", "Skip the confirmation prompt").action(async (options) => {
3901
4222
  const { apiService, paymentService } = appContext.services;
3902
- const zeroDir = join5(homedir4(), ".zero");
3903
- const configPath = join5(zeroDir, "config.json");
3904
- const config = existsSync3(configPath) ? readConfig(configPath) : {};
4223
+ const zeroDir = join6(homedir5(), ".zero");
4224
+ const configPath = join6(zeroDir, "config.json");
4225
+ const config = existsSync4(configPath) ? readConfig(configPath) : {};
3905
4226
  const privateKey = appContext.env.ZERO_PRIVATE_KEY ?? config.privateKey;
3906
4227
  if (!privateKey) {
3907
4228
  console.error(
@@ -4019,7 +4340,7 @@ var walletMigrateCommand = (appContext) => new Command11("migrate").description(
4019
4340
  if (anyFailed) process.exitCode = 1;
4020
4341
  });
4021
4342
  var walletCommand = (appContext) => {
4022
- const cmd = new Command11("wallet").description("Manage your wallet");
4343
+ const cmd = new Command12("wallet").description("Manage your wallet");
4023
4344
  cmd.addCommand(walletBalanceCommand(appContext));
4024
4345
  cmd.addCommand(walletFundCommand(appContext));
4025
4346
  cmd.addCommand(walletAddressCommand(appContext));
@@ -4030,18 +4351,18 @@ var walletCommand = (appContext) => {
4030
4351
  };
4031
4352
 
4032
4353
  // src/commands/welcome-command.ts
4033
- import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
4034
- import { homedir as homedir5 } from "os";
4035
- import { join as join6 } from "path";
4036
- import { Command as Command12 } from "commander";
4354
+ import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
4355
+ import { homedir as homedir6 } from "os";
4356
+ import { join as join7 } from "path";
4357
+ import { Command as Command13 } from "commander";
4037
4358
  import open3 from "open";
4038
4359
  import { getAddress } from "viem";
4039
4360
  import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
4040
4361
  var readPrivateKey = () => {
4041
- const configPath = join6(homedir5(), ".zero", "config.json");
4042
- if (!existsSync4(configPath)) return null;
4362
+ const configPath = join7(homedir6(), ".zero", "config.json");
4363
+ if (!existsSync5(configPath)) return null;
4043
4364
  try {
4044
- const config = JSON.parse(readFileSync9(configPath, "utf8"));
4365
+ const config = JSON.parse(readFileSync10(configPath, "utf8"));
4045
4366
  if (typeof config.privateKey === "string") {
4046
4367
  return config.privateKey;
4047
4368
  }
@@ -4076,7 +4397,7 @@ var printManualFallback = (url) => {
4076
4397
  }
4077
4398
  console.log(lines.join("\n"));
4078
4399
  };
4079
- var welcomeCommand = (appContext) => new Command12("welcome").description("Claim your $5 welcome bonus.").action(async () => {
4400
+ var welcomeCommand = (appContext) => new Command13("welcome").description("Claim your $5 welcome bonus.").action(async () => {
4080
4401
  const { analyticsService } = appContext.services;
4081
4402
  analyticsService.capture("welcome_started", {});
4082
4403
  let walletAddress;
@@ -4136,7 +4457,7 @@ If your browser didn't open, paste the URL above.`
4136
4457
  // src/app.ts
4137
4458
  var createApp = (appContext) => {
4138
4459
  const { analyticsService } = appContext.services;
4139
- 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) => {
4460
+ const program = new Command14().name("zero").description("Zero CLI \u2014 Search engine for AI agents").version(package_default.version, "-v, --version").exitOverride().hook("preAction", async (_thisCommand, actionCommand) => {
4140
4461
  const agentFlag = actionCommand.opts().agent;
4141
4462
  if (typeof agentFlag === "string" && agentFlag.trim().length > 0) {
4142
4463
  analyticsService.setAgentHost(agentFlag.trim());
@@ -4150,6 +4471,7 @@ var createApp = (appContext) => {
4150
4471
  });
4151
4472
  });
4152
4473
  program.addCommand(initCommand(appContext));
4474
+ program.addCommand(uninstallCommand(appContext));
4153
4475
  program.addCommand(searchCommand(appContext));
4154
4476
  program.addCommand(getCommand(appContext));
4155
4477
  program.addCommand(fetchCommand(appContext));
@@ -4185,14 +4507,14 @@ var getEnv = () => {
4185
4507
 
4186
4508
  // src/app/app-services.ts
4187
4509
  import { randomUUID as randomUUID2 } from "crypto";
4188
- import { existsSync as existsSync7 } from "fs";
4189
- import { homedir as homedir6 } from "os";
4190
- import { join as join8 } from "path";
4510
+ import { existsSync as existsSync8 } from "fs";
4511
+ import { homedir as homedir7 } from "os";
4512
+ import { join as join9 } from "path";
4191
4513
  import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
4192
4514
 
4193
4515
  // src/services/analytics-service.ts
4194
4516
  import { randomUUID } from "crypto";
4195
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
4517
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
4196
4518
  import { dirname as dirname2 } from "path";
4197
4519
  import { PostHog } from "posthog-node";
4198
4520
  var POSTHOG_API_KEY = "phc_B2vLyNxAf2mnqvdPQajf4d4b2iXc35dep2ZrvebMJLuX";
@@ -4214,8 +4536,8 @@ var AnalyticsService = class {
4214
4536
  let telemetryEnabled = true;
4215
4537
  let persistedAnonId;
4216
4538
  try {
4217
- if (existsSync5(opts.configPath)) {
4218
- const config = JSON.parse(readFileSync10(opts.configPath, "utf8"));
4539
+ if (existsSync6(opts.configPath)) {
4540
+ const config = JSON.parse(readFileSync11(opts.configPath, "utf8"));
4219
4541
  if (config.telemetry === false) {
4220
4542
  telemetryEnabled = false;
4221
4543
  }
@@ -4241,8 +4563,8 @@ var AnalyticsService = class {
4241
4563
  try {
4242
4564
  const dir = dirname2(opts.configPath);
4243
4565
  mkdirSync4(dir, { recursive: true });
4244
- const existing = existsSync5(opts.configPath) ? JSON.parse(readFileSync10(opts.configPath, "utf8")) : {};
4245
- writeFileSync4(
4566
+ const existing = existsSync6(opts.configPath) ? JSON.parse(readFileSync11(opts.configPath, "utf8")) : {};
4567
+ writeFileSync5(
4246
4568
  opts.configPath,
4247
4569
  JSON.stringify({ ...existing, anonId: newAnonId }, null, 2)
4248
4570
  );
@@ -4282,7 +4604,7 @@ var AnalyticsService = class {
4282
4604
  if (anonId === walletAddress) return;
4283
4605
  let aliasedTo;
4284
4606
  try {
4285
- const config = JSON.parse(readFileSync10(configPath, "utf8"));
4607
+ const config = JSON.parse(readFileSync11(configPath, "utf8"));
4286
4608
  if (typeof config.aliasedTo === "string") {
4287
4609
  aliasedTo = config.aliasedTo;
4288
4610
  }
@@ -4292,8 +4614,8 @@ var AnalyticsService = class {
4292
4614
  this.posthog.alias({ distinctId: walletAddress, alias: anonId });
4293
4615
  if (process.env.VITEST) return;
4294
4616
  try {
4295
- const config = existsSync5(configPath) ? JSON.parse(readFileSync10(configPath, "utf8")) : {};
4296
- writeFileSync4(
4617
+ const config = existsSync6(configPath) ? JSON.parse(readFileSync11(configPath, "utf8")) : {};
4618
+ writeFileSync5(
4297
4619
  configPath,
4298
4620
  JSON.stringify({ ...config, aliasedTo: walletAddress }, null, 2)
4299
4621
  );
@@ -4396,34 +4718,34 @@ var createApiAccount = (walletAddress, api) => toAccount({
4396
4718
  });
4397
4719
 
4398
4720
  // src/services/state-service.ts
4399
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
4400
- import { join as join7 } from "path";
4721
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
4722
+ import { join as join8 } from "path";
4401
4723
  var RECENT_SEARCH_LIMIT = 10;
4402
4724
  var StateService = class {
4403
4725
  constructor(zeroDir) {
4404
4726
  this.zeroDir = zeroDir;
4405
- this.lastSearchPath = join7(zeroDir, "last_search.json");
4406
- this.recentSearchesPath = join7(zeroDir, "recent_searches.json");
4727
+ this.lastSearchPath = join8(zeroDir, "last_search.json");
4728
+ this.recentSearchesPath = join8(zeroDir, "recent_searches.json");
4407
4729
  }
4408
4730
  lastSearchPath;
4409
4731
  recentSearchesPath;
4410
4732
  saveLastSearch = (data) => {
4411
4733
  mkdirSync5(this.zeroDir, { recursive: true });
4412
- writeFileSync5(this.lastSearchPath, JSON.stringify(data, null, 2));
4734
+ writeFileSync6(this.lastSearchPath, JSON.stringify(data, null, 2));
4413
4735
  const recent = this.loadRecentSearches();
4414
4736
  const filtered = recent.searches.filter(
4415
4737
  (s) => s.searchId !== data.searchId
4416
4738
  );
4417
4739
  const next = [data, ...filtered].slice(0, RECENT_SEARCH_LIMIT);
4418
- writeFileSync5(
4740
+ writeFileSync6(
4419
4741
  this.recentSearchesPath,
4420
4742
  JSON.stringify({ searches: next }, null, 2)
4421
4743
  );
4422
4744
  };
4423
4745
  loadLastSearch = () => {
4424
4746
  try {
4425
- if (!existsSync6(this.lastSearchPath)) return null;
4426
- const raw = readFileSync11(this.lastSearchPath, "utf8");
4747
+ if (!existsSync7(this.lastSearchPath)) return null;
4748
+ const raw = readFileSync12(this.lastSearchPath, "utf8");
4427
4749
  return JSON.parse(raw);
4428
4750
  } catch {
4429
4751
  return null;
@@ -4431,11 +4753,11 @@ var StateService = class {
4431
4753
  };
4432
4754
  loadRecentSearches = () => {
4433
4755
  try {
4434
- if (!existsSync6(this.recentSearchesPath)) {
4756
+ if (!existsSync7(this.recentSearchesPath)) {
4435
4757
  const last = this.loadLastSearch();
4436
4758
  return { searches: last ? [last] : [] };
4437
4759
  }
4438
- const raw = readFileSync11(this.recentSearchesPath, "utf8");
4760
+ const raw = readFileSync12(this.recentSearchesPath, "utf8");
4439
4761
  const parsed = JSON.parse(raw);
4440
4762
  return { searches: parsed.searches ?? [] };
4441
4763
  } catch {
@@ -4563,9 +4885,9 @@ var buildOnSessionRefreshed = (configPath) => async (tokens) => {
4563
4885
  writeSecureFile(configPath, JSON.stringify(next, null, 2));
4564
4886
  };
4565
4887
  var getServices = async (env) => {
4566
- const zeroDir = join8(homedir6(), ".zero");
4567
- const configPath = join8(zeroDir, "config.json");
4568
- const config = existsSync7(configPath) ? readConfig(configPath) : {};
4888
+ const zeroDir = join9(homedir7(), ".zero");
4889
+ const configPath = join9(zeroDir, "config.json");
4890
+ const config = existsSync8(configPath) ? readConfig(configPath) : {};
4569
4891
  const { credentials, privateKey } = resolveCredentials(env, config);
4570
4892
  const lowBalanceWarning = typeof config.lowBalanceWarning === "number" ? config.lowBalanceWarning : 1;
4571
4893
  const apiService = new ApiService(
@@ -4638,15 +4960,15 @@ var createAppContext = async () => {
4638
4960
 
4639
4961
  // src/util/update-check.ts
4640
4962
  import {
4641
- existsSync as existsSync8,
4963
+ existsSync as existsSync9,
4642
4964
  lstatSync,
4643
4965
  mkdirSync as mkdirSync6,
4644
- readFileSync as readFileSync12,
4966
+ readFileSync as readFileSync13,
4645
4967
  readlinkSync,
4646
- writeFileSync as writeFileSync6
4968
+ writeFileSync as writeFileSync7
4647
4969
  } from "fs";
4648
- import { homedir as homedir7 } from "os";
4649
- import { dirname as dirname3, join as join9, resolve } from "path";
4970
+ import { homedir as homedir8 } from "os";
4971
+ import { dirname as dirname3, join as join10, resolve } from "path";
4650
4972
  var CACHE_FILENAME = "update_check.json";
4651
4973
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/@zeroxyz/cli/latest";
4652
4974
  var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
@@ -4670,10 +4992,10 @@ var resolveExecPath = (execPath) => {
4670
4992
  var detectInstallMethod = (opts = {}) => {
4671
4993
  const execPath = opts.execPath ?? process.execPath;
4672
4994
  const pkg = opts.pkg ?? process.pkg;
4673
- const home = opts.home ?? homedir7();
4995
+ const home = opts.home ?? homedir8();
4674
4996
  if (pkg) return "binary";
4675
4997
  const resolved = resolveExecPath(execPath);
4676
- const zeroBin = join9(home, ".zero", "bin");
4998
+ const zeroBin = join10(home, ".zero", "bin");
4677
4999
  if (resolved.startsWith(zeroBin)) return "binary";
4678
5000
  return "npm";
4679
5001
  };
@@ -4698,12 +5020,12 @@ var compareVersions = (a, b) => {
4698
5020
  if (pb.pre === null) return -1;
4699
5021
  return pa.pre < pb.pre ? -1 : 1;
4700
5022
  };
4701
- var cachePath = (zeroDir) => join9(zeroDir, CACHE_FILENAME);
5023
+ var cachePath = (zeroDir) => join10(zeroDir, CACHE_FILENAME);
4702
5024
  var readCache = (zeroDir) => {
4703
5025
  try {
4704
5026
  const path = cachePath(zeroDir);
4705
- if (!existsSync8(path)) return emptyCache;
4706
- const raw = readFileSync12(path, "utf8");
5027
+ if (!existsSync9(path)) return emptyCache;
5028
+ const raw = readFileSync13(path, "utf8");
4707
5029
  const parsed = JSON.parse(raw);
4708
5030
  return {
4709
5031
  lastCheckedMs: typeof parsed.lastCheckedMs === "number" ? parsed.lastCheckedMs : 0,
@@ -4717,7 +5039,7 @@ var readCache = (zeroDir) => {
4717
5039
  var writeCache = (zeroDir, cache) => {
4718
5040
  try {
4719
5041
  mkdirSync6(zeroDir, { recursive: true });
4720
- writeFileSync6(cachePath(zeroDir), JSON.stringify(cache, null, 2));
5042
+ writeFileSync7(cachePath(zeroDir), JSON.stringify(cache, null, 2));
4721
5043
  } catch {
4722
5044
  }
4723
5045
  };
@@ -4792,7 +5114,7 @@ var main = async () => {
4792
5114
  console.error("Failed to create app context");
4793
5115
  process.exit(1);
4794
5116
  }
4795
- const zeroDir = join10(homedir8(), ".zero");
5117
+ const zeroDir = join11(homedir9(), ".zero");
4796
5118
  maybePrintUpdateBanner(zeroDir, package_default.version);
4797
5119
  const app = createApp(appContext);
4798
5120
  let caughtError = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",