@alchemy/cli 0.4.0 → 0.5.1
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/auth-7E33EMAI.js +13 -0
- package/dist/auth-E26YCAJV.js +23 -0
- package/dist/chunk-44OGGLN4.js +681 -0
- package/dist/chunk-56ZVYB4G.js +536 -0
- package/dist/{chunk-Z3LXQFIY.js → chunk-5X6YRTPU.js} +15 -5
- package/dist/chunk-IGD4NIK7.js +300 -0
- package/dist/chunk-LYUW7O6X.js +231 -0
- package/dist/chunk-T2XSNZE3.js +1398 -0
- package/dist/chunk-Z7J64GJJ.js +118 -0
- package/dist/index.js +127 -60
- package/dist/{interactive-NASSNJHQ.js → interactive-G4ON47AR.js} +12 -7
- package/dist/{onboarding-WQ2TWDM3.js → onboarding-CWCVWSUG.js} +57 -15
- package/package.json +1 -1
- package/scripts/postinstall.cjs +17 -1
- package/dist/chunk-PIWNNNMZ.js +0 -1327
- package/dist/chunk-TH75DFAY.js +0 -1246
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
|
+
import {
|
|
4
|
+
configPath
|
|
5
|
+
} from "./chunk-T2XSNZE3.js";
|
|
6
|
+
import {
|
|
7
|
+
esc
|
|
8
|
+
} from "./chunk-56ZVYB4G.js";
|
|
9
|
+
|
|
10
|
+
// src/lib/update-check.ts
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
13
|
+
import { dirname } from "path";
|
|
14
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
15
|
+
var UPDATE_INSTALL_COMMAND = "npm i -g @alchemy/cli@latest";
|
|
16
|
+
function cachePath() {
|
|
17
|
+
return configPath().replace(/config\.json$/, ".update-check");
|
|
18
|
+
}
|
|
19
|
+
function readCache() {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(cachePath(), "utf-8"));
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function writeCache(cache) {
|
|
27
|
+
try {
|
|
28
|
+
const p = cachePath();
|
|
29
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
30
|
+
writeFileSync(p, JSON.stringify(cache), { mode: 384 });
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function fetchLatestVersion() {
|
|
35
|
+
try {
|
|
36
|
+
const result = execFileSync("npm", ["view", "@alchemy/cli", "version"], {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
timeout: 5e3,
|
|
39
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
40
|
+
});
|
|
41
|
+
return result.trim() || null;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function semverLT(a, b) {
|
|
47
|
+
const pa = a.split(".").map(Number);
|
|
48
|
+
const pb = b.split(".").map(Number);
|
|
49
|
+
for (let i = 0; i < 3; i++) {
|
|
50
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0)) return true;
|
|
51
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0)) return false;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function currentVersion() {
|
|
56
|
+
return true ? "0.5.1" : "0.0.0";
|
|
57
|
+
}
|
|
58
|
+
function toUpdateStatus(latestVersion, checkedAt) {
|
|
59
|
+
const current = currentVersion();
|
|
60
|
+
return {
|
|
61
|
+
currentVersion: current,
|
|
62
|
+
latestVersion,
|
|
63
|
+
updateAvailable: latestVersion ? semverLT(current, latestVersion) : false,
|
|
64
|
+
installCommand: UPDATE_INSTALL_COMMAND,
|
|
65
|
+
checkedAt
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function getUpdateStatus() {
|
|
69
|
+
const cache = readCache();
|
|
70
|
+
if (cache && Date.now() - cache.checkedAt < CACHE_TTL_MS) {
|
|
71
|
+
return toUpdateStatus(cache.latest, cache.checkedAt);
|
|
72
|
+
}
|
|
73
|
+
const latest = fetchLatestVersion();
|
|
74
|
+
if (latest) {
|
|
75
|
+
const checkedAt = Date.now();
|
|
76
|
+
writeCache({ latest, checkedAt });
|
|
77
|
+
return toUpdateStatus(latest, checkedAt);
|
|
78
|
+
}
|
|
79
|
+
if (cache) {
|
|
80
|
+
return toUpdateStatus(cache.latest, cache.checkedAt);
|
|
81
|
+
}
|
|
82
|
+
return toUpdateStatus(null, null);
|
|
83
|
+
}
|
|
84
|
+
function getAvailableUpdate() {
|
|
85
|
+
const current = currentVersion();
|
|
86
|
+
const cache = readCache();
|
|
87
|
+
if (cache && Date.now() - cache.checkedAt < CACHE_TTL_MS) {
|
|
88
|
+
return semverLT(current, cache.latest) ? cache.latest : null;
|
|
89
|
+
}
|
|
90
|
+
const latest = fetchLatestVersion();
|
|
91
|
+
if (latest) {
|
|
92
|
+
writeCache({ latest, checkedAt: Date.now() });
|
|
93
|
+
return semverLT(current, latest) ? latest : null;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function getUpdateNoticeLines(latest) {
|
|
98
|
+
const yellow = esc("33");
|
|
99
|
+
const bold = esc("1");
|
|
100
|
+
const dim = esc("2");
|
|
101
|
+
return [
|
|
102
|
+
` ${yellow("Update available")} ${dim(currentVersion())} \u2192 ${bold(latest)}`,
|
|
103
|
+
` Run ${bold(UPDATE_INSTALL_COMMAND)} to update`
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
function printUpdateNotice(latest) {
|
|
107
|
+
process.stderr.write(`
|
|
108
|
+
${getUpdateNoticeLines(latest).join("\n")}
|
|
109
|
+
|
|
110
|
+
`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
getUpdateStatus,
|
|
115
|
+
getAvailableUpdate,
|
|
116
|
+
getUpdateNoticeLines,
|
|
117
|
+
printUpdateNotice
|
|
118
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
registerAuth
|
|
5
|
+
} from "./chunk-LYUW7O6X.js";
|
|
6
|
+
import "./chunk-IGD4NIK7.js";
|
|
7
|
+
import {
|
|
7
8
|
readStdinArg,
|
|
8
9
|
readStdinLines,
|
|
9
10
|
registerConfig,
|
|
10
11
|
registerWallet,
|
|
11
|
-
resolveAPIKey,
|
|
12
12
|
resolveAddress,
|
|
13
|
-
resolveAppId,
|
|
14
|
-
resolveNetwork,
|
|
15
13
|
splitCommaList,
|
|
16
14
|
validateAddress,
|
|
17
15
|
validateTxHash
|
|
18
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-44OGGLN4.js";
|
|
19
17
|
import {
|
|
20
18
|
getRPCNetworks,
|
|
21
19
|
getSetupStatus,
|
|
22
20
|
isSetupComplete,
|
|
23
21
|
nativeTokenSymbol,
|
|
24
22
|
shouldRunOnboarding
|
|
25
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-5X6YRTPU.js";
|
|
26
24
|
import {
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
getAvailableUpdate,
|
|
26
|
+
getUpdateStatus,
|
|
27
|
+
printUpdateNotice
|
|
28
|
+
} from "./chunk-Z7J64GJJ.js";
|
|
29
|
+
import {
|
|
30
|
+
adminClientFromFlags,
|
|
29
31
|
bold,
|
|
30
32
|
brand,
|
|
31
33
|
brandedHelp,
|
|
32
|
-
|
|
34
|
+
clientFromFlags,
|
|
33
35
|
dim,
|
|
34
36
|
emptyState,
|
|
37
|
+
etherscanTxURL,
|
|
38
|
+
failBadge,
|
|
39
|
+
green,
|
|
40
|
+
isInteractiveAllowed,
|
|
41
|
+
load,
|
|
42
|
+
maskIf,
|
|
43
|
+
printKeyValueBox,
|
|
44
|
+
printSyntaxJSON,
|
|
45
|
+
printTable,
|
|
46
|
+
promptConfirm,
|
|
47
|
+
promptSelect,
|
|
48
|
+
red,
|
|
49
|
+
resolveAPIKey,
|
|
50
|
+
resolveAppId,
|
|
51
|
+
resolveNetwork,
|
|
52
|
+
resolveX402Client,
|
|
53
|
+
save,
|
|
54
|
+
successBadge,
|
|
55
|
+
timeAgo,
|
|
56
|
+
weiToEth,
|
|
57
|
+
withSpinner
|
|
58
|
+
} from "./chunk-T2XSNZE3.js";
|
|
59
|
+
import {
|
|
60
|
+
EXIT_CODES,
|
|
61
|
+
ErrorCode,
|
|
62
|
+
debug,
|
|
35
63
|
errAppRequired,
|
|
36
64
|
errAuthRequired,
|
|
37
65
|
errInvalidAPIKey,
|
|
@@ -41,37 +69,20 @@ import {
|
|
|
41
69
|
errRateLimited,
|
|
42
70
|
errSetupRequired,
|
|
43
71
|
esc,
|
|
44
|
-
etherscanTxURL,
|
|
45
72
|
exitWithError,
|
|
46
|
-
|
|
73
|
+
fetchWithTimeout,
|
|
47
74
|
formatCommanderError,
|
|
48
|
-
|
|
49
|
-
getUpdateStatus,
|
|
50
|
-
green,
|
|
75
|
+
getBaseDomain,
|
|
51
76
|
identity,
|
|
52
|
-
isInteractiveAllowed,
|
|
53
77
|
isJSONMode,
|
|
54
|
-
load,
|
|
55
|
-
maskIf,
|
|
56
78
|
noColor,
|
|
57
79
|
printHuman,
|
|
58
80
|
printJSON,
|
|
59
|
-
printKeyValueBox,
|
|
60
|
-
printSyntaxJSON,
|
|
61
|
-
printTable,
|
|
62
|
-
printUpdateNotice,
|
|
63
|
-
promptConfirm,
|
|
64
|
-
promptSelect,
|
|
65
81
|
quiet,
|
|
66
|
-
red,
|
|
67
82
|
setFlags,
|
|
68
83
|
setNoColor,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
verbose,
|
|
72
|
-
weiToEth,
|
|
73
|
-
withSpinner
|
|
74
|
-
} from "./chunk-TH75DFAY.js";
|
|
84
|
+
verbose
|
|
85
|
+
} from "./chunk-56ZVYB4G.js";
|
|
75
86
|
|
|
76
87
|
// src/index.ts
|
|
77
88
|
import { Command, Help } from "commander";
|
|
@@ -829,11 +840,11 @@ Examples:
|
|
|
829
840
|
const result = await withSpinner(
|
|
830
841
|
"Fetching token allowance\u2026",
|
|
831
842
|
"Token allowance fetched",
|
|
832
|
-
() => client.call("alchemy_getTokenAllowance", [
|
|
833
|
-
opts.owner,
|
|
834
|
-
opts.spender,
|
|
835
|
-
opts.contract
|
|
836
|
-
])
|
|
843
|
+
() => client.call("alchemy_getTokenAllowance", [{
|
|
844
|
+
owner: opts.owner,
|
|
845
|
+
spender: opts.spender,
|
|
846
|
+
contract: opts.contract
|
|
847
|
+
}])
|
|
837
848
|
);
|
|
838
849
|
if (isJSONMode()) printJSON(result);
|
|
839
850
|
else printSyntaxJSON(result);
|
|
@@ -1310,6 +1321,64 @@ function registerApps(program2) {
|
|
|
1310
1321
|
exitWithError(err);
|
|
1311
1322
|
}
|
|
1312
1323
|
});
|
|
1324
|
+
cmd.command("select [id]").description("Select an app to use as the default").action(async (id) => {
|
|
1325
|
+
try {
|
|
1326
|
+
const admin = adminClientFromFlags(program2);
|
|
1327
|
+
let selected;
|
|
1328
|
+
if (id) {
|
|
1329
|
+
selected = await withSpinner(
|
|
1330
|
+
"Fetching app\u2026",
|
|
1331
|
+
"App fetched",
|
|
1332
|
+
() => admin.getApp(id)
|
|
1333
|
+
);
|
|
1334
|
+
} else {
|
|
1335
|
+
const result = await withSpinner(
|
|
1336
|
+
"Fetching apps\u2026",
|
|
1337
|
+
"Apps fetched",
|
|
1338
|
+
() => admin.listAllApps()
|
|
1339
|
+
);
|
|
1340
|
+
if (result.apps.length === 0) {
|
|
1341
|
+
emptyState("No apps found.");
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
if (isJSONMode()) {
|
|
1345
|
+
printJSON({ apps: result.apps.map(maskAppSecrets) });
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
const appId = await promptSelect({
|
|
1349
|
+
message: "Select an app",
|
|
1350
|
+
options: result.apps.map((app) => ({
|
|
1351
|
+
value: app.id,
|
|
1352
|
+
label: app.name,
|
|
1353
|
+
hint: `${app.chainNetworks.length} networks`
|
|
1354
|
+
})),
|
|
1355
|
+
cancelMessage: "Cancelled app selection."
|
|
1356
|
+
});
|
|
1357
|
+
if (!appId) return;
|
|
1358
|
+
const found = result.apps.find((a) => a.id === appId);
|
|
1359
|
+
if (!found) return;
|
|
1360
|
+
selected = found;
|
|
1361
|
+
}
|
|
1362
|
+
const cfg = load();
|
|
1363
|
+
save({
|
|
1364
|
+
...cfg,
|
|
1365
|
+
app: {
|
|
1366
|
+
id: selected.id,
|
|
1367
|
+
name: selected.name,
|
|
1368
|
+
apiKey: selected.apiKey,
|
|
1369
|
+
webhookApiKey: selected.webhookApiKey
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
if (isJSONMode()) {
|
|
1373
|
+
printJSON(maskAppSecrets(selected));
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
console.log(` ${green("\u2713")} Selected app: ${selected.name}`);
|
|
1377
|
+
console.log(` ${dim("Saved to config")}`);
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
exitWithError(err);
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1313
1382
|
cmd.command("chains").description("List Admin API chain identifiers for app configuration (e.g. ETH_MAINNET)").action(async () => {
|
|
1314
1383
|
try {
|
|
1315
1384
|
const admin = adminClientFromFlags(program2);
|
|
@@ -1599,13 +1668,13 @@ async function requestJSON(url, options) {
|
|
|
1599
1668
|
}
|
|
1600
1669
|
async function callApiData(apiKey, path, options = {}) {
|
|
1601
1670
|
if (!apiKey) throw errAuthRequired();
|
|
1602
|
-
const base = new URL(`https://api.g
|
|
1671
|
+
const base = new URL(`https://api.g.${getBaseDomain()}/data/v1/${apiKey}/`);
|
|
1603
1672
|
const url = withQuery(new URL(path.replace(/^\//, ""), base), options.query);
|
|
1604
1673
|
return requestJSON(url, { ...options, path });
|
|
1605
1674
|
}
|
|
1606
1675
|
async function callApiPrices(apiKey, path, options = {}) {
|
|
1607
1676
|
if (!apiKey) throw errAuthRequired();
|
|
1608
|
-
const base = new URL(`https://api.g
|
|
1677
|
+
const base = new URL(`https://api.g.${getBaseDomain()}/prices/v1/${apiKey}/`);
|
|
1609
1678
|
const url = withQuery(new URL(path.replace(/^\//, ""), base), options.query);
|
|
1610
1679
|
return requestJSON(url, { ...options, path });
|
|
1611
1680
|
}
|
|
@@ -1615,7 +1684,7 @@ async function callNotify(token, path, options = {}) {
|
|
|
1615
1684
|
"Webhook API key required. Set ALCHEMY_WEBHOOK_API_KEY (or ALCHEMY_NOTIFY_AUTH_TOKEN) or pass --webhook-api-key."
|
|
1616
1685
|
);
|
|
1617
1686
|
}
|
|
1618
|
-
const base = new URL(
|
|
1687
|
+
const base = new URL(`https://dashboard.${getBaseDomain()}/api/`);
|
|
1619
1688
|
const url = withQuery(new URL(path.replace(/^\//, ""), base), options.query);
|
|
1620
1689
|
return requestJSON(url, {
|
|
1621
1690
|
...options,
|
|
@@ -1632,14 +1701,14 @@ function registerPrices(program2) {
|
|
|
1632
1701
|
const cmd = program2.command("prices").description("Prices API wrappers");
|
|
1633
1702
|
cmd.command("symbol <symbols>").description("Get spot prices by symbol (comma-separated)").action(async (symbols) => {
|
|
1634
1703
|
try {
|
|
1635
|
-
const apiKey = resolveAPIKey(program2);
|
|
1636
1704
|
const values = splitCommaList(symbols);
|
|
1637
1705
|
const query = new URLSearchParams();
|
|
1638
1706
|
for (const symbol of values) query.append("symbols", symbol);
|
|
1707
|
+
const x402 = resolveX402Client(program2);
|
|
1639
1708
|
const result = await withSpinner(
|
|
1640
1709
|
"Fetching prices\u2026",
|
|
1641
1710
|
"Prices fetched",
|
|
1642
|
-
() => callApiPrices(
|
|
1711
|
+
() => x402 ? x402.callRest(`prices/v1/tokens/by-symbol?${query.toString()}`) : callApiPrices(resolveAPIKey(program2), `/tokens/by-symbol?${query.toString()}`)
|
|
1643
1712
|
);
|
|
1644
1713
|
if (isJSONMode()) printJSON(result);
|
|
1645
1714
|
else printSyntaxJSON(result);
|
|
@@ -1649,12 +1718,12 @@ function registerPrices(program2) {
|
|
|
1649
1718
|
});
|
|
1650
1719
|
cmd.command("address").description("Get spot prices by token addresses").requiredOption("--addresses <json>", "JSON array of {network,address}").action(async (opts) => {
|
|
1651
1720
|
try {
|
|
1652
|
-
const apiKey = resolveAPIKey(program2);
|
|
1653
1721
|
const body = { addresses: JSON.parse(opts.addresses) };
|
|
1722
|
+
const x402 = resolveX402Client(program2);
|
|
1654
1723
|
const result = await withSpinner(
|
|
1655
1724
|
"Fetching prices\u2026",
|
|
1656
1725
|
"Prices fetched",
|
|
1657
|
-
() => callApiPrices(
|
|
1726
|
+
() => x402 ? x402.callRest("prices/v1/tokens/by-address", { method: "POST", body }) : callApiPrices(resolveAPIKey(program2), "/tokens/by-address", { method: "POST", body })
|
|
1658
1727
|
);
|
|
1659
1728
|
if (isJSONMode()) printJSON(result);
|
|
1660
1729
|
else printSyntaxJSON(result);
|
|
@@ -1670,12 +1739,12 @@ Examples:
|
|
|
1670
1739
|
alchemy prices historical --body '{"address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","network":"eth-mainnet","startTime":"2024-06-01","endTime":"2024-06-07","interval":"1d"}'`
|
|
1671
1740
|
).action(async (opts) => {
|
|
1672
1741
|
try {
|
|
1673
|
-
const apiKey = resolveAPIKey(program2);
|
|
1674
1742
|
const payload = JSON.parse(opts.body);
|
|
1743
|
+
const x402 = resolveX402Client(program2);
|
|
1675
1744
|
const result = await withSpinner(
|
|
1676
1745
|
"Fetching historical prices\u2026",
|
|
1677
1746
|
"Historical prices fetched",
|
|
1678
|
-
() => callApiPrices(
|
|
1747
|
+
() => x402 ? x402.callRest("prices/v1/tokens/historical", { method: "POST", body: payload }) : callApiPrices(resolveAPIKey(program2), "/tokens/historical", { method: "POST", body: payload })
|
|
1679
1748
|
);
|
|
1680
1749
|
if (isJSONMode()) printJSON(result);
|
|
1681
1750
|
else printSyntaxJSON(result);
|
|
@@ -1686,20 +1755,20 @@ Examples:
|
|
|
1686
1755
|
}
|
|
1687
1756
|
|
|
1688
1757
|
// src/commands/portfolio.ts
|
|
1689
|
-
async function runDataCall(
|
|
1758
|
+
async function runDataCall(program2, title, path, body) {
|
|
1759
|
+
const x402 = resolveX402Client(program2);
|
|
1690
1760
|
return withSpinner(
|
|
1691
1761
|
`Fetching ${title}\u2026`,
|
|
1692
1762
|
`${title} fetched`,
|
|
1693
|
-
() => callApiData(
|
|
1763
|
+
() => x402 ? x402.callRest(`data/v1${path}`, { method: "POST", body }) : callApiData(resolveAPIKey(program2), path, { method: "POST", body })
|
|
1694
1764
|
);
|
|
1695
1765
|
}
|
|
1696
1766
|
function registerPortfolio(program2) {
|
|
1697
1767
|
const cmd = program2.command("portfolio").description("Portfolio API wrappers");
|
|
1698
1768
|
cmd.command("tokens").description("Get token portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/by-address").action(async (opts) => {
|
|
1699
1769
|
try {
|
|
1700
|
-
const apiKey = resolveAPIKey(program2);
|
|
1701
1770
|
const result = await runDataCall(
|
|
1702
|
-
|
|
1771
|
+
program2,
|
|
1703
1772
|
"token portfolio",
|
|
1704
1773
|
"/assets/tokens/by-address",
|
|
1705
1774
|
JSON.parse(opts.body)
|
|
@@ -1712,9 +1781,8 @@ function registerPortfolio(program2) {
|
|
|
1712
1781
|
});
|
|
1713
1782
|
cmd.command("token-balances").description("Get token balances by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/balances/by-address").action(async (opts) => {
|
|
1714
1783
|
try {
|
|
1715
|
-
const apiKey = resolveAPIKey(program2);
|
|
1716
1784
|
const result = await runDataCall(
|
|
1717
|
-
|
|
1785
|
+
program2,
|
|
1718
1786
|
"token balances",
|
|
1719
1787
|
"/assets/tokens/balances/by-address",
|
|
1720
1788
|
JSON.parse(opts.body)
|
|
@@ -1727,9 +1795,8 @@ function registerPortfolio(program2) {
|
|
|
1727
1795
|
});
|
|
1728
1796
|
cmd.command("nfts").description("Get NFT portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/by-address").action(async (opts) => {
|
|
1729
1797
|
try {
|
|
1730
|
-
const apiKey = resolveAPIKey(program2);
|
|
1731
1798
|
const result = await runDataCall(
|
|
1732
|
-
|
|
1799
|
+
program2,
|
|
1733
1800
|
"NFT portfolio",
|
|
1734
1801
|
"/assets/nfts/by-address",
|
|
1735
1802
|
JSON.parse(opts.body)
|
|
@@ -1742,9 +1809,8 @@ function registerPortfolio(program2) {
|
|
|
1742
1809
|
});
|
|
1743
1810
|
cmd.command("nft-contracts").description("Get NFT contracts by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/contracts/by-address").action(async (opts) => {
|
|
1744
1811
|
try {
|
|
1745
|
-
const apiKey = resolveAPIKey(program2);
|
|
1746
1812
|
const result = await runDataCall(
|
|
1747
|
-
|
|
1813
|
+
program2,
|
|
1748
1814
|
"NFT contracts",
|
|
1749
1815
|
"/assets/nfts/contracts/by-address",
|
|
1750
1816
|
JSON.parse(opts.body)
|
|
@@ -2623,7 +2689,7 @@ var ROOT_COMMAND_PILLARS = [
|
|
|
2623
2689
|
},
|
|
2624
2690
|
{
|
|
2625
2691
|
label: "Admin",
|
|
2626
|
-
commands: ["apps", "config", "setup", "completions", "agent-prompt", "update-check", "version", "help"]
|
|
2692
|
+
commands: ["apps", "auth", "config", "setup", "completions", "agent-prompt", "update-check", "version", "help"]
|
|
2627
2693
|
}
|
|
2628
2694
|
];
|
|
2629
2695
|
function formatCommandSignature(sub) {
|
|
@@ -2672,7 +2738,7 @@ function resetUpdateNoticeState() {
|
|
|
2672
2738
|
}
|
|
2673
2739
|
program.name("alchemy").description(
|
|
2674
2740
|
"The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
|
|
2675
|
-
).version("0.
|
|
2741
|
+
).version("0.5.1", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option("--access-key <key>", "Alchemy access key (env: ALCHEMY_ACCESS_KEY)").option(
|
|
2676
2742
|
"-n, --network <network>",
|
|
2677
2743
|
"Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
|
|
2678
2744
|
).option("--x402", "Use x402 wallet-based gateway auth").option("--wallet-key-file <path>", "Path to wallet private key file for x402").option("--json", "Force JSON output (auto-enabled when piped)").option("-q, --quiet", "Suppress non-essential output").option("--verbose", "Enable verbose output").option("--no-color", "Disable color output").option("--reveal", "Show secrets in plain text").option("--timeout <ms>", "Request timeout in milliseconds (default: none)", parseInt).option("--debug", "Enable debug diagnostics").option("--no-interactive", "Disable REPL and prompt-driven interactions").addHelpCommand(false).allowExcessArguments(true).exitOverride((err) => {
|
|
@@ -2852,7 +2918,7 @@ ${styledLine}`;
|
|
|
2852
2918
|
if (isInteractiveAllowed(program)) {
|
|
2853
2919
|
let latestForInteractiveStartup = null;
|
|
2854
2920
|
if (shouldRunOnboarding(program, cfg)) {
|
|
2855
|
-
const { runOnboarding } = await import("./onboarding-
|
|
2921
|
+
const { runOnboarding } = await import("./onboarding-CWCVWSUG.js");
|
|
2856
2922
|
const latest = getAvailableUpdateOnce();
|
|
2857
2923
|
const completed = await runOnboarding(program, latest);
|
|
2858
2924
|
updateShownDuringInteractiveStartup = Boolean(latest);
|
|
@@ -2864,7 +2930,7 @@ ${styledLine}`;
|
|
|
2864
2930
|
latestForInteractiveStartup = getAvailableUpdateOnce();
|
|
2865
2931
|
updateShownDuringInteractiveStartup = Boolean(latestForInteractiveStartup);
|
|
2866
2932
|
}
|
|
2867
|
-
const { startREPL } = await import("./interactive-
|
|
2933
|
+
const { startREPL } = await import("./interactive-G4ON47AR.js");
|
|
2868
2934
|
program.exitOverride();
|
|
2869
2935
|
program.configureOutput({
|
|
2870
2936
|
writeErr: () => {
|
|
@@ -2896,6 +2962,7 @@ registerGasManager(program);
|
|
|
2896
2962
|
registerWebhooks(program);
|
|
2897
2963
|
registerNetwork(program);
|
|
2898
2964
|
registerApps(program);
|
|
2965
|
+
registerAuth(program);
|
|
2899
2966
|
registerSetup(program);
|
|
2900
2967
|
registerConfig(program);
|
|
2901
2968
|
registerSolana(program);
|
|
@@ -3,23 +3,27 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
|
3
3
|
import {
|
|
4
4
|
getRPCNetworkIds,
|
|
5
5
|
getSetupMethod
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5X6YRTPU.js";
|
|
7
|
+
import {
|
|
8
|
+
getUpdateNoticeLines
|
|
9
|
+
} from "./chunk-Z7J64GJJ.js";
|
|
7
10
|
import {
|
|
8
|
-
bgRgb,
|
|
9
11
|
bold,
|
|
10
12
|
brand,
|
|
11
13
|
brandedHelp,
|
|
12
14
|
configDir,
|
|
13
15
|
dim,
|
|
14
|
-
getUpdateNoticeLines,
|
|
15
16
|
green,
|
|
16
|
-
isJSONMode,
|
|
17
17
|
load,
|
|
18
|
+
setBrandedHelpSuppressed
|
|
19
|
+
} from "./chunk-T2XSNZE3.js";
|
|
20
|
+
import {
|
|
21
|
+
bgRgb,
|
|
22
|
+
isJSONMode,
|
|
18
23
|
noColor,
|
|
19
24
|
rgb,
|
|
20
|
-
setBrandedHelpSuppressed,
|
|
21
25
|
setReplMode
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-56ZVYB4G.js";
|
|
23
27
|
|
|
24
28
|
// src/commands/interactive.ts
|
|
25
29
|
import * as readline from "readline";
|
|
@@ -118,7 +122,8 @@ function formatSetupMethodLabel() {
|
|
|
118
122
|
const method = getSetupMethod(load());
|
|
119
123
|
if (method === "api_key") return "API key";
|
|
120
124
|
if (method === "access_key_app") return "Access key + app";
|
|
121
|
-
if (method === "x402_wallet") return "
|
|
125
|
+
if (method === "x402_wallet") return "SIWx wallet";
|
|
126
|
+
if (method === "auth_token") return "Auth token";
|
|
122
127
|
return "Not configured";
|
|
123
128
|
}
|
|
124
129
|
function replHistoryPath() {
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
3
|
import {
|
|
4
|
-
AdminClient,
|
|
5
4
|
generateAndPersistWallet,
|
|
6
5
|
importAndPersistWallet,
|
|
7
6
|
selectOrCreateApp
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-44OGGLN4.js";
|
|
8
|
+
import {
|
|
9
|
+
getUpdateNoticeLines
|
|
10
|
+
} from "./chunk-Z7J64GJJ.js";
|
|
9
11
|
import {
|
|
12
|
+
AdminClient,
|
|
10
13
|
bold,
|
|
11
14
|
brand,
|
|
12
15
|
brandedHelp,
|
|
13
16
|
dim,
|
|
14
|
-
getUpdateNoticeLines,
|
|
15
17
|
green,
|
|
16
18
|
load,
|
|
17
19
|
maskIf,
|
|
@@ -19,17 +21,19 @@ import {
|
|
|
19
21
|
promptSelect,
|
|
20
22
|
promptText,
|
|
21
23
|
save
|
|
22
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-T2XSNZE3.js";
|
|
25
|
+
import "./chunk-56ZVYB4G.js";
|
|
23
26
|
|
|
24
27
|
// src/commands/onboarding.ts
|
|
25
28
|
function printNextSteps(method) {
|
|
26
29
|
const commandsByMethod = {
|
|
30
|
+
"browser-login": ["alchemy auth"],
|
|
27
31
|
"api-key": ["alchemy config set api-key <key>"],
|
|
28
32
|
"access-key": [
|
|
29
33
|
"alchemy config set access-key <key>",
|
|
30
34
|
"alchemy config set app <app-id>"
|
|
31
35
|
],
|
|
32
|
-
|
|
36
|
+
siwx: [
|
|
33
37
|
"alchemy wallet generate",
|
|
34
38
|
"alchemy config set wallet-key-file <path>",
|
|
35
39
|
"alchemy config set x402 true"
|
|
@@ -83,15 +87,15 @@ async function runAccessKeyOnboarding() {
|
|
|
83
87
|
console.log(` ${green("\u2713")} Saved access key`);
|
|
84
88
|
await selectOrCreateApp(new AdminClient(key.trim()));
|
|
85
89
|
}
|
|
86
|
-
async function
|
|
90
|
+
async function runSiwxOnboarding() {
|
|
87
91
|
const action = await promptSelect({
|
|
88
|
-
message: "
|
|
92
|
+
message: "SIWx wallet setup",
|
|
89
93
|
options: [
|
|
90
94
|
{ label: "Generate a new wallet", value: "generate" },
|
|
91
95
|
{ label: "Import wallet from key file", value: "import" }
|
|
92
96
|
],
|
|
93
97
|
initialValue: "generate",
|
|
94
|
-
cancelMessage: "Skipped
|
|
98
|
+
cancelMessage: "Skipped SIWx setup."
|
|
95
99
|
});
|
|
96
100
|
if (!action) return;
|
|
97
101
|
const wallet = action === "generate" ? generateAndPersistWallet() : await (async () => {
|
|
@@ -105,7 +109,18 @@ async function runX402Onboarding() {
|
|
|
105
109
|
if (!wallet) return;
|
|
106
110
|
const cfg = load();
|
|
107
111
|
save({ ...cfg, x402: true });
|
|
108
|
-
console.log(` ${green("\u2713")}
|
|
112
|
+
console.log(` ${green("\u2713")} SIWx enabled with wallet ${wallet.address}`);
|
|
113
|
+
try {
|
|
114
|
+
const { signSiwe } = await import("@alchemy/x402");
|
|
115
|
+
const { readFileSync } = await import("fs");
|
|
116
|
+
const keyPath = wallet.keyFile;
|
|
117
|
+
const privateKey = readFileSync(keyPath, "utf-8").trim();
|
|
118
|
+
const siweToken = await signSiwe({ privateKey, expiresAfter: "1h" });
|
|
119
|
+
const expiresAt = new Date(Date.now() + 60 * 60 * 1e3).toISOString();
|
|
120
|
+
save({ ...load(), siwe_token: siweToken, siwe_token_expires_at: expiresAt });
|
|
121
|
+
console.log(` ${green("\u2713")} Signed SIWE token (cached for 1h)`);
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
109
124
|
}
|
|
110
125
|
async function runOnboarding(_program, latestUpdate = null) {
|
|
111
126
|
process.stdout.write(brandedHelp({ force: true }));
|
|
@@ -125,6 +140,11 @@ async function runOnboarding(_program, latestUpdate = null) {
|
|
|
125
140
|
const method = await promptSelect({
|
|
126
141
|
message: "Choose an auth setup path",
|
|
127
142
|
options: [
|
|
143
|
+
{
|
|
144
|
+
label: "Browser login",
|
|
145
|
+
hint: "Log in via browser (recommended)",
|
|
146
|
+
value: "browser-login"
|
|
147
|
+
},
|
|
128
148
|
{
|
|
129
149
|
label: "API key",
|
|
130
150
|
hint: "Query Alchemy RPC nodes",
|
|
@@ -136,16 +156,16 @@ async function runOnboarding(_program, latestUpdate = null) {
|
|
|
136
156
|
value: "access-key"
|
|
137
157
|
},
|
|
138
158
|
{
|
|
139
|
-
label: "
|
|
140
|
-
hint: "
|
|
141
|
-
value: "
|
|
159
|
+
label: "SIWx",
|
|
160
|
+
hint: "Sign-In with Ethereum/Solana wallet",
|
|
161
|
+
value: "siwx"
|
|
142
162
|
},
|
|
143
163
|
{
|
|
144
164
|
label: "exit",
|
|
145
165
|
value: "exit"
|
|
146
166
|
}
|
|
147
167
|
],
|
|
148
|
-
initialValue: "
|
|
168
|
+
initialValue: "browser-login",
|
|
149
169
|
cancelMessage: "Skipped onboarding."
|
|
150
170
|
});
|
|
151
171
|
if (!method) return false;
|
|
@@ -153,6 +173,28 @@ async function runOnboarding(_program, latestUpdate = null) {
|
|
|
153
173
|
console.log(` ${dim("Exited onboarding.")}`);
|
|
154
174
|
return false;
|
|
155
175
|
}
|
|
176
|
+
if (method === "browser-login") {
|
|
177
|
+
const { performBrowserLogin, AUTH_PORT, getLoginUrl } = await import("./auth-E26YCAJV.js");
|
|
178
|
+
console.log(` Opening browser to log in...`);
|
|
179
|
+
console.log(` ${dim(getLoginUrl(AUTH_PORT))}`);
|
|
180
|
+
console.log(` ${dim("Waiting for authentication...")}`);
|
|
181
|
+
try {
|
|
182
|
+
const result = await performBrowserLogin();
|
|
183
|
+
const cfg2 = load();
|
|
184
|
+
save({
|
|
185
|
+
...cfg2,
|
|
186
|
+
auth_token: result.token,
|
|
187
|
+
auth_token_expires_at: result.expiresAt
|
|
188
|
+
});
|
|
189
|
+
console.log(` ${green("\u2713")} Logged in successfully`);
|
|
190
|
+
const { selectAppAfterAuth } = await import("./auth-7E33EMAI.js");
|
|
191
|
+
await selectAppAfterAuth(result.token);
|
|
192
|
+
return true;
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.log(` ${dim(`Login failed: ${err instanceof Error ? err.message : String(err)}`)}`);
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
156
198
|
if (method === "api-key") {
|
|
157
199
|
await runAPIKeyOnboarding();
|
|
158
200
|
const complete2 = Boolean(load().api_key?.trim());
|
|
@@ -172,11 +214,11 @@ async function runOnboarding(_program, latestUpdate = null) {
|
|
|
172
214
|
}
|
|
173
215
|
return complete2;
|
|
174
216
|
}
|
|
175
|
-
await
|
|
217
|
+
await runSiwxOnboarding();
|
|
176
218
|
const cfg = load();
|
|
177
219
|
const complete = cfg.x402 === true && Boolean(cfg.wallet_key_file?.trim());
|
|
178
220
|
if (!complete) {
|
|
179
|
-
printNextSteps("
|
|
221
|
+
printNextSteps("siwx");
|
|
180
222
|
}
|
|
181
223
|
return complete;
|
|
182
224
|
}
|