@zeroxyz/cli 0.0.40 → 0.0.42
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 +667 -151
- 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 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.
|
|
10
|
+
version: "0.0.42",
|
|
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
|
|
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", "
|
|
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", "
|
|
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(),
|
|
@@ -360,6 +358,9 @@ var signResultSchema = z.object({
|
|
|
360
358
|
signature: z.string(),
|
|
361
359
|
walletAddress: z.string()
|
|
362
360
|
});
|
|
361
|
+
var migrateResultSchema = z.object({
|
|
362
|
+
transactionHash: z.string()
|
|
363
|
+
});
|
|
363
364
|
var deviceStartResultSchema = z.object({
|
|
364
365
|
deviceCode: z.string(),
|
|
365
366
|
userCode: z.string(),
|
|
@@ -583,16 +584,36 @@ var ApiService = class _ApiService {
|
|
|
583
584
|
});
|
|
584
585
|
return createBugReportResponseSchema.parse(json);
|
|
585
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. Honors the `provider` choice.
|
|
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. Coinbase
|
|
593
|
+
// only (the managed onramp doesn't take a provider), so `provider` is
|
|
594
|
+
// ignored on this path.
|
|
595
|
+
// Returns null on any failure so callers can fall back to manual transfer.
|
|
586
596
|
getFundingUrl = async (amount, provider = "coinbase") => {
|
|
587
597
|
try {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
598
|
+
if (this.account) {
|
|
599
|
+
const params = new URLSearchParams({ provider });
|
|
600
|
+
if (amount) params.set("amount", amount);
|
|
601
|
+
const json = await this.request(
|
|
602
|
+
"GET",
|
|
603
|
+
`/v1/wallet/fund-url?${params.toString()}`,
|
|
604
|
+
void 0,
|
|
605
|
+
{ auth: "wallet-attributed" }
|
|
606
|
+
);
|
|
607
|
+
return z.object({ url: z.string() }).parse(json).url;
|
|
608
|
+
}
|
|
609
|
+
if (this.credentials.kind === "session") {
|
|
610
|
+
const params = new URLSearchParams();
|
|
611
|
+
if (amount) params.set("amount", amount);
|
|
612
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
613
|
+
const json = await this.request("GET", `/v1/users/me/fund-url${qs}`);
|
|
614
|
+
return z.object({ url: z.string(), walletAddress: z.string() }).parse(json).url;
|
|
615
|
+
}
|
|
616
|
+
return null;
|
|
596
617
|
} catch {
|
|
597
618
|
return null;
|
|
598
619
|
}
|
|
@@ -605,6 +626,15 @@ var ApiService = class _ApiService {
|
|
|
605
626
|
const json = await this.request("POST", "/v1/users/me/wallets/provision");
|
|
606
627
|
return userWalletDtoSchema.parse(json);
|
|
607
628
|
};
|
|
629
|
+
// Hands a BYO-wallet-signed EIP-3009 authorization to the server, which
|
|
630
|
+
// broadcasts it on Base via the gas-paying relayer (sweeping USDC into the
|
|
631
|
+
// caller's provisioned wallet). Session-authed.
|
|
632
|
+
migrateWallet = async (authorization) => {
|
|
633
|
+
const json = await this.request("POST", "/v1/users/me/wallets/migrate", {
|
|
634
|
+
authorization
|
|
635
|
+
});
|
|
636
|
+
return migrateResultSchema.parse(json);
|
|
637
|
+
};
|
|
608
638
|
signTypedDataRemote = async (typedData) => {
|
|
609
639
|
const json = await this.request("POST", "/v1/users/me/sign-typed-data", {
|
|
610
640
|
typedData
|
|
@@ -931,6 +961,7 @@ import { Command as Command4 } from "commander";
|
|
|
931
961
|
import { formatUnits as formatUnits2 } from "viem";
|
|
932
962
|
|
|
933
963
|
// src/services/payment-service.ts
|
|
964
|
+
import { randomBytes } from "crypto";
|
|
934
965
|
import {
|
|
935
966
|
adaptViemWallet,
|
|
936
967
|
convertViemChainToRelayChain,
|
|
@@ -951,7 +982,7 @@ import {
|
|
|
951
982
|
formatUnits,
|
|
952
983
|
http
|
|
953
984
|
} from "viem";
|
|
954
|
-
import { base, baseSepolia } from "viem/chains";
|
|
985
|
+
import { base, baseSepolia, tempo as viemTempoChain } from "viem/chains";
|
|
955
986
|
var SessionCloseFailedError = class extends Error {
|
|
956
987
|
session;
|
|
957
988
|
response;
|
|
@@ -1025,6 +1056,32 @@ var ERC20_BALANCE_ABI = [
|
|
|
1025
1056
|
type: "function"
|
|
1026
1057
|
}
|
|
1027
1058
|
];
|
|
1059
|
+
var ERC20_TRANSFER_ABI = [
|
|
1060
|
+
{
|
|
1061
|
+
inputs: [
|
|
1062
|
+
{ name: "to", type: "address" },
|
|
1063
|
+
{ name: "amount", type: "uint256" }
|
|
1064
|
+
],
|
|
1065
|
+
name: "transfer",
|
|
1066
|
+
outputs: [{ name: "", type: "bool" }],
|
|
1067
|
+
stateMutability: "nonpayable",
|
|
1068
|
+
type: "function"
|
|
1069
|
+
}
|
|
1070
|
+
];
|
|
1071
|
+
var USDC_BASE_SWEEP_DOMAIN = { name: "USD Coin", version: "2" };
|
|
1072
|
+
var SWEEP_AUTHORIZATION_TTL_S = 900;
|
|
1073
|
+
var TEMPO_FEE_RESERVE_RAW = 10000n;
|
|
1074
|
+
var EIP3009_TRANSFER_TYPES = {
|
|
1075
|
+
// biome-ignore lint/style/useNamingConvention: EIP-712 primaryType must match the on-chain type name
|
|
1076
|
+
TransferWithAuthorization: [
|
|
1077
|
+
{ name: "from", type: "address" },
|
|
1078
|
+
{ name: "to", type: "address" },
|
|
1079
|
+
{ name: "value", type: "uint256" },
|
|
1080
|
+
{ name: "validAfter", type: "uint256" },
|
|
1081
|
+
{ name: "validBefore", type: "uint256" },
|
|
1082
|
+
{ name: "nonce", type: "bytes32" }
|
|
1083
|
+
]
|
|
1084
|
+
};
|
|
1028
1085
|
var decodeSessionReceiptHeader = (header) => {
|
|
1029
1086
|
if (!header) return null;
|
|
1030
1087
|
try {
|
|
@@ -1231,11 +1288,6 @@ var PaymentService = class {
|
|
|
1231
1288
|
`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
1289
|
);
|
|
1233
1290
|
}
|
|
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
1291
|
await this.bridgeToTempo(requiredRaw, onProgress);
|
|
1240
1292
|
}
|
|
1241
1293
|
return capturedAmount;
|
|
@@ -1475,6 +1527,115 @@ var PaymentService = class {
|
|
|
1475
1527
|
]);
|
|
1476
1528
|
return { amount: formatUnits(baseRaw + tempoRaw, 6), asset: "USDC" };
|
|
1477
1529
|
};
|
|
1530
|
+
// Total USDC across both legs (Base + Tempo) for the configured wallet — what
|
|
1531
|
+
// the migrate confirmation prompt quotes. Best-effort: a chain whose RPC read
|
|
1532
|
+
// fails counts as 0 rather than blocking the other leg.
|
|
1533
|
+
getSweepableBalance = async () => {
|
|
1534
|
+
const [baseRaw, tempoRaw] = await Promise.all([
|
|
1535
|
+
this.getBalanceRaw("base").catch(() => 0n),
|
|
1536
|
+
this.getBalanceRaw("tempo").catch(() => 0n)
|
|
1537
|
+
]);
|
|
1538
|
+
const raw = baseRaw + tempoRaw;
|
|
1539
|
+
return { raw, usdc: formatUnits(raw, 6) };
|
|
1540
|
+
};
|
|
1541
|
+
// Sweeps all USDC from the configured wallet into `to` across Base and Tempo.
|
|
1542
|
+
// Each leg is independent and never throws — failures are captured in the
|
|
1543
|
+
// returned per-chain result so the caller can decide whether to retire the
|
|
1544
|
+
// source key.
|
|
1545
|
+
migrateAllUsdc = async (to, relayer) => {
|
|
1546
|
+
const baseResult = await this.sweepBaseUsdc(to, relayer);
|
|
1547
|
+
const tempoResult = await this.sweepTempoUsdc(to);
|
|
1548
|
+
return [baseResult, tempoResult];
|
|
1549
|
+
};
|
|
1550
|
+
// Base leg: sign an EIP-3009 transferWithAuthorization for the full balance
|
|
1551
|
+
// and hand it to the relayer, which broadcasts it gaslessly.
|
|
1552
|
+
sweepBaseUsdc = async (to, relayer) => {
|
|
1553
|
+
try {
|
|
1554
|
+
if (!this.account) throw new Error("No wallet configured");
|
|
1555
|
+
const balance = await this.getBalanceRaw("base");
|
|
1556
|
+
if (balance <= 0n) return { chain: "base", status: "skipped" };
|
|
1557
|
+
const nowS = Math.floor(Date.now() / 1e3);
|
|
1558
|
+
const message = {
|
|
1559
|
+
from: this.account.address,
|
|
1560
|
+
to,
|
|
1561
|
+
value: balance,
|
|
1562
|
+
validAfter: 0n,
|
|
1563
|
+
validBefore: BigInt(nowS + SWEEP_AUTHORIZATION_TTL_S),
|
|
1564
|
+
nonce: `0x${randomBytes(32).toString("hex")}`
|
|
1565
|
+
};
|
|
1566
|
+
const signature = await this.account.signTypedData({
|
|
1567
|
+
domain: {
|
|
1568
|
+
name: USDC_BASE_SWEEP_DOMAIN.name,
|
|
1569
|
+
version: USDC_BASE_SWEEP_DOMAIN.version,
|
|
1570
|
+
chainId: BASE_CHAIN_ID,
|
|
1571
|
+
verifyingContract: USDC_BASE
|
|
1572
|
+
},
|
|
1573
|
+
types: EIP3009_TRANSFER_TYPES,
|
|
1574
|
+
primaryType: "TransferWithAuthorization",
|
|
1575
|
+
message
|
|
1576
|
+
});
|
|
1577
|
+
const { transactionHash } = await relayer.migrateWallet({
|
|
1578
|
+
from: message.from,
|
|
1579
|
+
to: message.to,
|
|
1580
|
+
value: message.value.toString(),
|
|
1581
|
+
validAfter: message.validAfter.toString(),
|
|
1582
|
+
validBefore: message.validBefore.toString(),
|
|
1583
|
+
nonce: message.nonce,
|
|
1584
|
+
signature
|
|
1585
|
+
});
|
|
1586
|
+
return {
|
|
1587
|
+
chain: "base",
|
|
1588
|
+
status: "swept",
|
|
1589
|
+
amount: formatUnits(balance, 6),
|
|
1590
|
+
txHash: transactionHash
|
|
1591
|
+
};
|
|
1592
|
+
} catch (err) {
|
|
1593
|
+
return { chain: "base", status: "failed", error: err.message };
|
|
1594
|
+
}
|
|
1595
|
+
};
|
|
1596
|
+
// Tempo leg: self-broadcast an ERC-20 transfer paying the fee in USDC via
|
|
1597
|
+
// `feeToken` (no native gas token needed). Uses viem's tempo chain for its
|
|
1598
|
+
// type-0x76 serializer; reserves a tiny amount to cover the fee.
|
|
1599
|
+
sweepTempoUsdc = async (to) => {
|
|
1600
|
+
try {
|
|
1601
|
+
if (!this.account) throw new Error("No wallet configured");
|
|
1602
|
+
const balance = await this.getBalanceRaw("tempo");
|
|
1603
|
+
if (balance <= TEMPO_FEE_RESERVE_RAW) {
|
|
1604
|
+
return { chain: "tempo", status: "skipped" };
|
|
1605
|
+
}
|
|
1606
|
+
const amount = balance - TEMPO_FEE_RESERVE_RAW;
|
|
1607
|
+
const wallet = createWalletClient({
|
|
1608
|
+
account: this.account,
|
|
1609
|
+
chain: viemTempoChain,
|
|
1610
|
+
transport: http()
|
|
1611
|
+
});
|
|
1612
|
+
const txHash = await wallet.writeContract({
|
|
1613
|
+
address: USDC_TEMPO,
|
|
1614
|
+
abi: ERC20_TRANSFER_ABI,
|
|
1615
|
+
functionName: "transfer",
|
|
1616
|
+
args: [to, amount],
|
|
1617
|
+
feeToken: USDC_TEMPO
|
|
1618
|
+
// biome-ignore lint/suspicious/noExplicitAny: tempo feeToken param
|
|
1619
|
+
});
|
|
1620
|
+
const publicClient = createPublicClient({
|
|
1621
|
+
chain: viemTempoChain,
|
|
1622
|
+
transport: http()
|
|
1623
|
+
});
|
|
1624
|
+
await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
1625
|
+
return {
|
|
1626
|
+
chain: "tempo",
|
|
1627
|
+
status: "swept",
|
|
1628
|
+
amount: formatUnits(amount, 6),
|
|
1629
|
+
txHash
|
|
1630
|
+
};
|
|
1631
|
+
} catch (err) {
|
|
1632
|
+
return {
|
|
1633
|
+
chain: "tempo",
|
|
1634
|
+
status: "failed",
|
|
1635
|
+
error: err.message
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
};
|
|
1478
1639
|
};
|
|
1479
1640
|
|
|
1480
1641
|
// src/util/infer-schema.ts
|
|
@@ -1726,55 +1887,6 @@ var fetchCommand = (appContext) => new Command4("fetch").description(
|
|
|
1726
1887
|
} else {
|
|
1727
1888
|
resolvedUrl = url;
|
|
1728
1889
|
}
|
|
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
1890
|
let resolvedBody;
|
|
1779
1891
|
try {
|
|
1780
1892
|
resolvedBody = resolveRequestBody(
|
|
@@ -1830,6 +1942,7 @@ var fetchCommand = (appContext) => new Command4("fetch").description(
|
|
|
1830
1942
|
const capabilitySlug = matchCtx?.capabilitySlug ?? null;
|
|
1831
1943
|
const searchId = matchCtx?.searchId;
|
|
1832
1944
|
const resultRank = matchCtx?.resultRank;
|
|
1945
|
+
const fetchOrigin = matchCtx ? "from_search" : options.capability ? "direct_slug" : "direct_url";
|
|
1833
1946
|
const matchedDisplayCostAmount = matchCtx?.displayCostAmount;
|
|
1834
1947
|
const skipReasons = [];
|
|
1835
1948
|
if (!apiService.walletAddress) {
|
|
@@ -2047,6 +2160,9 @@ var fetchCommand = (appContext) => new Command4("fetch").description(
|
|
|
2047
2160
|
resultRank: resultRank ?? void 0,
|
|
2048
2161
|
runId: runId ?? void 0,
|
|
2049
2162
|
runTracked: !!runId,
|
|
2163
|
+
// A NULL searchId on a direct_slug fetch is correct here,
|
|
2164
|
+
// not a funnel gap to paper over.
|
|
2165
|
+
fetchOrigin,
|
|
2050
2166
|
...fetchError && { error: truncateError(fetchError.message) }
|
|
2051
2167
|
});
|
|
2052
2168
|
const isFetchFailure = Boolean(fetchError) || !finalResponse || typeof status === "number" && (status < 200 || status >= 300);
|
|
@@ -2305,7 +2421,7 @@ var formatCapability = (capability) => {
|
|
|
2305
2421
|
const lines = [];
|
|
2306
2422
|
lines.push(capability.name);
|
|
2307
2423
|
lines.push(` Rating: ${formatRating(capability.rating)}`);
|
|
2308
|
-
lines.push(` Status: ${capability.
|
|
2424
|
+
lines.push(` Status: ${capability.availabilityStatus ?? "unknown"}`);
|
|
2309
2425
|
lines.push(...formatCost(capability));
|
|
2310
2426
|
lines.push(` URL: ${capability.url}`);
|
|
2311
2427
|
lines.push(` Method: ${capability.method}`);
|
|
@@ -2547,6 +2663,8 @@ var AGENT_TOOLS = [
|
|
|
2547
2663
|
},
|
|
2548
2664
|
{ name: "Cursor", detectDir: ".cursor", skillsDir: ".cursor/skills" }
|
|
2549
2665
|
];
|
|
2666
|
+
var HOOK_FILES = ["auto-approve-zero.sh", "zero-context.sh"];
|
|
2667
|
+
var ZERO_SANDBOX_DOMAIN = "*.zero.xyz";
|
|
2550
2668
|
var findResourceDir = (startDir, resourceName) => {
|
|
2551
2669
|
let current = startDir;
|
|
2552
2670
|
while (true) {
|
|
@@ -2616,10 +2734,9 @@ var installHook = (home, verbose = false) => {
|
|
|
2616
2734
|
const zeroHooksDir = join3(home, ".zero", "hooks");
|
|
2617
2735
|
mkdirSync3(zeroHooksDir, { recursive: true });
|
|
2618
2736
|
if (verbose) stepInfo(`staged hook dir at ${zeroHooksDir}`);
|
|
2619
|
-
const hookFiles = ["auto-approve-zero.sh", "zero-context.sh"];
|
|
2620
2737
|
const hookDests = {};
|
|
2621
2738
|
const hooksSourceDir = findResourceDir(getCliModuleDir(), "hooks");
|
|
2622
|
-
for (const hookFile of
|
|
2739
|
+
for (const hookFile of HOOK_FILES) {
|
|
2623
2740
|
const hookSource = join3(hooksSourceDir, hookFile);
|
|
2624
2741
|
const hookDest = join3(zeroHooksDir, hookFile);
|
|
2625
2742
|
copyFile(hookSource, hookDest);
|
|
@@ -2722,12 +2839,15 @@ var installHook = (home, verbose = false) => {
|
|
|
2722
2839
|
network.allowedDomains = [];
|
|
2723
2840
|
}
|
|
2724
2841
|
const allowedDomains = network.allowedDomains;
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2842
|
+
if (!allowedDomains.includes(ZERO_SANDBOX_DOMAIN)) {
|
|
2843
|
+
allowedDomains.push(ZERO_SANDBOX_DOMAIN);
|
|
2844
|
+
if (verbose) {
|
|
2845
|
+
stepInfo(`sandbox.network.allowedDomains += ${ZERO_SANDBOX_DOMAIN}`);
|
|
2846
|
+
}
|
|
2729
2847
|
} else if (verbose) {
|
|
2730
|
-
stepInfo(
|
|
2848
|
+
stepInfo(
|
|
2849
|
+
`${ZERO_SANDBOX_DOMAIN} already in sandbox allowlist \u2014 not modified`
|
|
2850
|
+
);
|
|
2731
2851
|
}
|
|
2732
2852
|
writeFileSync3(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
2733
2853
|
`);
|
|
@@ -2763,6 +2883,14 @@ var removeConflictingSkills = (home) => {
|
|
|
2763
2883
|
}
|
|
2764
2884
|
return removed;
|
|
2765
2885
|
};
|
|
2886
|
+
var getBundledSkillNames = () => {
|
|
2887
|
+
try {
|
|
2888
|
+
const skillsSourceDir = findResourceDir(getCliModuleDir(), "skills");
|
|
2889
|
+
return readdirSync(skillsSourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
2890
|
+
} catch {
|
|
2891
|
+
return ["zero"];
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2766
2894
|
var installSkills = (home, verbose = false) => {
|
|
2767
2895
|
const skillsSourceDir = findResourceDir(getCliModuleDir(), "skills");
|
|
2768
2896
|
const skillDirs = readdirSync(skillsSourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -3306,8 +3434,16 @@ var formatRatingBadge = (rating) => {
|
|
|
3306
3434
|
return `${successPct} success \xB7 ${reviews} reviews`;
|
|
3307
3435
|
};
|
|
3308
3436
|
var formatStatusBadge = (status) => {
|
|
3309
|
-
|
|
3310
|
-
|
|
3437
|
+
switch (status) {
|
|
3438
|
+
case "healthy":
|
|
3439
|
+
return ` \u2014 ${color.green("\u2713 healthy")}`;
|
|
3440
|
+
case "down":
|
|
3441
|
+
return ` \u2014 ${color.red("\u2717 down")}`;
|
|
3442
|
+
case "unknown":
|
|
3443
|
+
return ` \u2014 ${color.yellow("? unknown")}`;
|
|
3444
|
+
default:
|
|
3445
|
+
return "";
|
|
3446
|
+
}
|
|
3311
3447
|
};
|
|
3312
3448
|
var formatSearchResults = (results) => {
|
|
3313
3449
|
if (results.length === 0) return "No capabilities found.";
|
|
@@ -3316,7 +3452,7 @@ var formatSearchResults = (results) => {
|
|
|
3316
3452
|
const displayName = r.brandName ? `${r.brandName} ${baseName}` : baseName;
|
|
3317
3453
|
const displayDescription = r.whatItDoes ?? r.description;
|
|
3318
3454
|
const ratingBadge = formatRatingBadge(r.rating);
|
|
3319
|
-
const statusBadge = formatStatusBadge(r.
|
|
3455
|
+
const statusBadge = formatStatusBadge(r.availabilityStatus);
|
|
3320
3456
|
const costLabel = r.cost.amount === "0" ? "Free" : r.cost.amount === "unknown" ? "variable pricing" : `$${r.cost.amount}/call`;
|
|
3321
3457
|
return ` ${r.position}. ${displayName} \u2014 ${costLabel} \u2014 ${ratingBadge}${statusBadge}
|
|
3322
3458
|
"${displayDescription}"`;
|
|
@@ -3327,7 +3463,7 @@ var searchCommand = (appContext) => new Command9("search").description("Search f
|
|
|
3327
3463
|
`Maximum cost per call in USD (default: ${DEFAULT_MAX_COST_USD})`
|
|
3328
3464
|
).option("--protocol <protocol>", "Payment protocol (x402 or mpp)").option(
|
|
3329
3465
|
"--status <status>",
|
|
3330
|
-
"Filter by availability (healthy, unknown, down).
|
|
3466
|
+
"Filter by availability (healthy, unknown, down). Default (unset) returns healthy + unknown with healthy first; pass 'healthy' for only-healthy, or --all to include everything."
|
|
3331
3467
|
).option("--all", "Disable default quality filtering").option(
|
|
3332
3468
|
"--source <source>",
|
|
3333
3469
|
"Only show results from this crawl source (e.g. mpp, bazaar)"
|
|
@@ -3362,12 +3498,8 @@ var searchCommand = (appContext) => new Command9("search").description("Search f
|
|
|
3362
3498
|
process.exitCode = 1;
|
|
3363
3499
|
return;
|
|
3364
3500
|
}
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
if (effectiveStatus === void 0 && !options.all) {
|
|
3368
|
-
effectiveStatus = "healthy";
|
|
3369
|
-
appliedDefaultStatus = true;
|
|
3370
|
-
}
|
|
3501
|
+
const effectiveStatus = options.status;
|
|
3502
|
+
const appliedDefaultStatus = effectiveStatus === void 0 && !options.all;
|
|
3371
3503
|
const result = await apiService.search({
|
|
3372
3504
|
query,
|
|
3373
3505
|
offset: options.offset,
|
|
@@ -3464,15 +3596,264 @@ Read the full terms at: ${TERMS_URL}
|
|
|
3464
3596
|
}
|
|
3465
3597
|
});
|
|
3466
3598
|
|
|
3467
|
-
// src/commands/
|
|
3468
|
-
import {
|
|
3599
|
+
// src/commands/uninstall-command.ts
|
|
3600
|
+
import {
|
|
3601
|
+
existsSync as existsSync3,
|
|
3602
|
+
readdirSync as readdirSync2,
|
|
3603
|
+
readFileSync as readFileSync7,
|
|
3604
|
+
rmSync as rmSync2,
|
|
3605
|
+
writeFileSync as writeFileSync4
|
|
3606
|
+
} from "fs";
|
|
3469
3607
|
import { homedir as homedir4 } from "os";
|
|
3470
3608
|
import { join as join4 } from "path";
|
|
3471
3609
|
import { Command as Command11 } from "commander";
|
|
3610
|
+
var removeSkills = (home, verbose = false) => {
|
|
3611
|
+
const skillNames = getBundledSkillNames();
|
|
3612
|
+
const removed = [];
|
|
3613
|
+
for (const tool of AGENT_TOOLS) {
|
|
3614
|
+
const toolSkillsPath = join4(home, tool.skillsDir);
|
|
3615
|
+
if (!existsSync3(toolSkillsPath)) {
|
|
3616
|
+
if (verbose) {
|
|
3617
|
+
stepInfo(
|
|
3618
|
+
`${tool.name}: ~/${tool.skillsDir} not found \u2014 nothing to remove`
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
3621
|
+
continue;
|
|
3622
|
+
}
|
|
3623
|
+
for (const skillName of skillNames) {
|
|
3624
|
+
const skillPath = join4(toolSkillsPath, skillName);
|
|
3625
|
+
if (!existsSync3(skillPath)) continue;
|
|
3626
|
+
rmSync2(skillPath, { recursive: true, force: true });
|
|
3627
|
+
removed.push(`${tool.name}: ${skillPath}`);
|
|
3628
|
+
if (verbose) stepInfo(`${tool.name}: removed skill '${skillName}'`);
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
return removed;
|
|
3632
|
+
};
|
|
3633
|
+
var removeHooks = (home, verbose = false) => {
|
|
3634
|
+
let settingsChanged = false;
|
|
3635
|
+
const settingsPath = join4(home, ".claude", "settings.json");
|
|
3636
|
+
if (!existsSync3(settingsPath)) {
|
|
3637
|
+
if (verbose) stepInfo(`${settingsPath} not found \u2014 no settings to clean`);
|
|
3638
|
+
} else {
|
|
3639
|
+
let settings = null;
|
|
3640
|
+
try {
|
|
3641
|
+
settings = JSON.parse(readFileSync7(settingsPath, "utf-8"));
|
|
3642
|
+
} catch {
|
|
3643
|
+
if (verbose) {
|
|
3644
|
+
stepInfo(`${settingsPath} was unparseable JSON \u2014 leaving it untouched`);
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
if (settings) {
|
|
3648
|
+
settingsChanged = stripZeroFromSettings(settings, verbose);
|
|
3649
|
+
if (settingsChanged) {
|
|
3650
|
+
writeFileSync4(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
3651
|
+
`);
|
|
3652
|
+
if (verbose) stepInfo(`rewrote ${settingsPath} without Zero entries`);
|
|
3653
|
+
} else if (verbose) {
|
|
3654
|
+
stepInfo(`no Zero entries found in ${settingsPath}`);
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
let scriptsRemoved = 0;
|
|
3659
|
+
const zeroHooksDir = join4(home, ".zero", "hooks");
|
|
3660
|
+
if (existsSync3(zeroHooksDir)) {
|
|
3661
|
+
for (const hookFile of HOOK_FILES) {
|
|
3662
|
+
const hookPath = join4(zeroHooksDir, hookFile);
|
|
3663
|
+
if (!existsSync3(hookPath)) continue;
|
|
3664
|
+
rmSync2(hookPath, { force: true });
|
|
3665
|
+
scriptsRemoved++;
|
|
3666
|
+
if (verbose) stepInfo(`removed ${hookPath}`);
|
|
3667
|
+
}
|
|
3668
|
+
if (readdirSync2(zeroHooksDir).length === 0) {
|
|
3669
|
+
rmSync2(zeroHooksDir, { recursive: true, force: true });
|
|
3670
|
+
if (verbose) stepInfo(`removed empty ${zeroHooksDir}`);
|
|
3671
|
+
}
|
|
3672
|
+
} else if (verbose) {
|
|
3673
|
+
stepInfo(`${zeroHooksDir} not found \u2014 no hook scripts to remove`);
|
|
3674
|
+
}
|
|
3675
|
+
return { settingsChanged, scriptsRemoved };
|
|
3676
|
+
};
|
|
3677
|
+
var stripZeroFromSettings = (settings, verbose) => {
|
|
3678
|
+
let changed = false;
|
|
3679
|
+
const hooks = settings.hooks && typeof settings.hooks === "object" ? settings.hooks : null;
|
|
3680
|
+
if (hooks) {
|
|
3681
|
+
changed = removeHookEntries(hooks, "PreToolUse", "auto-approve-zero", verbose) || changed;
|
|
3682
|
+
changed = removeHookEntries(hooks, "UserPromptSubmit", "zero-context", verbose) || changed;
|
|
3683
|
+
for (const key of ["PreToolUse", "UserPromptSubmit"]) {
|
|
3684
|
+
const arr = hooks[key];
|
|
3685
|
+
if (Array.isArray(arr) && arr.length === 0) delete hooks[key];
|
|
3686
|
+
}
|
|
3687
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
3688
|
+
}
|
|
3689
|
+
const sandbox = settings.sandbox && typeof settings.sandbox === "object" ? settings.sandbox : null;
|
|
3690
|
+
const network = sandbox?.network && typeof sandbox.network === "object" ? sandbox.network : null;
|
|
3691
|
+
if (network && Array.isArray(network.allowedDomains)) {
|
|
3692
|
+
const domains = network.allowedDomains;
|
|
3693
|
+
const next = domains.filter((d) => d !== ZERO_SANDBOX_DOMAIN);
|
|
3694
|
+
if (next.length !== domains.length) {
|
|
3695
|
+
changed = true;
|
|
3696
|
+
if (verbose) {
|
|
3697
|
+
stepInfo(`sandbox.network.allowedDomains -= ${ZERO_SANDBOX_DOMAIN}`);
|
|
3698
|
+
}
|
|
3699
|
+
if (next.length === 0) {
|
|
3700
|
+
delete network.allowedDomains;
|
|
3701
|
+
if (sandbox && Object.keys(network).length === 0)
|
|
3702
|
+
delete sandbox.network;
|
|
3703
|
+
if (sandbox && Object.keys(sandbox).length === 0)
|
|
3704
|
+
delete settings.sandbox;
|
|
3705
|
+
} else {
|
|
3706
|
+
network.allowedDomains = next;
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
return changed;
|
|
3711
|
+
};
|
|
3712
|
+
var removeHookEntries = (hooks, hookType, commandSubstring, verbose) => {
|
|
3713
|
+
const entries = hooks[hookType];
|
|
3714
|
+
if (!Array.isArray(entries)) return false;
|
|
3715
|
+
const next = entries.filter((entry) => {
|
|
3716
|
+
const entryHooks = entry?.hooks;
|
|
3717
|
+
if (!Array.isArray(entryHooks)) return true;
|
|
3718
|
+
return !entryHooks.some(
|
|
3719
|
+
(h) => typeof h.command === "string" && h.command.includes(commandSubstring)
|
|
3720
|
+
);
|
|
3721
|
+
});
|
|
3722
|
+
if (next.length === entries.length) return false;
|
|
3723
|
+
hooks[hookType] = next;
|
|
3724
|
+
if (verbose) stepInfo(`removed ${hookType} entry (${commandSubstring})`);
|
|
3725
|
+
return true;
|
|
3726
|
+
};
|
|
3727
|
+
var removeWallet = (home, verbose = false) => {
|
|
3728
|
+
const zeroDir = join4(home, ".zero");
|
|
3729
|
+
const configPath = join4(zeroDir, "config.json");
|
|
3730
|
+
let removed = false;
|
|
3731
|
+
if (existsSync3(configPath)) {
|
|
3732
|
+
rmSync2(configPath, { force: true });
|
|
3733
|
+
removed = true;
|
|
3734
|
+
if (verbose) stepInfo(`removed ${configPath}`);
|
|
3735
|
+
}
|
|
3736
|
+
if (existsSync3(zeroDir) && readdirSync2(zeroDir).length === 0) {
|
|
3737
|
+
rmSync2(zeroDir, { recursive: true, force: true });
|
|
3738
|
+
if (verbose) stepInfo(`removed empty ${zeroDir}`);
|
|
3739
|
+
}
|
|
3740
|
+
return removed;
|
|
3741
|
+
};
|
|
3742
|
+
var runUninstall = async (appContext, options = {}) => {
|
|
3743
|
+
const verbose = options.verbose ?? false;
|
|
3744
|
+
const purge = options.purge ?? false;
|
|
3745
|
+
appContext.services.analyticsService.capture("uninstall_started", { purge });
|
|
3746
|
+
let currentStep = "skills";
|
|
3747
|
+
try {
|
|
3748
|
+
console.log("");
|
|
3749
|
+
console.log(` ${color.boldCyan("Uninstalling Zero")}`);
|
|
3750
|
+
console.log("");
|
|
3751
|
+
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"));
|
|
3752
|
+
console.log("");
|
|
3753
|
+
const home = homedir4();
|
|
3754
|
+
currentStep = "skills";
|
|
3755
|
+
const skillsRemoved = removeSkills(home, verbose);
|
|
3756
|
+
if (skillsRemoved.length > 0) {
|
|
3757
|
+
stepSuccess("Skills removed", `${skillsRemoved.length} removed`);
|
|
3758
|
+
} else {
|
|
3759
|
+
stepSkip("No Zero skills found");
|
|
3760
|
+
}
|
|
3761
|
+
currentStep = "hooks";
|
|
3762
|
+
const { settingsChanged, scriptsRemoved } = removeHooks(home, verbose);
|
|
3763
|
+
if (settingsChanged || scriptsRemoved > 0) {
|
|
3764
|
+
stepSuccess("Hooks removed");
|
|
3765
|
+
} else {
|
|
3766
|
+
stepSkip("No Zero hooks found");
|
|
3767
|
+
}
|
|
3768
|
+
currentStep = "wallet";
|
|
3769
|
+
let walletRemoved = false;
|
|
3770
|
+
if (purge) {
|
|
3771
|
+
walletRemoved = removeWallet(home, verbose);
|
|
3772
|
+
if (walletRemoved) {
|
|
3773
|
+
stepWarn("Wallet config deleted", "~/.zero/config.json");
|
|
3774
|
+
} else {
|
|
3775
|
+
stepSkip("No wallet config to delete");
|
|
3776
|
+
}
|
|
3777
|
+
} else {
|
|
3778
|
+
stepSkip(
|
|
3779
|
+
"Wallet preserved",
|
|
3780
|
+
"run `zero uninstall --purge` to also delete ~/.zero"
|
|
3781
|
+
);
|
|
3782
|
+
}
|
|
3783
|
+
currentStep = "complete";
|
|
3784
|
+
appContext.services.analyticsService.capture("uninstall_completed", {
|
|
3785
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
3786
|
+
skills_removed: skillsRemoved,
|
|
3787
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
3788
|
+
skills_removed_count: skillsRemoved.length,
|
|
3789
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
3790
|
+
hooks_removed: settingsChanged,
|
|
3791
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
3792
|
+
hook_scripts_removed: scriptsRemoved,
|
|
3793
|
+
// biome-ignore lint/style/useNamingConvention: snake_case for analytics
|
|
3794
|
+
wallet_removed: walletRemoved,
|
|
3795
|
+
purge
|
|
3796
|
+
});
|
|
3797
|
+
sectionDivider();
|
|
3798
|
+
console.log(` ${color.boldGreen("Zero uninstalled.")}`);
|
|
3799
|
+
if (!purge) {
|
|
3800
|
+
console.log(
|
|
3801
|
+
` ${color.dim("Your wallet is kept at ~/.zero \u2014 reinstall anytime with `zero init`.")}`
|
|
3802
|
+
);
|
|
3803
|
+
}
|
|
3804
|
+
console.log("");
|
|
3805
|
+
return {
|
|
3806
|
+
skillsRemoved,
|
|
3807
|
+
hooksRemoved: settingsChanged,
|
|
3808
|
+
hookScriptsRemoved: scriptsRemoved,
|
|
3809
|
+
walletRemoved
|
|
3810
|
+
};
|
|
3811
|
+
} catch (err) {
|
|
3812
|
+
appContext.services.analyticsService.capture("uninstall_failed", {
|
|
3813
|
+
step: currentStep,
|
|
3814
|
+
error: truncateError(err instanceof Error ? err.message : String(err)),
|
|
3815
|
+
purge
|
|
3816
|
+
});
|
|
3817
|
+
throw err;
|
|
3818
|
+
}
|
|
3819
|
+
};
|
|
3820
|
+
var uninstallCommand = (appContext) => new Command11("uninstall").description(
|
|
3821
|
+
"Remove Zero skills and hooks installed by `zero init` (keeps your wallet)"
|
|
3822
|
+
).option(
|
|
3823
|
+
"--purge",
|
|
3824
|
+
"Also delete the wallet config at ~/.zero (irreversible \u2014 destroys the private key)"
|
|
3825
|
+
).option(
|
|
3826
|
+
"-v, --verbose",
|
|
3827
|
+
"Explain why each removal step was taken or skipped"
|
|
3828
|
+
).action(async (options) => {
|
|
3829
|
+
await runUninstall(appContext, options);
|
|
3830
|
+
});
|
|
3831
|
+
|
|
3832
|
+
// src/commands/wallet-command.ts
|
|
3833
|
+
import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
|
|
3834
|
+
import { homedir as homedir5 } from "os";
|
|
3835
|
+
import { join as join6 } from "path";
|
|
3836
|
+
import { confirm, isCancel } from "@clack/prompts";
|
|
3837
|
+
import { Command as Command12 } from "commander";
|
|
3472
3838
|
import open2 from "open";
|
|
3473
3839
|
import { isAddress } from "viem";
|
|
3474
3840
|
import { generatePrivateKey as generatePrivateKey2, privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
3475
3841
|
|
|
3842
|
+
// src/util/migrate-config.ts
|
|
3843
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
3844
|
+
import { join as join5 } from "path";
|
|
3845
|
+
var backupAndStripPrivateKey = (zeroDir) => {
|
|
3846
|
+
const configPath = join5(zeroDir, "config.json");
|
|
3847
|
+
const backupPath = join5(zeroDir, "config.backup.json");
|
|
3848
|
+
const raw = readFileSync8(configPath, "utf8");
|
|
3849
|
+
ensureSecureDir(zeroDir);
|
|
3850
|
+
writeSecureFile(backupPath, raw);
|
|
3851
|
+
const parsed = JSON.parse(raw);
|
|
3852
|
+
delete parsed.privateKey;
|
|
3853
|
+
writeSecureFile(configPath, JSON.stringify(parsed, null, 2));
|
|
3854
|
+
return { backupPath, configPath };
|
|
3855
|
+
};
|
|
3856
|
+
|
|
3476
3857
|
// src/util/stdin.ts
|
|
3477
3858
|
var readStdin = async () => {
|
|
3478
3859
|
const chunks = [];
|
|
@@ -3485,7 +3866,7 @@ var readStdin = async () => {
|
|
|
3485
3866
|
// src/commands/wallet-command.ts
|
|
3486
3867
|
var PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/;
|
|
3487
3868
|
var parseProvider = (raw) => raw === "stripe" ? "stripe" : "coinbase";
|
|
3488
|
-
var walletBalanceCommand = (appContext) => new
|
|
3869
|
+
var walletBalanceCommand = (appContext) => new Command12("balance").description("Show wallet balance").option(
|
|
3489
3870
|
"--address <address>",
|
|
3490
3871
|
"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."
|
|
3491
3872
|
).action(async (options) => {
|
|
@@ -3534,7 +3915,7 @@ var readPrivateKeyFromStdin = async () => {
|
|
|
3534
3915
|
}
|
|
3535
3916
|
return raw;
|
|
3536
3917
|
};
|
|
3537
|
-
var walletFundCommand = (appContext) => new
|
|
3918
|
+
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(
|
|
3538
3919
|
"--no-open",
|
|
3539
3920
|
"Print the funding URL instead of opening a browser (for agents \u2014 funding links are one-time use, hand the URL to the user)"
|
|
3540
3921
|
).option(
|
|
@@ -3631,7 +4012,7 @@ ${address}`);
|
|
|
3631
4012
|
}
|
|
3632
4013
|
}
|
|
3633
4014
|
);
|
|
3634
|
-
var walletAddressCommand = (appContext) => new
|
|
4015
|
+
var walletAddressCommand = (appContext) => new Command12("address").description("Show wallet address").action(() => {
|
|
3635
4016
|
const { walletService } = appContext.services;
|
|
3636
4017
|
const address = walletService.getAddress();
|
|
3637
4018
|
if (!address) {
|
|
@@ -3641,7 +4022,7 @@ var walletAddressCommand = (appContext) => new Command11("address").description(
|
|
|
3641
4022
|
}
|
|
3642
4023
|
console.log(address);
|
|
3643
4024
|
});
|
|
3644
|
-
var walletSetCommand = (appContext) => new
|
|
4025
|
+
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) => {
|
|
3645
4026
|
const { analyticsService } = appContext.services;
|
|
3646
4027
|
if (!privateKey.startsWith("0x")) {
|
|
3647
4028
|
console.error("Private key must be 0x-prefixed hex string.");
|
|
@@ -3656,11 +4037,11 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
|
|
|
3656
4037
|
process.exitCode = 1;
|
|
3657
4038
|
return;
|
|
3658
4039
|
}
|
|
3659
|
-
const zeroDir =
|
|
3660
|
-
const configPath =
|
|
3661
|
-
if (!options.force &&
|
|
4040
|
+
const zeroDir = join6(homedir5(), ".zero");
|
|
4041
|
+
const configPath = join6(zeroDir, "config.json");
|
|
4042
|
+
if (!options.force && existsSync4(configPath)) {
|
|
3662
4043
|
try {
|
|
3663
|
-
const existing2 = JSON.parse(
|
|
4044
|
+
const existing2 = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
3664
4045
|
if (existing2.privateKey) {
|
|
3665
4046
|
console.error(
|
|
3666
4047
|
"Wallet already configured. Use --force to overwrite."
|
|
@@ -3672,7 +4053,7 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
|
|
|
3672
4053
|
}
|
|
3673
4054
|
}
|
|
3674
4055
|
ensureSecureDir(zeroDir);
|
|
3675
|
-
const existing =
|
|
4056
|
+
const existing = existsSync4(configPath) ? JSON.parse(readFileSync9(configPath, "utf8")) : {};
|
|
3676
4057
|
writeSecureFile(
|
|
3677
4058
|
configPath,
|
|
3678
4059
|
JSON.stringify(
|
|
@@ -3691,7 +4072,7 @@ var walletSetCommand = (appContext) => new Command11("set").description("Set wal
|
|
|
3691
4072
|
force: options.force ?? false
|
|
3692
4073
|
});
|
|
3693
4074
|
});
|
|
3694
|
-
var walletGenerateCommand = (appContext) => new
|
|
4075
|
+
var walletGenerateCommand = (appContext) => new Command12("generate").description(
|
|
3695
4076
|
"Generate a fresh wallet (address + private key) without touching your configured wallet"
|
|
3696
4077
|
).option("--json", "Emit { address, privateKey } as JSON").option(
|
|
3697
4078
|
"--fund",
|
|
@@ -3768,29 +4149,166 @@ var walletGenerateCommand = (appContext) => new Command11("generate").descriptio
|
|
|
3768
4149
|
});
|
|
3769
4150
|
}
|
|
3770
4151
|
);
|
|
4152
|
+
var resolveZeroWalletAddress = async (apiService) => {
|
|
4153
|
+
try {
|
|
4154
|
+
const wallets = await apiService.getWallets();
|
|
4155
|
+
const primary = wallets.find(
|
|
4156
|
+
(w) => w.source === "privy_embedded" && w.isPrimary
|
|
4157
|
+
);
|
|
4158
|
+
if (primary) return primary.walletAddress;
|
|
4159
|
+
const provisioned = await apiService.provisionWallet();
|
|
4160
|
+
return provisioned.walletAddress ?? null;
|
|
4161
|
+
} catch {
|
|
4162
|
+
return null;
|
|
4163
|
+
}
|
|
4164
|
+
};
|
|
4165
|
+
var walletMigrateCommand = (appContext) => new Command12("migrate").description(
|
|
4166
|
+
"Sweep all USDC from your private-key wallet into your Zero wallet"
|
|
4167
|
+
).option("--json", "Emit the migration result as JSON").option("-y, --yes", "Skip the confirmation prompt").action(async (options) => {
|
|
4168
|
+
const { apiService, paymentService } = appContext.services;
|
|
4169
|
+
const zeroDir = join6(homedir5(), ".zero");
|
|
4170
|
+
const configPath = join6(zeroDir, "config.json");
|
|
4171
|
+
const config = existsSync4(configPath) ? readConfig(configPath) : {};
|
|
4172
|
+
const privateKey = appContext.env.ZERO_PRIVATE_KEY ?? config.privateKey;
|
|
4173
|
+
if (!privateKey) {
|
|
4174
|
+
console.error(
|
|
4175
|
+
"No private-key wallet found. Run `zero wallet set <key>` first."
|
|
4176
|
+
);
|
|
4177
|
+
process.exitCode = 1;
|
|
4178
|
+
return;
|
|
4179
|
+
}
|
|
4180
|
+
const hasSession = Boolean(
|
|
4181
|
+
appContext.env.ZERO_SESSION_TOKEN || config.session
|
|
4182
|
+
);
|
|
4183
|
+
if (!hasSession) {
|
|
4184
|
+
console.error("Not logged in. Run `zero auth login` first.");
|
|
4185
|
+
process.exitCode = 1;
|
|
4186
|
+
return;
|
|
4187
|
+
}
|
|
4188
|
+
let source;
|
|
4189
|
+
try {
|
|
4190
|
+
source = privateKeyToAccount2(privateKey);
|
|
4191
|
+
} catch {
|
|
4192
|
+
console.error("Invalid private key in config.");
|
|
4193
|
+
process.exitCode = 1;
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4196
|
+
const to = await resolveZeroWalletAddress(apiService);
|
|
4197
|
+
if (!to) {
|
|
4198
|
+
console.error(
|
|
4199
|
+
"Could not resolve your Zero wallet. Run `zero auth login` and try again."
|
|
4200
|
+
);
|
|
4201
|
+
process.exitCode = 1;
|
|
4202
|
+
return;
|
|
4203
|
+
}
|
|
4204
|
+
if (to.toLowerCase() === source.address.toLowerCase()) {
|
|
4205
|
+
console.error(
|
|
4206
|
+
"Source and destination are the same wallet \u2014 nothing to migrate."
|
|
4207
|
+
);
|
|
4208
|
+
process.exitCode = 1;
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
const balance = await paymentService.getSweepableBalance();
|
|
4212
|
+
if (balance.raw <= 0n) {
|
|
4213
|
+
if (options.json) {
|
|
4214
|
+
console.log(
|
|
4215
|
+
JSON.stringify({ destination: to, legs: [], keyRemoved: false })
|
|
4216
|
+
);
|
|
4217
|
+
} else {
|
|
4218
|
+
console.log("Nothing to migrate (no USDC found).");
|
|
4219
|
+
}
|
|
4220
|
+
return;
|
|
4221
|
+
}
|
|
4222
|
+
if (!options.yes) {
|
|
4223
|
+
if (options.json || !process.stdin.isTTY) {
|
|
4224
|
+
console.error(
|
|
4225
|
+
"Refusing to migrate without confirmation. Re-run with --yes to proceed."
|
|
4226
|
+
);
|
|
4227
|
+
process.exitCode = 1;
|
|
4228
|
+
return;
|
|
4229
|
+
}
|
|
4230
|
+
const proceed = await confirm({
|
|
4231
|
+
message: `Please confirm you would like to transfer $${balance.usdc} from your private wallet to your Zero managed wallet`
|
|
4232
|
+
});
|
|
4233
|
+
if (isCancel(proceed) || !proceed) {
|
|
4234
|
+
console.log("Migration cancelled.");
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
const results = await paymentService.migrateAllUsdc(to, apiService);
|
|
4239
|
+
const anyFailed = results.some((r) => r.status === "failed");
|
|
4240
|
+
const anySwept = results.some((r) => r.status === "swept");
|
|
4241
|
+
let backupPath = null;
|
|
4242
|
+
if (anySwept && !anyFailed && config.privateKey) {
|
|
4243
|
+
try {
|
|
4244
|
+
backupPath = backupAndStripPrivateKey(zeroDir).backupPath;
|
|
4245
|
+
} catch {
|
|
4246
|
+
backupPath = null;
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
if (options.json) {
|
|
4250
|
+
console.log(
|
|
4251
|
+
JSON.stringify({
|
|
4252
|
+
destination: to,
|
|
4253
|
+
legs: results,
|
|
4254
|
+
keyRemoved: backupPath !== null,
|
|
4255
|
+
backupPath
|
|
4256
|
+
})
|
|
4257
|
+
);
|
|
4258
|
+
} else {
|
|
4259
|
+
console.log(`Migrating USDC \u2192 ${to}`);
|
|
4260
|
+
for (const r of results) {
|
|
4261
|
+
if (r.status === "swept") {
|
|
4262
|
+
console.log(
|
|
4263
|
+
` ${r.chain}: swept ${r.amount} USDC (tx ${r.txHash})`
|
|
4264
|
+
);
|
|
4265
|
+
} else if (r.status === "skipped") {
|
|
4266
|
+
console.log(` ${r.chain}: skipped (no balance)`);
|
|
4267
|
+
} else {
|
|
4268
|
+
console.log(` ${r.chain}: FAILED \u2014 ${r.error}`);
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
if (backupPath) {
|
|
4272
|
+
console.log("");
|
|
4273
|
+
console.log(
|
|
4274
|
+
`Private key removed from config (backed up to ${backupPath}). Your Zero wallet is now primary.`
|
|
4275
|
+
);
|
|
4276
|
+
} else if (anyFailed) {
|
|
4277
|
+
console.log("");
|
|
4278
|
+
console.log(
|
|
4279
|
+
"A transfer failed \u2014 your private key was kept. Re-run `zero wallet migrate` to retry."
|
|
4280
|
+
);
|
|
4281
|
+
} else if (!anySwept) {
|
|
4282
|
+
console.log("");
|
|
4283
|
+
console.log("Nothing to migrate (no USDC found).");
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
if (anyFailed) process.exitCode = 1;
|
|
4287
|
+
});
|
|
3771
4288
|
var walletCommand = (appContext) => {
|
|
3772
|
-
const cmd = new
|
|
4289
|
+
const cmd = new Command12("wallet").description("Manage your wallet");
|
|
3773
4290
|
cmd.addCommand(walletBalanceCommand(appContext));
|
|
3774
4291
|
cmd.addCommand(walletFundCommand(appContext));
|
|
3775
4292
|
cmd.addCommand(walletAddressCommand(appContext));
|
|
3776
4293
|
cmd.addCommand(walletSetCommand(appContext));
|
|
3777
4294
|
cmd.addCommand(walletGenerateCommand(appContext));
|
|
4295
|
+
cmd.addCommand(walletMigrateCommand(appContext), { hidden: true });
|
|
3778
4296
|
return cmd;
|
|
3779
4297
|
};
|
|
3780
4298
|
|
|
3781
4299
|
// src/commands/welcome-command.ts
|
|
3782
|
-
import { existsSync as
|
|
3783
|
-
import { homedir as
|
|
3784
|
-
import { join as
|
|
3785
|
-
import { Command as
|
|
4300
|
+
import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
|
|
4301
|
+
import { homedir as homedir6 } from "os";
|
|
4302
|
+
import { join as join7 } from "path";
|
|
4303
|
+
import { Command as Command13 } from "commander";
|
|
3786
4304
|
import open3 from "open";
|
|
3787
4305
|
import { getAddress } from "viem";
|
|
3788
4306
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
3789
4307
|
var readPrivateKey = () => {
|
|
3790
|
-
const configPath =
|
|
3791
|
-
if (!
|
|
4308
|
+
const configPath = join7(homedir6(), ".zero", "config.json");
|
|
4309
|
+
if (!existsSync5(configPath)) return null;
|
|
3792
4310
|
try {
|
|
3793
|
-
const config = JSON.parse(
|
|
4311
|
+
const config = JSON.parse(readFileSync10(configPath, "utf8"));
|
|
3794
4312
|
if (typeof config.privateKey === "string") {
|
|
3795
4313
|
return config.privateKey;
|
|
3796
4314
|
}
|
|
@@ -3825,7 +4343,7 @@ var printManualFallback = (url) => {
|
|
|
3825
4343
|
}
|
|
3826
4344
|
console.log(lines.join("\n"));
|
|
3827
4345
|
};
|
|
3828
|
-
var welcomeCommand = (appContext) => new
|
|
4346
|
+
var welcomeCommand = (appContext) => new Command13("welcome").description("Claim your $5 welcome bonus.").action(async () => {
|
|
3829
4347
|
const { analyticsService } = appContext.services;
|
|
3830
4348
|
analyticsService.capture("welcome_started", {});
|
|
3831
4349
|
let walletAddress;
|
|
@@ -3885,7 +4403,7 @@ If your browser didn't open, paste the URL above.`
|
|
|
3885
4403
|
// src/app.ts
|
|
3886
4404
|
var createApp = (appContext) => {
|
|
3887
4405
|
const { analyticsService } = appContext.services;
|
|
3888
|
-
const program = new
|
|
4406
|
+
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) => {
|
|
3889
4407
|
const agentFlag = actionCommand.opts().agent;
|
|
3890
4408
|
if (typeof agentFlag === "string" && agentFlag.trim().length > 0) {
|
|
3891
4409
|
analyticsService.setAgentHost(agentFlag.trim());
|
|
@@ -3899,6 +4417,7 @@ var createApp = (appContext) => {
|
|
|
3899
4417
|
});
|
|
3900
4418
|
});
|
|
3901
4419
|
program.addCommand(initCommand(appContext));
|
|
4420
|
+
program.addCommand(uninstallCommand(appContext));
|
|
3902
4421
|
program.addCommand(searchCommand(appContext));
|
|
3903
4422
|
program.addCommand(getCommand(appContext));
|
|
3904
4423
|
program.addCommand(fetchCommand(appContext));
|
|
@@ -3934,14 +4453,14 @@ var getEnv = () => {
|
|
|
3934
4453
|
|
|
3935
4454
|
// src/app/app-services.ts
|
|
3936
4455
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3937
|
-
import { existsSync as
|
|
3938
|
-
import { homedir as
|
|
3939
|
-
import { join as
|
|
4456
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4457
|
+
import { homedir as homedir7 } from "os";
|
|
4458
|
+
import { join as join9 } from "path";
|
|
3940
4459
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
3941
4460
|
|
|
3942
4461
|
// src/services/analytics-service.ts
|
|
3943
4462
|
import { randomUUID } from "crypto";
|
|
3944
|
-
import { existsSync as
|
|
4463
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
3945
4464
|
import { dirname as dirname2 } from "path";
|
|
3946
4465
|
import { PostHog } from "posthog-node";
|
|
3947
4466
|
var POSTHOG_API_KEY = "phc_B2vLyNxAf2mnqvdPQajf4d4b2iXc35dep2ZrvebMJLuX";
|
|
@@ -3963,8 +4482,8 @@ var AnalyticsService = class {
|
|
|
3963
4482
|
let telemetryEnabled = true;
|
|
3964
4483
|
let persistedAnonId;
|
|
3965
4484
|
try {
|
|
3966
|
-
if (
|
|
3967
|
-
const config = JSON.parse(
|
|
4485
|
+
if (existsSync6(opts.configPath)) {
|
|
4486
|
+
const config = JSON.parse(readFileSync11(opts.configPath, "utf8"));
|
|
3968
4487
|
if (config.telemetry === false) {
|
|
3969
4488
|
telemetryEnabled = false;
|
|
3970
4489
|
}
|
|
@@ -3990,8 +4509,8 @@ var AnalyticsService = class {
|
|
|
3990
4509
|
try {
|
|
3991
4510
|
const dir = dirname2(opts.configPath);
|
|
3992
4511
|
mkdirSync4(dir, { recursive: true });
|
|
3993
|
-
const existing =
|
|
3994
|
-
|
|
4512
|
+
const existing = existsSync6(opts.configPath) ? JSON.parse(readFileSync11(opts.configPath, "utf8")) : {};
|
|
4513
|
+
writeFileSync5(
|
|
3995
4514
|
opts.configPath,
|
|
3996
4515
|
JSON.stringify({ ...existing, anonId: newAnonId }, null, 2)
|
|
3997
4516
|
);
|
|
@@ -4031,7 +4550,7 @@ var AnalyticsService = class {
|
|
|
4031
4550
|
if (anonId === walletAddress) return;
|
|
4032
4551
|
let aliasedTo;
|
|
4033
4552
|
try {
|
|
4034
|
-
const config = JSON.parse(
|
|
4553
|
+
const config = JSON.parse(readFileSync11(configPath, "utf8"));
|
|
4035
4554
|
if (typeof config.aliasedTo === "string") {
|
|
4036
4555
|
aliasedTo = config.aliasedTo;
|
|
4037
4556
|
}
|
|
@@ -4041,8 +4560,8 @@ var AnalyticsService = class {
|
|
|
4041
4560
|
this.posthog.alias({ distinctId: walletAddress, alias: anonId });
|
|
4042
4561
|
if (process.env.VITEST) return;
|
|
4043
4562
|
try {
|
|
4044
|
-
const config =
|
|
4045
|
-
|
|
4563
|
+
const config = existsSync6(configPath) ? JSON.parse(readFileSync11(configPath, "utf8")) : {};
|
|
4564
|
+
writeFileSync5(
|
|
4046
4565
|
configPath,
|
|
4047
4566
|
JSON.stringify({ ...config, aliasedTo: walletAddress }, null, 2)
|
|
4048
4567
|
);
|
|
@@ -4145,34 +4664,34 @@ var createApiAccount = (walletAddress, api) => toAccount({
|
|
|
4145
4664
|
});
|
|
4146
4665
|
|
|
4147
4666
|
// src/services/state-service.ts
|
|
4148
|
-
import { existsSync as
|
|
4149
|
-
import { join as
|
|
4667
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
|
|
4668
|
+
import { join as join8 } from "path";
|
|
4150
4669
|
var RECENT_SEARCH_LIMIT = 10;
|
|
4151
4670
|
var StateService = class {
|
|
4152
4671
|
constructor(zeroDir) {
|
|
4153
4672
|
this.zeroDir = zeroDir;
|
|
4154
|
-
this.lastSearchPath =
|
|
4155
|
-
this.recentSearchesPath =
|
|
4673
|
+
this.lastSearchPath = join8(zeroDir, "last_search.json");
|
|
4674
|
+
this.recentSearchesPath = join8(zeroDir, "recent_searches.json");
|
|
4156
4675
|
}
|
|
4157
4676
|
lastSearchPath;
|
|
4158
4677
|
recentSearchesPath;
|
|
4159
4678
|
saveLastSearch = (data) => {
|
|
4160
4679
|
mkdirSync5(this.zeroDir, { recursive: true });
|
|
4161
|
-
|
|
4680
|
+
writeFileSync6(this.lastSearchPath, JSON.stringify(data, null, 2));
|
|
4162
4681
|
const recent = this.loadRecentSearches();
|
|
4163
4682
|
const filtered = recent.searches.filter(
|
|
4164
4683
|
(s) => s.searchId !== data.searchId
|
|
4165
4684
|
);
|
|
4166
4685
|
const next = [data, ...filtered].slice(0, RECENT_SEARCH_LIMIT);
|
|
4167
|
-
|
|
4686
|
+
writeFileSync6(
|
|
4168
4687
|
this.recentSearchesPath,
|
|
4169
4688
|
JSON.stringify({ searches: next }, null, 2)
|
|
4170
4689
|
);
|
|
4171
4690
|
};
|
|
4172
4691
|
loadLastSearch = () => {
|
|
4173
4692
|
try {
|
|
4174
|
-
if (!
|
|
4175
|
-
const raw =
|
|
4693
|
+
if (!existsSync7(this.lastSearchPath)) return null;
|
|
4694
|
+
const raw = readFileSync12(this.lastSearchPath, "utf8");
|
|
4176
4695
|
return JSON.parse(raw);
|
|
4177
4696
|
} catch {
|
|
4178
4697
|
return null;
|
|
@@ -4180,11 +4699,11 @@ var StateService = class {
|
|
|
4180
4699
|
};
|
|
4181
4700
|
loadRecentSearches = () => {
|
|
4182
4701
|
try {
|
|
4183
|
-
if (!
|
|
4702
|
+
if (!existsSync7(this.recentSearchesPath)) {
|
|
4184
4703
|
const last = this.loadLastSearch();
|
|
4185
4704
|
return { searches: last ? [last] : [] };
|
|
4186
4705
|
}
|
|
4187
|
-
const raw =
|
|
4706
|
+
const raw = readFileSync12(this.recentSearchesPath, "utf8");
|
|
4188
4707
|
const parsed = JSON.parse(raw);
|
|
4189
4708
|
return { searches: parsed.searches ?? [] };
|
|
4190
4709
|
} catch {
|
|
@@ -4312,9 +4831,9 @@ var buildOnSessionRefreshed = (configPath) => async (tokens) => {
|
|
|
4312
4831
|
writeSecureFile(configPath, JSON.stringify(next, null, 2));
|
|
4313
4832
|
};
|
|
4314
4833
|
var getServices = async (env) => {
|
|
4315
|
-
const zeroDir =
|
|
4316
|
-
const configPath =
|
|
4317
|
-
const config =
|
|
4834
|
+
const zeroDir = join9(homedir7(), ".zero");
|
|
4835
|
+
const configPath = join9(zeroDir, "config.json");
|
|
4836
|
+
const config = existsSync8(configPath) ? readConfig(configPath) : {};
|
|
4318
4837
|
const { credentials, privateKey } = resolveCredentials(env, config);
|
|
4319
4838
|
const lowBalanceWarning = typeof config.lowBalanceWarning === "number" ? config.lowBalanceWarning : 1;
|
|
4320
4839
|
const apiService = new ApiService(
|
|
@@ -4324,20 +4843,17 @@ var getServices = async (env) => {
|
|
|
4324
4843
|
buildOnSessionRefreshed(configPath)
|
|
4325
4844
|
);
|
|
4326
4845
|
let account = privateKey ? privateKeyToAccount4(privateKey) : null;
|
|
4327
|
-
let managed = false;
|
|
4328
4846
|
if (!account && credentials.kind === "session") {
|
|
4329
4847
|
const address = await resolveManagedWalletAddress(apiService);
|
|
4330
4848
|
if (address) {
|
|
4331
4849
|
account = createApiAccount(address, apiService);
|
|
4332
|
-
managed = true;
|
|
4333
4850
|
}
|
|
4334
4851
|
}
|
|
4335
4852
|
if (account && !apiService.walletAddress) {
|
|
4336
4853
|
apiService.setWalletAddress(account.address);
|
|
4337
4854
|
}
|
|
4338
4855
|
const paymentService = new PaymentService(account, {
|
|
4339
|
-
lowBalanceWarning
|
|
4340
|
-
managed
|
|
4856
|
+
lowBalanceWarning
|
|
4341
4857
|
});
|
|
4342
4858
|
const stateService = new StateService(zeroDir);
|
|
4343
4859
|
const walletService = new WalletService(
|
|
@@ -4390,15 +4906,15 @@ var createAppContext = async () => {
|
|
|
4390
4906
|
|
|
4391
4907
|
// src/util/update-check.ts
|
|
4392
4908
|
import {
|
|
4393
|
-
existsSync as
|
|
4909
|
+
existsSync as existsSync9,
|
|
4394
4910
|
lstatSync,
|
|
4395
4911
|
mkdirSync as mkdirSync6,
|
|
4396
|
-
readFileSync as
|
|
4912
|
+
readFileSync as readFileSync13,
|
|
4397
4913
|
readlinkSync,
|
|
4398
|
-
writeFileSync as
|
|
4914
|
+
writeFileSync as writeFileSync7
|
|
4399
4915
|
} from "fs";
|
|
4400
|
-
import { homedir as
|
|
4401
|
-
import { dirname as dirname3, join as
|
|
4916
|
+
import { homedir as homedir8 } from "os";
|
|
4917
|
+
import { dirname as dirname3, join as join10, resolve } from "path";
|
|
4402
4918
|
var CACHE_FILENAME = "update_check.json";
|
|
4403
4919
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@zeroxyz/cli/latest";
|
|
4404
4920
|
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -4422,10 +4938,10 @@ var resolveExecPath = (execPath) => {
|
|
|
4422
4938
|
var detectInstallMethod = (opts = {}) => {
|
|
4423
4939
|
const execPath = opts.execPath ?? process.execPath;
|
|
4424
4940
|
const pkg = opts.pkg ?? process.pkg;
|
|
4425
|
-
const home = opts.home ??
|
|
4941
|
+
const home = opts.home ?? homedir8();
|
|
4426
4942
|
if (pkg) return "binary";
|
|
4427
4943
|
const resolved = resolveExecPath(execPath);
|
|
4428
|
-
const zeroBin =
|
|
4944
|
+
const zeroBin = join10(home, ".zero", "bin");
|
|
4429
4945
|
if (resolved.startsWith(zeroBin)) return "binary";
|
|
4430
4946
|
return "npm";
|
|
4431
4947
|
};
|
|
@@ -4450,12 +4966,12 @@ var compareVersions = (a, b) => {
|
|
|
4450
4966
|
if (pb.pre === null) return -1;
|
|
4451
4967
|
return pa.pre < pb.pre ? -1 : 1;
|
|
4452
4968
|
};
|
|
4453
|
-
var cachePath = (zeroDir) =>
|
|
4969
|
+
var cachePath = (zeroDir) => join10(zeroDir, CACHE_FILENAME);
|
|
4454
4970
|
var readCache = (zeroDir) => {
|
|
4455
4971
|
try {
|
|
4456
4972
|
const path = cachePath(zeroDir);
|
|
4457
|
-
if (!
|
|
4458
|
-
const raw =
|
|
4973
|
+
if (!existsSync9(path)) return emptyCache;
|
|
4974
|
+
const raw = readFileSync13(path, "utf8");
|
|
4459
4975
|
const parsed = JSON.parse(raw);
|
|
4460
4976
|
return {
|
|
4461
4977
|
lastCheckedMs: typeof parsed.lastCheckedMs === "number" ? parsed.lastCheckedMs : 0,
|
|
@@ -4469,7 +4985,7 @@ var readCache = (zeroDir) => {
|
|
|
4469
4985
|
var writeCache = (zeroDir, cache) => {
|
|
4470
4986
|
try {
|
|
4471
4987
|
mkdirSync6(zeroDir, { recursive: true });
|
|
4472
|
-
|
|
4988
|
+
writeFileSync7(cachePath(zeroDir), JSON.stringify(cache, null, 2));
|
|
4473
4989
|
} catch {
|
|
4474
4990
|
}
|
|
4475
4991
|
};
|
|
@@ -4544,7 +5060,7 @@ var main = async () => {
|
|
|
4544
5060
|
console.error("Failed to create app context");
|
|
4545
5061
|
process.exit(1);
|
|
4546
5062
|
}
|
|
4547
|
-
const zeroDir =
|
|
5063
|
+
const zeroDir = join11(homedir9(), ".zero");
|
|
4548
5064
|
maybePrintUpdateBanner(zeroDir, package_default.version);
|
|
4549
5065
|
const app = createApp(appContext);
|
|
4550
5066
|
let caughtError = null;
|