@alchemy/cli 0.14.0 → 0.15.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/dist/index.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
getAvailableUpdate,
|
|
69
69
|
getUpdateStatus,
|
|
70
70
|
printUpdateNotice
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-UL5ZSWTE.js";
|
|
72
72
|
import {
|
|
73
73
|
bold,
|
|
74
74
|
brand,
|
|
@@ -3371,6 +3371,60 @@ function registerWebhooks(program2) {
|
|
|
3371
3371
|
});
|
|
3372
3372
|
}
|
|
3373
3373
|
|
|
3374
|
+
// src/lib/solana-rpc.ts
|
|
3375
|
+
var SOLANA_DAS_METHODS = /* @__PURE__ */ new Set([
|
|
3376
|
+
"getAsset",
|
|
3377
|
+
"getAssetsByOwner",
|
|
3378
|
+
"searchAssets",
|
|
3379
|
+
"getTokenAccounts",
|
|
3380
|
+
"getAssets",
|
|
3381
|
+
"getAssetProof",
|
|
3382
|
+
"getAssetsByAuthority",
|
|
3383
|
+
"getAssetsByGroup",
|
|
3384
|
+
"getAssetsByCreator",
|
|
3385
|
+
"getAssetSignatures",
|
|
3386
|
+
"getNftEditions"
|
|
3387
|
+
]);
|
|
3388
|
+
function errorText(err) {
|
|
3389
|
+
if (err instanceof CLIError) {
|
|
3390
|
+
return [err.message, err.details].filter(Boolean).join(" ");
|
|
3391
|
+
}
|
|
3392
|
+
if (err instanceof Error) {
|
|
3393
|
+
return err.message;
|
|
3394
|
+
}
|
|
3395
|
+
return String(err);
|
|
3396
|
+
}
|
|
3397
|
+
function solanaRpcFamilyForMethod(method) {
|
|
3398
|
+
return SOLANA_DAS_METHODS.has(method) ? "das" : "rpc";
|
|
3399
|
+
}
|
|
3400
|
+
function normalizeSolanaDasPaywallError(err) {
|
|
3401
|
+
if (err instanceof CLIError && err.code === ErrorCode.PAYMENT_REQUIRED) {
|
|
3402
|
+
return err;
|
|
3403
|
+
}
|
|
3404
|
+
const detail = errorText(err);
|
|
3405
|
+
if (!/\b(free tier|payg|paid tier|paid plan|upgrade|plan does not|current plan|not available on your plan)\b/i.test(detail)) {
|
|
3406
|
+
return null;
|
|
3407
|
+
}
|
|
3408
|
+
return new CLIError(
|
|
3409
|
+
ErrorCode.PAYMENT_REQUIRED,
|
|
3410
|
+
"Solana DAS request requires a paid Alchemy plan.",
|
|
3411
|
+
"Upgrade your app to PAYG or use a plan with Solana DAS access at https://dashboard.alchemy.com/.",
|
|
3412
|
+
detail || void 0
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
async function callSolana(client, family, method, params) {
|
|
3416
|
+
const startedAt = Date.now();
|
|
3417
|
+
debug(`solana ${family} ${method} params`, params);
|
|
3418
|
+
try {
|
|
3419
|
+
const result = await client.call(method, params);
|
|
3420
|
+
debug(`solana ${family} ${method} ok ${Date.now() - startedAt}ms`);
|
|
3421
|
+
return result;
|
|
3422
|
+
} catch (err) {
|
|
3423
|
+
debug(`solana ${family} ${method} failed ${Date.now() - startedAt}ms`, errorText(err));
|
|
3424
|
+
throw err;
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3374
3428
|
// src/commands/network.ts
|
|
3375
3429
|
function registerNetwork(program2) {
|
|
3376
3430
|
const cmd = program2.command("network").description("Manage networks");
|
|
@@ -6020,7 +6074,7 @@ function registerRpcSurfaceCommand({ root, parent, method }) {
|
|
|
6020
6074
|
result = await withSpinner(
|
|
6021
6075
|
`Calling ${method.name}...`,
|
|
6022
6076
|
`Called ${method.name}`,
|
|
6023
|
-
() => client
|
|
6077
|
+
() => callNetworkRpcMethod(client, method, params)
|
|
6024
6078
|
);
|
|
6025
6079
|
}
|
|
6026
6080
|
printSyntaxJSON(result);
|
|
@@ -6029,6 +6083,20 @@ function registerRpcSurfaceCommand({ root, parent, method }) {
|
|
|
6029
6083
|
}
|
|
6030
6084
|
});
|
|
6031
6085
|
}
|
|
6086
|
+
async function callNetworkRpcMethod(client, method, params) {
|
|
6087
|
+
if (method.family !== "solana") {
|
|
6088
|
+
return await client.call(method.rpcMethod, params);
|
|
6089
|
+
}
|
|
6090
|
+
const solanaFamily = solanaRpcFamilyForMethod(method.rpcMethod);
|
|
6091
|
+
try {
|
|
6092
|
+
return await callSolana(client, solanaFamily, method.rpcMethod, params);
|
|
6093
|
+
} catch (err) {
|
|
6094
|
+
if (solanaFamily === "das") {
|
|
6095
|
+
throw normalizeSolanaDasPaywallError(err) ?? err;
|
|
6096
|
+
}
|
|
6097
|
+
throw err;
|
|
6098
|
+
}
|
|
6099
|
+
}
|
|
6032
6100
|
function parseRpcParams(input) {
|
|
6033
6101
|
const parsed = parseRequiredJSON(input, "--params");
|
|
6034
6102
|
if (Array.isArray(parsed)) {
|
|
@@ -6141,7 +6209,7 @@ function registerSolana(program2) {
|
|
|
6141
6209
|
const result = await withSpinner(
|
|
6142
6210
|
`Calling ${method}\u2026`,
|
|
6143
6211
|
`Called ${method}`,
|
|
6144
|
-
() => client
|
|
6212
|
+
() => callSolana(client, "rpc", method, parseCLIParams(params))
|
|
6145
6213
|
);
|
|
6146
6214
|
printSyntaxJSON(result);
|
|
6147
6215
|
} catch (err) {
|
|
@@ -6158,7 +6226,9 @@ function registerSolana(program2) {
|
|
|
6158
6226
|
const result = await withSpinner(
|
|
6159
6227
|
`Calling ${method}\u2026`,
|
|
6160
6228
|
`Called ${method}`,
|
|
6161
|
-
() => client
|
|
6229
|
+
() => callSolana(client, "das", method, rpcParams).catch((err) => {
|
|
6230
|
+
throw normalizeSolanaDasPaywallError(err) ?? err;
|
|
6231
|
+
})
|
|
6162
6232
|
);
|
|
6163
6233
|
printSyntaxJSON(result);
|
|
6164
6234
|
} catch (err) {
|
|
@@ -6807,7 +6877,7 @@ function registrySymbolSuggestions(network) {
|
|
|
6807
6877
|
}
|
|
6808
6878
|
|
|
6809
6879
|
// src/lib/preflight-errors.ts
|
|
6810
|
-
function
|
|
6880
|
+
function errorText2(err) {
|
|
6811
6881
|
if (err instanceof CLIError) {
|
|
6812
6882
|
return [err.message, err.details].filter(Boolean).join(" ");
|
|
6813
6883
|
}
|
|
@@ -6842,7 +6912,7 @@ function normalizePreflightRevertError(err) {
|
|
|
6842
6912
|
if (err instanceof CLIError && err.code === ErrorCode.PREFLIGHT_REVERT) {
|
|
6843
6913
|
return err;
|
|
6844
6914
|
}
|
|
6845
|
-
const detail =
|
|
6915
|
+
const detail = errorText2(err);
|
|
6846
6916
|
const reason = extractRevertReason(detail);
|
|
6847
6917
|
if (!reason) return null;
|
|
6848
6918
|
return errPreflightRevert(reason, detail || void 0);
|
|
@@ -8180,6 +8250,13 @@ Examples:
|
|
|
8180
8250
|
}
|
|
8181
8251
|
|
|
8182
8252
|
// src/commands/portfolio.ts
|
|
8253
|
+
var BALANCE_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
8254
|
+
"balance",
|
|
8255
|
+
"tokenbalance",
|
|
8256
|
+
"rawbalance",
|
|
8257
|
+
"rawtokenbalance",
|
|
8258
|
+
"amount"
|
|
8259
|
+
]);
|
|
8183
8260
|
async function runDataCall(program2, title, path, body) {
|
|
8184
8261
|
const x402 = resolveX402Client(program2);
|
|
8185
8262
|
return withSpinner(
|
|
@@ -8191,22 +8268,82 @@ async function runDataCall(program2, title, path, body) {
|
|
|
8191
8268
|
function limitPayload(value, limit) {
|
|
8192
8269
|
if (limit === void 0) return value;
|
|
8193
8270
|
if (Array.isArray(value)) {
|
|
8194
|
-
return value.slice(0, limit);
|
|
8271
|
+
return value.slice(0, limit).map((item) => limitPayload(item, limit));
|
|
8195
8272
|
}
|
|
8196
|
-
if (value
|
|
8273
|
+
if (isRecord(value)) {
|
|
8197
8274
|
return Object.fromEntries(
|
|
8198
8275
|
Object.entries(value).map(([key, nested]) => [
|
|
8199
8276
|
key,
|
|
8200
|
-
|
|
8277
|
+
limitPayload(nested, limit)
|
|
8201
8278
|
])
|
|
8202
8279
|
);
|
|
8203
8280
|
}
|
|
8204
8281
|
return value;
|
|
8205
8282
|
}
|
|
8283
|
+
function isRecord(value) {
|
|
8284
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8285
|
+
}
|
|
8286
|
+
function isHexQuantity(value) {
|
|
8287
|
+
return /^0x[0-9a-f]+$/i.test(value);
|
|
8288
|
+
}
|
|
8289
|
+
function parseDecimals2(value) {
|
|
8290
|
+
const decimals = typeof value === "number" ? value : typeof value === "string" && /^\d+$/.test(value) ? Number(value) : void 0;
|
|
8291
|
+
if (decimals === void 0 || !Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
|
|
8292
|
+
return void 0;
|
|
8293
|
+
}
|
|
8294
|
+
return decimals;
|
|
8295
|
+
}
|
|
8296
|
+
function decimalsForRecord(value) {
|
|
8297
|
+
const direct = parseDecimals2(value.decimals);
|
|
8298
|
+
if (direct !== void 0) return direct;
|
|
8299
|
+
for (const key of ["tokenMetadata", "metadata", "token"]) {
|
|
8300
|
+
const nested = value[key];
|
|
8301
|
+
if (isRecord(nested)) {
|
|
8302
|
+
const decimals = parseDecimals2(nested.decimals);
|
|
8303
|
+
if (decimals !== void 0) return decimals;
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
8306
|
+
return void 0;
|
|
8307
|
+
}
|
|
8308
|
+
function formatRawAmount(raw, decimals) {
|
|
8309
|
+
if (decimals === 0) return raw.toString();
|
|
8310
|
+
const divisor = 10n ** BigInt(decimals);
|
|
8311
|
+
const whole = raw / divisor;
|
|
8312
|
+
const fraction = raw % divisor;
|
|
8313
|
+
if (fraction === 0n) return whole.toString();
|
|
8314
|
+
const fractionText = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
8315
|
+
return `${whole}.${fractionText}`;
|
|
8316
|
+
}
|
|
8317
|
+
function shouldNormalizeBalanceField(key) {
|
|
8318
|
+
const normalized = key.toLowerCase();
|
|
8319
|
+
return BALANCE_FIELD_NAMES.has(normalized) || normalized.endsWith("balance");
|
|
8320
|
+
}
|
|
8321
|
+
function normalizePortfolioOutput(value) {
|
|
8322
|
+
if (Array.isArray(value)) {
|
|
8323
|
+
return value.map(normalizePortfolioOutput);
|
|
8324
|
+
}
|
|
8325
|
+
if (!isRecord(value)) {
|
|
8326
|
+
return value;
|
|
8327
|
+
}
|
|
8328
|
+
const decimals = decimalsForRecord(value);
|
|
8329
|
+
const output = {};
|
|
8330
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
8331
|
+
output[key] = normalizePortfolioOutput(nested);
|
|
8332
|
+
if (typeof nested !== "string" || !shouldNormalizeBalanceField(key) || !isHexQuantity(nested)) {
|
|
8333
|
+
continue;
|
|
8334
|
+
}
|
|
8335
|
+
const raw = BigInt(nested);
|
|
8336
|
+
output[`${key}Decimal`] = raw.toString();
|
|
8337
|
+
if (decimals !== void 0) {
|
|
8338
|
+
output[`${key}Formatted`] = formatRawAmount(raw, decimals);
|
|
8339
|
+
}
|
|
8340
|
+
}
|
|
8341
|
+
return output;
|
|
8342
|
+
}
|
|
8206
8343
|
async function runPortfolioCommand(program2, title, path, opts) {
|
|
8207
8344
|
const limit = parseOptionalInt(opts.limit, "--limit");
|
|
8208
8345
|
const result = await runDataCall(program2, title, path, JSON.parse(opts.body));
|
|
8209
|
-
const output = limitPayload(result, limit);
|
|
8346
|
+
const output = normalizePortfolioOutput(limitPayload(result, limit));
|
|
8210
8347
|
if (isJSONMode()) printJSON(output);
|
|
8211
8348
|
else printSyntaxJSON(output);
|
|
8212
8349
|
}
|
|
@@ -8864,7 +9001,7 @@ function buildWalletQuoteClient(program2, address3) {
|
|
|
8864
9001
|
}
|
|
8865
9002
|
|
|
8866
9003
|
// src/lib/quote-errors.ts
|
|
8867
|
-
function
|
|
9004
|
+
function errorText3(err) {
|
|
8868
9005
|
if (err instanceof CLIError) {
|
|
8869
9006
|
return [err.message, err.details].filter(Boolean).join(" ");
|
|
8870
9007
|
}
|
|
@@ -8922,7 +9059,7 @@ function normalizeQuoteError(err, flow) {
|
|
|
8922
9059
|
if (err instanceof CLIError && err.code !== ErrorCode.RPC_ERROR && err.code !== ErrorCode.INTERNAL_ERROR) {
|
|
8923
9060
|
return err;
|
|
8924
9061
|
}
|
|
8925
|
-
const detail =
|
|
9062
|
+
const detail = errorText3(err);
|
|
8926
9063
|
const cause = classifyQuoteFailure(detail);
|
|
8927
9064
|
return new CLIError(
|
|
8928
9065
|
ErrorCode.QUOTE_FAILED,
|
|
@@ -9962,7 +10099,7 @@ function displayPath(path) {
|
|
|
9962
10099
|
const home = getHomeDir();
|
|
9963
10100
|
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
9964
10101
|
}
|
|
9965
|
-
function
|
|
10102
|
+
function isRecord2(value) {
|
|
9966
10103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9967
10104
|
}
|
|
9968
10105
|
function isClientId(value) {
|
|
@@ -10149,7 +10286,7 @@ async function readJSONConfig(path) {
|
|
|
10149
10286
|
const raw = await readFile(path, "utf8");
|
|
10150
10287
|
if (raw.trim().length === 0) return {};
|
|
10151
10288
|
const parsed = JSON.parse(raw);
|
|
10152
|
-
if (!
|
|
10289
|
+
if (!isRecord2(parsed)) {
|
|
10153
10290
|
throw errInvalidArgs(`${displayPath(path)} must contain a JSON object.`);
|
|
10154
10291
|
}
|
|
10155
10292
|
return parsed;
|
|
@@ -10163,7 +10300,7 @@ async function readJSONConfig(path) {
|
|
|
10163
10300
|
}
|
|
10164
10301
|
function withMcpServerConfig(config) {
|
|
10165
10302
|
const existing = config.mcpServers;
|
|
10166
|
-
if (existing !== void 0 && !
|
|
10303
|
+
if (existing !== void 0 && !isRecord2(existing)) {
|
|
10167
10304
|
throw errInvalidArgs("Cursor mcpServers must be a JSON object.");
|
|
10168
10305
|
}
|
|
10169
10306
|
return {
|
|
@@ -10581,7 +10718,7 @@ async function flushProcessOutput() {
|
|
|
10581
10718
|
}
|
|
10582
10719
|
program.name("alchemy").description(
|
|
10583
10720
|
"The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
|
|
10584
|
-
).version("0.
|
|
10721
|
+
).version("0.15.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
|
|
10585
10722
|
"-n, --network <network>",
|
|
10586
10723
|
"Target network for networked commands"
|
|
10587
10724
|
).option("--x402", "Use x402 wallet-based gateway auth").option(
|
|
@@ -10807,7 +10944,7 @@ ${styledLine}`;
|
|
|
10807
10944
|
if (isInteractiveAllowed(program)) {
|
|
10808
10945
|
let latestForInteractiveStartup = null;
|
|
10809
10946
|
if (shouldRunOnboarding(program, cfg)) {
|
|
10810
|
-
const { runOnboarding } = await import("./onboarding-
|
|
10947
|
+
const { runOnboarding } = await import("./onboarding-L2FTZRYN.js");
|
|
10811
10948
|
const latest = getAvailableUpdateOnce();
|
|
10812
10949
|
const completed = await runOnboarding(program, latest);
|
|
10813
10950
|
updateShownDuringInteractiveStartup = Boolean(latest);
|
|
@@ -10821,7 +10958,7 @@ ${styledLine}`;
|
|
|
10821
10958
|
latestForInteractiveStartup
|
|
10822
10959
|
);
|
|
10823
10960
|
}
|
|
10824
|
-
const { startREPL } = await import("./interactive-
|
|
10961
|
+
const { startREPL } = await import("./interactive-3ACOLHPG.js");
|
|
10825
10962
|
program.exitOverride();
|
|
10826
10963
|
program.configureOutput({
|
|
10827
10964
|
writeErr: () => {
|