@zeroxyz/cli 0.0.40 → 0.0.41

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 +339 -91
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { homedir as homedir8 } from "os";
5
- import { join as join9 } from "path";
5
+ import { join as join10 } from "path";
6
6
 
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@zeroxyz/cli",
10
- version: "0.0.40",
10
+ version: "0.0.41",
11
11
  type: "module",
12
12
  bin: {
13
13
  zero: "dist/index.js",
@@ -360,6 +360,9 @@ var signResultSchema = z.object({
360
360
  signature: z.string(),
361
361
  walletAddress: z.string()
362
362
  });
363
+ var migrateResultSchema = z.object({
364
+ transactionHash: z.string()
365
+ });
363
366
  var deviceStartResultSchema = z.object({
364
367
  deviceCode: z.string(),
365
368
  userCode: z.string(),
@@ -605,6 +608,15 @@ var ApiService = class _ApiService {
605
608
  const json = await this.request("POST", "/v1/users/me/wallets/provision");
606
609
  return userWalletDtoSchema.parse(json);
607
610
  };
611
+ // Hands a BYO-wallet-signed EIP-3009 authorization to the server, which
612
+ // broadcasts it on Base via the gas-paying relayer (sweeping USDC into the
613
+ // caller's provisioned wallet). Session-authed.
614
+ migrateWallet = async (authorization) => {
615
+ const json = await this.request("POST", "/v1/users/me/wallets/migrate", {
616
+ authorization
617
+ });
618
+ return migrateResultSchema.parse(json);
619
+ };
608
620
  signTypedDataRemote = async (typedData) => {
609
621
  const json = await this.request("POST", "/v1/users/me/sign-typed-data", {
610
622
  typedData
@@ -931,6 +943,7 @@ import { Command as Command4 } from "commander";
931
943
  import { formatUnits as formatUnits2 } from "viem";
932
944
 
933
945
  // src/services/payment-service.ts
946
+ import { randomBytes } from "crypto";
934
947
  import {
935
948
  adaptViemWallet,
936
949
  convertViemChainToRelayChain,
@@ -951,7 +964,7 @@ import {
951
964
  formatUnits,
952
965
  http
953
966
  } from "viem";
954
- import { base, baseSepolia } from "viem/chains";
967
+ import { base, baseSepolia, tempo as viemTempoChain } from "viem/chains";
955
968
  var SessionCloseFailedError = class extends Error {
956
969
  session;
957
970
  response;
@@ -1025,6 +1038,32 @@ var ERC20_BALANCE_ABI = [
1025
1038
  type: "function"
1026
1039
  }
1027
1040
  ];
1041
+ var ERC20_TRANSFER_ABI = [
1042
+ {
1043
+ inputs: [
1044
+ { name: "to", type: "address" },
1045
+ { name: "amount", type: "uint256" }
1046
+ ],
1047
+ name: "transfer",
1048
+ outputs: [{ name: "", type: "bool" }],
1049
+ stateMutability: "nonpayable",
1050
+ type: "function"
1051
+ }
1052
+ ];
1053
+ var USDC_BASE_SWEEP_DOMAIN = { name: "USD Coin", version: "2" };
1054
+ var SWEEP_AUTHORIZATION_TTL_S = 900;
1055
+ var TEMPO_FEE_RESERVE_RAW = 10000n;
1056
+ var EIP3009_TRANSFER_TYPES = {
1057
+ // biome-ignore lint/style/useNamingConvention: EIP-712 primaryType must match the on-chain type name
1058
+ TransferWithAuthorization: [
1059
+ { name: "from", type: "address" },
1060
+ { name: "to", type: "address" },
1061
+ { name: "value", type: "uint256" },
1062
+ { name: "validAfter", type: "uint256" },
1063
+ { name: "validBefore", type: "uint256" },
1064
+ { name: "nonce", type: "bytes32" }
1065
+ ]
1066
+ };
1028
1067
  var decodeSessionReceiptHeader = (header) => {
1029
1068
  if (!header) return null;
1030
1069
  try {
@@ -1231,11 +1270,6 @@ var PaymentService = class {
1231
1270
  `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`
1232
1271
  );
1233
1272
  }
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
- }
1239
1273
  await this.bridgeToTempo(requiredRaw, onProgress);
1240
1274
  }
1241
1275
  return capturedAmount;
@@ -1475,6 +1509,115 @@ var PaymentService = class {
1475
1509
  ]);
1476
1510
  return { amount: formatUnits(baseRaw + tempoRaw, 6), asset: "USDC" };
1477
1511
  };
1512
+ // Total USDC across both legs (Base + Tempo) for the configured wallet — what
1513
+ // the migrate confirmation prompt quotes. Best-effort: a chain whose RPC read
1514
+ // fails counts as 0 rather than blocking the other leg.
1515
+ getSweepableBalance = async () => {
1516
+ const [baseRaw, tempoRaw] = await Promise.all([
1517
+ this.getBalanceRaw("base").catch(() => 0n),
1518
+ this.getBalanceRaw("tempo").catch(() => 0n)
1519
+ ]);
1520
+ const raw = baseRaw + tempoRaw;
1521
+ return { raw, usdc: formatUnits(raw, 6) };
1522
+ };
1523
+ // Sweeps all USDC from the configured wallet into `to` across Base and Tempo.
1524
+ // Each leg is independent and never throws — failures are captured in the
1525
+ // returned per-chain result so the caller can decide whether to retire the
1526
+ // source key.
1527
+ migrateAllUsdc = async (to, relayer) => {
1528
+ const baseResult = await this.sweepBaseUsdc(to, relayer);
1529
+ const tempoResult = await this.sweepTempoUsdc(to);
1530
+ return [baseResult, tempoResult];
1531
+ };
1532
+ // Base leg: sign an EIP-3009 transferWithAuthorization for the full balance
1533
+ // and hand it to the relayer, which broadcasts it gaslessly.
1534
+ sweepBaseUsdc = async (to, relayer) => {
1535
+ try {
1536
+ if (!this.account) throw new Error("No wallet configured");
1537
+ const balance = await this.getBalanceRaw("base");
1538
+ if (balance <= 0n) return { chain: "base", status: "skipped" };
1539
+ const nowS = Math.floor(Date.now() / 1e3);
1540
+ const message = {
1541
+ from: this.account.address,
1542
+ to,
1543
+ value: balance,
1544
+ validAfter: 0n,
1545
+ validBefore: BigInt(nowS + SWEEP_AUTHORIZATION_TTL_S),
1546
+ nonce: `0x${randomBytes(32).toString("hex")}`
1547
+ };
1548
+ const signature = await this.account.signTypedData({
1549
+ domain: {
1550
+ name: USDC_BASE_SWEEP_DOMAIN.name,
1551
+ version: USDC_BASE_SWEEP_DOMAIN.version,
1552
+ chainId: BASE_CHAIN_ID,
1553
+ verifyingContract: USDC_BASE
1554
+ },
1555
+ types: EIP3009_TRANSFER_TYPES,
1556
+ primaryType: "TransferWithAuthorization",
1557
+ message
1558
+ });
1559
+ const { transactionHash } = await relayer.migrateWallet({
1560
+ from: message.from,
1561
+ to: message.to,
1562
+ value: message.value.toString(),
1563
+ validAfter: message.validAfter.toString(),
1564
+ validBefore: message.validBefore.toString(),
1565
+ nonce: message.nonce,
1566
+ signature
1567
+ });
1568
+ return {
1569
+ chain: "base",
1570
+ status: "swept",
1571
+ amount: formatUnits(balance, 6),
1572
+ txHash: transactionHash
1573
+ };
1574
+ } catch (err) {
1575
+ return { chain: "base", status: "failed", error: err.message };
1576
+ }
1577
+ };
1578
+ // Tempo leg: self-broadcast an ERC-20 transfer paying the fee in USDC via
1579
+ // `feeToken` (no native gas token needed). Uses viem's tempo chain for its
1580
+ // type-0x76 serializer; reserves a tiny amount to cover the fee.
1581
+ sweepTempoUsdc = async (to) => {
1582
+ try {
1583
+ if (!this.account) throw new Error("No wallet configured");
1584
+ const balance = await this.getBalanceRaw("tempo");
1585
+ if (balance <= TEMPO_FEE_RESERVE_RAW) {
1586
+ return { chain: "tempo", status: "skipped" };
1587
+ }
1588
+ const amount = balance - TEMPO_FEE_RESERVE_RAW;
1589
+ const wallet = createWalletClient({
1590
+ account: this.account,
1591
+ chain: viemTempoChain,
1592
+ transport: http()
1593
+ });
1594
+ const txHash = await wallet.writeContract({
1595
+ address: USDC_TEMPO,
1596
+ abi: ERC20_TRANSFER_ABI,
1597
+ functionName: "transfer",
1598
+ args: [to, amount],
1599
+ feeToken: USDC_TEMPO
1600
+ // biome-ignore lint/suspicious/noExplicitAny: tempo feeToken param
1601
+ });
1602
+ const publicClient = createPublicClient({
1603
+ chain: viemTempoChain,
1604
+ transport: http()
1605
+ });
1606
+ await publicClient.waitForTransactionReceipt({ hash: txHash });
1607
+ return {
1608
+ chain: "tempo",
1609
+ status: "swept",
1610
+ amount: formatUnits(amount, 6),
1611
+ txHash
1612
+ };
1613
+ } catch (err) {
1614
+ return {
1615
+ chain: "tempo",
1616
+ status: "failed",
1617
+ error: err.message
1618
+ };
1619
+ }
1620
+ };
1478
1621
  };
1479
1622
 
1480
1623
  // src/util/infer-schema.ts
@@ -1726,55 +1869,6 @@ var fetchCommand = (appContext) => new Command4("fetch").description(
1726
1869
  } else {
1727
1870
  resolvedUrl = url;
1728
1871
  }
1729
- if (!url && options.capability && !stateService.findSearchContextByCapability(options.capability)) {
1730
- try {
1731
- let capName = resolvedCapabilityName;
1732
- if (capName === void 0) {
1733
- const cap = await apiService.getCapability(options.capability);
1734
- capName = cap.name;
1735
- }
1736
- const searchResult = await apiService.search({ query: capName });
1737
- const matchedEntry = searchResult.capabilities.find(
1738
- (c) => c.slug === options.capability || c.id === options.capability
1739
- );
1740
- const slugFoundInResults = Boolean(matchedEntry);
1741
- stateService.saveLastSearch({
1742
- searchId: searchResult.searchId,
1743
- capabilities: searchResult.capabilities.map((c) => ({
1744
- position: c.position,
1745
- id: c.id,
1746
- slug: c.slug,
1747
- url: c.url,
1748
- urlTemplate: c.urlTemplate ?? null,
1749
- displayCostAmount: c.cost.amount
1750
- }))
1751
- });
1752
- analyticsService.capture("search_executed", {
1753
- query: truncateQuery(capName),
1754
- queryLength: capName.length,
1755
- resultCount: searchResult.capabilities.length,
1756
- searchId: searchResult.searchId,
1757
- total: searchResult.total,
1758
- hasMore: searchResult.hasMore,
1759
- json: false,
1760
- triggeredBy: "slug_handoff",
1761
- slugFoundInResults
1762
- });
1763
- analyticsService.capture("capability_viewed", {
1764
- // Existing field preserved as the raw --capability
1765
- // input for back-compat.
1766
- capabilityId: options.capability,
1767
- // New canonical identifier fields hydrated from the
1768
- // resolved search result (when the slug was found).
1769
- capabilityUid: matchedEntry?.id,
1770
- capabilitySlug: matchedEntry?.slug,
1771
- fromLastSearch: false,
1772
- searchId: searchResult.searchId,
1773
- triggeredBy: "slug_handoff"
1774
- });
1775
- } catch {
1776
- }
1777
- }
1778
1872
  let resolvedBody;
1779
1873
  try {
1780
1874
  resolvedBody = resolveRequestBody(
@@ -1830,6 +1924,7 @@ var fetchCommand = (appContext) => new Command4("fetch").description(
1830
1924
  const capabilitySlug = matchCtx?.capabilitySlug ?? null;
1831
1925
  const searchId = matchCtx?.searchId;
1832
1926
  const resultRank = matchCtx?.resultRank;
1927
+ const fetchOrigin = matchCtx ? "from_search" : options.capability ? "direct_slug" : "direct_url";
1833
1928
  const matchedDisplayCostAmount = matchCtx?.displayCostAmount;
1834
1929
  const skipReasons = [];
1835
1930
  if (!apiService.walletAddress) {
@@ -2047,6 +2142,9 @@ var fetchCommand = (appContext) => new Command4("fetch").description(
2047
2142
  resultRank: resultRank ?? void 0,
2048
2143
  runId: runId ?? void 0,
2049
2144
  runTracked: !!runId,
2145
+ // A NULL searchId on a direct_slug fetch is correct here,
2146
+ // not a funnel gap to paper over.
2147
+ fetchOrigin,
2050
2148
  ...fetchError && { error: truncateError(fetchError.message) }
2051
2149
  });
2052
2150
  const isFetchFailure = Boolean(fetchError) || !finalResponse || typeof status === "number" && (status < 200 || status >= 300);
@@ -3465,14 +3563,30 @@ Read the full terms at: ${TERMS_URL}
3465
3563
  });
3466
3564
 
3467
3565
  // src/commands/wallet-command.ts
3468
- import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
3566
+ import { existsSync as existsSync3, readFileSync as readFileSync8 } from "fs";
3469
3567
  import { homedir as homedir4 } from "os";
3470
- import { join as join4 } from "path";
3568
+ import { join as join5 } from "path";
3569
+ import { confirm, isCancel } from "@clack/prompts";
3471
3570
  import { Command as Command11 } from "commander";
3472
3571
  import open2 from "open";
3473
3572
  import { isAddress } from "viem";
3474
3573
  import { generatePrivateKey as generatePrivateKey2, privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
3475
3574
 
3575
+ // src/util/migrate-config.ts
3576
+ import { readFileSync as readFileSync7 } from "fs";
3577
+ import { join as join4 } from "path";
3578
+ var backupAndStripPrivateKey = (zeroDir) => {
3579
+ const configPath = join4(zeroDir, "config.json");
3580
+ const backupPath = join4(zeroDir, "config.backup.json");
3581
+ const raw = readFileSync7(configPath, "utf8");
3582
+ ensureSecureDir(zeroDir);
3583
+ writeSecureFile(backupPath, raw);
3584
+ const parsed = JSON.parse(raw);
3585
+ delete parsed.privateKey;
3586
+ writeSecureFile(configPath, JSON.stringify(parsed, null, 2));
3587
+ return { backupPath, configPath };
3588
+ };
3589
+
3476
3590
  // src/util/stdin.ts
3477
3591
  var readStdin = async () => {
3478
3592
  const chunks = [];
@@ -3656,11 +3770,11 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
3656
3770
  process.exitCode = 1;
3657
3771
  return;
3658
3772
  }
3659
- const zeroDir = join4(homedir4(), ".zero");
3660
- const configPath = join4(zeroDir, "config.json");
3773
+ const zeroDir = join5(homedir4(), ".zero");
3774
+ const configPath = join5(zeroDir, "config.json");
3661
3775
  if (!options.force && existsSync3(configPath)) {
3662
3776
  try {
3663
- const existing2 = JSON.parse(readFileSync7(configPath, "utf8"));
3777
+ const existing2 = JSON.parse(readFileSync8(configPath, "utf8"));
3664
3778
  if (existing2.privateKey) {
3665
3779
  console.error(
3666
3780
  "Wallet already configured. Use --force to overwrite."
@@ -3672,7 +3786,7 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
3672
3786
  }
3673
3787
  }
3674
3788
  ensureSecureDir(zeroDir);
3675
- const existing = existsSync3(configPath) ? JSON.parse(readFileSync7(configPath, "utf8")) : {};
3789
+ const existing = existsSync3(configPath) ? JSON.parse(readFileSync8(configPath, "utf8")) : {};
3676
3790
  writeSecureFile(
3677
3791
  configPath,
3678
3792
  JSON.stringify(
@@ -3768,6 +3882,142 @@ var walletGenerateCommand = (appContext) => new Command11("generate").descriptio
3768
3882
  });
3769
3883
  }
3770
3884
  );
3885
+ var resolveZeroWalletAddress = async (apiService) => {
3886
+ try {
3887
+ const wallets = await apiService.getWallets();
3888
+ const primary = wallets.find(
3889
+ (w) => w.source === "privy_embedded" && w.isPrimary
3890
+ );
3891
+ if (primary) return primary.walletAddress;
3892
+ const provisioned = await apiService.provisionWallet();
3893
+ return provisioned.walletAddress ?? null;
3894
+ } catch {
3895
+ return null;
3896
+ }
3897
+ };
3898
+ var walletMigrateCommand = (appContext) => new Command11("migrate").description(
3899
+ "Sweep all USDC from your private-key wallet into your Zero wallet"
3900
+ ).option("--json", "Emit the migration result as JSON").option("-y, --yes", "Skip the confirmation prompt").action(async (options) => {
3901
+ 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) : {};
3905
+ const privateKey = appContext.env.ZERO_PRIVATE_KEY ?? config.privateKey;
3906
+ if (!privateKey) {
3907
+ console.error(
3908
+ "No private-key wallet found. Run `zero wallet set <key>` first."
3909
+ );
3910
+ process.exitCode = 1;
3911
+ return;
3912
+ }
3913
+ const hasSession = Boolean(
3914
+ appContext.env.ZERO_SESSION_TOKEN || config.session
3915
+ );
3916
+ if (!hasSession) {
3917
+ console.error("Not logged in. Run `zero auth login` first.");
3918
+ process.exitCode = 1;
3919
+ return;
3920
+ }
3921
+ let source;
3922
+ try {
3923
+ source = privateKeyToAccount2(privateKey);
3924
+ } catch {
3925
+ console.error("Invalid private key in config.");
3926
+ process.exitCode = 1;
3927
+ return;
3928
+ }
3929
+ const to = await resolveZeroWalletAddress(apiService);
3930
+ if (!to) {
3931
+ console.error(
3932
+ "Could not resolve your Zero wallet. Run `zero auth login` and try again."
3933
+ );
3934
+ process.exitCode = 1;
3935
+ return;
3936
+ }
3937
+ if (to.toLowerCase() === source.address.toLowerCase()) {
3938
+ console.error(
3939
+ "Source and destination are the same wallet \u2014 nothing to migrate."
3940
+ );
3941
+ process.exitCode = 1;
3942
+ return;
3943
+ }
3944
+ const balance = await paymentService.getSweepableBalance();
3945
+ if (balance.raw <= 0n) {
3946
+ if (options.json) {
3947
+ console.log(
3948
+ JSON.stringify({ destination: to, legs: [], keyRemoved: false })
3949
+ );
3950
+ } else {
3951
+ console.log("Nothing to migrate (no USDC found).");
3952
+ }
3953
+ return;
3954
+ }
3955
+ if (!options.yes) {
3956
+ if (options.json || !process.stdin.isTTY) {
3957
+ console.error(
3958
+ "Refusing to migrate without confirmation. Re-run with --yes to proceed."
3959
+ );
3960
+ process.exitCode = 1;
3961
+ return;
3962
+ }
3963
+ const proceed = await confirm({
3964
+ message: `Please confirm you would like to transfer $${balance.usdc} from your private wallet to your Zero managed wallet`
3965
+ });
3966
+ if (isCancel(proceed) || !proceed) {
3967
+ console.log("Migration cancelled.");
3968
+ return;
3969
+ }
3970
+ }
3971
+ const results = await paymentService.migrateAllUsdc(to, apiService);
3972
+ const anyFailed = results.some((r) => r.status === "failed");
3973
+ const anySwept = results.some((r) => r.status === "swept");
3974
+ let backupPath = null;
3975
+ if (anySwept && !anyFailed && config.privateKey) {
3976
+ try {
3977
+ backupPath = backupAndStripPrivateKey(zeroDir).backupPath;
3978
+ } catch {
3979
+ backupPath = null;
3980
+ }
3981
+ }
3982
+ if (options.json) {
3983
+ console.log(
3984
+ JSON.stringify({
3985
+ destination: to,
3986
+ legs: results,
3987
+ keyRemoved: backupPath !== null,
3988
+ backupPath
3989
+ })
3990
+ );
3991
+ } else {
3992
+ console.log(`Migrating USDC \u2192 ${to}`);
3993
+ for (const r of results) {
3994
+ if (r.status === "swept") {
3995
+ console.log(
3996
+ ` ${r.chain}: swept ${r.amount} USDC (tx ${r.txHash})`
3997
+ );
3998
+ } else if (r.status === "skipped") {
3999
+ console.log(` ${r.chain}: skipped (no balance)`);
4000
+ } else {
4001
+ console.log(` ${r.chain}: FAILED \u2014 ${r.error}`);
4002
+ }
4003
+ }
4004
+ if (backupPath) {
4005
+ console.log("");
4006
+ console.log(
4007
+ `Private key removed from config (backed up to ${backupPath}). Your Zero wallet is now primary.`
4008
+ );
4009
+ } else if (anyFailed) {
4010
+ console.log("");
4011
+ console.log(
4012
+ "A transfer failed \u2014 your private key was kept. Re-run `zero wallet migrate` to retry."
4013
+ );
4014
+ } else if (!anySwept) {
4015
+ console.log("");
4016
+ console.log("Nothing to migrate (no USDC found).");
4017
+ }
4018
+ }
4019
+ if (anyFailed) process.exitCode = 1;
4020
+ });
3771
4021
  var walletCommand = (appContext) => {
3772
4022
  const cmd = new Command11("wallet").description("Manage your wallet");
3773
4023
  cmd.addCommand(walletBalanceCommand(appContext));
@@ -3775,22 +4025,23 @@ var walletCommand = (appContext) => {
3775
4025
  cmd.addCommand(walletAddressCommand(appContext));
3776
4026
  cmd.addCommand(walletSetCommand(appContext));
3777
4027
  cmd.addCommand(walletGenerateCommand(appContext));
4028
+ cmd.addCommand(walletMigrateCommand(appContext), { hidden: true });
3778
4029
  return cmd;
3779
4030
  };
3780
4031
 
3781
4032
  // src/commands/welcome-command.ts
3782
- import { existsSync as existsSync4, readFileSync as readFileSync8 } from "fs";
4033
+ import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
3783
4034
  import { homedir as homedir5 } from "os";
3784
- import { join as join5 } from "path";
4035
+ import { join as join6 } from "path";
3785
4036
  import { Command as Command12 } from "commander";
3786
4037
  import open3 from "open";
3787
4038
  import { getAddress } from "viem";
3788
4039
  import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
3789
4040
  var readPrivateKey = () => {
3790
- const configPath = join5(homedir5(), ".zero", "config.json");
4041
+ const configPath = join6(homedir5(), ".zero", "config.json");
3791
4042
  if (!existsSync4(configPath)) return null;
3792
4043
  try {
3793
- const config = JSON.parse(readFileSync8(configPath, "utf8"));
4044
+ const config = JSON.parse(readFileSync9(configPath, "utf8"));
3794
4045
  if (typeof config.privateKey === "string") {
3795
4046
  return config.privateKey;
3796
4047
  }
@@ -3936,12 +4187,12 @@ var getEnv = () => {
3936
4187
  import { randomUUID as randomUUID2 } from "crypto";
3937
4188
  import { existsSync as existsSync7 } from "fs";
3938
4189
  import { homedir as homedir6 } from "os";
3939
- import { join as join7 } from "path";
4190
+ import { join as join8 } from "path";
3940
4191
  import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
3941
4192
 
3942
4193
  // src/services/analytics-service.ts
3943
4194
  import { randomUUID } from "crypto";
3944
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
4195
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
3945
4196
  import { dirname as dirname2 } from "path";
3946
4197
  import { PostHog } from "posthog-node";
3947
4198
  var POSTHOG_API_KEY = "phc_B2vLyNxAf2mnqvdPQajf4d4b2iXc35dep2ZrvebMJLuX";
@@ -3964,7 +4215,7 @@ var AnalyticsService = class {
3964
4215
  let persistedAnonId;
3965
4216
  try {
3966
4217
  if (existsSync5(opts.configPath)) {
3967
- const config = JSON.parse(readFileSync9(opts.configPath, "utf8"));
4218
+ const config = JSON.parse(readFileSync10(opts.configPath, "utf8"));
3968
4219
  if (config.telemetry === false) {
3969
4220
  telemetryEnabled = false;
3970
4221
  }
@@ -3990,7 +4241,7 @@ var AnalyticsService = class {
3990
4241
  try {
3991
4242
  const dir = dirname2(opts.configPath);
3992
4243
  mkdirSync4(dir, { recursive: true });
3993
- const existing = existsSync5(opts.configPath) ? JSON.parse(readFileSync9(opts.configPath, "utf8")) : {};
4244
+ const existing = existsSync5(opts.configPath) ? JSON.parse(readFileSync10(opts.configPath, "utf8")) : {};
3994
4245
  writeFileSync4(
3995
4246
  opts.configPath,
3996
4247
  JSON.stringify({ ...existing, anonId: newAnonId }, null, 2)
@@ -4031,7 +4282,7 @@ var AnalyticsService = class {
4031
4282
  if (anonId === walletAddress) return;
4032
4283
  let aliasedTo;
4033
4284
  try {
4034
- const config = JSON.parse(readFileSync9(configPath, "utf8"));
4285
+ const config = JSON.parse(readFileSync10(configPath, "utf8"));
4035
4286
  if (typeof config.aliasedTo === "string") {
4036
4287
  aliasedTo = config.aliasedTo;
4037
4288
  }
@@ -4041,7 +4292,7 @@ var AnalyticsService = class {
4041
4292
  this.posthog.alias({ distinctId: walletAddress, alias: anonId });
4042
4293
  if (process.env.VITEST) return;
4043
4294
  try {
4044
- const config = existsSync5(configPath) ? JSON.parse(readFileSync9(configPath, "utf8")) : {};
4295
+ const config = existsSync5(configPath) ? JSON.parse(readFileSync10(configPath, "utf8")) : {};
4045
4296
  writeFileSync4(
4046
4297
  configPath,
4047
4298
  JSON.stringify({ ...config, aliasedTo: walletAddress }, null, 2)
@@ -4145,14 +4396,14 @@ var createApiAccount = (walletAddress, api) => toAccount({
4145
4396
  });
4146
4397
 
4147
4398
  // src/services/state-service.ts
4148
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
4149
- import { join as join6 } from "path";
4399
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
4400
+ import { join as join7 } from "path";
4150
4401
  var RECENT_SEARCH_LIMIT = 10;
4151
4402
  var StateService = class {
4152
4403
  constructor(zeroDir) {
4153
4404
  this.zeroDir = zeroDir;
4154
- this.lastSearchPath = join6(zeroDir, "last_search.json");
4155
- this.recentSearchesPath = join6(zeroDir, "recent_searches.json");
4405
+ this.lastSearchPath = join7(zeroDir, "last_search.json");
4406
+ this.recentSearchesPath = join7(zeroDir, "recent_searches.json");
4156
4407
  }
4157
4408
  lastSearchPath;
4158
4409
  recentSearchesPath;
@@ -4172,7 +4423,7 @@ var StateService = class {
4172
4423
  loadLastSearch = () => {
4173
4424
  try {
4174
4425
  if (!existsSync6(this.lastSearchPath)) return null;
4175
- const raw = readFileSync10(this.lastSearchPath, "utf8");
4426
+ const raw = readFileSync11(this.lastSearchPath, "utf8");
4176
4427
  return JSON.parse(raw);
4177
4428
  } catch {
4178
4429
  return null;
@@ -4184,7 +4435,7 @@ var StateService = class {
4184
4435
  const last = this.loadLastSearch();
4185
4436
  return { searches: last ? [last] : [] };
4186
4437
  }
4187
- const raw = readFileSync10(this.recentSearchesPath, "utf8");
4438
+ const raw = readFileSync11(this.recentSearchesPath, "utf8");
4188
4439
  const parsed = JSON.parse(raw);
4189
4440
  return { searches: parsed.searches ?? [] };
4190
4441
  } catch {
@@ -4312,8 +4563,8 @@ var buildOnSessionRefreshed = (configPath) => async (tokens) => {
4312
4563
  writeSecureFile(configPath, JSON.stringify(next, null, 2));
4313
4564
  };
4314
4565
  var getServices = async (env) => {
4315
- const zeroDir = join7(homedir6(), ".zero");
4316
- const configPath = join7(zeroDir, "config.json");
4566
+ const zeroDir = join8(homedir6(), ".zero");
4567
+ const configPath = join8(zeroDir, "config.json");
4317
4568
  const config = existsSync7(configPath) ? readConfig(configPath) : {};
4318
4569
  const { credentials, privateKey } = resolveCredentials(env, config);
4319
4570
  const lowBalanceWarning = typeof config.lowBalanceWarning === "number" ? config.lowBalanceWarning : 1;
@@ -4324,20 +4575,17 @@ var getServices = async (env) => {
4324
4575
  buildOnSessionRefreshed(configPath)
4325
4576
  );
4326
4577
  let account = privateKey ? privateKeyToAccount4(privateKey) : null;
4327
- let managed = false;
4328
4578
  if (!account && credentials.kind === "session") {
4329
4579
  const address = await resolveManagedWalletAddress(apiService);
4330
4580
  if (address) {
4331
4581
  account = createApiAccount(address, apiService);
4332
- managed = true;
4333
4582
  }
4334
4583
  }
4335
4584
  if (account && !apiService.walletAddress) {
4336
4585
  apiService.setWalletAddress(account.address);
4337
4586
  }
4338
4587
  const paymentService = new PaymentService(account, {
4339
- lowBalanceWarning,
4340
- managed
4588
+ lowBalanceWarning
4341
4589
  });
4342
4590
  const stateService = new StateService(zeroDir);
4343
4591
  const walletService = new WalletService(
@@ -4393,12 +4641,12 @@ import {
4393
4641
  existsSync as existsSync8,
4394
4642
  lstatSync,
4395
4643
  mkdirSync as mkdirSync6,
4396
- readFileSync as readFileSync11,
4644
+ readFileSync as readFileSync12,
4397
4645
  readlinkSync,
4398
4646
  writeFileSync as writeFileSync6
4399
4647
  } from "fs";
4400
4648
  import { homedir as homedir7 } from "os";
4401
- import { dirname as dirname3, join as join8, resolve } from "path";
4649
+ import { dirname as dirname3, join as join9, resolve } from "path";
4402
4650
  var CACHE_FILENAME = "update_check.json";
4403
4651
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/@zeroxyz/cli/latest";
4404
4652
  var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
@@ -4425,7 +4673,7 @@ var detectInstallMethod = (opts = {}) => {
4425
4673
  const home = opts.home ?? homedir7();
4426
4674
  if (pkg) return "binary";
4427
4675
  const resolved = resolveExecPath(execPath);
4428
- const zeroBin = join8(home, ".zero", "bin");
4676
+ const zeroBin = join9(home, ".zero", "bin");
4429
4677
  if (resolved.startsWith(zeroBin)) return "binary";
4430
4678
  return "npm";
4431
4679
  };
@@ -4450,12 +4698,12 @@ var compareVersions = (a, b) => {
4450
4698
  if (pb.pre === null) return -1;
4451
4699
  return pa.pre < pb.pre ? -1 : 1;
4452
4700
  };
4453
- var cachePath = (zeroDir) => join8(zeroDir, CACHE_FILENAME);
4701
+ var cachePath = (zeroDir) => join9(zeroDir, CACHE_FILENAME);
4454
4702
  var readCache = (zeroDir) => {
4455
4703
  try {
4456
4704
  const path = cachePath(zeroDir);
4457
4705
  if (!existsSync8(path)) return emptyCache;
4458
- const raw = readFileSync11(path, "utf8");
4706
+ const raw = readFileSync12(path, "utf8");
4459
4707
  const parsed = JSON.parse(raw);
4460
4708
  return {
4461
4709
  lastCheckedMs: typeof parsed.lastCheckedMs === "number" ? parsed.lastCheckedMs : 0,
@@ -4544,7 +4792,7 @@ var main = async () => {
4544
4792
  console.error("Failed to create app context");
4545
4793
  process.exit(1);
4546
4794
  }
4547
- const zeroDir = join9(homedir8(), ".zero");
4795
+ const zeroDir = join10(homedir8(), ".zero");
4548
4796
  maybePrintUpdateBanner(zeroDir, package_default.version);
4549
4797
  const app = createApp(appContext);
4550
4798
  let caughtError = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",