@alchemy/cli 0.7.4 → 0.9.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/auth-65X7EMCF.js +16 -0
- package/dist/{auth-GD7BJOMK.js → auth-IAM4AMBK.js} +2 -2
- package/dist/{chunk-64A5W4M2.js → chunk-75ICFV5K.js} +1 -1
- package/dist/{chunk-K6V3R7SH.js → chunk-7ZSEELHZ.js} +312 -8
- package/dist/chunk-AJPOUEO6.js +186 -0
- package/dist/{chunk-JUCUKTP3.js → chunk-GDLPBPG3.js} +1 -1
- package/dist/{chunk-5IL2PMZ6.js → chunk-MV7O3XBG.js} +1 -1
- package/dist/{chunk-R4W44A6E.js → chunk-NMT7XH3C.js} +5 -5
- package/dist/{chunk-2BALTY22.js → chunk-OVLQH6KL.js} +12 -4
- package/dist/{chunk-C5HNQOLB.js → chunk-RGVM5SNE.js} +1 -1
- package/dist/{chunk-N36ZNOVV.js → chunk-WT4RGQLC.js} +3 -3
- package/dist/{chunk-XSN4XA5Z.js → chunk-XCKUCXC6.js} +6 -6
- package/dist/{errors-E2P6WHTX.js → errors-T6XE2I2L.js} +3 -1
- package/dist/index.js +1062 -234
- package/dist/{interactive-MHAC5WQI.js → interactive-GVMPYXNJ.js} +7 -7
- package/dist/{onboarding-CT4RRH6O.js → onboarding-MDHVOUY3.js} +6 -6
- package/dist/policy-prompt-PHVO6VRZ.js +18 -0
- package/dist/{resolve-WXT5ZCUK.js → resolve-GBS26K44.js} +7 -3
- package/package.json +1 -1
- package/dist/auth-F2IXC6CM.js +0 -16
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
|
+
import {
|
|
4
|
+
registerAuth,
|
|
5
|
+
selectAppAfterAuth
|
|
6
|
+
} from "./chunk-XCKUCXC6.js";
|
|
7
|
+
import "./chunk-RGVM5SNE.js";
|
|
8
|
+
import "./chunk-75ICFV5K.js";
|
|
9
|
+
import "./chunk-7ZSEELHZ.js";
|
|
10
|
+
import "./chunk-MV7O3XBG.js";
|
|
11
|
+
import "./chunk-GDLPBPG3.js";
|
|
12
|
+
import "./chunk-OVLQH6KL.js";
|
|
13
|
+
export {
|
|
14
|
+
registerAuth,
|
|
15
|
+
selectAppAfterAuth
|
|
16
|
+
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
configDir,
|
|
5
5
|
load,
|
|
6
6
|
save
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GDLPBPG3.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-
|
|
33
|
+
} from "./chunk-OVLQH6KL.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,7 +1051,20 @@ var walletSessionEnvironmentSchema = z.object({
|
|
|
884
1051
|
clientInstanceId: z.string().uuid().optional(),
|
|
885
1052
|
clientInstanceName: z.string().min(1).max(64).optional()
|
|
886
1053
|
}).strict();
|
|
1054
|
+
var chainWalletSessionSchema = z.object({
|
|
1055
|
+
sessionId: z.string().uuid(),
|
|
1056
|
+
status: z.enum(["pending", "approved", "revoked", "expired"]),
|
|
1057
|
+
expiresAt: z.string().datetime(),
|
|
1058
|
+
chainType: z.enum(["evm", "solana"]),
|
|
1059
|
+
walletId: z.string().min(1).optional(),
|
|
1060
|
+
walletAddress: z.string().min(1).optional(),
|
|
1061
|
+
providerKeyQuorumId: z.string().min(1).optional(),
|
|
1062
|
+
providerSignerId: z.string().min(1).optional(),
|
|
1063
|
+
capabilities: walletSessionCapabilitiesSchema.optional()
|
|
1064
|
+
}).strict();
|
|
887
1065
|
var walletSessionSchema = z.object({
|
|
1066
|
+
version: z.number().int().positive().optional(),
|
|
1067
|
+
connectionRequestId: z.string().min(1).optional(),
|
|
888
1068
|
sessionId: z.string().uuid(),
|
|
889
1069
|
status: z.enum(["pending", "approved", "revoked", "expired"]),
|
|
890
1070
|
createdAt: z.string().datetime(),
|
|
@@ -903,7 +1083,11 @@ var walletSessionSchema = z.object({
|
|
|
903
1083
|
chainType: z.string().min(1).optional(),
|
|
904
1084
|
backendBaseUrl: z.string().min(1).optional(),
|
|
905
1085
|
environment: walletSessionEnvironmentSchema.optional(),
|
|
906
|
-
capabilities: walletSessionCapabilitiesSchema.optional()
|
|
1086
|
+
capabilities: walletSessionCapabilitiesSchema.optional(),
|
|
1087
|
+
sessionsByChain: z.object({
|
|
1088
|
+
evm: chainWalletSessionSchema.optional(),
|
|
1089
|
+
solana: chainWalletSessionSchema.optional()
|
|
1090
|
+
}).strict().optional()
|
|
907
1091
|
}).strict();
|
|
908
1092
|
function sessionPath() {
|
|
909
1093
|
return join(configDir(), SESSION_FILE);
|
|
@@ -911,7 +1095,59 @@ function sessionPath() {
|
|
|
911
1095
|
function parseStoredSession(value) {
|
|
912
1096
|
const parsed = walletSessionSchema.safeParse(value);
|
|
913
1097
|
if (!parsed.success) return null;
|
|
914
|
-
return parsed.data;
|
|
1098
|
+
return withLegacyChainSessions(parsed.data);
|
|
1099
|
+
}
|
|
1100
|
+
function withLegacyChainSessions(session) {
|
|
1101
|
+
if (session.sessionsByChain) {
|
|
1102
|
+
return withCompatibilityAliases(session);
|
|
1103
|
+
}
|
|
1104
|
+
const chainType = session.chainType === "solana" ? "solana" : "evm";
|
|
1105
|
+
const walletId = chainType === "solana" ? session.solanaWalletId ?? session.walletId : session.evmWalletId ?? session.walletId;
|
|
1106
|
+
const walletAddress = chainType === "solana" ? session.solanaAddress : session.evmAddress;
|
|
1107
|
+
return withCompatibilityAliases({
|
|
1108
|
+
...session,
|
|
1109
|
+
version: session.version ?? 1,
|
|
1110
|
+
sessionsByChain: {
|
|
1111
|
+
[chainType]: {
|
|
1112
|
+
sessionId: session.sessionId,
|
|
1113
|
+
status: session.status,
|
|
1114
|
+
expiresAt: session.expiresAt,
|
|
1115
|
+
chainType,
|
|
1116
|
+
...walletId ? { walletId } : {},
|
|
1117
|
+
...walletAddress ? { walletAddress } : {},
|
|
1118
|
+
...session.privyKeyQuorumId ? { providerKeyQuorumId: session.privyKeyQuorumId } : {},
|
|
1119
|
+
...session.privySignerId ? { providerSignerId: session.privySignerId } : {},
|
|
1120
|
+
...session.capabilities ? { capabilities: session.capabilities } : {}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
function withCompatibilityAliases(session) {
|
|
1126
|
+
const evm = session.sessionsByChain?.evm;
|
|
1127
|
+
const solana = session.sessionsByChain?.solana;
|
|
1128
|
+
const preferred = evm ?? solana;
|
|
1129
|
+
if (!preferred) {
|
|
1130
|
+
return session;
|
|
1131
|
+
}
|
|
1132
|
+
return {
|
|
1133
|
+
...session,
|
|
1134
|
+
sessionId: preferred.sessionId,
|
|
1135
|
+
status: preferred.status,
|
|
1136
|
+
expiresAt: preferred.expiresAt,
|
|
1137
|
+
walletId: evm?.walletId ?? solana?.walletId ?? session.walletId,
|
|
1138
|
+
evmWalletId: evm?.walletId ?? session.evmWalletId,
|
|
1139
|
+
evmAddress: evm?.walletAddress ?? session.evmAddress,
|
|
1140
|
+
solanaWalletId: solana?.walletId ?? session.solanaWalletId,
|
|
1141
|
+
solanaAddress: solana?.walletAddress ?? session.solanaAddress,
|
|
1142
|
+
privyKeyQuorumId: evm?.providerKeyQuorumId ?? solana?.providerKeyQuorumId ?? session.privyKeyQuorumId,
|
|
1143
|
+
privySignerId: evm?.providerSignerId ?? solana?.providerSignerId ?? session.privySignerId,
|
|
1144
|
+
chainType: evm && solana ? "both" : preferred.chainType,
|
|
1145
|
+
capabilities: {
|
|
1146
|
+
...evm?.capabilities ?? {},
|
|
1147
|
+
...solana?.capabilities ?? {},
|
|
1148
|
+
...session.capabilities ?? {}
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
915
1151
|
}
|
|
916
1152
|
function loadStoredSessionFromPath(path) {
|
|
917
1153
|
if (!existsSync(path)) return null;
|
|
@@ -925,13 +1161,23 @@ function isExpired(session) {
|
|
|
925
1161
|
const expiresAt = new Date(session.expiresAt);
|
|
926
1162
|
return !Number.isNaN(expiresAt.getTime()) && expiresAt <= /* @__PURE__ */ new Date();
|
|
927
1163
|
}
|
|
1164
|
+
function isSessionLoadable(session) {
|
|
1165
|
+
if (session.sessionsByChain) {
|
|
1166
|
+
return Object.values(session.sessionsByChain).some((chainSession) => {
|
|
1167
|
+
return Boolean(
|
|
1168
|
+
chainSession && chainSession.status !== "revoked" && !isExpired(chainSession)
|
|
1169
|
+
);
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
if (session.status === "revoked") return false;
|
|
1173
|
+
return !isExpired(session);
|
|
1174
|
+
}
|
|
928
1175
|
function createFileWalletSessionStore(path = sessionPath()) {
|
|
929
1176
|
return {
|
|
930
1177
|
load() {
|
|
931
1178
|
const session = loadStoredSessionFromPath(path);
|
|
932
1179
|
if (!session) return null;
|
|
933
|
-
if (session
|
|
934
|
-
if (isExpired(session)) return null;
|
|
1180
|
+
if (!isSessionLoadable(session)) return null;
|
|
935
1181
|
return session;
|
|
936
1182
|
},
|
|
937
1183
|
loadRaw() {
|
|
@@ -975,6 +1221,7 @@ function createPendingSession() {
|
|
|
975
1221
|
const now = /* @__PURE__ */ new Date();
|
|
976
1222
|
const expiresAt = new Date(now.getTime() + SESSION_TTL_DAYS * 24 * 60 * 60 * 1e3);
|
|
977
1223
|
const session = {
|
|
1224
|
+
version: 2,
|
|
978
1225
|
sessionId: randomUUID(),
|
|
979
1226
|
status: "pending",
|
|
980
1227
|
createdAt: now.toISOString(),
|
|
@@ -997,12 +1244,12 @@ function loadStoredSession() {
|
|
|
997
1244
|
return store.load();
|
|
998
1245
|
}
|
|
999
1246
|
function saveSession(session) {
|
|
1000
|
-
getWalletSessionStore().save(session);
|
|
1247
|
+
getWalletSessionStore().save(withCompatibilityAliases(session));
|
|
1001
1248
|
}
|
|
1002
1249
|
function updateSession(updates) {
|
|
1003
1250
|
const session = loadSession();
|
|
1004
1251
|
if (!session) return null;
|
|
1005
|
-
const updated = { ...session, ...updates };
|
|
1252
|
+
const updated = withCompatibilityAliases({ ...session, ...updates });
|
|
1006
1253
|
saveSession(updated);
|
|
1007
1254
|
return updated;
|
|
1008
1255
|
}
|
|
@@ -1010,11 +1257,54 @@ function clearSession() {
|
|
|
1010
1257
|
return getWalletSessionStore().clear();
|
|
1011
1258
|
}
|
|
1012
1259
|
function isSessionValid(session) {
|
|
1260
|
+
if (session.sessionsByChain) {
|
|
1261
|
+
return Object.values(session.sessionsByChain).some((chainSession) => {
|
|
1262
|
+
return Boolean(chainSession && isChainSessionValid(chainSession));
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1013
1265
|
if (session.status !== "approved") return false;
|
|
1014
1266
|
const expiresAt = new Date(session.expiresAt);
|
|
1015
1267
|
if (Number.isNaN(expiresAt.getTime())) return false;
|
|
1016
1268
|
return expiresAt > /* @__PURE__ */ new Date();
|
|
1017
1269
|
}
|
|
1270
|
+
function isChainSessionValid(session) {
|
|
1271
|
+
if (session.status !== "approved") return false;
|
|
1272
|
+
const expiresAt = new Date(session.expiresAt);
|
|
1273
|
+
if (Number.isNaN(expiresAt.getTime())) return false;
|
|
1274
|
+
return expiresAt > /* @__PURE__ */ new Date();
|
|
1275
|
+
}
|
|
1276
|
+
function getWalletSessionByChain(session, chainType) {
|
|
1277
|
+
const chainSession = session.sessionsByChain?.[chainType];
|
|
1278
|
+
if (!chainSession) {
|
|
1279
|
+
if (!isSessionValid(session)) return null;
|
|
1280
|
+
const legacyChainType = session.chainType === "solana" ? "solana" : "evm";
|
|
1281
|
+
if (chainType !== legacyChainType) return null;
|
|
1282
|
+
if (legacyChainType === "evm" && session.evmAddress) return session;
|
|
1283
|
+
if (legacyChainType === "solana" && session.solanaAddress) return session;
|
|
1284
|
+
return null;
|
|
1285
|
+
}
|
|
1286
|
+
if (!isChainSessionValid(chainSession)) {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
return withCompatibilityAliases({
|
|
1290
|
+
...session,
|
|
1291
|
+
sessionId: chainSession.sessionId,
|
|
1292
|
+
status: chainSession.status,
|
|
1293
|
+
expiresAt: chainSession.expiresAt,
|
|
1294
|
+
walletId: chainSession.walletId,
|
|
1295
|
+
evmWalletId: chainType === "evm" ? chainSession.walletId : session.evmWalletId,
|
|
1296
|
+
evmAddress: chainType === "evm" ? chainSession.walletAddress : session.evmAddress,
|
|
1297
|
+
solanaWalletId: chainType === "solana" ? chainSession.walletId : session.solanaWalletId,
|
|
1298
|
+
solanaAddress: chainType === "solana" ? chainSession.walletAddress : session.solanaAddress,
|
|
1299
|
+
privyKeyQuorumId: chainSession.providerKeyQuorumId,
|
|
1300
|
+
privySignerId: chainSession.providerSignerId,
|
|
1301
|
+
chainType,
|
|
1302
|
+
capabilities: chainSession.capabilities ?? session.capabilities,
|
|
1303
|
+
sessionsByChain: {
|
|
1304
|
+
[chainType]: chainSession
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1018
1308
|
|
|
1019
1309
|
// src/lib/resolve.ts
|
|
1020
1310
|
function getCommandOptions(program) {
|
|
@@ -1094,6 +1384,15 @@ function adminClientFromFlags(program) {
|
|
|
1094
1384
|
if (authToken) return new AdminClient({ type: "auth_token", token: authToken });
|
|
1095
1385
|
throw errAccessKeyRequired();
|
|
1096
1386
|
}
|
|
1387
|
+
function hasAuthLoginToken(cfg) {
|
|
1388
|
+
return Boolean(resolveAuthToken(cfg));
|
|
1389
|
+
}
|
|
1390
|
+
function gasManagerClientFromFlags(_program) {
|
|
1391
|
+
const cfg = load();
|
|
1392
|
+
const authToken = resolveAuthToken(cfg);
|
|
1393
|
+
if (!authToken) throw errLoginRequired();
|
|
1394
|
+
return new GasManagerClient({ type: "auth_token", token: authToken });
|
|
1395
|
+
}
|
|
1097
1396
|
function resolveX402(program, cfg) {
|
|
1098
1397
|
const opts = getCommandOptions(program);
|
|
1099
1398
|
if (opts.x402) return true;
|
|
@@ -1240,6 +1539,8 @@ export {
|
|
|
1240
1539
|
getRPCNetworks,
|
|
1241
1540
|
getRPCNetworkIds,
|
|
1242
1541
|
isSolanaNetwork,
|
|
1542
|
+
toAdminNetworkId,
|
|
1543
|
+
fromAdminNetworkId,
|
|
1243
1544
|
nativeTokenSymbol,
|
|
1244
1545
|
AdminClient,
|
|
1245
1546
|
createPendingSession,
|
|
@@ -1249,6 +1550,7 @@ export {
|
|
|
1249
1550
|
updateSession,
|
|
1250
1551
|
clearSession,
|
|
1251
1552
|
isSessionValid,
|
|
1553
|
+
getWalletSessionByChain,
|
|
1252
1554
|
resolveAPIKey,
|
|
1253
1555
|
resolveAccessKey,
|
|
1254
1556
|
resolveNetwork,
|
|
@@ -1256,6 +1558,8 @@ export {
|
|
|
1256
1558
|
resolveAppId,
|
|
1257
1559
|
resolveAuthToken,
|
|
1258
1560
|
adminClientFromFlags,
|
|
1561
|
+
hasAuthLoginToken,
|
|
1562
|
+
gasManagerClientFromFlags,
|
|
1259
1563
|
resolveX402,
|
|
1260
1564
|
resolveX402Client,
|
|
1261
1565
|
resolveWalletKey,
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
|
+
import {
|
|
4
|
+
gasManagerClientFromFlags,
|
|
5
|
+
toAdminNetworkId
|
|
6
|
+
} from "./chunk-7ZSEELHZ.js";
|
|
7
|
+
import {
|
|
8
|
+
dim,
|
|
9
|
+
green,
|
|
10
|
+
promptAutocomplete,
|
|
11
|
+
promptConfirm,
|
|
12
|
+
promptText,
|
|
13
|
+
withSpinner
|
|
14
|
+
} from "./chunk-MV7O3XBG.js";
|
|
15
|
+
import {
|
|
16
|
+
load,
|
|
17
|
+
save
|
|
18
|
+
} from "./chunk-GDLPBPG3.js";
|
|
19
|
+
import {
|
|
20
|
+
errAppRequired,
|
|
21
|
+
errInvalidArgs,
|
|
22
|
+
errLoginRequired
|
|
23
|
+
} from "./chunk-OVLQH6KL.js";
|
|
24
|
+
|
|
25
|
+
// src/lib/policy-prompt.ts
|
|
26
|
+
var CREATE_NEW_SENTINEL = "__create_new__";
|
|
27
|
+
var DEFAULT_SPONSORSHIP_EXPIRY_MS = "600000";
|
|
28
|
+
var DEFAULT_SOLANA_MAX_COUNT = "1000";
|
|
29
|
+
function flavorNoun(flavor) {
|
|
30
|
+
return flavor === "solana" ? "fee sponsorship" : "gas sponsorship";
|
|
31
|
+
}
|
|
32
|
+
async function selectOrCreatePolicy(opts) {
|
|
33
|
+
const cfg = load();
|
|
34
|
+
const appId = cfg.app?.id;
|
|
35
|
+
if (!appId) throw errAppRequired();
|
|
36
|
+
const client = opts.client ?? gasManagerClientFromFlags(opts.program);
|
|
37
|
+
const flavorLabel = flavorNoun(opts.flavor);
|
|
38
|
+
const policies = await withSpinner(
|
|
39
|
+
"Fetching gas policies\u2026",
|
|
40
|
+
"Policies fetched",
|
|
41
|
+
() => client.listAllPolicies({ appId })
|
|
42
|
+
);
|
|
43
|
+
const matching = filterPolicies(policies, opts.flavor, opts.network);
|
|
44
|
+
let selectedId = null;
|
|
45
|
+
if (matching.length === 0) {
|
|
46
|
+
console.log(
|
|
47
|
+
` ${dim(`No ${flavorLabel} policies found for ${opts.network} on app ${appId}. Let's create one.`)}`
|
|
48
|
+
);
|
|
49
|
+
const created = await runCreate(opts, client, appId);
|
|
50
|
+
if (!created) return null;
|
|
51
|
+
selectedId = created.policyId;
|
|
52
|
+
} else {
|
|
53
|
+
const selected = await promptAutocomplete({
|
|
54
|
+
message: `Select a ${flavorLabel} policy for ${opts.network}`,
|
|
55
|
+
placeholder: "Policy name or ID",
|
|
56
|
+
options: [
|
|
57
|
+
...matching.map((p) => ({
|
|
58
|
+
label: formatPolicyOption(p),
|
|
59
|
+
value: p.policyId
|
|
60
|
+
})),
|
|
61
|
+
{ label: "Create a new policy", value: CREATE_NEW_SENTINEL }
|
|
62
|
+
],
|
|
63
|
+
cancelMessage: "Cancelled policy selection.",
|
|
64
|
+
commitLabel: null
|
|
65
|
+
});
|
|
66
|
+
if (selected === null) return null;
|
|
67
|
+
if (selected === CREATE_NEW_SENTINEL) {
|
|
68
|
+
const created = await runCreate(opts, client, appId);
|
|
69
|
+
if (!created) return null;
|
|
70
|
+
selectedId = created.policyId;
|
|
71
|
+
} else {
|
|
72
|
+
selectedId = selected;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!opts.skipPersistPrompt && selectedId) {
|
|
76
|
+
await maybePersist(opts.flavor, selectedId);
|
|
77
|
+
}
|
|
78
|
+
return selectedId;
|
|
79
|
+
}
|
|
80
|
+
async function createPolicyInteractive(opts) {
|
|
81
|
+
const cfg = load();
|
|
82
|
+
const appId = cfg.app?.id;
|
|
83
|
+
if (!appId) throw errAppRequired();
|
|
84
|
+
const client = opts.client ?? gasManagerClientFromFlags(opts.program);
|
|
85
|
+
const result = await runCreate(opts, client, appId);
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function filterPolicies(policies, flavor, network) {
|
|
89
|
+
const adminId = toAdminNetworkId(network);
|
|
90
|
+
return policies.filter((p) => {
|
|
91
|
+
if (p.policyType !== flavor) return false;
|
|
92
|
+
if (!Array.isArray(p.networks)) return false;
|
|
93
|
+
return p.networks.includes(adminId);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function formatPolicyOption(p) {
|
|
97
|
+
const status = p.status === "active" ? green("active") : dim(p.status);
|
|
98
|
+
return `${p.policyName} (${p.policyId.slice(0, 10)}\u2026) \u2014 ${status}`;
|
|
99
|
+
}
|
|
100
|
+
async function runCreate(opts, client, appId) {
|
|
101
|
+
const name = await promptText({
|
|
102
|
+
message: "Policy name",
|
|
103
|
+
cancelMessage: "Cancelled policy creation."
|
|
104
|
+
});
|
|
105
|
+
if (name === null) return null;
|
|
106
|
+
if (!name.trim()) {
|
|
107
|
+
console.log(` ${dim("Skipped policy creation (no name).")}`);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const adminNetworkId = toAdminNetworkId(opts.network);
|
|
111
|
+
const nowUnix = String(Math.floor(Date.now() / 1e3));
|
|
112
|
+
const payload = opts.flavor === "sponsorship" ? {
|
|
113
|
+
policyName: name.trim(),
|
|
114
|
+
policyType: "sponsorship",
|
|
115
|
+
appId,
|
|
116
|
+
networks: [adminNetworkId],
|
|
117
|
+
rules: {
|
|
118
|
+
startTimeUnix: nowUnix,
|
|
119
|
+
sponsorshipExpiryMs: DEFAULT_SPONSORSHIP_EXPIRY_MS
|
|
120
|
+
}
|
|
121
|
+
} : {
|
|
122
|
+
policyName: name.trim(),
|
|
123
|
+
policyType: "solana",
|
|
124
|
+
appId,
|
|
125
|
+
networks: [adminNetworkId],
|
|
126
|
+
solanaRules: {
|
|
127
|
+
startTimeUnix: nowUnix,
|
|
128
|
+
maxCount: DEFAULT_SOLANA_MAX_COUNT
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const created = await withSpinner(
|
|
132
|
+
"Creating policy\u2026",
|
|
133
|
+
"Policy created",
|
|
134
|
+
() => client.createPolicy(payload)
|
|
135
|
+
);
|
|
136
|
+
console.log(` ${green("\u2713")} Created ${created.policyName} (${created.policyId})`);
|
|
137
|
+
const activate = await promptConfirm({
|
|
138
|
+
message: "Activate this policy now? (required before use)",
|
|
139
|
+
initialValue: true,
|
|
140
|
+
cancelMessage: "Skipped activation."
|
|
141
|
+
});
|
|
142
|
+
let activated = false;
|
|
143
|
+
if (activate === true) {
|
|
144
|
+
await withSpinner(
|
|
145
|
+
"Activating policy\u2026",
|
|
146
|
+
"Policy active",
|
|
147
|
+
() => client.setPolicyStatus(created.policyId, "active")
|
|
148
|
+
);
|
|
149
|
+
activated = true;
|
|
150
|
+
} else {
|
|
151
|
+
console.log(
|
|
152
|
+
` ${dim("Policy left inactive. Activate later with `alchemy gas-manager policy activate " + created.policyId + "`.")}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return { policyId: created.policyId, status: "created", activated };
|
|
156
|
+
}
|
|
157
|
+
async function maybePersist(flavor, policyId) {
|
|
158
|
+
const confirmed = await promptConfirm({
|
|
159
|
+
message: "Save as default policy for this CLI?",
|
|
160
|
+
initialValue: true,
|
|
161
|
+
cancelMessage: "Skipped saving default."
|
|
162
|
+
});
|
|
163
|
+
if (confirmed !== true) return;
|
|
164
|
+
const cfg = load();
|
|
165
|
+
const updated = flavor === "sponsorship" ? { ...cfg, evm_gas_policy_id: policyId } : { ...cfg, solana_fee_policy_id: policyId };
|
|
166
|
+
save(updated);
|
|
167
|
+
const key = flavor === "sponsorship" ? "evm-gas-policy-id" : "solana-fee-policy-id";
|
|
168
|
+
console.log(` ${green("\u2713")} Saved ${key} = ${policyId}`);
|
|
169
|
+
}
|
|
170
|
+
function errSponsorshipNeedsPolicy(flavor) {
|
|
171
|
+
const flag = flavor === "sponsorship" ? "--gas-policy-id" : "--fee-policy-id";
|
|
172
|
+
const label = flavor === "sponsorship" ? "Gas sponsorship" : "Fee sponsorship";
|
|
173
|
+
return errInvalidArgs(
|
|
174
|
+
`${label} requires a policy ID. Run 'alchemy gas-manager policy list' to pick one, pass ${flag} <id>, or run interactively to be prompted.`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
function errNotLoggedInForPolicyLookup() {
|
|
178
|
+
return errLoginRequired();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export {
|
|
182
|
+
selectOrCreatePolicy,
|
|
183
|
+
createPolicyInteractive,
|
|
184
|
+
errSponsorshipNeedsPolicy,
|
|
185
|
+
errNotLoggedInForPolicyLookup
|
|
186
|
+
};
|