@alchemy/cli 0.7.4-alpha.37 → 0.8.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.
@@ -4,7 +4,7 @@ import {
4
4
  configDir,
5
5
  load,
6
6
  save
7
- } from "./chunk-PX2YJ7XC.js";
7
+ } from "./chunk-ROBA7SR7.js";
8
8
  import {
9
9
  CLIError,
10
10
  ErrorCode,
@@ -17,6 +17,7 @@ import {
17
17
  errInvalidAPIKey,
18
18
  errInvalidAccessKey,
19
19
  errInvalidArgs,
20
+ errLoginRequired,
20
21
  errNetwork,
21
22
  errNetworkNotEnabled,
22
23
  errNotFound,
@@ -29,7 +30,7 @@ import {
29
30
  parseBaseURLOverride,
30
31
  redactSensitiveText,
31
32
  verbose
32
- } from "./chunk-MYHXAACL.js";
33
+ } from "./chunk-46LMXT54.js";
33
34
 
34
35
  // src/lib/resolve.ts
35
36
  import { readFileSync as readFileSync2 } from "fs";
@@ -293,6 +294,12 @@ var NATIVE_TOKEN_SYMBOLS = {
293
294
  function isSolanaNetwork(networkId) {
294
295
  return networkId.startsWith("solana-");
295
296
  }
297
+ function toAdminNetworkId(slug) {
298
+ return slug.trim().toUpperCase().replace(/-/g, "_");
299
+ }
300
+ function fromAdminNetworkId(id) {
301
+ return id.trim().toLowerCase().replace(/_/g, "-");
302
+ }
296
303
  function nativeTokenSymbol(networkId) {
297
304
  const prefix = networkId.replace(/-(mainnet|testnet|sepolia|holesky|hoodi|devnet|amoy|fuji|cardona|saigon|chiado|signet|mocha|blaze|curtis|bepolia).*$/, "");
298
305
  return NATIVE_TOKEN_SYMBOLS[prefix] ?? "ETH";
@@ -862,6 +869,166 @@ var AdminClient = class _AdminClient {
862
869
  }
863
870
  };
864
871
 
872
+ // src/lib/gas-manager-client.ts
873
+ var GasManagerClient = class _GasManagerClient {
874
+ static get HOST() {
875
+ return `manage.g.${getBaseDomain()}`;
876
+ }
877
+ // Test/debug only: route requests to a local mock.
878
+ static BASE_URL_ENV = "ALCHEMY_GAS_MANAGER_BASE_URL";
879
+ credential;
880
+ constructor(credential) {
881
+ if (typeof credential === "string") {
882
+ this.validateAccessKey(credential);
883
+ this.credential = { type: "access_key", key: credential };
884
+ } else {
885
+ if (credential.type === "access_key") {
886
+ this.validateAccessKey(credential.key);
887
+ } else if (!credential.token.trim()) {
888
+ throw errAuthRequired();
889
+ }
890
+ this.credential = credential;
891
+ }
892
+ }
893
+ baseURL() {
894
+ const override = this.baseURLOverride();
895
+ if (override) return override.toString().replace(/\/$/, "");
896
+ return `https://manage.g.${getBaseDomain()}`;
897
+ }
898
+ allowedHosts() {
899
+ const hosts = /* @__PURE__ */ new Set([_GasManagerClient.HOST]);
900
+ const override = this.baseURLOverride();
901
+ if (override) hosts.add(override.hostname);
902
+ return hosts;
903
+ }
904
+ allowInsecureTransport(hostname) {
905
+ return isLocalhost(hostname);
906
+ }
907
+ baseURLOverride() {
908
+ return parseBaseURLOverride(_GasManagerClient.BASE_URL_ENV);
909
+ }
910
+ validateAccessKey(accessKey) {
911
+ if (!accessKey.trim() || /\s/.test(accessKey)) {
912
+ throw errInvalidAccessKey();
913
+ }
914
+ }
915
+ assertSafeRequestTarget(url) {
916
+ let parsed;
917
+ try {
918
+ parsed = new URL(url);
919
+ } catch {
920
+ throw errInvalidArgs("Invalid gas manager API URL.");
921
+ }
922
+ if (!this.allowedHosts().has(parsed.hostname)) {
923
+ throw errInvalidArgs(`Refusing to send credentials to unexpected host: ${parsed.hostname}`);
924
+ }
925
+ if (parsed.protocol !== "https:" && !this.allowInsecureTransport(parsed.hostname)) {
926
+ throw errInvalidArgs("Refusing to send credentials over non-HTTPS connection.");
927
+ }
928
+ }
929
+ async request(method, path, body) {
930
+ const url = `${this.baseURL()}${path}`;
931
+ debug(`${method} ${url}`);
932
+ this.assertSafeRequestTarget(url);
933
+ const token = this.credential.type === "access_key" ? this.credential.key : this.credential.token;
934
+ const resp = await fetchWithTimeout(url, {
935
+ method,
936
+ redirect: "error",
937
+ headers: {
938
+ Authorization: `Bearer ${token}`,
939
+ "Content-Type": "application/json",
940
+ Accept: "application/json"
941
+ },
942
+ ...body !== void 0 && { body: JSON.stringify(body) }
943
+ });
944
+ if (resp.status === 401) {
945
+ throw this.credential.type === "auth_token" ? errLoginRequired() : errInvalidAccessKey();
946
+ }
947
+ if (resp.status === 403) {
948
+ const detail = await resp.text().catch(() => "");
949
+ let reason;
950
+ try {
951
+ const parsed = JSON.parse(detail);
952
+ reason = parsed?.error?.msg ?? parsed?.message ?? parsed?.error ?? void 0;
953
+ } catch {
954
+ reason = detail || void 0;
955
+ }
956
+ if (this.credential.type === "auth_token") {
957
+ const tail = reason ? ` (${reason})` : "";
958
+ throw new CLIError(
959
+ ErrorCode.AUTH_REQUIRED,
960
+ `Access denied for this Alchemy account or app${tail}. Re-authenticate with 'alchemy auth login' or check that the default app is one you have permission to manage.`,
961
+ "alchemy auth login"
962
+ );
963
+ }
964
+ throw errAccessDenied(typeof reason === "string" ? reason : void 0);
965
+ }
966
+ if (resp.status === 404) {
967
+ const text = await resp.text().catch(() => "");
968
+ throw errNotFound(text || path);
969
+ }
970
+ if (resp.status === 429) throw errRateLimited();
971
+ if (!resp.ok) {
972
+ const text = await resp.text().catch(() => "");
973
+ throw errAdminAPI(resp.status, text);
974
+ }
975
+ return resp.json();
976
+ }
977
+ async listPolicies(opts) {
978
+ const params = new URLSearchParams();
979
+ params.set("appId", opts.appId);
980
+ if (opts.limit !== void 0) params.set("limit", String(opts.limit));
981
+ if (opts.after) params.set("after", opts.after);
982
+ if (opts.before) params.set("before", opts.before);
983
+ const resp = await this.request(
984
+ "GET",
985
+ `/api/gasManager/policies?${params.toString()}`
986
+ );
987
+ return resp.data;
988
+ }
989
+ async listAllPolicies(opts) {
990
+ const policies = [];
991
+ const seen = /* @__PURE__ */ new Set();
992
+ let after;
993
+ do {
994
+ const page = await this.listPolicies({
995
+ appId: opts.appId,
996
+ ...opts.limit !== void 0 && { limit: opts.limit },
997
+ ...after && { after }
998
+ });
999
+ policies.push(...page.policies);
1000
+ const next = page.after ?? void 0;
1001
+ if (next && seen.has(next)) break;
1002
+ if (next) seen.add(next);
1003
+ after = next;
1004
+ } while (after);
1005
+ return policies;
1006
+ }
1007
+ async getPolicy(policyId) {
1008
+ const resp = await this.request(
1009
+ "GET",
1010
+ `/api/gasManager/policy/${encodeURIComponent(policyId)}`
1011
+ );
1012
+ return resp.data.policy;
1013
+ }
1014
+ async createPolicy(payload) {
1015
+ const resp = await this.request(
1016
+ "POST",
1017
+ "/api/gasManager/policy",
1018
+ payload
1019
+ );
1020
+ return resp.data.policy;
1021
+ }
1022
+ async setPolicyStatus(policyId, status) {
1023
+ const resp = await this.request(
1024
+ "PUT",
1025
+ `/api/gasManager/policy/${encodeURIComponent(policyId)}/status`,
1026
+ { status }
1027
+ );
1028
+ return resp.data.policy;
1029
+ }
1030
+ };
1031
+
865
1032
  // src/lib/wallet-session.ts
866
1033
  import { generateKeyPairSync, randomUUID } from "crypto";
867
1034
  import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync } from "fs";
@@ -884,20 +1051,7 @@ var walletSessionEnvironmentSchema = z.object({
884
1051
  clientInstanceId: z.string().uuid().optional(),
885
1052
  clientInstanceName: z.string().min(1).max(64).optional()
886
1053
  }).strict();
887
- var chainWalletSessionSchema = z.object({
888
- sessionId: z.string().uuid(),
889
- status: z.enum(["pending", "approved", "revoked", "expired"]),
890
- expiresAt: z.string().datetime(),
891
- chainType: z.enum(["evm", "solana"]),
892
- walletId: z.string().min(1).optional(),
893
- walletAddress: z.string().min(1).optional(),
894
- providerKeyQuorumId: z.string().min(1).optional(),
895
- providerSignerId: z.string().min(1).optional(),
896
- capabilities: walletSessionCapabilitiesSchema.optional()
897
- }).strict();
898
1054
  var walletSessionSchema = z.object({
899
- version: z.number().int().positive().optional(),
900
- connectionRequestId: z.string().min(1).optional(),
901
1055
  sessionId: z.string().uuid(),
902
1056
  status: z.enum(["pending", "approved", "revoked", "expired"]),
903
1057
  createdAt: z.string().datetime(),
@@ -916,11 +1070,7 @@ var walletSessionSchema = z.object({
916
1070
  chainType: z.string().min(1).optional(),
917
1071
  backendBaseUrl: z.string().min(1).optional(),
918
1072
  environment: walletSessionEnvironmentSchema.optional(),
919
- capabilities: walletSessionCapabilitiesSchema.optional(),
920
- sessionsByChain: z.object({
921
- evm: chainWalletSessionSchema.optional(),
922
- solana: chainWalletSessionSchema.optional()
923
- }).strict().optional()
1073
+ capabilities: walletSessionCapabilitiesSchema.optional()
924
1074
  }).strict();
925
1075
  function sessionPath() {
926
1076
  return join(configDir(), SESSION_FILE);
@@ -928,59 +1078,7 @@ function sessionPath() {
928
1078
  function parseStoredSession(value) {
929
1079
  const parsed = walletSessionSchema.safeParse(value);
930
1080
  if (!parsed.success) return null;
931
- return withLegacyChainSessions(parsed.data);
932
- }
933
- function withLegacyChainSessions(session) {
934
- if (session.sessionsByChain) {
935
- return withCompatibilityAliases(session);
936
- }
937
- const chainType = session.chainType === "solana" ? "solana" : "evm";
938
- const walletId = chainType === "solana" ? session.solanaWalletId ?? session.walletId : session.evmWalletId ?? session.walletId;
939
- const walletAddress = chainType === "solana" ? session.solanaAddress : session.evmAddress;
940
- return withCompatibilityAliases({
941
- ...session,
942
- version: session.version ?? 1,
943
- sessionsByChain: {
944
- [chainType]: {
945
- sessionId: session.sessionId,
946
- status: session.status,
947
- expiresAt: session.expiresAt,
948
- chainType,
949
- ...walletId ? { walletId } : {},
950
- ...walletAddress ? { walletAddress } : {},
951
- ...session.privyKeyQuorumId ? { providerKeyQuorumId: session.privyKeyQuorumId } : {},
952
- ...session.privySignerId ? { providerSignerId: session.privySignerId } : {},
953
- ...session.capabilities ? { capabilities: session.capabilities } : {}
954
- }
955
- }
956
- });
957
- }
958
- function withCompatibilityAliases(session) {
959
- const evm = session.sessionsByChain?.evm;
960
- const solana = session.sessionsByChain?.solana;
961
- const preferred = evm ?? solana;
962
- if (!preferred) {
963
- return session;
964
- }
965
- return {
966
- ...session,
967
- sessionId: preferred.sessionId,
968
- status: preferred.status,
969
- expiresAt: preferred.expiresAt,
970
- walletId: evm?.walletId ?? solana?.walletId ?? session.walletId,
971
- evmWalletId: evm?.walletId ?? session.evmWalletId,
972
- evmAddress: evm?.walletAddress ?? session.evmAddress,
973
- solanaWalletId: solana?.walletId ?? session.solanaWalletId,
974
- solanaAddress: solana?.walletAddress ?? session.solanaAddress,
975
- privyKeyQuorumId: evm?.providerKeyQuorumId ?? solana?.providerKeyQuorumId ?? session.privyKeyQuorumId,
976
- privySignerId: evm?.providerSignerId ?? solana?.providerSignerId ?? session.privySignerId,
977
- chainType: evm && solana ? "both" : preferred.chainType,
978
- capabilities: {
979
- ...evm?.capabilities ?? {},
980
- ...solana?.capabilities ?? {},
981
- ...session.capabilities ?? {}
982
- }
983
- };
1081
+ return parsed.data;
984
1082
  }
985
1083
  function loadStoredSessionFromPath(path) {
986
1084
  if (!existsSync(path)) return null;
@@ -994,23 +1092,13 @@ function isExpired(session) {
994
1092
  const expiresAt = new Date(session.expiresAt);
995
1093
  return !Number.isNaN(expiresAt.getTime()) && expiresAt <= /* @__PURE__ */ new Date();
996
1094
  }
997
- function isSessionLoadable(session) {
998
- if (session.sessionsByChain) {
999
- return Object.values(session.sessionsByChain).some((chainSession) => {
1000
- return Boolean(
1001
- chainSession && chainSession.status !== "revoked" && !isExpired(chainSession)
1002
- );
1003
- });
1004
- }
1005
- if (session.status === "revoked") return false;
1006
- return !isExpired(session);
1007
- }
1008
1095
  function createFileWalletSessionStore(path = sessionPath()) {
1009
1096
  return {
1010
1097
  load() {
1011
1098
  const session = loadStoredSessionFromPath(path);
1012
1099
  if (!session) return null;
1013
- if (!isSessionLoadable(session)) return null;
1100
+ if (session.status === "revoked") return null;
1101
+ if (isExpired(session)) return null;
1014
1102
  return session;
1015
1103
  },
1016
1104
  loadRaw() {
@@ -1054,7 +1142,6 @@ function createPendingSession() {
1054
1142
  const now = /* @__PURE__ */ new Date();
1055
1143
  const expiresAt = new Date(now.getTime() + SESSION_TTL_DAYS * 24 * 60 * 60 * 1e3);
1056
1144
  const session = {
1057
- version: 2,
1058
1145
  sessionId: randomUUID(),
1059
1146
  status: "pending",
1060
1147
  createdAt: now.toISOString(),
@@ -1077,12 +1164,12 @@ function loadStoredSession() {
1077
1164
  return store.load();
1078
1165
  }
1079
1166
  function saveSession(session) {
1080
- getWalletSessionStore().save(withCompatibilityAliases(session));
1167
+ getWalletSessionStore().save(session);
1081
1168
  }
1082
1169
  function updateSession(updates) {
1083
1170
  const session = loadSession();
1084
1171
  if (!session) return null;
1085
- const updated = withCompatibilityAliases({ ...session, ...updates });
1172
+ const updated = { ...session, ...updates };
1086
1173
  saveSession(updated);
1087
1174
  return updated;
1088
1175
  }
@@ -1090,52 +1177,11 @@ function clearSession() {
1090
1177
  return getWalletSessionStore().clear();
1091
1178
  }
1092
1179
  function isSessionValid(session) {
1093
- if (session.sessionsByChain) {
1094
- return Object.values(session.sessionsByChain).some((chainSession) => {
1095
- return Boolean(chainSession && isChainSessionValid(chainSession));
1096
- });
1097
- }
1098
1180
  if (session.status !== "approved") return false;
1099
1181
  const expiresAt = new Date(session.expiresAt);
1100
1182
  if (Number.isNaN(expiresAt.getTime())) return false;
1101
1183
  return expiresAt > /* @__PURE__ */ new Date();
1102
1184
  }
1103
- function isChainSessionValid(session) {
1104
- if (session.status !== "approved") return false;
1105
- const expiresAt = new Date(session.expiresAt);
1106
- if (Number.isNaN(expiresAt.getTime())) return false;
1107
- return expiresAt > /* @__PURE__ */ new Date();
1108
- }
1109
- function getWalletSessionByChain(session, chainType) {
1110
- const chainSession = session.sessionsByChain?.[chainType];
1111
- if (!chainSession) {
1112
- if (!isSessionValid(session)) return null;
1113
- if (chainType === "evm" && session.evmAddress) return session;
1114
- if (chainType === "solana" && session.solanaAddress) return session;
1115
- return null;
1116
- }
1117
- if (!isChainSessionValid(chainSession)) {
1118
- return null;
1119
- }
1120
- return withCompatibilityAliases({
1121
- ...session,
1122
- sessionId: chainSession.sessionId,
1123
- status: chainSession.status,
1124
- expiresAt: chainSession.expiresAt,
1125
- walletId: chainSession.walletId,
1126
- evmWalletId: chainType === "evm" ? chainSession.walletId : session.evmWalletId,
1127
- evmAddress: chainType === "evm" ? chainSession.walletAddress : session.evmAddress,
1128
- solanaWalletId: chainType === "solana" ? chainSession.walletId : session.solanaWalletId,
1129
- solanaAddress: chainType === "solana" ? chainSession.walletAddress : session.solanaAddress,
1130
- privyKeyQuorumId: chainSession.providerKeyQuorumId,
1131
- privySignerId: chainSession.providerSignerId,
1132
- chainType,
1133
- capabilities: chainSession.capabilities ?? session.capabilities,
1134
- sessionsByChain: {
1135
- [chainType]: chainSession
1136
- }
1137
- });
1138
- }
1139
1185
 
1140
1186
  // src/lib/resolve.ts
1141
1187
  function getCommandOptions(program) {
@@ -1215,6 +1261,15 @@ function adminClientFromFlags(program) {
1215
1261
  if (authToken) return new AdminClient({ type: "auth_token", token: authToken });
1216
1262
  throw errAccessKeyRequired();
1217
1263
  }
1264
+ function hasAuthLoginToken(cfg) {
1265
+ return Boolean(resolveAuthToken(cfg));
1266
+ }
1267
+ function gasManagerClientFromFlags(_program) {
1268
+ const cfg = load();
1269
+ const authToken = resolveAuthToken(cfg);
1270
+ if (!authToken) throw errLoginRequired();
1271
+ return new GasManagerClient({ type: "auth_token", token: authToken });
1272
+ }
1218
1273
  function resolveX402(program, cfg) {
1219
1274
  const opts = getCommandOptions(program);
1220
1275
  if (opts.x402) return true;
@@ -1361,6 +1416,8 @@ export {
1361
1416
  getRPCNetworks,
1362
1417
  getRPCNetworkIds,
1363
1418
  isSolanaNetwork,
1419
+ toAdminNetworkId,
1420
+ fromAdminNetworkId,
1364
1421
  nativeTokenSymbol,
1365
1422
  AdminClient,
1366
1423
  createPendingSession,
@@ -1370,7 +1427,6 @@ export {
1370
1427
  updateSession,
1371
1428
  clearSession,
1372
1429
  isSessionValid,
1373
- getWalletSessionByChain,
1374
1430
  resolveAPIKey,
1375
1431
  resolveAccessKey,
1376
1432
  resolveNetwork,
@@ -1378,6 +1434,8 @@ export {
1378
1434
  resolveAppId,
1379
1435
  resolveAuthToken,
1380
1436
  adminClientFromFlags,
1437
+ hasAuthLoginToken,
1438
+ gasManagerClientFromFlags,
1381
1439
  resolveX402,
1382
1440
  resolveX402Client,
1383
1441
  resolveWalletKey,
@@ -2,7 +2,7 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  isJSONMode
5
- } from "./chunk-MYHXAACL.js";
5
+ } from "./chunk-46LMXT54.js";
6
6
 
7
7
  // src/lib/interaction.ts
8
8
  import { stdin, stdout } from "process";
@@ -2,7 +2,7 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  getBaseDomain
5
- } from "./chunk-MYHXAACL.js";
5
+ } from "./chunk-46LMXT54.js";
6
6
 
7
7
  // src/lib/auth.ts
8
8
  import { createHash, randomBytes } from "crypto";
@@ -2,7 +2,7 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  isRevealMode
5
- } from "./chunk-MYHXAACL.js";
5
+ } from "./chunk-46LMXT54.js";
6
6
 
7
7
  // src/lib/secrets.ts
8
8
  function maskSecret(value) {
@@ -12,6 +12,7 @@ import {
12
12
  errInvalidAPIKey,
13
13
  errInvalidAccessKey,
14
14
  errInvalidArgs,
15
+ errLoginRequired,
15
16
  errNetwork,
16
17
  errNetworkNotEnabled,
17
18
  errNoActiveSession,
@@ -26,7 +27,7 @@ import {
26
27
  exitWithError,
27
28
  isReplMode,
28
29
  setReplMode
29
- } from "./chunk-MYHXAACL.js";
30
+ } from "./chunk-46LMXT54.js";
30
31
  export {
31
32
  CLIError,
32
33
  EXIT_CODES,
@@ -39,6 +40,7 @@ export {
39
40
  errInvalidAPIKey,
40
41
  errInvalidAccessKey,
41
42
  errInvalidArgs,
43
+ errLoginRequired,
42
44
  errNetwork,
43
45
  errNetworkNotEnabled,
44
46
  errNoActiveSession,