@zoralabs/cli 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/index.js +834 -221
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.tsx
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command12 } from "commander";
|
|
5
5
|
import { ExitPromptError } from "@inquirer/core";
|
|
6
6
|
import "fs";
|
|
7
7
|
import { setApiBaseUrl } from "@zoralabs/coins-sdk";
|
|
@@ -18,6 +18,50 @@ import {
|
|
|
18
18
|
chmodSync
|
|
19
19
|
} from "fs";
|
|
20
20
|
import { join } from "path";
|
|
21
|
+
|
|
22
|
+
// src/lib/errors.ts
|
|
23
|
+
import { BaseError as ViemBaseError, InsufficientFundsError } from "viem";
|
|
24
|
+
function formatError(err) {
|
|
25
|
+
if (!(err instanceof Error)) return String(err);
|
|
26
|
+
const msg = err.message;
|
|
27
|
+
return msg.length > 120 ? msg.slice(0, 120) + "..." : msg;
|
|
28
|
+
}
|
|
29
|
+
function tradeErrorMessage(err) {
|
|
30
|
+
if (!(err instanceof Error)) return String(err);
|
|
31
|
+
if (err instanceof ViemBaseError) {
|
|
32
|
+
const insufficient = err.walk((e) => e instanceof InsufficientFundsError);
|
|
33
|
+
if (insufficient)
|
|
34
|
+
return "Not enough funds. Try a lower amount or run 'zora balance spendable' to check your balance.";
|
|
35
|
+
return err.shortMessage;
|
|
36
|
+
}
|
|
37
|
+
return apiErrorMessage(err);
|
|
38
|
+
}
|
|
39
|
+
function apiErrorMessage(err) {
|
|
40
|
+
if (!(err instanceof Error)) return String(err);
|
|
41
|
+
const code = err.code;
|
|
42
|
+
if (code === "ECONNREFUSED" || code === "ENOTFOUND")
|
|
43
|
+
return "Can't connect. Check your internet connection.";
|
|
44
|
+
if (code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT")
|
|
45
|
+
return "Request timed out. Try again.";
|
|
46
|
+
const status = err.status;
|
|
47
|
+
if (status === 429)
|
|
48
|
+
return "Rate limited. Wait a moment or run 'zora auth configure' for higher limits.";
|
|
49
|
+
if (status === 401 || status === 403)
|
|
50
|
+
return "Auth failed. Run 'zora auth configure' to update your API key.";
|
|
51
|
+
if (typeof status === "number" && status >= 500)
|
|
52
|
+
return "Zora is temporarily unavailable. Try again later.";
|
|
53
|
+
return formatError(err);
|
|
54
|
+
}
|
|
55
|
+
function fsErrorMessage(err, path) {
|
|
56
|
+
if (!(err instanceof Error)) return String(err);
|
|
57
|
+
const code = err.code;
|
|
58
|
+
if (code === "EACCES") return `Permission denied accessing ${path}.`;
|
|
59
|
+
if (code === "EISDIR")
|
|
60
|
+
return `Expected a file but found a directory at ${path}.`;
|
|
61
|
+
return formatError(err);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/lib/config.ts
|
|
21
65
|
import { homedir, platform } from "os";
|
|
22
66
|
function getConfigDir() {
|
|
23
67
|
if (platform() === "win32") {
|
|
@@ -55,7 +99,7 @@ function readConfig() {
|
|
|
55
99
|
parsed = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
56
100
|
} catch (err) {
|
|
57
101
|
console.error(
|
|
58
|
-
`Warning: could not parse ${CONFIG_FILE}: ${err
|
|
102
|
+
`Warning: could not parse ${CONFIG_FILE}: ${formatError(err)}. Run 'zora auth configure' to fix.`
|
|
59
103
|
);
|
|
60
104
|
configReadOnly = true;
|
|
61
105
|
return { version: CONFIG_VERSION };
|
|
@@ -64,7 +108,7 @@ function readConfig() {
|
|
|
64
108
|
assertVersion(parsed, CONFIG_VERSION, CONFIG_FILE);
|
|
65
109
|
} catch (err) {
|
|
66
110
|
console.error(
|
|
67
|
-
`Warning: ${err
|
|
111
|
+
`Warning: ${formatError(err)}. Delete ${CONFIG_FILE} or run 'zora auth configure' to reset.`
|
|
68
112
|
);
|
|
69
113
|
configReadOnly = true;
|
|
70
114
|
return { version: CONFIG_VERSION };
|
|
@@ -155,18 +199,29 @@ function maskKey(key) {
|
|
|
155
199
|
}
|
|
156
200
|
|
|
157
201
|
// src/lib/output.ts
|
|
158
|
-
var VALID_OUTPUT_MODES = ["table", "json", "live"];
|
|
159
202
|
var getOutputMode = (cmd, defaultMode) => {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
203
|
+
const globals = cmd.optsWithGlobals();
|
|
204
|
+
const json = globals.json ?? false;
|
|
205
|
+
const live = globals.live ?? false;
|
|
206
|
+
const static_ = globals.static ?? false;
|
|
207
|
+
const set = [
|
|
208
|
+
json && "--json",
|
|
209
|
+
live && "--live",
|
|
210
|
+
static_ && "--static"
|
|
211
|
+
].filter(Boolean);
|
|
212
|
+
if (set.length > 1) {
|
|
213
|
+
return outputErrorAndExit(
|
|
214
|
+
false,
|
|
215
|
+
`${set.join(", ")} cannot be used together.`,
|
|
216
|
+
"Choose one: --json, --live, or --static"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (json) return "json";
|
|
220
|
+
if (live) return "live";
|
|
221
|
+
if (static_) return "static";
|
|
222
|
+
return defaultMode;
|
|
168
223
|
};
|
|
169
|
-
var getJson = (cmd) =>
|
|
224
|
+
var getJson = (cmd) => cmd.optsWithGlobals().json ?? false;
|
|
170
225
|
var getYes = (cmd) => cmd.optsWithGlobals().yes ?? false;
|
|
171
226
|
var outputJson = (data) => {
|
|
172
227
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -188,14 +243,18 @@ var outputData = (json, opts) => {
|
|
|
188
243
|
if (json) {
|
|
189
244
|
outputJson(opts.json);
|
|
190
245
|
} else {
|
|
191
|
-
opts.
|
|
246
|
+
opts.render();
|
|
192
247
|
}
|
|
193
248
|
};
|
|
194
|
-
var getLiveConfig = (cmd,
|
|
195
|
-
const mode = getOutputMode(cmd, defaultMode);
|
|
249
|
+
var getLiveConfig = (cmd, mode) => {
|
|
196
250
|
const live = mode === "live";
|
|
197
|
-
const intervalRaw = parseInt(cmd.
|
|
251
|
+
const intervalRaw = parseInt(cmd.opts().refresh, 10);
|
|
198
252
|
const intervalSeconds = isNaN(intervalRaw) || intervalRaw < 5 ? 30 : intervalRaw;
|
|
253
|
+
if (!live && cmd.getOptionValueSource("refresh") === "cli") {
|
|
254
|
+
console.warn(
|
|
255
|
+
"\x1B[33mWarning:\x1B[0m --refresh has no effect without --live"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
199
258
|
return { live, intervalSeconds };
|
|
200
259
|
};
|
|
201
260
|
|
|
@@ -282,9 +341,7 @@ var resolveAccount = (json = false) => {
|
|
|
282
341
|
try {
|
|
283
342
|
return privateKeyToAccount(normalizeKey(key));
|
|
284
343
|
} catch (err) {
|
|
285
|
-
console.error(
|
|
286
|
-
`\u2717 Invalid private key: ${err instanceof Error ? err.message : String(err)}`
|
|
287
|
-
);
|
|
344
|
+
console.error(`\u2717 Invalid private key: ${formatError(err)}`);
|
|
288
345
|
console.error(" Run 'zora setup --force' to replace it.");
|
|
289
346
|
return process.exit(1);
|
|
290
347
|
}
|
|
@@ -374,7 +431,7 @@ var getClient = () => {
|
|
|
374
431
|
return client;
|
|
375
432
|
};
|
|
376
433
|
var commonProperties = () => ({
|
|
377
|
-
cli_version: true ? "0.
|
|
434
|
+
cli_version: true ? "0.3.0" : "development",
|
|
378
435
|
os: process.platform,
|
|
379
436
|
arch: process.arch,
|
|
380
437
|
node_version: process.version
|
|
@@ -447,7 +504,7 @@ authCommand.command("configure").description("Set your Zora API key").option("--
|
|
|
447
504
|
status: "env_override",
|
|
448
505
|
message: "API key is set via ZORA_API_KEY environment variable."
|
|
449
506
|
},
|
|
450
|
-
|
|
507
|
+
render: () => console.log(
|
|
451
508
|
"API key is set via ZORA_API_KEY environment variable. Unset it to configure manually."
|
|
452
509
|
)
|
|
453
510
|
});
|
|
@@ -475,7 +532,7 @@ authCommand.command("configure").description("Set your Zora API key").option("--
|
|
|
475
532
|
saveApiKey(trimmed);
|
|
476
533
|
outputData(json, {
|
|
477
534
|
json: { saved: true, path: getConfigPath() },
|
|
478
|
-
|
|
535
|
+
render: () => console.log(`API key saved to ${getConfigPath()}`)
|
|
479
536
|
});
|
|
480
537
|
track("cli_auth_configure", {
|
|
481
538
|
output_format: json ? "json" : "text"
|
|
@@ -483,7 +540,7 @@ authCommand.command("configure").description("Set your Zora API key").option("--
|
|
|
483
540
|
} catch (err) {
|
|
484
541
|
outputErrorAndExit(
|
|
485
542
|
json,
|
|
486
|
-
`Failed to save API key: ${err
|
|
543
|
+
`Failed to save API key: ${fsErrorMessage(err, getConfigPath())}`
|
|
487
544
|
);
|
|
488
545
|
}
|
|
489
546
|
});
|
|
@@ -493,7 +550,7 @@ authCommand.command("status").description("Check authentication status").action(
|
|
|
493
550
|
if (!apiKey) {
|
|
494
551
|
outputData(json, {
|
|
495
552
|
json: { authenticated: false },
|
|
496
|
-
|
|
553
|
+
render: () => {
|
|
497
554
|
console.log(
|
|
498
555
|
"No API key configured. The CLI works without one, but requests are rate-limited."
|
|
499
556
|
);
|
|
@@ -512,7 +569,7 @@ authCommand.command("status").description("Check authentication status").action(
|
|
|
512
569
|
const source = getEnvApiKey() ? "env (ZORA_API_KEY)" : getConfigPath();
|
|
513
570
|
outputData(json, {
|
|
514
571
|
json: { authenticated: true, key: maskKey(apiKey), source },
|
|
515
|
-
|
|
572
|
+
render: () => {
|
|
516
573
|
console.log(`Authenticated: ${maskKey(apiKey)}`);
|
|
517
574
|
console.log(`Source: ${source}`);
|
|
518
575
|
}
|
|
@@ -577,7 +634,8 @@ var Table = ({
|
|
|
577
634
|
title,
|
|
578
635
|
subtitle,
|
|
579
636
|
fullWidth = true,
|
|
580
|
-
footer
|
|
637
|
+
footer,
|
|
638
|
+
selectedRow
|
|
581
639
|
}) => {
|
|
582
640
|
const widths = computeColumnWidths(columns, fullWidth);
|
|
583
641
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingTop: 1, paddingBottom: 1, children: [
|
|
@@ -589,19 +647,24 @@ var Table = ({
|
|
|
589
647
|
] })
|
|
590
648
|
] }),
|
|
591
649
|
/* @__PURE__ */ jsx(Box, { paddingLeft: PADDING_LEFT, children: columns.map((col, i) => /* @__PURE__ */ jsx(Box, { width: widths[i], children: /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, wrap: "truncate", children: col.header }) }, col.header)) }),
|
|
592
|
-
data.map((row, i) =>
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
{
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
650
|
+
data.map((row, i) => {
|
|
651
|
+
const isSelected = selectedRow === i;
|
|
652
|
+
return /* @__PURE__ */ jsx(Box, { paddingLeft: PADDING_LEFT, children: columns.map((col, colIdx) => {
|
|
653
|
+
const colWidth = widths[colIdx];
|
|
654
|
+
const value = col.noTruncate ? col.accessor(row) : truncate(col.accessor(row), colWidth - 2);
|
|
655
|
+
const colorName = col.color?.(row);
|
|
656
|
+
return /* @__PURE__ */ jsx(Box, { width: colWidth, children: /* @__PURE__ */ jsx(
|
|
657
|
+
Text,
|
|
658
|
+
{
|
|
659
|
+
color: colorName,
|
|
660
|
+
bold: isSelected,
|
|
661
|
+
inverse: isSelected,
|
|
662
|
+
wrap: col.noTruncate ? "wrap" : "truncate",
|
|
663
|
+
children: value
|
|
664
|
+
}
|
|
665
|
+
) }, col.header);
|
|
666
|
+
}) }, i);
|
|
667
|
+
}),
|
|
605
668
|
footer && /* @__PURE__ */ jsx(Box, { paddingLeft: PADDING_LEFT, marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: footer }) })
|
|
606
669
|
] });
|
|
607
670
|
};
|
|
@@ -664,6 +727,7 @@ var formatEthDisplay = (wei) => {
|
|
|
664
727
|
const trimmed = parts[1].replace(/0+$/, "") || "0";
|
|
665
728
|
return `${parts[0]}.${trimmed}`;
|
|
666
729
|
};
|
|
730
|
+
var truncateAddress = (address) => `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
667
731
|
var formatCoinsDisplay = (coinsOut) => new Intl.NumberFormat("en-US", {
|
|
668
732
|
maximumFractionDigits: 2
|
|
669
733
|
}).format(Number(coinsOut));
|
|
@@ -800,6 +864,7 @@ var BalanceView = ({
|
|
|
800
864
|
fetchData,
|
|
801
865
|
sort,
|
|
802
866
|
mode = "full",
|
|
867
|
+
initialCursor,
|
|
803
868
|
autoRefresh = false,
|
|
804
869
|
intervalSeconds = 30
|
|
805
870
|
}) => {
|
|
@@ -808,37 +873,64 @@ var BalanceView = ({
|
|
|
808
873
|
const [isRefreshing, setIsRefreshing] = useState2(false);
|
|
809
874
|
const [error, setError] = useState2(null);
|
|
810
875
|
const [data, setData] = useState2(null);
|
|
876
|
+
const paginated = mode === "coins";
|
|
877
|
+
const [page, setPage] = useState2(1);
|
|
878
|
+
const [cursorHistory, setCursorHistory] = useState2(
|
|
879
|
+
[]
|
|
880
|
+
);
|
|
881
|
+
const [currentCursor, setCurrentCursor] = useState2(
|
|
882
|
+
initialCursor
|
|
883
|
+
);
|
|
811
884
|
const { refreshCount, secondsUntilRefresh, triggerManualRefresh } = useAutoRefresh(intervalSeconds, autoRefresh);
|
|
812
885
|
const [manualRefreshCount, setManualRefreshCount] = useState2(0);
|
|
813
886
|
const hasLoadedOnce = useRef(false);
|
|
814
|
-
const load = useCallback2(
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
887
|
+
const load = useCallback2(
|
|
888
|
+
async (cursor) => {
|
|
889
|
+
if (hasLoadedOnce.current) {
|
|
890
|
+
setIsRefreshing(true);
|
|
891
|
+
} else {
|
|
892
|
+
setLoading(true);
|
|
893
|
+
}
|
|
894
|
+
setError(null);
|
|
895
|
+
try {
|
|
896
|
+
const result = await fetchData(cursor);
|
|
897
|
+
setData(result);
|
|
898
|
+
hasLoadedOnce.current = true;
|
|
899
|
+
} catch (err) {
|
|
900
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
901
|
+
}
|
|
902
|
+
setLoading(false);
|
|
903
|
+
setIsRefreshing(false);
|
|
904
|
+
},
|
|
905
|
+
[fetchData]
|
|
906
|
+
);
|
|
831
907
|
useEffect2(() => {
|
|
832
|
-
load();
|
|
833
|
-
}, [refreshCount, manualRefreshCount]);
|
|
908
|
+
load(currentCursor);
|
|
909
|
+
}, [load, refreshCount, manualRefreshCount, currentCursor]);
|
|
834
910
|
useInput((input, key) => {
|
|
835
911
|
if (input === "q" || key.escape) {
|
|
836
912
|
exit();
|
|
837
913
|
return;
|
|
838
914
|
}
|
|
839
|
-
if (
|
|
915
|
+
if (loading) return;
|
|
916
|
+
if (input === "r") {
|
|
840
917
|
triggerManualRefresh();
|
|
841
918
|
setManualRefreshCount((c) => c + 1);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (!paginated) return;
|
|
922
|
+
const canGoNext = data?.pageInfo?.hasNextPage && data.pageInfo.endCursor;
|
|
923
|
+
const canGoPrev = cursorHistory.length > 0;
|
|
924
|
+
if ((input === "n" || key.rightArrow) && canGoNext) {
|
|
925
|
+
setCursorHistory((prev) => [...prev, currentCursor]);
|
|
926
|
+
setCurrentCursor(data.pageInfo.endCursor);
|
|
927
|
+
setPage((p) => p + 1);
|
|
928
|
+
}
|
|
929
|
+
if ((input === "p" || key.leftArrow) && canGoPrev) {
|
|
930
|
+
const prev = cursorHistory[cursorHistory.length - 1];
|
|
931
|
+
setCursorHistory((h) => h.slice(0, -1));
|
|
932
|
+
setCurrentCursor(prev);
|
|
933
|
+
setPage((p) => p - 1);
|
|
842
934
|
}
|
|
843
935
|
});
|
|
844
936
|
if (error && !data) {
|
|
@@ -866,7 +958,10 @@ var BalanceView = ({
|
|
|
866
958
|
] }) });
|
|
867
959
|
}
|
|
868
960
|
if (!data) return null;
|
|
869
|
-
const hints = [
|
|
961
|
+
const hints = [];
|
|
962
|
+
if (paginated && cursorHistory.length > 0) hints.push("\u2190 prev");
|
|
963
|
+
if (paginated && data?.pageInfo?.hasNextPage) hints.push("\u2192 next");
|
|
964
|
+
hints.push("r refresh");
|
|
870
965
|
if (autoRefresh) hints.push(`auto: ${secondsUntilRefresh}s`);
|
|
871
966
|
hints.push("q quit");
|
|
872
967
|
const footer = hints.join(" \xB7 ");
|
|
@@ -910,7 +1005,7 @@ var BalanceView = ({
|
|
|
910
1005
|
columns: balanceColumns,
|
|
911
1006
|
data: data.rankedBalances,
|
|
912
1007
|
title: `Coins \xB7 sorted by ${SORT_LABELS[sort]}`,
|
|
913
|
-
subtitle: `${data.rankedBalances.length} of ${data.total}`
|
|
1008
|
+
subtitle: paginated ? `Page ${page} \xB7 ${data.rankedBalances.length} result${data.rankedBalances.length !== 1 ? "s" : ""}` : `${data.rankedBalances.length} of ${data.total}`
|
|
914
1009
|
}
|
|
915
1010
|
) : null,
|
|
916
1011
|
/* @__PURE__ */ jsx2(Box2, { paddingLeft: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: footer }) })
|
|
@@ -957,7 +1052,7 @@ var fetchTokenPriceUsd = async (address, chainId = BASE_CHAIN_ID) => {
|
|
|
957
1052
|
return res.data?.erc20Token?.currency?.priceUsd ? Number(res.data.erc20Token.currency.priceUsd) : null;
|
|
958
1053
|
} catch (err) {
|
|
959
1054
|
console.warn(
|
|
960
|
-
`Warning: failed to fetch price for ${address}: ${
|
|
1055
|
+
`Warning: failed to fetch price for ${address}: ${formatError(err)}`
|
|
961
1056
|
);
|
|
962
1057
|
return null;
|
|
963
1058
|
}
|
|
@@ -1094,7 +1189,7 @@ function resolveContext(json) {
|
|
|
1094
1189
|
function renderWallet(json, walletResult) {
|
|
1095
1190
|
outputData(json, {
|
|
1096
1191
|
json: { wallet: walletResult.walletBalancesJson },
|
|
1097
|
-
|
|
1192
|
+
render: () => {
|
|
1098
1193
|
renderOnce(
|
|
1099
1194
|
/* @__PURE__ */ jsx3(
|
|
1100
1195
|
Table,
|
|
@@ -1108,7 +1203,7 @@ function renderWallet(json, walletResult) {
|
|
|
1108
1203
|
}
|
|
1109
1204
|
});
|
|
1110
1205
|
}
|
|
1111
|
-
function renderCoins(json, balances, total, sort) {
|
|
1206
|
+
function renderCoins(json, balances, total, sort, limit, pageInfo) {
|
|
1112
1207
|
const rankedBalances = balances.map((balance, index) => ({
|
|
1113
1208
|
...balance,
|
|
1114
1209
|
rank: index + 1
|
|
@@ -1117,14 +1212,16 @@ function renderCoins(json, balances, total, sort) {
|
|
|
1117
1212
|
json: {
|
|
1118
1213
|
coins: rankedBalances.map(
|
|
1119
1214
|
(balance) => formatBalanceJson(balance, balance.rank)
|
|
1120
|
-
)
|
|
1215
|
+
),
|
|
1216
|
+
pageInfo: pageInfo ?? null
|
|
1121
1217
|
},
|
|
1122
|
-
|
|
1218
|
+
render: () => {
|
|
1123
1219
|
if (balances.length === 0) {
|
|
1124
1220
|
console.log("\n No coin balances found.\n");
|
|
1125
1221
|
console.log(" Buy coins to see them here:");
|
|
1126
1222
|
console.log(" zora buy <address> --eth 0.001\n");
|
|
1127
1223
|
} else {
|
|
1224
|
+
const footer = pageInfo?.hasNextPage && pageInfo.endCursor ? `Next page: zora balance coins --sort ${sort} --limit ${limit} --after ${pageInfo.endCursor}` : void 0;
|
|
1128
1225
|
renderOnce(
|
|
1129
1226
|
/* @__PURE__ */ jsx3(
|
|
1130
1227
|
Table,
|
|
@@ -1132,7 +1229,8 @@ function renderCoins(json, balances, total, sort) {
|
|
|
1132
1229
|
columns: balanceColumns,
|
|
1133
1230
|
data: rankedBalances,
|
|
1134
1231
|
title: `Coins \xB7 sorted by ${SORT_LABELS[sort]}`,
|
|
1135
|
-
subtitle: `${balances.length} of ${total}
|
|
1232
|
+
subtitle: `${balances.length} of ${total}`,
|
|
1233
|
+
footer
|
|
1136
1234
|
}
|
|
1137
1235
|
)
|
|
1138
1236
|
);
|
|
@@ -1140,19 +1238,17 @@ function renderCoins(json, balances, total, sort) {
|
|
|
1140
1238
|
}
|
|
1141
1239
|
});
|
|
1142
1240
|
}
|
|
1143
|
-
async function fetchCoins(json, address, sort, limit) {
|
|
1241
|
+
async function fetchCoins(json, address, sort, limit, after) {
|
|
1144
1242
|
let response;
|
|
1145
1243
|
try {
|
|
1146
1244
|
response = await getProfileBalances({
|
|
1147
1245
|
identifier: address,
|
|
1148
1246
|
count: limit,
|
|
1149
|
-
sortOption: SORT_MAP[sort]
|
|
1247
|
+
sortOption: SORT_MAP[sort],
|
|
1248
|
+
after
|
|
1150
1249
|
});
|
|
1151
1250
|
} catch (err) {
|
|
1152
|
-
outputErrorAndExit(
|
|
1153
|
-
json,
|
|
1154
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1155
|
-
);
|
|
1251
|
+
outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`);
|
|
1156
1252
|
}
|
|
1157
1253
|
if (response.error) {
|
|
1158
1254
|
outputErrorAndExit(
|
|
@@ -1165,7 +1261,8 @@ async function fetchCoins(json, address, sort, limit) {
|
|
|
1165
1261
|
(e) => e.node
|
|
1166
1262
|
);
|
|
1167
1263
|
const total = response.data?.profile?.coinBalances?.count ?? balances.length;
|
|
1168
|
-
|
|
1264
|
+
const pageInfo = response.data?.profile?.coinBalances?.pageInfo;
|
|
1265
|
+
return { balances, total, pageInfo };
|
|
1169
1266
|
}
|
|
1170
1267
|
function validateCoinOpts(json, sort, limitStr) {
|
|
1171
1268
|
if (!SORT_MAP[sort]) {
|
|
@@ -1184,11 +1281,15 @@ function validateCoinOpts(json, sort, limitStr) {
|
|
|
1184
1281
|
}
|
|
1185
1282
|
return { sort, limit };
|
|
1186
1283
|
}
|
|
1187
|
-
var balanceCommand = new Command2("balance").description("Show balances in your wallet").
|
|
1284
|
+
var balanceCommand = new Command2("balance").description("Show balances in your wallet").option("--live", "Interactive live-updating display (default)").option("--static", "Static snapshot").option(
|
|
1285
|
+
"--refresh <seconds>",
|
|
1286
|
+
"Auto-refresh interval in seconds, requires --live (min 5)",
|
|
1287
|
+
"30"
|
|
1288
|
+
).action(async function() {
|
|
1188
1289
|
const output = getOutputMode(this, "live");
|
|
1189
1290
|
const json = output === "json";
|
|
1190
1291
|
const account = resolveContext(json);
|
|
1191
|
-
const { live, intervalSeconds } = getLiveConfig(this,
|
|
1292
|
+
const { live, intervalSeconds } = getLiveConfig(this, output);
|
|
1192
1293
|
const sort = "usd-value";
|
|
1193
1294
|
const limit = 10;
|
|
1194
1295
|
const fetchBalanceData = async () => {
|
|
@@ -1198,7 +1299,7 @@ var balanceCommand = new Command2("balance").description("Show balances in your
|
|
|
1198
1299
|
]);
|
|
1199
1300
|
if (walletResult.status === "rejected" || coinsResult.status === "rejected") {
|
|
1200
1301
|
const err = walletResult.status === "rejected" ? walletResult.reason : coinsResult.reason;
|
|
1201
|
-
throw
|
|
1302
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
1202
1303
|
}
|
|
1203
1304
|
const rankedBalances = coinsResult.value.balances.map(
|
|
1204
1305
|
(balance, index) => ({
|
|
@@ -1215,10 +1316,7 @@ var balanceCommand = new Command2("balance").description("Show balances in your
|
|
|
1215
1316
|
};
|
|
1216
1317
|
if (json) {
|
|
1217
1318
|
const data = await fetchBalanceData().catch(
|
|
1218
|
-
(err) => outputErrorAndExit(
|
|
1219
|
-
json,
|
|
1220
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1221
|
-
)
|
|
1319
|
+
(err) => outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`)
|
|
1222
1320
|
);
|
|
1223
1321
|
outputData(json, {
|
|
1224
1322
|
json: {
|
|
@@ -1227,7 +1325,7 @@ var balanceCommand = new Command2("balance").description("Show balances in your
|
|
|
1227
1325
|
(balance) => formatBalanceJson(balance, balance.rank)
|
|
1228
1326
|
)
|
|
1229
1327
|
},
|
|
1230
|
-
|
|
1328
|
+
render: () => {
|
|
1231
1329
|
}
|
|
1232
1330
|
});
|
|
1233
1331
|
track("cli_balances", {
|
|
@@ -1260,10 +1358,7 @@ var balanceCommand = new Command2("balance").description("Show balances in your
|
|
|
1260
1358
|
});
|
|
1261
1359
|
} else {
|
|
1262
1360
|
const data = await fetchBalanceData().catch(
|
|
1263
|
-
(err) => outputErrorAndExit(
|
|
1264
|
-
json,
|
|
1265
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1266
|
-
)
|
|
1361
|
+
(err) => outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`)
|
|
1267
1362
|
);
|
|
1268
1363
|
renderOnce(
|
|
1269
1364
|
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
@@ -1311,15 +1406,19 @@ var balanceCommand = new Command2("balance").description("Show balances in your
|
|
|
1311
1406
|
live: false,
|
|
1312
1407
|
result_count: data.rankedBalances.length,
|
|
1313
1408
|
total_count: data.total,
|
|
1314
|
-
output_format: "
|
|
1409
|
+
output_format: "static"
|
|
1315
1410
|
});
|
|
1316
1411
|
}
|
|
1317
1412
|
});
|
|
1318
|
-
balanceCommand.command("spendable").description("Show wallet token balances (ETH, USDC, ZORA)").
|
|
1413
|
+
balanceCommand.command("spendable").description("Show wallet token balances (ETH, USDC, ZORA)").option("--live", "Interactive live-updating display (default)").option("--static", "Static snapshot").option(
|
|
1414
|
+
"--refresh <seconds>",
|
|
1415
|
+
"Auto-refresh interval in seconds, requires --live (min 5)",
|
|
1416
|
+
"30"
|
|
1417
|
+
).action(async function() {
|
|
1319
1418
|
const output = getOutputMode(this, "live");
|
|
1320
1419
|
const json = output === "json";
|
|
1321
1420
|
const account = resolveContext(json);
|
|
1322
|
-
const { live, intervalSeconds } = getLiveConfig(this,
|
|
1421
|
+
const { live, intervalSeconds } = getLiveConfig(this, output);
|
|
1323
1422
|
const fetchSpendableData = async () => {
|
|
1324
1423
|
const walletResult = await fetchWalletBalances(account.address);
|
|
1325
1424
|
return {
|
|
@@ -1331,14 +1430,11 @@ balanceCommand.command("spendable").description("Show wallet token balances (ETH
|
|
|
1331
1430
|
};
|
|
1332
1431
|
if (json) {
|
|
1333
1432
|
const data = await fetchSpendableData().catch(
|
|
1334
|
-
(err) => outputErrorAndExit(
|
|
1335
|
-
json,
|
|
1336
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1337
|
-
)
|
|
1433
|
+
(err) => outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`)
|
|
1338
1434
|
);
|
|
1339
1435
|
outputData(json, {
|
|
1340
1436
|
json: { wallet: data.walletBalancesJson },
|
|
1341
|
-
|
|
1437
|
+
render: () => {
|
|
1342
1438
|
}
|
|
1343
1439
|
});
|
|
1344
1440
|
} else if (live) {
|
|
@@ -1356,26 +1452,29 @@ balanceCommand.command("spendable").description("Show wallet token balances (ETH
|
|
|
1356
1452
|
);
|
|
1357
1453
|
} else {
|
|
1358
1454
|
const walletResult = await fetchWalletBalances(account.address).catch(
|
|
1359
|
-
(err) => outputErrorAndExit(
|
|
1360
|
-
json,
|
|
1361
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1362
|
-
)
|
|
1455
|
+
(err) => outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`)
|
|
1363
1456
|
);
|
|
1364
1457
|
renderWallet(json, walletResult);
|
|
1365
1458
|
}
|
|
1366
1459
|
});
|
|
1367
|
-
balanceCommand.command("coins").description("Show coin positions").option("--sort <sort>", `Sort by: ${SORT_OPTIONS}`, "usd-value").option("--limit <n>", "Number of results (max 20)", "10").
|
|
1460
|
+
balanceCommand.command("coins").description("Show coin positions").option("--sort <sort>", `Sort by: ${SORT_OPTIONS}`, "usd-value").option("--limit <n>", "Number of results (max 20)", "10").option("--live", "Interactive live-updating display (default)").option("--static", "Static snapshot").option(
|
|
1461
|
+
"--refresh <seconds>",
|
|
1462
|
+
"Auto-refresh interval in seconds, requires --live (min 5)",
|
|
1463
|
+
"30"
|
|
1464
|
+
).option("--after <cursor>", "Pagination cursor from a previous result").action(async function(opts) {
|
|
1368
1465
|
const output = getOutputMode(this, "live");
|
|
1369
1466
|
const json = output === "json";
|
|
1370
1467
|
const { sort, limit } = validateCoinOpts(json, opts.sort, opts.limit);
|
|
1468
|
+
const after = opts.after;
|
|
1371
1469
|
const account = resolveContext(json);
|
|
1372
|
-
const { live, intervalSeconds } = getLiveConfig(this,
|
|
1373
|
-
const
|
|
1374
|
-
const { balances, total } = await fetchCoins(
|
|
1470
|
+
const { live, intervalSeconds } = getLiveConfig(this, output);
|
|
1471
|
+
const fetchCoinsPage = async (cursor) => {
|
|
1472
|
+
const { balances, total, pageInfo } = await fetchCoins(
|
|
1375
1473
|
json,
|
|
1376
1474
|
account.address,
|
|
1377
1475
|
sort,
|
|
1378
|
-
limit
|
|
1476
|
+
limit,
|
|
1477
|
+
cursor
|
|
1379
1478
|
);
|
|
1380
1479
|
const rankedBalances = balances.map((balance, index) => ({
|
|
1381
1480
|
...balance,
|
|
@@ -1385,33 +1484,43 @@ balanceCommand.command("coins").description("Show coin positions").option("--sor
|
|
|
1385
1484
|
walletBalances: [],
|
|
1386
1485
|
walletBalancesJson: [],
|
|
1387
1486
|
rankedBalances,
|
|
1388
|
-
total
|
|
1487
|
+
total,
|
|
1488
|
+
pageInfo
|
|
1389
1489
|
};
|
|
1390
1490
|
};
|
|
1391
1491
|
if (json) {
|
|
1392
|
-
const data = await
|
|
1393
|
-
renderCoins(
|
|
1492
|
+
const data = await fetchCoinsPage(after);
|
|
1493
|
+
renderCoins(
|
|
1494
|
+
json,
|
|
1495
|
+
data.rankedBalances,
|
|
1496
|
+
data.total,
|
|
1497
|
+
sort,
|
|
1498
|
+
limit,
|
|
1499
|
+
data.pageInfo
|
|
1500
|
+
);
|
|
1394
1501
|
} else if (live) {
|
|
1395
1502
|
await renderLive(
|
|
1396
1503
|
/* @__PURE__ */ jsx3(
|
|
1397
1504
|
BalanceView,
|
|
1398
1505
|
{
|
|
1399
|
-
fetchData:
|
|
1506
|
+
fetchData: fetchCoinsPage,
|
|
1400
1507
|
sort,
|
|
1401
1508
|
mode: "coins",
|
|
1509
|
+
initialCursor: after,
|
|
1402
1510
|
autoRefresh: live,
|
|
1403
1511
|
intervalSeconds
|
|
1404
1512
|
}
|
|
1405
1513
|
)
|
|
1406
1514
|
);
|
|
1407
1515
|
} else {
|
|
1408
|
-
const { balances, total } = await fetchCoins(
|
|
1516
|
+
const { balances, total, pageInfo } = await fetchCoins(
|
|
1409
1517
|
json,
|
|
1410
1518
|
account.address,
|
|
1411
1519
|
sort,
|
|
1412
|
-
limit
|
|
1520
|
+
limit,
|
|
1521
|
+
after
|
|
1413
1522
|
);
|
|
1414
|
-
renderCoins(json, balances, total, sort);
|
|
1523
|
+
renderCoins(json, balances, total, sort, limit, pageInfo);
|
|
1415
1524
|
}
|
|
1416
1525
|
});
|
|
1417
1526
|
|
|
@@ -1599,7 +1708,7 @@ var printTradeResult = (json, info) => {
|
|
|
1599
1708
|
};
|
|
1600
1709
|
|
|
1601
1710
|
// src/commands/buy.ts
|
|
1602
|
-
var buyCommand = new Command3("buy").description("Buy a coin").argument("
|
|
1711
|
+
var buyCommand = new Command3("buy").description("Buy a coin").argument("[address]", "Coin contract address (0x\u2026)").option("--eth <value>", "Buy with ETH amount").option("--usd <value>", "Buy with USD equivalent (use with --token)").option("--token <asset>", "Token to spend: eth, usdc, zora", "eth").option("--percent <value>", "Buy with percentage of ETH balance").option("--all", "Swap all ETH for coin").option("--quote", "Print quote and exit without trading").option("--yes", "Skip confirmation and execute directly").option("--slippage <pct>", "Slippage tolerance percent", "1").option("--debug", "Print full quote request/response JSON").action(async function(coinAddress, opts) {
|
|
1603
1712
|
const json = getJson(this);
|
|
1604
1713
|
const debug = opts.debug === true;
|
|
1605
1714
|
if (!isAddress(coinAddress)) {
|
|
@@ -1638,10 +1747,7 @@ var buyCommand = new Command3("buy").description("Buy a coin").argument("<addres
|
|
|
1638
1747
|
const response = await getCoin({ address: coinAddress });
|
|
1639
1748
|
token = response.data?.zora20Token;
|
|
1640
1749
|
} catch (err) {
|
|
1641
|
-
outputErrorAndExit(
|
|
1642
|
-
json,
|
|
1643
|
-
`Failed to fetch coin: ${err instanceof Error ? err.message : String(err)}`
|
|
1644
|
-
);
|
|
1750
|
+
outputErrorAndExit(json, `Failed to fetch coin: ${apiErrorMessage(err)}`);
|
|
1645
1751
|
}
|
|
1646
1752
|
if (!token) {
|
|
1647
1753
|
outputErrorAndExit(json, `Coin not found: ${coinAddress}`);
|
|
@@ -1816,7 +1922,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
1816
1922
|
}
|
|
1817
1923
|
outputErrorAndExit(
|
|
1818
1924
|
json,
|
|
1819
|
-
`Quote failed: ${
|
|
1925
|
+
`Quote failed: ${apiErrorMessage(err)}`,
|
|
1820
1926
|
"Check the coin address is valid and try again. Use --debug for full error details."
|
|
1821
1927
|
);
|
|
1822
1928
|
}
|
|
@@ -1849,7 +1955,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
1849
1955
|
valueUsd: swapAmountUsd,
|
|
1850
1956
|
swapCoinType: token.coinType ?? null,
|
|
1851
1957
|
slippage: slippagePct,
|
|
1852
|
-
output_format: json ? "json" : "
|
|
1958
|
+
output_format: json ? "json" : "static"
|
|
1853
1959
|
});
|
|
1854
1960
|
return;
|
|
1855
1961
|
}
|
|
@@ -1897,15 +2003,12 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
1897
2003
|
valueUsd: swapAmountUsd,
|
|
1898
2004
|
swapCoinType,
|
|
1899
2005
|
slippage: slippagePct,
|
|
1900
|
-
output_format: json ? "json" : "
|
|
2006
|
+
output_format: json ? "json" : "static",
|
|
1901
2007
|
success: false,
|
|
1902
2008
|
error_type: err instanceof Error ? err.constructor.name : "unknown"
|
|
1903
2009
|
});
|
|
1904
2010
|
await shutdownAnalytics();
|
|
1905
|
-
outputErrorAndExit(
|
|
1906
|
-
json,
|
|
1907
|
-
`Transaction failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1908
|
-
);
|
|
2011
|
+
outputErrorAndExit(json, tradeErrorMessage(err));
|
|
1909
2012
|
}
|
|
1910
2013
|
txHash = receipt.transactionHash;
|
|
1911
2014
|
try {
|
|
@@ -1947,7 +2050,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
1947
2050
|
transactionHash: txHash,
|
|
1948
2051
|
logIndex: swapLogIndex,
|
|
1949
2052
|
slippage: slippagePct,
|
|
1950
|
-
output_format: json ? "json" : "
|
|
2053
|
+
output_format: json ? "json" : "static",
|
|
1951
2054
|
success: true,
|
|
1952
2055
|
tx_hash: txHash
|
|
1953
2056
|
});
|
|
@@ -2007,11 +2110,47 @@ var COIN_TYPE_DISPLAY = {
|
|
|
2007
2110
|
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
2008
2111
|
import { Box as Box4, Text as Text4, useInput as useInput2, useApp as useApp2 } from "ink";
|
|
2009
2112
|
import Spinner2 from "ink-spinner";
|
|
2113
|
+
|
|
2114
|
+
// src/lib/clipboard.ts
|
|
2115
|
+
import { execFileSync } from "child_process";
|
|
2116
|
+
import { platform as platform2 } from "os";
|
|
2117
|
+
var copyToClipboard = (text) => {
|
|
2118
|
+
const os = platform2();
|
|
2119
|
+
try {
|
|
2120
|
+
if (os === "darwin") {
|
|
2121
|
+
execFileSync("pbcopy", {
|
|
2122
|
+
input: text,
|
|
2123
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
2124
|
+
});
|
|
2125
|
+
} else if (os === "linux") {
|
|
2126
|
+
execFileSync("xclip", ["-selection", "clipboard"], {
|
|
2127
|
+
input: text,
|
|
2128
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
2129
|
+
});
|
|
2130
|
+
} else if (os === "win32") {
|
|
2131
|
+
execFileSync("clip", {
|
|
2132
|
+
input: text,
|
|
2133
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
2134
|
+
});
|
|
2135
|
+
} else {
|
|
2136
|
+
return false;
|
|
2137
|
+
}
|
|
2138
|
+
return true;
|
|
2139
|
+
} catch {
|
|
2140
|
+
return false;
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
|
|
2144
|
+
// src/components/ExploreView.tsx
|
|
2010
2145
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2011
2146
|
var COLUMNS = [
|
|
2012
2147
|
{ header: "#", width: 4, accessor: (c) => String(c.rank) },
|
|
2013
2148
|
{ header: "Name", width: 20, accessor: (c) => c.name ?? "Unknown" },
|
|
2014
|
-
{
|
|
2149
|
+
{
|
|
2150
|
+
header: "Address",
|
|
2151
|
+
width: 14,
|
|
2152
|
+
accessor: (c) => c.address ? truncateAddress(c.address) : ""
|
|
2153
|
+
},
|
|
2015
2154
|
{
|
|
2016
2155
|
header: "Type",
|
|
2017
2156
|
width: 14,
|
|
@@ -2061,6 +2200,11 @@ var ExploreView = ({
|
|
|
2061
2200
|
const cache = useRef2(/* @__PURE__ */ new Map());
|
|
2062
2201
|
const { refreshCount, secondsUntilRefresh, triggerManualRefresh } = useAutoRefresh(intervalSeconds, autoRefresh);
|
|
2063
2202
|
const [manualRefreshCount, setManualRefreshCount] = useState3(0);
|
|
2203
|
+
const [selectedRow, setSelectedRow] = useState3(0);
|
|
2204
|
+
const [copyFeedback, setCopyFeedback] = useState3(null);
|
|
2205
|
+
useEffect3(() => {
|
|
2206
|
+
setSelectedRow((r) => Math.min(r, Math.max(0, coins.length - 1)));
|
|
2207
|
+
}, [coins.length]);
|
|
2064
2208
|
const loadPage = useCallback3(
|
|
2065
2209
|
async (cursor) => {
|
|
2066
2210
|
const cacheKey = cursor ?? CACHE_KEY_FIRST;
|
|
@@ -2101,24 +2245,44 @@ var ExploreView = ({
|
|
|
2101
2245
|
return;
|
|
2102
2246
|
}
|
|
2103
2247
|
if (loading) return;
|
|
2248
|
+
if (key.upArrow || input === "k") {
|
|
2249
|
+
setSelectedRow((r) => Math.max(0, r - 1));
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
if (key.downArrow || input === "j") {
|
|
2253
|
+
setSelectedRow((r) => Math.min(coins.length - 1, r + 1));
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
if (input === "c") {
|
|
2257
|
+
const coin = coins[selectedRow];
|
|
2258
|
+
if (coin?.address) {
|
|
2259
|
+
const ok = copyToClipboard(coin.address);
|
|
2260
|
+
setCopyFeedback(ok ? "Copied!" : "Copy failed");
|
|
2261
|
+
setTimeout(() => setCopyFeedback(null), 1500);
|
|
2262
|
+
}
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2104
2265
|
const canGoNext = pageInfo?.hasNextPage && pageInfo.endCursor;
|
|
2105
2266
|
const canGoPrev = cursorHistory.length > 0;
|
|
2106
2267
|
if ((input === "n" || key.rightArrow) && canGoNext) {
|
|
2107
2268
|
setCursorHistory((prev) => [...prev, currentCursor]);
|
|
2108
2269
|
setCurrentCursor(pageInfo.endCursor);
|
|
2109
2270
|
setPage((p) => p + 1);
|
|
2271
|
+
setSelectedRow(0);
|
|
2110
2272
|
}
|
|
2111
2273
|
if ((input === "p" || key.leftArrow) && canGoPrev) {
|
|
2112
2274
|
const prev = cursorHistory[cursorHistory.length - 1];
|
|
2113
2275
|
setCursorHistory((h) => h.slice(0, -1));
|
|
2114
2276
|
setCurrentCursor(prev);
|
|
2115
2277
|
setPage((p) => p - 1);
|
|
2278
|
+
setSelectedRow(0);
|
|
2116
2279
|
}
|
|
2117
2280
|
if (input === "r") {
|
|
2118
2281
|
const cacheKey = currentCursor ?? CACHE_KEY_FIRST;
|
|
2119
2282
|
cache.current.delete(cacheKey);
|
|
2120
2283
|
triggerManualRefresh();
|
|
2121
2284
|
setManualRefreshCount((c) => c + 1);
|
|
2285
|
+
setSelectedRow(0);
|
|
2122
2286
|
}
|
|
2123
2287
|
});
|
|
2124
2288
|
if (error) {
|
|
@@ -2172,12 +2336,14 @@ var ExploreView = ({
|
|
|
2172
2336
|
rank: (page - 1) * limit + i + 1
|
|
2173
2337
|
}));
|
|
2174
2338
|
const hints = [];
|
|
2339
|
+
hints.push("\u2191\u2193 select");
|
|
2340
|
+
hints.push("c copy address");
|
|
2175
2341
|
if (cursorHistory.length > 0) hints.push("\u2190 prev");
|
|
2176
2342
|
if (pageInfo?.hasNextPage) hints.push("\u2192 next");
|
|
2177
2343
|
hints.push("r refresh");
|
|
2178
2344
|
if (autoRefresh) hints.push(`auto: ${secondsUntilRefresh}s`);
|
|
2179
2345
|
hints.push("q quit");
|
|
2180
|
-
const footer = hints.join(" \xB7 ");
|
|
2346
|
+
const footer = hints.join(" \xB7 ") + (copyFeedback ? ` ${copyFeedback}` : "");
|
|
2181
2347
|
return /* @__PURE__ */ jsx4(
|
|
2182
2348
|
Table,
|
|
2183
2349
|
{
|
|
@@ -2185,7 +2351,8 @@ var ExploreView = ({
|
|
|
2185
2351
|
columns: COLUMNS,
|
|
2186
2352
|
title,
|
|
2187
2353
|
subtitle,
|
|
2188
|
-
footer
|
|
2354
|
+
footer,
|
|
2355
|
+
selectedRow
|
|
2189
2356
|
}
|
|
2190
2357
|
);
|
|
2191
2358
|
};
|
|
@@ -2231,12 +2398,42 @@ var QUERY_MAP = {
|
|
|
2231
2398
|
post: getExploreFeaturedVideos
|
|
2232
2399
|
}
|
|
2233
2400
|
};
|
|
2401
|
+
var STATIC_COLUMNS = [
|
|
2402
|
+
{ header: "#", width: 4, accessor: (c) => String(c.rank) },
|
|
2403
|
+
{ header: "Name", width: 20, accessor: (c) => c.name ?? "Unknown" },
|
|
2404
|
+
{ header: "Address", width: 48, accessor: (c) => c.address ?? "" },
|
|
2405
|
+
{
|
|
2406
|
+
header: "Type",
|
|
2407
|
+
width: 14,
|
|
2408
|
+
accessor: (c) => COIN_TYPE_DISPLAY[c.coinType ?? ""] ?? c.coinType ?? ""
|
|
2409
|
+
},
|
|
2410
|
+
{
|
|
2411
|
+
header: "Market Cap",
|
|
2412
|
+
width: 12,
|
|
2413
|
+
accessor: (c) => formatCompactUsd(c.marketCap)
|
|
2414
|
+
},
|
|
2415
|
+
{
|
|
2416
|
+
header: "24h Vol",
|
|
2417
|
+
width: 12,
|
|
2418
|
+
accessor: (c) => formatCompactUsd(c.volume24h)
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
header: "24h Change",
|
|
2422
|
+
width: 11,
|
|
2423
|
+
accessor: (c) => formatMcapChange(c.marketCap, c.marketCapDelta24h).text,
|
|
2424
|
+
color: (c) => formatMcapChange(c.marketCap, c.marketCapDelta24h).color
|
|
2425
|
+
}
|
|
2426
|
+
];
|
|
2234
2427
|
var SORT_OPTIONS2 = Object.keys(SORT_LABELS2).join(", ");
|
|
2235
2428
|
var exploreCommand = new Command4("explore").description("Browse top, new, and highest volume coins").option("--sort <sort>", `Sort by: ${SORT_OPTIONS2}`, "mcap").option(
|
|
2236
2429
|
"--type <type>",
|
|
2237
2430
|
"Filter by type: all, trend, creator-coin, post (availability varies by sort)",
|
|
2238
2431
|
"post"
|
|
2239
|
-
).option("--limit <n>", "Number of results (max 20)", "10").option("--after <cursor>", "Pagination cursor from a previous result").
|
|
2432
|
+
).option("--limit <n>", "Number of results (max 20)", "10").option("--after <cursor>", "Pagination cursor from a previous result").option("--live", "Interactive live-updating display (default)").option("--static", "Static snapshot").option(
|
|
2433
|
+
"--refresh <seconds>",
|
|
2434
|
+
"Auto-refresh interval in seconds, requires --live (min 5)",
|
|
2435
|
+
"30"
|
|
2436
|
+
).action(async function(opts) {
|
|
2240
2437
|
const output = getOutputMode(this, "live");
|
|
2241
2438
|
const json = output === "json";
|
|
2242
2439
|
const sort = opts.sort;
|
|
@@ -2275,10 +2472,7 @@ var exploreCommand = new Command4("explore").description("Browse top, new, and h
|
|
|
2275
2472
|
try {
|
|
2276
2473
|
response = await queryFn({ count: limit, after });
|
|
2277
2474
|
} catch (err) {
|
|
2278
|
-
outputErrorAndExit(
|
|
2279
|
-
json,
|
|
2280
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2281
|
-
);
|
|
2475
|
+
outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`);
|
|
2282
2476
|
}
|
|
2283
2477
|
if (response.error) {
|
|
2284
2478
|
const msg = typeof response.error === "object" && response.error.error ? response.error.error : JSON.stringify(response.error);
|
|
@@ -2298,7 +2492,7 @@ var exploreCommand = new Command4("explore").description("Browse top, new, and h
|
|
|
2298
2492
|
output_format: "json"
|
|
2299
2493
|
});
|
|
2300
2494
|
} else {
|
|
2301
|
-
const { live, intervalSeconds } = getLiveConfig(this,
|
|
2495
|
+
const { live, intervalSeconds } = getLiveConfig(this, output);
|
|
2302
2496
|
const fetchPage = async (cursor) => {
|
|
2303
2497
|
const response = await queryFn({ count: limit, after: cursor });
|
|
2304
2498
|
if (response.error) {
|
|
@@ -2310,29 +2504,55 @@ var exploreCommand = new Command4("explore").description("Browse top, new, and h
|
|
|
2310
2504
|
const pageInfo = response.data?.exploreList?.pageInfo;
|
|
2311
2505
|
return { coins, pageInfo };
|
|
2312
2506
|
};
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2507
|
+
if (live) {
|
|
2508
|
+
await renderLive(
|
|
2509
|
+
/* @__PURE__ */ jsx5(
|
|
2510
|
+
ExploreView,
|
|
2511
|
+
{
|
|
2512
|
+
fetchPage,
|
|
2513
|
+
sort,
|
|
2514
|
+
type,
|
|
2515
|
+
limit,
|
|
2516
|
+
initialCursor: after,
|
|
2517
|
+
autoRefresh: live,
|
|
2518
|
+
intervalSeconds
|
|
2519
|
+
}
|
|
2520
|
+
)
|
|
2521
|
+
);
|
|
2522
|
+
track("cli_explore", {
|
|
2523
|
+
sort,
|
|
2524
|
+
type,
|
|
2525
|
+
limit,
|
|
2526
|
+
live,
|
|
2527
|
+
interval: intervalSeconds,
|
|
2528
|
+
paginated: after !== void 0,
|
|
2529
|
+
output_format: "live"
|
|
2530
|
+
});
|
|
2531
|
+
} else {
|
|
2532
|
+
const { coins } = await fetchPage(after).catch(
|
|
2533
|
+
(err) => outputErrorAndExit(
|
|
2534
|
+
false,
|
|
2535
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2536
|
+
)
|
|
2537
|
+
);
|
|
2538
|
+
const title = type !== "all" ? `${SORT_LABELS2[sort]} \xB7 ${TYPE_LABELS[type]}` : SORT_LABELS2[sort];
|
|
2539
|
+
const rankedCoins = coins.map((c, i) => ({
|
|
2540
|
+
...c,
|
|
2541
|
+
rank: i + 1
|
|
2542
|
+
}));
|
|
2543
|
+
renderOnce(
|
|
2544
|
+
/* @__PURE__ */ jsx5(Table, { columns: STATIC_COLUMNS, data: rankedCoins, title })
|
|
2545
|
+
);
|
|
2546
|
+
track("cli_explore", {
|
|
2547
|
+
sort,
|
|
2548
|
+
type,
|
|
2549
|
+
limit,
|
|
2550
|
+
live: false,
|
|
2551
|
+
paginated: after !== void 0,
|
|
2552
|
+
result_count: coins.length,
|
|
2553
|
+
output_format: "static"
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2336
2556
|
}
|
|
2337
2557
|
});
|
|
2338
2558
|
|
|
@@ -2483,7 +2703,7 @@ function formatCoinJson(coin) {
|
|
|
2483
2703
|
};
|
|
2484
2704
|
}
|
|
2485
2705
|
var VALID_TYPES = ["creator-coin", "post", "trend"];
|
|
2486
|
-
var getCommand = new Command5("get").description("Look up a coin by address or name").argument("
|
|
2706
|
+
var getCommand = new Command5("get").description("Look up a coin by address or name").argument("[identifier]", "Coin address (0x...) or creator name").option("--type <type>", "Coin type: creator-coin, post, trend").action(async function(identifier, opts) {
|
|
2487
2707
|
const json = getJson(this);
|
|
2488
2708
|
if (opts.type !== void 0 && !VALID_TYPES.includes(opts.type)) {
|
|
2489
2709
|
outputErrorAndExit(
|
|
@@ -2509,10 +2729,7 @@ var getCommand = new Command5("get").description("Look up a coin by address or n
|
|
|
2509
2729
|
try {
|
|
2510
2730
|
result = await resolveCoin(ref);
|
|
2511
2731
|
} catch (err) {
|
|
2512
|
-
outputErrorAndExit(
|
|
2513
|
-
json,
|
|
2514
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2515
|
-
);
|
|
2732
|
+
outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`);
|
|
2516
2733
|
return;
|
|
2517
2734
|
}
|
|
2518
2735
|
if (type && result.kind === "found" && result.coin.coinType !== type) {
|
|
@@ -2529,7 +2746,7 @@ var getCommand = new Command5("get").description("Look up a coin by address or n
|
|
|
2529
2746
|
}
|
|
2530
2747
|
outputData(json, {
|
|
2531
2748
|
json: formatCoinJson(result.coin),
|
|
2532
|
-
|
|
2749
|
+
render: () => {
|
|
2533
2750
|
renderOnce(/* @__PURE__ */ jsx7(CoinDetail, { coin: result.coin }));
|
|
2534
2751
|
}
|
|
2535
2752
|
});
|
|
@@ -2651,7 +2868,7 @@ var fetchPriceHistory = async (address, interval) => {
|
|
|
2651
2868
|
price: Number(p.closePrice)
|
|
2652
2869
|
}));
|
|
2653
2870
|
};
|
|
2654
|
-
var priceHistoryCommand = new Command6("price-history").description("Display price history for a coin").argument("
|
|
2871
|
+
var priceHistoryCommand = new Command6("price-history").description("Display price history for a coin").argument("[identifier]", "Coin address (0x...) or name").option("--type <type>", "Coin type: creator-coin, post, trend").option(
|
|
2655
2872
|
"--interval <interval>",
|
|
2656
2873
|
`Time range: ${VALID_INTERVALS.join(", ")}`,
|
|
2657
2874
|
"1w"
|
|
@@ -2740,7 +2957,7 @@ var priceHistoryCommand = new Command6("price-history").description("Display pri
|
|
|
2740
2957
|
price: p.price
|
|
2741
2958
|
}))
|
|
2742
2959
|
},
|
|
2743
|
-
|
|
2960
|
+
render: () => {
|
|
2744
2961
|
renderOnce(
|
|
2745
2962
|
/* @__PURE__ */ jsx9(
|
|
2746
2963
|
PriceHistory,
|
|
@@ -2853,13 +3070,13 @@ function printSellResult(output, info) {
|
|
|
2853
3070
|
console.log(` Tx ${info.txHash}
|
|
2854
3071
|
`);
|
|
2855
3072
|
}
|
|
2856
|
-
var sellCommand = new Command7("sell").description("Sell a coin").argument("
|
|
3073
|
+
var sellCommand = new Command7("sell").description("Sell a coin").argument("[address]", "Coin contract address (0x\u2026)").option("--amount <value>", "Sell specific number of coins").option("--usd <value>", "Sell USD equivalent worth of coins").option("--percent <value>", "Sell percentage of coin balance").option("--all", "Sell entire coin balance").option("--to <asset>", "Receive asset: eth, usdc, zora", "eth").option("--token <asset>", "Receive asset: eth, usdc, zora (alias for --to)").option("--quote", "Print quote and exit without trading").option("--yes", "Skip confirmation and execute directly").option("--slippage <pct>", "Slippage tolerance percent", "1").option("--debug", "Print full quote request/response JSON").action(async function(coinAddress, opts) {
|
|
2857
3074
|
const json = getJson(this);
|
|
2858
3075
|
const debug = opts.debug === true;
|
|
2859
3076
|
if (!isAddress2(coinAddress)) {
|
|
2860
3077
|
outputErrorAndExit(json, `Invalid address: ${coinAddress}`);
|
|
2861
3078
|
}
|
|
2862
|
-
const output = json ? "json" : "
|
|
3079
|
+
const output = json ? "json" : "static";
|
|
2863
3080
|
const outputAsset = opts.token ? opts.token.toLowerCase() : opts.to;
|
|
2864
3081
|
if (!(outputAsset in BASE_TRADE_TOKENS)) {
|
|
2865
3082
|
outputErrorAndExit(
|
|
@@ -2893,10 +3110,7 @@ var sellCommand = new Command7("sell").description("Sell a coin").argument("<add
|
|
|
2893
3110
|
const response = await getCoin3({ address: coinAddress });
|
|
2894
3111
|
token = response.data?.zora20Token;
|
|
2895
3112
|
} catch (err) {
|
|
2896
|
-
outputErrorAndExit(
|
|
2897
|
-
json,
|
|
2898
|
-
`Failed to fetch coin: ${err instanceof Error ? err.message : String(err)}`
|
|
2899
|
-
);
|
|
3113
|
+
outputErrorAndExit(json, `Failed to fetch coin: ${apiErrorMessage(err)}`);
|
|
2900
3114
|
}
|
|
2901
3115
|
if (!token) {
|
|
2902
3116
|
outputErrorAndExit(json, `Coin not found: ${coinAddress}`);
|
|
@@ -3037,7 +3251,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3037
3251
|
}
|
|
3038
3252
|
outputErrorAndExit(
|
|
3039
3253
|
json,
|
|
3040
|
-
`Quote failed: ${
|
|
3254
|
+
`Quote failed: ${apiErrorMessage(err)}`,
|
|
3041
3255
|
"Check the coin address and amount, then try again. Use --debug for full error details."
|
|
3042
3256
|
);
|
|
3043
3257
|
}
|
|
@@ -3076,7 +3290,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3076
3290
|
return;
|
|
3077
3291
|
}
|
|
3078
3292
|
if (!opts.yes) {
|
|
3079
|
-
printSellQuote("
|
|
3293
|
+
printSellQuote("static", {
|
|
3080
3294
|
coinName,
|
|
3081
3295
|
coinSymbol,
|
|
3082
3296
|
address: coinAddress,
|
|
@@ -3128,10 +3342,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3128
3342
|
error_type: err instanceof Error ? err.constructor.name : "unknown"
|
|
3129
3343
|
});
|
|
3130
3344
|
await shutdownAnalytics();
|
|
3131
|
-
outputErrorAndExit(
|
|
3132
|
-
json,
|
|
3133
|
-
`Transaction failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3134
|
-
);
|
|
3345
|
+
outputErrorAndExit(json, tradeErrorMessage(err));
|
|
3135
3346
|
}
|
|
3136
3347
|
txHash = receipt.transactionHash;
|
|
3137
3348
|
if (outputToken.trade.type === "erc20") {
|
|
@@ -3179,8 +3390,402 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3179
3390
|
});
|
|
3180
3391
|
});
|
|
3181
3392
|
|
|
3182
|
-
// src/commands/
|
|
3393
|
+
// src/commands/profile.tsx
|
|
3183
3394
|
import { Command as Command8 } from "commander";
|
|
3395
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
3396
|
+
import {
|
|
3397
|
+
getProfileCoins,
|
|
3398
|
+
getProfileBalances as getProfileBalances2,
|
|
3399
|
+
setApiKey as setApiKey7
|
|
3400
|
+
} from "@zoralabs/coins-sdk";
|
|
3401
|
+
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
3402
|
+
|
|
3403
|
+
// src/components/ProfileView.tsx
|
|
3404
|
+
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef3 } from "react";
|
|
3405
|
+
import { Box as Box7, Text as Text7, useInput as useInput3, useApp as useApp3 } from "ink";
|
|
3406
|
+
import Spinner3 from "ink-spinner";
|
|
3407
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3408
|
+
var TAB_NAMES = ["Posts", "Holdings"];
|
|
3409
|
+
var postColumns = [
|
|
3410
|
+
{ header: "#", width: 4, accessor: (c) => String(c.rank) },
|
|
3411
|
+
{ header: "Name", width: 20, accessor: (c) => c.name ?? "Unknown" },
|
|
3412
|
+
{
|
|
3413
|
+
header: "Type",
|
|
3414
|
+
width: 14,
|
|
3415
|
+
accessor: (c) => COIN_TYPE_DISPLAY[c.coinType ?? ""] ?? c.coinType ?? ""
|
|
3416
|
+
},
|
|
3417
|
+
{
|
|
3418
|
+
header: "Market Cap",
|
|
3419
|
+
width: 12,
|
|
3420
|
+
accessor: (c) => formatCompactUsd(c.marketCap)
|
|
3421
|
+
},
|
|
3422
|
+
{
|
|
3423
|
+
header: "24h Vol",
|
|
3424
|
+
width: 12,
|
|
3425
|
+
accessor: (c) => formatCompactUsd(c.volume24h)
|
|
3426
|
+
},
|
|
3427
|
+
{
|
|
3428
|
+
header: "24h Change",
|
|
3429
|
+
width: 11,
|
|
3430
|
+
accessor: (c) => formatMcapChange(c.marketCap, c.marketCapDelta24h).text,
|
|
3431
|
+
color: (c) => formatMcapChange(c.marketCap, c.marketCapDelta24h).color
|
|
3432
|
+
},
|
|
3433
|
+
{
|
|
3434
|
+
header: "Created",
|
|
3435
|
+
width: 16,
|
|
3436
|
+
accessor: (c) => {
|
|
3437
|
+
if (!c.createdAt) return "-";
|
|
3438
|
+
const date = new Date(c.createdAt);
|
|
3439
|
+
if (isNaN(date.getTime())) return "-";
|
|
3440
|
+
return formatRelativeTime(date);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
];
|
|
3444
|
+
var ProfileView = ({
|
|
3445
|
+
fetchData,
|
|
3446
|
+
identifier,
|
|
3447
|
+
autoRefresh = false,
|
|
3448
|
+
intervalSeconds = 30
|
|
3449
|
+
}) => {
|
|
3450
|
+
const { exit } = useApp3();
|
|
3451
|
+
const [activeTab, setActiveTab] = useState4(0);
|
|
3452
|
+
const [loading, setLoading] = useState4(true);
|
|
3453
|
+
const [isRefreshing, setIsRefreshing] = useState4(false);
|
|
3454
|
+
const [error, setError] = useState4(null);
|
|
3455
|
+
const [data, setData] = useState4(null);
|
|
3456
|
+
const { refreshCount, secondsUntilRefresh, triggerManualRefresh } = useAutoRefresh(intervalSeconds, autoRefresh);
|
|
3457
|
+
const [manualRefreshCount, setManualRefreshCount] = useState4(0);
|
|
3458
|
+
const hasLoadedOnce = useRef3(false);
|
|
3459
|
+
const load = useCallback4(async () => {
|
|
3460
|
+
if (hasLoadedOnce.current) {
|
|
3461
|
+
setIsRefreshing(true);
|
|
3462
|
+
} else {
|
|
3463
|
+
setLoading(true);
|
|
3464
|
+
}
|
|
3465
|
+
setError(null);
|
|
3466
|
+
try {
|
|
3467
|
+
const result = await fetchData();
|
|
3468
|
+
setData(result);
|
|
3469
|
+
hasLoadedOnce.current = true;
|
|
3470
|
+
} catch (err) {
|
|
3471
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
3472
|
+
}
|
|
3473
|
+
setLoading(false);
|
|
3474
|
+
setIsRefreshing(false);
|
|
3475
|
+
}, [fetchData]);
|
|
3476
|
+
useEffect4(() => {
|
|
3477
|
+
load();
|
|
3478
|
+
}, [load, refreshCount, manualRefreshCount]);
|
|
3479
|
+
useInput3((input, key) => {
|
|
3480
|
+
if (input === "q" || key.escape) {
|
|
3481
|
+
exit();
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3484
|
+
if (input === "r" && !loading) {
|
|
3485
|
+
triggerManualRefresh();
|
|
3486
|
+
setManualRefreshCount((c) => c + 1);
|
|
3487
|
+
}
|
|
3488
|
+
if (key.leftArrow || input === "1") {
|
|
3489
|
+
setActiveTab(0);
|
|
3490
|
+
}
|
|
3491
|
+
if (key.rightArrow || input === "2") {
|
|
3492
|
+
setActiveTab(1);
|
|
3493
|
+
}
|
|
3494
|
+
});
|
|
3495
|
+
if (error && !data) {
|
|
3496
|
+
return /* @__PURE__ */ jsxs7(
|
|
3497
|
+
Box7,
|
|
3498
|
+
{
|
|
3499
|
+
flexDirection: "column",
|
|
3500
|
+
paddingLeft: 1,
|
|
3501
|
+
paddingTop: 1,
|
|
3502
|
+
paddingBottom: 1,
|
|
3503
|
+
children: [
|
|
3504
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "red", children: [
|
|
3505
|
+
"Error: ",
|
|
3506
|
+
error
|
|
3507
|
+
] }),
|
|
3508
|
+
/* @__PURE__ */ jsx10(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text7, { dimColor: true, children: "Press q to exit" }) })
|
|
3509
|
+
]
|
|
3510
|
+
}
|
|
3511
|
+
);
|
|
3512
|
+
}
|
|
3513
|
+
if (loading && !data) {
|
|
3514
|
+
return /* @__PURE__ */ jsx10(Box7, { paddingLeft: 1, paddingTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3515
|
+
/* @__PURE__ */ jsx10(Spinner3, { type: "dots" }),
|
|
3516
|
+
" Loading profile\u2026"
|
|
3517
|
+
] }) });
|
|
3518
|
+
}
|
|
3519
|
+
if (!data) return null;
|
|
3520
|
+
const hints = ["\u2190 \u2192 switch tab", "r refresh"];
|
|
3521
|
+
if (autoRefresh) hints.push(`auto: ${secondsUntilRefresh}s`);
|
|
3522
|
+
hints.push("q quit");
|
|
3523
|
+
const footer = hints.join(" \xB7 ");
|
|
3524
|
+
const rankedPosts = data.posts.map((p, i) => ({ ...p, rank: i + 1 }));
|
|
3525
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3526
|
+
isRefreshing && /* @__PURE__ */ jsx10(Box7, { paddingLeft: 1, children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
3527
|
+
/* @__PURE__ */ jsx10(Spinner3, { type: "dots" }),
|
|
3528
|
+
" Refreshing\u2026"
|
|
3529
|
+
] }) }),
|
|
3530
|
+
/* @__PURE__ */ jsxs7(Box7, { paddingLeft: 1, paddingTop: 1, gap: 2, children: [
|
|
3531
|
+
TAB_NAMES.map((name, i) => /* @__PURE__ */ jsx10(Text7, { bold: activeTab === i, dimColor: activeTab !== i, children: activeTab === i ? `[${name}]` : name }, name)),
|
|
3532
|
+
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
3533
|
+
" ",
|
|
3534
|
+
identifier
|
|
3535
|
+
] })
|
|
3536
|
+
] }),
|
|
3537
|
+
activeTab === 0 ? rankedPosts.length === 0 ? /* @__PURE__ */ jsx10(
|
|
3538
|
+
Box7,
|
|
3539
|
+
{
|
|
3540
|
+
flexDirection: "column",
|
|
3541
|
+
paddingLeft: 1,
|
|
3542
|
+
paddingTop: 1,
|
|
3543
|
+
paddingBottom: 1,
|
|
3544
|
+
children: /* @__PURE__ */ jsx10(Text7, { children: "No posts found for this profile." })
|
|
3545
|
+
}
|
|
3546
|
+
) : /* @__PURE__ */ jsx10(
|
|
3547
|
+
Table,
|
|
3548
|
+
{
|
|
3549
|
+
columns: postColumns,
|
|
3550
|
+
data: rankedPosts,
|
|
3551
|
+
title: "Posts",
|
|
3552
|
+
subtitle: `${rankedPosts.length} of ${data.postsCount}`
|
|
3553
|
+
}
|
|
3554
|
+
) : data.holdings.length === 0 ? /* @__PURE__ */ jsx10(
|
|
3555
|
+
Box7,
|
|
3556
|
+
{
|
|
3557
|
+
flexDirection: "column",
|
|
3558
|
+
paddingLeft: 1,
|
|
3559
|
+
paddingTop: 1,
|
|
3560
|
+
paddingBottom: 1,
|
|
3561
|
+
children: /* @__PURE__ */ jsx10(Text7, { children: "No holdings found for this profile." })
|
|
3562
|
+
}
|
|
3563
|
+
) : /* @__PURE__ */ jsx10(
|
|
3564
|
+
Table,
|
|
3565
|
+
{
|
|
3566
|
+
columns: balanceColumns,
|
|
3567
|
+
data: data.holdings,
|
|
3568
|
+
title: "Holdings",
|
|
3569
|
+
subtitle: `${data.holdings.length} of ${data.holdingsCount}`
|
|
3570
|
+
}
|
|
3571
|
+
),
|
|
3572
|
+
/* @__PURE__ */ jsx10(Box7, { paddingLeft: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx10(Text7, { dimColor: true, children: footer }) })
|
|
3573
|
+
] });
|
|
3574
|
+
};
|
|
3575
|
+
|
|
3576
|
+
// src/commands/profile.tsx
|
|
3577
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3578
|
+
var extractErrorMessage2 = (error) => {
|
|
3579
|
+
if (typeof error === "object" && error !== null && "error" in error) {
|
|
3580
|
+
return String(error.error);
|
|
3581
|
+
}
|
|
3582
|
+
return JSON.stringify(error);
|
|
3583
|
+
};
|
|
3584
|
+
var resolveApiKey = (json) => {
|
|
3585
|
+
const apiKey = getApiKey();
|
|
3586
|
+
if (!apiKey) {
|
|
3587
|
+
outputErrorAndExit(
|
|
3588
|
+
json,
|
|
3589
|
+
"Not authenticated. Run 'zora auth configure' to set your API key."
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
setApiKey7(apiKey);
|
|
3593
|
+
};
|
|
3594
|
+
var formatPostJson = (post, rank) => ({
|
|
3595
|
+
rank,
|
|
3596
|
+
name: post.name,
|
|
3597
|
+
symbol: post.symbol,
|
|
3598
|
+
coinType: COIN_TYPE_DISPLAY[post.coinType] ?? post.coinType,
|
|
3599
|
+
address: post.address,
|
|
3600
|
+
marketCap: post.marketCap ?? null,
|
|
3601
|
+
marketCapDelta24h: post.marketCapDelta24h ?? null,
|
|
3602
|
+
volume24h: post.volume24h ?? null,
|
|
3603
|
+
createdAt: post.createdAt ?? null
|
|
3604
|
+
});
|
|
3605
|
+
var formatHoldingJson = (balance) => {
|
|
3606
|
+
const priceUsd = balance.coin?.tokenPrice?.priceInUsdc ? Number(balance.coin.tokenPrice.priceInUsdc) : null;
|
|
3607
|
+
const usdValue = priceUsd !== null ? Number((parseRawBalance(balance.balance) * priceUsd).toFixed(6)) : null;
|
|
3608
|
+
return {
|
|
3609
|
+
rank: balance.rank,
|
|
3610
|
+
name: balance.coin?.name ?? null,
|
|
3611
|
+
symbol: balance.coin?.symbol ?? null,
|
|
3612
|
+
coinType: balance.coin?.coinType ?? null,
|
|
3613
|
+
address: balance.coin?.address ?? null,
|
|
3614
|
+
balance: normalizeTokenAmount(balance.balance),
|
|
3615
|
+
usdValue,
|
|
3616
|
+
priceUsd,
|
|
3617
|
+
marketCap: balance.coin?.marketCap ? Number(balance.coin.marketCap) : null
|
|
3618
|
+
};
|
|
3619
|
+
};
|
|
3620
|
+
var fetchProfileData = async (identifier) => {
|
|
3621
|
+
const [postsResult, holdingsResult] = await Promise.allSettled([
|
|
3622
|
+
getProfileCoins({ identifier, count: 20 }),
|
|
3623
|
+
getProfileBalances2({ identifier, count: 20, sortOption: "USD_VALUE" })
|
|
3624
|
+
]);
|
|
3625
|
+
if (postsResult.status === "rejected") {
|
|
3626
|
+
throw new Error(
|
|
3627
|
+
postsResult.reason instanceof Error ? postsResult.reason.message : String(postsResult.reason)
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
if (holdingsResult.status === "rejected") {
|
|
3631
|
+
throw new Error(
|
|
3632
|
+
holdingsResult.reason instanceof Error ? holdingsResult.reason.message : String(holdingsResult.reason)
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
if (postsResult.value.error) {
|
|
3636
|
+
throw new Error(
|
|
3637
|
+
`API error (posts): ${extractErrorMessage2(postsResult.value.error)}`
|
|
3638
|
+
);
|
|
3639
|
+
}
|
|
3640
|
+
if (holdingsResult.value.error) {
|
|
3641
|
+
throw new Error(
|
|
3642
|
+
`API error (holdings): ${extractErrorMessage2(holdingsResult.value.error)}`
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
const postEdges = postsResult.value.data?.profile?.createdCoins?.edges ?? [];
|
|
3646
|
+
const posts = postEdges.map((e) => e.node);
|
|
3647
|
+
const postsCount = postsResult.value.data?.profile?.createdCoins?.count ?? posts.length;
|
|
3648
|
+
const holdingEdges = holdingsResult.value.data?.profile?.coinBalances?.edges ?? [];
|
|
3649
|
+
const holdings = holdingEdges.map(
|
|
3650
|
+
(e, i) => ({
|
|
3651
|
+
...e.node,
|
|
3652
|
+
rank: i + 1
|
|
3653
|
+
})
|
|
3654
|
+
);
|
|
3655
|
+
const holdingsCount = holdingsResult.value.data?.profile?.coinBalances?.count ?? holdings.length;
|
|
3656
|
+
return { posts, postsCount, holdings, holdingsCount };
|
|
3657
|
+
};
|
|
3658
|
+
var profileCommand = new Command8("profile").description("View profile activity (posts and holdings)").argument(
|
|
3659
|
+
"[identifier]",
|
|
3660
|
+
"Wallet address or profile handle (defaults to your wallet)"
|
|
3661
|
+
).option("--live", "Interactive live-updating display (default)").option("--static", "Static snapshot").option(
|
|
3662
|
+
"--refresh <seconds>",
|
|
3663
|
+
"Auto-refresh interval in seconds, requires --live (min 5)",
|
|
3664
|
+
"30"
|
|
3665
|
+
).action(async function(identifierArg) {
|
|
3666
|
+
const output = getOutputMode(this, "live");
|
|
3667
|
+
const json = output === "json";
|
|
3668
|
+
resolveApiKey(json);
|
|
3669
|
+
const { live, intervalSeconds } = getLiveConfig(this, output);
|
|
3670
|
+
let identifier = identifierArg;
|
|
3671
|
+
if (!identifier) {
|
|
3672
|
+
const envKey = process.env.ZORA_PRIVATE_KEY;
|
|
3673
|
+
const key = envKey || getPrivateKey();
|
|
3674
|
+
if (!key) {
|
|
3675
|
+
outputErrorAndExit(
|
|
3676
|
+
json,
|
|
3677
|
+
"No identifier provided and no wallet configured.",
|
|
3678
|
+
"Pass an address or handle, or run 'zora setup' first."
|
|
3679
|
+
);
|
|
3680
|
+
}
|
|
3681
|
+
try {
|
|
3682
|
+
identifier = privateKeyToAccount3(normalizeKey(key)).address;
|
|
3683
|
+
} catch {
|
|
3684
|
+
outputErrorAndExit(
|
|
3685
|
+
json,
|
|
3686
|
+
"Invalid wallet key. Run 'zora setup --force' to replace it."
|
|
3687
|
+
);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
if (json) {
|
|
3691
|
+
const data = await fetchProfileData(identifier).catch(
|
|
3692
|
+
(err) => outputErrorAndExit(
|
|
3693
|
+
json,
|
|
3694
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3695
|
+
)
|
|
3696
|
+
);
|
|
3697
|
+
outputData(json, {
|
|
3698
|
+
json: {
|
|
3699
|
+
posts: data.posts.map((p, i) => formatPostJson(p, i + 1)),
|
|
3700
|
+
holdings: data.holdings.map(formatHoldingJson)
|
|
3701
|
+
},
|
|
3702
|
+
render: () => {
|
|
3703
|
+
}
|
|
3704
|
+
});
|
|
3705
|
+
track("cli_profile", {
|
|
3706
|
+
identifier,
|
|
3707
|
+
output_format: "json",
|
|
3708
|
+
posts_count: data.postsCount,
|
|
3709
|
+
holdings_count: data.holdingsCount
|
|
3710
|
+
});
|
|
3711
|
+
} else if (live) {
|
|
3712
|
+
const fetchData = () => fetchProfileData(identifier);
|
|
3713
|
+
await renderLive(
|
|
3714
|
+
/* @__PURE__ */ jsx11(
|
|
3715
|
+
ProfileView,
|
|
3716
|
+
{
|
|
3717
|
+
fetchData,
|
|
3718
|
+
identifier,
|
|
3719
|
+
autoRefresh: live,
|
|
3720
|
+
intervalSeconds
|
|
3721
|
+
}
|
|
3722
|
+
)
|
|
3723
|
+
);
|
|
3724
|
+
track("cli_profile", {
|
|
3725
|
+
identifier,
|
|
3726
|
+
output_format: "live",
|
|
3727
|
+
live,
|
|
3728
|
+
interval: intervalSeconds
|
|
3729
|
+
});
|
|
3730
|
+
} else {
|
|
3731
|
+
const data = await fetchProfileData(identifier).catch(
|
|
3732
|
+
(err) => outputErrorAndExit(
|
|
3733
|
+
json,
|
|
3734
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3735
|
+
)
|
|
3736
|
+
);
|
|
3737
|
+
const rankedPosts = data.posts.map((p, i) => ({ ...p, rank: i + 1 }));
|
|
3738
|
+
renderOnce(
|
|
3739
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
3740
|
+
rankedPosts.length === 0 ? /* @__PURE__ */ jsx11(
|
|
3741
|
+
Box8,
|
|
3742
|
+
{
|
|
3743
|
+
flexDirection: "column",
|
|
3744
|
+
paddingLeft: 1,
|
|
3745
|
+
paddingTop: 1,
|
|
3746
|
+
paddingBottom: 1,
|
|
3747
|
+
children: /* @__PURE__ */ jsx11(Box8, { children: /* @__PURE__ */ jsx11(Text8, { children: "No posts found for this profile." }) })
|
|
3748
|
+
}
|
|
3749
|
+
) : /* @__PURE__ */ jsx11(
|
|
3750
|
+
Table,
|
|
3751
|
+
{
|
|
3752
|
+
columns: postColumns,
|
|
3753
|
+
data: rankedPosts,
|
|
3754
|
+
title: "Posts",
|
|
3755
|
+
subtitle: `${rankedPosts.length} of ${data.postsCount}`
|
|
3756
|
+
}
|
|
3757
|
+
),
|
|
3758
|
+
data.holdings.length === 0 ? /* @__PURE__ */ jsx11(
|
|
3759
|
+
Box8,
|
|
3760
|
+
{
|
|
3761
|
+
flexDirection: "column",
|
|
3762
|
+
paddingLeft: 1,
|
|
3763
|
+
paddingTop: 1,
|
|
3764
|
+
paddingBottom: 1,
|
|
3765
|
+
children: /* @__PURE__ */ jsx11(Box8, { children: /* @__PURE__ */ jsx11(Text8, { children: "No holdings found for this profile." }) })
|
|
3766
|
+
}
|
|
3767
|
+
) : /* @__PURE__ */ jsx11(
|
|
3768
|
+
Table,
|
|
3769
|
+
{
|
|
3770
|
+
columns: balanceColumns,
|
|
3771
|
+
data: data.holdings,
|
|
3772
|
+
title: "Holdings",
|
|
3773
|
+
subtitle: `${data.holdings.length} of ${data.holdingsCount}`
|
|
3774
|
+
}
|
|
3775
|
+
)
|
|
3776
|
+
] })
|
|
3777
|
+
);
|
|
3778
|
+
track("cli_profile", {
|
|
3779
|
+
identifier,
|
|
3780
|
+
output_format: "static",
|
|
3781
|
+
posts_count: data.postsCount,
|
|
3782
|
+
holdings_count: data.holdingsCount
|
|
3783
|
+
});
|
|
3784
|
+
}
|
|
3785
|
+
});
|
|
3786
|
+
|
|
3787
|
+
// src/commands/send.ts
|
|
3788
|
+
import { Command as Command9 } from "commander";
|
|
3184
3789
|
import confirm4 from "@inquirer/confirm";
|
|
3185
3790
|
import {
|
|
3186
3791
|
erc20Abi as erc20Abi4,
|
|
@@ -3188,7 +3793,7 @@ import {
|
|
|
3188
3793
|
isAddress as isAddress3,
|
|
3189
3794
|
parseUnits as parseUnits3
|
|
3190
3795
|
} from "viem";
|
|
3191
|
-
import { setApiKey as
|
|
3796
|
+
import { setApiKey as setApiKey8 } from "@zoralabs/coins-sdk";
|
|
3192
3797
|
var SEND_AMOUNT_CHECKS = {
|
|
3193
3798
|
amount: (opts) => opts.amount !== void 0,
|
|
3194
3799
|
percent: (opts) => opts.percent !== void 0,
|
|
@@ -3236,8 +3841,15 @@ function printSendResult(json, info) {
|
|
|
3236
3841
|
console.log(` Tx ${info.txHash}
|
|
3237
3842
|
`);
|
|
3238
3843
|
}
|
|
3239
|
-
var sendCommand = new
|
|
3844
|
+
var sendCommand = new Command9("send").description("Send coins or ETH to an address").argument("[identifier]", "Coin address, name, or token (eth, usdc, zora)").option("--to <address>", "Recipient address (0x...)").option("--type <type>", "Coin type: creator-coin, post, trend").option("--amount <value>", "Send specific amount").option("--percent <value>", "Send percentage of balance (1-100)").option("--all", "Send entire balance").option("--yes", "Skip confirmation").action(async function(identifier, opts) {
|
|
3240
3845
|
const json = getJson(this);
|
|
3846
|
+
if (!opts.to) {
|
|
3847
|
+
outputErrorAndExit(
|
|
3848
|
+
json,
|
|
3849
|
+
"Missing --to flag.",
|
|
3850
|
+
"Usage: zora send <identifier> --to <address>"
|
|
3851
|
+
);
|
|
3852
|
+
}
|
|
3241
3853
|
if (!isAddress3(opts.to)) {
|
|
3242
3854
|
outputErrorAndExit(
|
|
3243
3855
|
json,
|
|
@@ -3362,7 +3974,7 @@ var sendCommand = new Command8("send").description("Send coins or ETH to an addr
|
|
|
3362
3974
|
} catch (err) {
|
|
3363
3975
|
track("cli_send", {
|
|
3364
3976
|
asset: "eth",
|
|
3365
|
-
output_format: json ? "json" : "
|
|
3977
|
+
output_format: json ? "json" : "static",
|
|
3366
3978
|
success: false,
|
|
3367
3979
|
error_type: err instanceof Error ? err.constructor.name : "unknown"
|
|
3368
3980
|
});
|
|
@@ -3391,7 +4003,7 @@ var sendCommand = new Command8("send").description("Send coins or ETH to an addr
|
|
|
3391
4003
|
amount_mode: amountMode,
|
|
3392
4004
|
amount_usd: amountUsd,
|
|
3393
4005
|
transactionHash: txHash,
|
|
3394
|
-
output_format: json ? "json" : "
|
|
4006
|
+
output_format: json ? "json" : "static",
|
|
3395
4007
|
success: true,
|
|
3396
4008
|
tx_hash: txHash
|
|
3397
4009
|
});
|
|
@@ -3413,7 +4025,7 @@ var sendCommand = new Command8("send").description("Send coins or ETH to an addr
|
|
|
3413
4025
|
} else {
|
|
3414
4026
|
const apiKey = getApiKey();
|
|
3415
4027
|
if (apiKey) {
|
|
3416
|
-
|
|
4028
|
+
setApiKey8(apiKey);
|
|
3417
4029
|
}
|
|
3418
4030
|
const ref = parseCoinRef(identifier, opts.type);
|
|
3419
4031
|
let result;
|
|
@@ -3558,7 +4170,7 @@ var sendCommand = new Command8("send").description("Send coins or ETH to an addr
|
|
|
3558
4170
|
coin_address: tokenAddress,
|
|
3559
4171
|
coin_name: tokenName,
|
|
3560
4172
|
coin_symbol: symbol,
|
|
3561
|
-
output_format: json ? "json" : "
|
|
4173
|
+
output_format: json ? "json" : "static",
|
|
3562
4174
|
success: false,
|
|
3563
4175
|
error_type: err instanceof Error ? err.constructor.name : "unknown"
|
|
3564
4176
|
});
|
|
@@ -3588,7 +4200,7 @@ var sendCommand = new Command8("send").description("Send coins or ETH to an addr
|
|
|
3588
4200
|
amount_mode: amountMode,
|
|
3589
4201
|
amount_usd: amountUsd,
|
|
3590
4202
|
transactionHash: txHash,
|
|
3591
|
-
output_format: json ? "json" : "
|
|
4203
|
+
output_format: json ? "json" : "static",
|
|
3592
4204
|
success: true,
|
|
3593
4205
|
tx_hash: txHash
|
|
3594
4206
|
});
|
|
@@ -3596,8 +4208,8 @@ var sendCommand = new Command8("send").description("Send coins or ETH to an addr
|
|
|
3596
4208
|
});
|
|
3597
4209
|
|
|
3598
4210
|
// src/commands/setup.ts
|
|
3599
|
-
import { Command as
|
|
3600
|
-
import { generatePrivateKey, privateKeyToAccount as
|
|
4211
|
+
import { Command as Command10 } from "commander";
|
|
4212
|
+
import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
3601
4213
|
|
|
3602
4214
|
// src/lib/strings.ts
|
|
3603
4215
|
var DEPOSIT_INSTRUCTIONS = "Deposit ETH or USDC to this address on Base to start trading.\n\n You can do this from:\n - Coinbase \u2014 withdraw directly to Base\n - Another wallet (MetaMask, Rainbow, etc.) \u2014 send on Base network\n - Bridge from other chains \u2014 use https://superbridge.app/base";
|
|
@@ -3610,7 +4222,7 @@ var BACKUP_WARNING = "Back up this file \u2014 it's the only copy of your key.";
|
|
|
3610
4222
|
var isValidPrivateKey = (key) => /^(0x)?[0-9a-fA-F]{64}$/.test(key);
|
|
3611
4223
|
var toAccount = (json, key, errorPrefix) => {
|
|
3612
4224
|
try {
|
|
3613
|
-
return
|
|
4225
|
+
return privateKeyToAccount4(normalizeKey(key));
|
|
3614
4226
|
} catch {
|
|
3615
4227
|
outputErrorAndExit(
|
|
3616
4228
|
json,
|
|
@@ -3618,7 +4230,7 @@ var toAccount = (json, key, errorPrefix) => {
|
|
|
3618
4230
|
);
|
|
3619
4231
|
}
|
|
3620
4232
|
};
|
|
3621
|
-
var setupCommand = new
|
|
4233
|
+
var setupCommand = new Command10("setup").description("Set up your Zora wallet").option("--create", "Create a new wallet without prompting").option("--force", "Overwrite existing wallet without prompting").option("--yes", "Skip interactive prompt and execute directly").action(async function(options) {
|
|
3622
4234
|
const json = getJson(this);
|
|
3623
4235
|
const nonInteractive = getYes(this);
|
|
3624
4236
|
const envKey = process.env.ZORA_PRIVATE_KEY;
|
|
@@ -3633,7 +4245,7 @@ var setupCommand = new Command9("setup").description("Set up your Zora wallet").
|
|
|
3633
4245
|
const account = toAccount(json, envKey, "ZORA_PRIVATE_KEY");
|
|
3634
4246
|
outputData(json, {
|
|
3635
4247
|
json: { source: "env", address: account.address },
|
|
3636
|
-
|
|
4248
|
+
render: () => {
|
|
3637
4249
|
console.log(" Using wallet from ZORA_PRIVATE_KEY.\n");
|
|
3638
4250
|
console.log(` Address: ${account.address}
|
|
3639
4251
|
`);
|
|
@@ -3654,14 +4266,14 @@ var setupCommand = new Command9("setup").description("Set up your Zora wallet").
|
|
|
3654
4266
|
} catch (err) {
|
|
3655
4267
|
outputErrorAndExit(
|
|
3656
4268
|
json,
|
|
3657
|
-
`\u2717 Could not read wallet: ${err
|
|
4269
|
+
`\u2717 Could not read wallet: ${formatError(err)}`,
|
|
3658
4270
|
"Run 'zora setup --force' to overwrite it."
|
|
3659
4271
|
);
|
|
3660
4272
|
}
|
|
3661
4273
|
}
|
|
3662
4274
|
if (existing) {
|
|
3663
4275
|
const account = toAccount(json, existing, "Stored private key");
|
|
3664
|
-
const truncated =
|
|
4276
|
+
const truncated = truncateAddress(account.address);
|
|
3665
4277
|
console.log(` Wallet already configured: ${truncated}
|
|
3666
4278
|
`);
|
|
3667
4279
|
if (!options.force) {
|
|
@@ -3723,7 +4335,7 @@ var setupCommand = new Command9("setup").description("Set up your Zora wallet").
|
|
|
3723
4335
|
address: account.address,
|
|
3724
4336
|
path: getWalletPath()
|
|
3725
4337
|
},
|
|
3726
|
-
|
|
4338
|
+
render: () => {
|
|
3727
4339
|
console.log("\n\u2713 Wallet imported\n");
|
|
3728
4340
|
console.log(` Address: ${account.address}`);
|
|
3729
4341
|
console.log(` Private key: saved to ${getWalletPath()}
|
|
@@ -3758,7 +4370,7 @@ var setupCommand = new Command9("setup").description("Set up your Zora wallet").
|
|
|
3758
4370
|
address: account.address,
|
|
3759
4371
|
path: getWalletPath()
|
|
3760
4372
|
},
|
|
3761
|
-
|
|
4373
|
+
render: () => {
|
|
3762
4374
|
console.log("\n\u2713 Wallet created\n");
|
|
3763
4375
|
console.log(` Address: ${account.address}`);
|
|
3764
4376
|
console.log(` Private key: saved to ${getWalletPath()}
|
|
@@ -3777,8 +4389,8 @@ var setupCommand = new Command9("setup").description("Set up your Zora wallet").
|
|
|
3777
4389
|
});
|
|
3778
4390
|
|
|
3779
4391
|
// src/commands/wallet.ts
|
|
3780
|
-
import { Command as
|
|
3781
|
-
import { privateKeyToAccount as
|
|
4392
|
+
import { Command as Command11 } from "commander";
|
|
4393
|
+
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
3782
4394
|
var resolvePrivateKey = () => {
|
|
3783
4395
|
const envKey = process.env.ZORA_PRIVATE_KEY;
|
|
3784
4396
|
if (envKey) {
|
|
@@ -3790,7 +4402,7 @@ var resolvePrivateKey = () => {
|
|
|
3790
4402
|
}
|
|
3791
4403
|
return void 0;
|
|
3792
4404
|
};
|
|
3793
|
-
var walletCommand = new
|
|
4405
|
+
var walletCommand = new Command11("wallet").description(
|
|
3794
4406
|
"Manage your Zora wallet"
|
|
3795
4407
|
);
|
|
3796
4408
|
walletCommand.command("info").description("Show wallet address and storage location").action(function() {
|
|
@@ -3801,7 +4413,7 @@ walletCommand.command("info").description("Show wallet address and storage locat
|
|
|
3801
4413
|
}
|
|
3802
4414
|
let account;
|
|
3803
4415
|
try {
|
|
3804
|
-
account =
|
|
4416
|
+
account = privateKeyToAccount5(normalizeKey(resolved.key));
|
|
3805
4417
|
} catch {
|
|
3806
4418
|
const msg = resolved.source === "env" ? "ZORA_PRIVATE_KEY is not a valid private key." : "Stored private key is invalid.";
|
|
3807
4419
|
const suggestion = resolved.source === "env" ? void 0 : "Run 'zora setup --force' to replace it.";
|
|
@@ -3810,7 +4422,7 @@ walletCommand.command("info").description("Show wallet address and storage locat
|
|
|
3810
4422
|
const source = resolved.source === "env" ? "env (ZORA_PRIVATE_KEY)" : getWalletPath();
|
|
3811
4423
|
outputData(json, {
|
|
3812
4424
|
json: { address: account.address, source },
|
|
3813
|
-
|
|
4425
|
+
render: () => {
|
|
3814
4426
|
console.log(` Address: ${account.address}`);
|
|
3815
4427
|
console.log(` Source: ${source}`);
|
|
3816
4428
|
}
|
|
@@ -3850,7 +4462,7 @@ walletCommand.command("export").description("Print the raw private key to stdout
|
|
|
3850
4462
|
});
|
|
3851
4463
|
|
|
3852
4464
|
// src/components/Zorb.tsx
|
|
3853
|
-
import { Text as
|
|
4465
|
+
import { Text as Text9, Box as Box9 } from "ink";
|
|
3854
4466
|
|
|
3855
4467
|
// src/lib/zorb-pixels.ts
|
|
3856
4468
|
function supportsTruecolor() {
|
|
@@ -3995,7 +4607,7 @@ function generateZorbPixels(size) {
|
|
|
3995
4607
|
}
|
|
3996
4608
|
|
|
3997
4609
|
// src/components/Zorb.tsx
|
|
3998
|
-
import { jsx as
|
|
4610
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3999
4611
|
var LOWER_HALF_BLOCK = "\u2584";
|
|
4000
4612
|
var UPPER_HALF_BLOCK = "\u2580";
|
|
4001
4613
|
function rgbString([r, g, b]) {
|
|
@@ -4018,19 +4630,19 @@ function Zorb({ size = 20 }) {
|
|
|
4018
4630
|
const topIsBlack = isBlack(top);
|
|
4019
4631
|
const bottomIsBlack = isBlack(bottom);
|
|
4020
4632
|
if (topIsBlack && bottomIsBlack) {
|
|
4021
|
-
cells.push(/* @__PURE__ */
|
|
4633
|
+
cells.push(/* @__PURE__ */ jsx12(Text9, { children: " " }, x));
|
|
4022
4634
|
} else if (topIsBlack) {
|
|
4023
4635
|
cells.push(
|
|
4024
|
-
/* @__PURE__ */
|
|
4636
|
+
/* @__PURE__ */ jsx12(Text9, { color: rgbString(bottom), children: LOWER_HALF_BLOCK }, x)
|
|
4025
4637
|
);
|
|
4026
4638
|
} else if (bottomIsBlack) {
|
|
4027
4639
|
cells.push(
|
|
4028
|
-
/* @__PURE__ */
|
|
4640
|
+
/* @__PURE__ */ jsx12(Text9, { color: rgbString(top), children: UPPER_HALF_BLOCK }, x)
|
|
4029
4641
|
);
|
|
4030
4642
|
} else {
|
|
4031
4643
|
cells.push(
|
|
4032
|
-
/* @__PURE__ */
|
|
4033
|
-
|
|
4644
|
+
/* @__PURE__ */ jsx12(
|
|
4645
|
+
Text9,
|
|
4034
4646
|
{
|
|
4035
4647
|
backgroundColor: rgbString(top),
|
|
4036
4648
|
color: rgbString(bottom),
|
|
@@ -4041,49 +4653,50 @@ function Zorb({ size = 20 }) {
|
|
|
4041
4653
|
);
|
|
4042
4654
|
}
|
|
4043
4655
|
}
|
|
4044
|
-
rows.push(/* @__PURE__ */
|
|
4656
|
+
rows.push(/* @__PURE__ */ jsx12(Text9, { children: cells }, y));
|
|
4045
4657
|
}
|
|
4046
|
-
return /* @__PURE__ */
|
|
4047
|
-
/* @__PURE__ */
|
|
4658
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4659
|
+
/* @__PURE__ */ jsx12(Text9, { children: " " }),
|
|
4048
4660
|
rows,
|
|
4049
|
-
/* @__PURE__ */
|
|
4661
|
+
/* @__PURE__ */ jsx12(Text9, { children: " " })
|
|
4050
4662
|
] });
|
|
4051
4663
|
}
|
|
4052
4664
|
|
|
4053
4665
|
// src/index.tsx
|
|
4054
|
-
import { jsx as
|
|
4666
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
4055
4667
|
if (process.env.ZORA_API_TARGET) {
|
|
4056
4668
|
setApiBaseUrl(process.env.ZORA_API_TARGET);
|
|
4057
4669
|
}
|
|
4058
|
-
var version = true ? "0.
|
|
4670
|
+
var version = true ? "0.3.0" : JSON.parse(
|
|
4059
4671
|
readFileSync2(new URL("../package.json", import.meta.url), "utf-8")
|
|
4060
4672
|
).version;
|
|
4061
4673
|
var buildProgram = () => {
|
|
4062
|
-
const program2 = new
|
|
4063
|
-
"--output <format>",
|
|
4064
|
-
"Output format: table, json, live (default varies by command)"
|
|
4065
|
-
).option(
|
|
4066
|
-
"--interval <seconds>",
|
|
4067
|
-
"Auto-refresh interval in seconds (min 5)",
|
|
4068
|
-
"30"
|
|
4069
|
-
);
|
|
4674
|
+
const program2 = new Command12().name("zora").description("Zora CLI").version(version).option("--json", "Output as JSON (for scripts and automation)", false);
|
|
4070
4675
|
program2.addCommand(authCommand);
|
|
4071
4676
|
program2.addCommand(balanceCommand);
|
|
4072
4677
|
program2.addCommand(buyCommand);
|
|
4073
4678
|
program2.addCommand(exploreCommand);
|
|
4074
4679
|
program2.addCommand(getCommand);
|
|
4075
4680
|
program2.addCommand(priceHistoryCommand);
|
|
4681
|
+
program2.addCommand(profileCommand);
|
|
4076
4682
|
program2.addCommand(setupCommand);
|
|
4077
4683
|
program2.addCommand(walletCommand);
|
|
4078
4684
|
program2.addCommand(sellCommand);
|
|
4079
4685
|
program2.addCommand(sendCommand);
|
|
4686
|
+
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
4687
|
+
const expected = actionCommand.registeredArguments.length;
|
|
4688
|
+
if (expected > 0 && actionCommand.args.length < expected) {
|
|
4689
|
+
actionCommand.outputHelp();
|
|
4690
|
+
process.exit(1);
|
|
4691
|
+
}
|
|
4692
|
+
});
|
|
4080
4693
|
return program2;
|
|
4081
4694
|
};
|
|
4082
4695
|
var program = buildProgram();
|
|
4083
4696
|
if (!process.env.VITEST) {
|
|
4084
4697
|
const showingHelp = process.argv.length <= 2 || process.argv.includes("--help") || process.argv.includes("-h");
|
|
4085
|
-
if (showingHelp && !process.argv.includes("--
|
|
4086
|
-
renderOnce(/* @__PURE__ */
|
|
4698
|
+
if (showingHelp && !process.argv.includes("--json") && supportsTruecolor()) {
|
|
4699
|
+
renderOnce(/* @__PURE__ */ jsx13(Zorb, { size: 20 }));
|
|
4087
4700
|
}
|
|
4088
4701
|
console.warn(
|
|
4089
4702
|
"\x1B[33m\u26A0 Beta:\x1B[0m This CLI is in beta and should be used with caution."
|