@elizaos/autonomous 2.0.0-alpha.10

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.
Files changed (241) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +270 -0
  3. package/src/actions/emote.ts +101 -0
  4. package/src/actions/restart.ts +101 -0
  5. package/src/actions/send-message.ts +168 -0
  6. package/src/actions/stream-control.ts +439 -0
  7. package/src/actions/switch-stream-source.ts +126 -0
  8. package/src/actions/terminal.ts +186 -0
  9. package/src/api/agent-admin-routes.ts +178 -0
  10. package/src/api/agent-lifecycle-routes.ts +129 -0
  11. package/src/api/agent-model.ts +143 -0
  12. package/src/api/agent-transfer-routes.ts +211 -0
  13. package/src/api/apps-routes.ts +210 -0
  14. package/src/api/auth-routes.ts +90 -0
  15. package/src/api/bsc-trade.ts +736 -0
  16. package/src/api/bug-report-routes.ts +161 -0
  17. package/src/api/character-routes.ts +421 -0
  18. package/src/api/cloud-billing-routes.ts +598 -0
  19. package/src/api/cloud-compat-routes.ts +192 -0
  20. package/src/api/cloud-routes.ts +529 -0
  21. package/src/api/cloud-status-routes.ts +234 -0
  22. package/src/api/compat-utils.ts +154 -0
  23. package/src/api/connector-health.ts +135 -0
  24. package/src/api/coordinator-wiring.ts +179 -0
  25. package/src/api/credit-detection.ts +47 -0
  26. package/src/api/database.ts +1357 -0
  27. package/src/api/diagnostics-routes.ts +389 -0
  28. package/src/api/drop-service.ts +205 -0
  29. package/src/api/early-logs.ts +111 -0
  30. package/src/api/http-helpers.ts +252 -0
  31. package/src/api/index.ts +85 -0
  32. package/src/api/knowledge-routes.ts +1189 -0
  33. package/src/api/knowledge-service-loader.ts +92 -0
  34. package/src/api/memory-bounds.ts +121 -0
  35. package/src/api/memory-routes.ts +349 -0
  36. package/src/api/merkle-tree.ts +239 -0
  37. package/src/api/models-routes.ts +72 -0
  38. package/src/api/nfa-routes.ts +169 -0
  39. package/src/api/nft-verify.ts +188 -0
  40. package/src/api/og-tracker.ts +72 -0
  41. package/src/api/parse-action-block.ts +145 -0
  42. package/src/api/permissions-routes.ts +222 -0
  43. package/src/api/plugin-validation.ts +355 -0
  44. package/src/api/provider-switch-config.ts +455 -0
  45. package/src/api/registry-routes.ts +165 -0
  46. package/src/api/registry-service.ts +292 -0
  47. package/src/api/route-helpers.ts +21 -0
  48. package/src/api/sandbox-routes.ts +1480 -0
  49. package/src/api/server.ts +17674 -0
  50. package/src/api/signal-routes.ts +265 -0
  51. package/src/api/stream-persistence.ts +297 -0
  52. package/src/api/stream-route-state.ts +48 -0
  53. package/src/api/stream-routes.ts +1046 -0
  54. package/src/api/stream-voice-routes.ts +208 -0
  55. package/src/api/streaming-text.ts +129 -0
  56. package/src/api/streaming-types.ts +23 -0
  57. package/src/api/subscription-routes.ts +283 -0
  58. package/src/api/terminal-run-limits.ts +31 -0
  59. package/src/api/training-backend-check.ts +40 -0
  60. package/src/api/training-routes.ts +314 -0
  61. package/src/api/training-service-like.ts +46 -0
  62. package/src/api/trajectory-routes.ts +714 -0
  63. package/src/api/trigger-routes.ts +438 -0
  64. package/src/api/twitter-verify.ts +226 -0
  65. package/src/api/tx-service.ts +193 -0
  66. package/src/api/wallet-dex-prices.ts +206 -0
  67. package/src/api/wallet-evm-balance.ts +989 -0
  68. package/src/api/wallet-routes.ts +505 -0
  69. package/src/api/wallet-rpc.ts +523 -0
  70. package/src/api/wallet-trading-profile.ts +694 -0
  71. package/src/api/wallet.ts +745 -0
  72. package/src/api/whatsapp-routes.ts +282 -0
  73. package/src/api/zip-utils.ts +130 -0
  74. package/src/auth/anthropic.ts +63 -0
  75. package/src/auth/apply-stealth.ts +38 -0
  76. package/src/auth/claude-code-stealth.ts +141 -0
  77. package/src/auth/credentials.ts +226 -0
  78. package/src/auth/index.ts +18 -0
  79. package/src/auth/openai-codex.ts +94 -0
  80. package/src/auth/types.ts +24 -0
  81. package/src/awareness/registry.ts +220 -0
  82. package/src/bin.ts +10 -0
  83. package/src/cli/index.ts +36 -0
  84. package/src/cli/parse-duration.ts +43 -0
  85. package/src/cloud/auth.test.ts +370 -0
  86. package/src/cloud/auth.ts +176 -0
  87. package/src/cloud/backup.test.ts +150 -0
  88. package/src/cloud/backup.ts +50 -0
  89. package/src/cloud/base-url.ts +45 -0
  90. package/src/cloud/bridge-client.test.ts +481 -0
  91. package/src/cloud/bridge-client.ts +307 -0
  92. package/src/cloud/cloud-manager.test.ts +223 -0
  93. package/src/cloud/cloud-manager.ts +151 -0
  94. package/src/cloud/cloud-proxy.test.ts +122 -0
  95. package/src/cloud/cloud-proxy.ts +52 -0
  96. package/src/cloud/index.ts +23 -0
  97. package/src/cloud/reconnect.test.ts +178 -0
  98. package/src/cloud/reconnect.ts +108 -0
  99. package/src/cloud/validate-url.test.ts +147 -0
  100. package/src/cloud/validate-url.ts +176 -0
  101. package/src/config/character-schema.ts +44 -0
  102. package/src/config/config.ts +149 -0
  103. package/src/config/env-vars.ts +86 -0
  104. package/src/config/includes.ts +196 -0
  105. package/src/config/index.ts +15 -0
  106. package/src/config/object-utils.ts +10 -0
  107. package/src/config/paths.ts +92 -0
  108. package/src/config/plugin-auto-enable.ts +520 -0
  109. package/src/config/schema.ts +1342 -0
  110. package/src/config/telegram-custom-commands.ts +99 -0
  111. package/src/config/types.agent-defaults.ts +342 -0
  112. package/src/config/types.agents.ts +112 -0
  113. package/src/config/types.gateway.ts +243 -0
  114. package/src/config/types.hooks.ts +124 -0
  115. package/src/config/types.messages.ts +201 -0
  116. package/src/config/types.milady.ts +791 -0
  117. package/src/config/types.tools.ts +416 -0
  118. package/src/config/types.ts +7 -0
  119. package/src/config/zod-schema.agent-runtime.ts +777 -0
  120. package/src/config/zod-schema.core.ts +778 -0
  121. package/src/config/zod-schema.hooks.ts +139 -0
  122. package/src/config/zod-schema.providers-core.ts +1126 -0
  123. package/src/config/zod-schema.session.ts +98 -0
  124. package/src/config/zod-schema.ts +865 -0
  125. package/src/contracts/apps.ts +46 -0
  126. package/src/contracts/awareness.ts +56 -0
  127. package/src/contracts/config.ts +172 -0
  128. package/src/contracts/drop.ts +21 -0
  129. package/src/contracts/index.ts +8 -0
  130. package/src/contracts/onboarding.ts +592 -0
  131. package/src/contracts/permissions.ts +52 -0
  132. package/src/contracts/verification.ts +9 -0
  133. package/src/contracts/wallet.ts +503 -0
  134. package/src/diagnostics/integration-observability.ts +132 -0
  135. package/src/emotes/catalog.ts +655 -0
  136. package/src/external-modules.d.ts +7 -0
  137. package/src/hooks/discovery.test.ts +357 -0
  138. package/src/hooks/discovery.ts +231 -0
  139. package/src/hooks/eligibility.ts +146 -0
  140. package/src/hooks/hooks.test.ts +320 -0
  141. package/src/hooks/index.ts +8 -0
  142. package/src/hooks/loader.test.ts +418 -0
  143. package/src/hooks/loader.ts +256 -0
  144. package/src/hooks/registry.test.ts +168 -0
  145. package/src/hooks/registry.ts +74 -0
  146. package/src/hooks/types.ts +121 -0
  147. package/src/index.ts +19 -0
  148. package/src/onboarding-presets.ts +828 -0
  149. package/src/plugins/custom-rtmp/index.ts +40 -0
  150. package/src/providers/admin-trust.ts +76 -0
  151. package/src/providers/session-bridge.ts +143 -0
  152. package/src/providers/session-utils.ts +42 -0
  153. package/src/providers/simple-mode.ts +113 -0
  154. package/src/providers/ui-catalog.ts +135 -0
  155. package/src/providers/workspace-provider.ts +213 -0
  156. package/src/providers/workspace.ts +497 -0
  157. package/src/runtime/agent-event-service.ts +57 -0
  158. package/src/runtime/cloud-onboarding.test.ts +489 -0
  159. package/src/runtime/cloud-onboarding.ts +408 -0
  160. package/src/runtime/core-plugins.ts +53 -0
  161. package/src/runtime/custom-actions.ts +605 -0
  162. package/src/runtime/eliza.ts +4941 -0
  163. package/src/runtime/embedding-presets.ts +73 -0
  164. package/src/runtime/index.ts +8 -0
  165. package/src/runtime/milady-plugin.ts +180 -0
  166. package/src/runtime/onboarding-names.ts +76 -0
  167. package/src/runtime/release-plugin-policy.ts +119 -0
  168. package/src/runtime/restart.ts +59 -0
  169. package/src/runtime/trajectory-persistence.ts +2584 -0
  170. package/src/runtime/version.ts +6 -0
  171. package/src/security/audit-log.ts +222 -0
  172. package/src/security/network-policy.ts +91 -0
  173. package/src/server/index.ts +6 -0
  174. package/src/services/agent-export.ts +976 -0
  175. package/src/services/app-manager.ts +755 -0
  176. package/src/services/browser-capture.ts +215 -0
  177. package/src/services/coding-agent-context.ts +355 -0
  178. package/src/services/fallback-training-service.ts +196 -0
  179. package/src/services/index.ts +17 -0
  180. package/src/services/mcp-marketplace.ts +327 -0
  181. package/src/services/plugin-manager-types.ts +185 -0
  182. package/src/services/privy-wallets.ts +352 -0
  183. package/src/services/registry-client-app-meta.ts +201 -0
  184. package/src/services/registry-client-endpoints.ts +253 -0
  185. package/src/services/registry-client-local.ts +485 -0
  186. package/src/services/registry-client-network.ts +173 -0
  187. package/src/services/registry-client-queries.ts +176 -0
  188. package/src/services/registry-client-types.ts +104 -0
  189. package/src/services/registry-client.ts +366 -0
  190. package/src/services/remote-signing-service.ts +261 -0
  191. package/src/services/sandbox-engine.ts +753 -0
  192. package/src/services/sandbox-manager.ts +503 -0
  193. package/src/services/self-updater.ts +213 -0
  194. package/src/services/signal-pairing.ts +189 -0
  195. package/src/services/signing-policy.ts +230 -0
  196. package/src/services/skill-catalog-client.ts +195 -0
  197. package/src/services/skill-marketplace.ts +909 -0
  198. package/src/services/stream-manager.ts +707 -0
  199. package/src/services/tts-stream-bridge.ts +465 -0
  200. package/src/services/update-checker.ts +163 -0
  201. package/src/services/version-compat.ts +367 -0
  202. package/src/services/whatsapp-pairing.ts +279 -0
  203. package/src/shared/ui-catalog-prompt.ts +1158 -0
  204. package/src/test-support/process-helpers.ts +35 -0
  205. package/src/test-support/route-test-helpers.ts +113 -0
  206. package/src/test-support/test-helpers.ts +304 -0
  207. package/src/testing/index.ts +3 -0
  208. package/src/triggers/action.ts +342 -0
  209. package/src/triggers/runtime.ts +432 -0
  210. package/src/triggers/scheduling.ts +472 -0
  211. package/src/triggers/types.ts +133 -0
  212. package/src/types/app-hyperscape-routes-shim.d.ts +29 -0
  213. package/src/types/external-modules.d.ts +7 -0
  214. package/src/utils/exec-safety.ts +23 -0
  215. package/src/utils/number-parsing.ts +112 -0
  216. package/src/utils/spoken-text.ts +65 -0
  217. package/src/version-resolver.ts +60 -0
  218. package/test/api/agent-admin-routes.test.ts +160 -0
  219. package/test/api/agent-lifecycle-routes.test.ts +164 -0
  220. package/test/api/agent-transfer-routes.test.ts +136 -0
  221. package/test/api/apps-routes.test.ts +140 -0
  222. package/test/api/auth-routes.test.ts +160 -0
  223. package/test/api/bug-report-routes.test.ts +88 -0
  224. package/test/api/knowledge-routes.test.ts +73 -0
  225. package/test/api/lifecycle.test.ts +342 -0
  226. package/test/api/memory-routes.test.ts +74 -0
  227. package/test/api/models-routes.test.ts +112 -0
  228. package/test/api/nfa-routes.test.ts +78 -0
  229. package/test/api/permissions-routes.test.ts +185 -0
  230. package/test/api/registry-routes.test.ts +157 -0
  231. package/test/api/signal-routes.test.ts +113 -0
  232. package/test/api/subscription-routes.test.ts +90 -0
  233. package/test/api/trigger-routes.test.ts +87 -0
  234. package/test/api/wallet-routes.observability.test.ts +191 -0
  235. package/test/api/wallet-routes.test.ts +502 -0
  236. package/test/diagnostics/integration-observability.test.ts +135 -0
  237. package/test/security/audit-log.test.ts +229 -0
  238. package/test/security/network-policy.test.ts +143 -0
  239. package/test/services/version-compat.test.ts +127 -0
  240. package/tsconfig.build.json +21 -0
  241. package/tsconfig.json +19 -0
@@ -0,0 +1,505 @@
1
+ import type http from "node:http";
2
+ import { logger } from "@elizaos/core";
3
+ import type { MiladyConfig } from "../config/config";
4
+ import {
5
+ normalizeWalletRpcSelections,
6
+ type WalletConfigUpdateRequest,
7
+ type WalletRpcSelections,
8
+ } from "../contracts/wallet";
9
+ import { createIntegrationTelemetrySpan } from "../diagnostics/integration-observability";
10
+ import type { RouteHelpers, RouteRequestMeta } from "./route-helpers";
11
+ import {
12
+ fetchEvmBalances,
13
+ fetchEvmNfts,
14
+ fetchSolanaBalances,
15
+ fetchSolanaNativeBalanceViaRpc,
16
+ fetchSolanaNfts,
17
+ generateWalletForChain,
18
+ getWalletAddresses,
19
+ importWallet,
20
+ validatePrivateKey,
21
+ type WalletBalancesResponse,
22
+ type WalletChain,
23
+ type WalletConfigStatus,
24
+ type WalletNftsResponse,
25
+ } from "./wallet";
26
+ import {
27
+ applyWalletRpcConfigUpdate,
28
+ getStoredWalletRpcSelections,
29
+ resolveWalletRpcReadiness,
30
+ } from "./wallet-rpc";
31
+
32
+ interface WalletExportRequestBody {
33
+ confirm?: boolean;
34
+ exportToken?: string;
35
+ }
36
+
37
+ interface WalletExportRejectionLike {
38
+ status: 401 | 403;
39
+ reason: string;
40
+ }
41
+
42
+ const WALLET_CONFIG_COMPAT_KEYS = new Set([
43
+ "ALCHEMY_API_KEY",
44
+ "INFURA_API_KEY",
45
+ "ANKR_API_KEY",
46
+ "ETHEREUM_RPC_URL",
47
+ "BASE_RPC_URL",
48
+ "AVALANCHE_RPC_URL",
49
+ "HELIUS_API_KEY",
50
+ "BIRDEYE_API_KEY",
51
+ "NODEREAL_BSC_RPC_URL",
52
+ "QUICKNODE_BSC_RPC_URL",
53
+ "BSC_RPC_URL",
54
+ "SOLANA_RPC_URL",
55
+ ]);
56
+
57
+ function resolveWalletConfigUpdateRequest(
58
+ body: unknown,
59
+ currentSelections: WalletRpcSelections,
60
+ ): WalletConfigUpdateRequest | null {
61
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
62
+ return null;
63
+ }
64
+
65
+ const record = body as Record<string, unknown>;
66
+ if (
67
+ record.selections &&
68
+ typeof record.selections === "object" &&
69
+ !Array.isArray(record.selections)
70
+ ) {
71
+ const credentials =
72
+ record.credentials &&
73
+ typeof record.credentials === "object" &&
74
+ !Array.isArray(record.credentials)
75
+ ? Object.fromEntries(
76
+ Object.entries(
77
+ record.credentials as Record<string, unknown>,
78
+ ).filter(([, value]) => typeof value === "string"),
79
+ )
80
+ : undefined;
81
+
82
+ return {
83
+ selections: normalizeWalletRpcSelections(
84
+ record.selections as Partial<Record<keyof WalletRpcSelections, string>>,
85
+ ),
86
+ credentials: credentials as WalletConfigUpdateRequest["credentials"],
87
+ };
88
+ }
89
+
90
+ const compatCredentials = Object.fromEntries(
91
+ Object.entries(record).filter(
92
+ ([key, value]) =>
93
+ WALLET_CONFIG_COMPAT_KEYS.has(key) && typeof value === "string",
94
+ ),
95
+ );
96
+
97
+ if (Object.keys(compatCredentials).length === 0) {
98
+ return null;
99
+ }
100
+
101
+ return {
102
+ selections: currentSelections,
103
+ credentials: compatCredentials as WalletConfigUpdateRequest["credentials"],
104
+ };
105
+ }
106
+
107
+ export interface WalletRouteDependencies {
108
+ getWalletAddresses: typeof getWalletAddresses;
109
+ fetchEvmBalances: typeof fetchEvmBalances;
110
+ fetchSolanaBalances: typeof fetchSolanaBalances;
111
+ fetchSolanaNativeBalanceViaRpc: typeof fetchSolanaNativeBalanceViaRpc;
112
+ fetchEvmNfts: typeof fetchEvmNfts;
113
+ fetchSolanaNfts: typeof fetchSolanaNfts;
114
+ validatePrivateKey: typeof validatePrivateKey;
115
+ importWallet: typeof importWallet;
116
+ generateWalletForChain: typeof generateWalletForChain;
117
+ }
118
+
119
+ export const DEFAULT_WALLET_ROUTE_DEPENDENCIES: WalletRouteDependencies = {
120
+ getWalletAddresses,
121
+ fetchEvmBalances,
122
+ fetchSolanaBalances,
123
+ fetchSolanaNativeBalanceViaRpc,
124
+ fetchEvmNfts,
125
+ fetchSolanaNfts,
126
+ validatePrivateKey,
127
+ importWallet,
128
+ generateWalletForChain,
129
+ };
130
+
131
+ export interface WalletRouteContext
132
+ extends RouteRequestMeta,
133
+ Pick<RouteHelpers, "readJsonBody" | "json" | "error"> {
134
+ config: MiladyConfig;
135
+ saveConfig: (config: MiladyConfig) => void;
136
+ ensureWalletKeysInEnvAndConfig: (config: MiladyConfig) => boolean;
137
+ resolveWalletExportRejection: (
138
+ req: http.IncomingMessage,
139
+ body: WalletExportRequestBody,
140
+ ) => WalletExportRejectionLike | null;
141
+ scheduleRuntimeRestart?: (reason: string) => void;
142
+ deps?: WalletRouteDependencies;
143
+ }
144
+
145
+ export async function handleWalletRoutes(
146
+ ctx: WalletRouteContext,
147
+ ): Promise<boolean> {
148
+ const {
149
+ req,
150
+ res,
151
+ method,
152
+ pathname,
153
+ config,
154
+ saveConfig,
155
+ ensureWalletKeysInEnvAndConfig,
156
+ resolveWalletExportRejection,
157
+ readJsonBody,
158
+ json,
159
+ error,
160
+ } = ctx;
161
+ const deps = ctx.deps ?? DEFAULT_WALLET_ROUTE_DEPENDENCIES;
162
+
163
+ // GET /api/wallet/addresses
164
+ if (method === "GET" && pathname === "/api/wallet/addresses") {
165
+ json(res, deps.getWalletAddresses());
166
+ return true;
167
+ }
168
+
169
+ // GET /api/wallet/balances
170
+ if (method === "GET" && pathname === "/api/wallet/balances") {
171
+ const addresses = deps.getWalletAddresses();
172
+ const rpcReadiness = resolveWalletRpcReadiness(config);
173
+ const alchemyKey = process.env.ALCHEMY_API_KEY?.trim() || null;
174
+ const ankrKey = process.env.ANKR_API_KEY?.trim() || null;
175
+ const heliusKey = process.env.HELIUS_API_KEY?.trim() || null;
176
+
177
+ const result: WalletBalancesResponse = { evm: null, solana: null };
178
+
179
+ if (addresses.evmAddress && rpcReadiness.evmBalanceReady) {
180
+ const evmBalancesSpan = createIntegrationTelemetrySpan({
181
+ boundary: "wallet",
182
+ operation: "fetch_evm_balances",
183
+ });
184
+ try {
185
+ const chains = await deps.fetchEvmBalances(addresses.evmAddress, {
186
+ alchemyKey,
187
+ ankrKey,
188
+ cloudManagedAccess: rpcReadiness.cloudManagedAccess,
189
+ bscRpcUrls: rpcReadiness.bscRpcUrls,
190
+ ethereumRpcUrls: rpcReadiness.ethereumRpcUrls,
191
+ baseRpcUrls: rpcReadiness.baseRpcUrls,
192
+ avaxRpcUrls: rpcReadiness.avalancheRpcUrls,
193
+ nodeRealBscRpcUrl: process.env.NODEREAL_BSC_RPC_URL,
194
+ quickNodeBscRpcUrl: process.env.QUICKNODE_BSC_RPC_URL,
195
+ bscRpcUrl: process.env.BSC_RPC_URL,
196
+ ethereumRpcUrl: process.env.ETHEREUM_RPC_URL,
197
+ baseRpcUrl: process.env.BASE_RPC_URL,
198
+ avaxRpcUrl: process.env.AVALANCHE_RPC_URL,
199
+ });
200
+ result.evm = { address: addresses.evmAddress, chains };
201
+ evmBalancesSpan.success();
202
+ } catch (err) {
203
+ evmBalancesSpan.failure({ error: err });
204
+ logger.warn(`[wallet] EVM balance fetch failed: ${err}`);
205
+ }
206
+ }
207
+
208
+ if (addresses.solanaAddress && rpcReadiness.solanaBalanceReady) {
209
+ const solanaBalancesSpan = createIntegrationTelemetrySpan({
210
+ boundary: "wallet",
211
+ operation: "fetch_solana_balances",
212
+ });
213
+ try {
214
+ const solanaData = heliusKey
215
+ ? await deps.fetchSolanaBalances(addresses.solanaAddress, heliusKey)
216
+ : await deps.fetchSolanaNativeBalanceViaRpc(
217
+ addresses.solanaAddress,
218
+ rpcReadiness.solanaRpcUrls,
219
+ );
220
+ result.solana = { address: addresses.solanaAddress, ...solanaData };
221
+ solanaBalancesSpan.success();
222
+ } catch (err) {
223
+ solanaBalancesSpan.failure({ error: err });
224
+ logger.warn(`[wallet] Solana balance fetch failed: ${err}`);
225
+ }
226
+ }
227
+
228
+ json(res, result);
229
+ return true;
230
+ }
231
+
232
+ // GET /api/wallet/nfts
233
+ if (method === "GET" && pathname === "/api/wallet/nfts") {
234
+ const addresses = deps.getWalletAddresses();
235
+ const rpcReadiness = resolveWalletRpcReadiness(config);
236
+ const alchemyKey = process.env.ALCHEMY_API_KEY?.trim() || null;
237
+ const ankrKey = process.env.ANKR_API_KEY?.trim() || null;
238
+ const heliusKey = process.env.HELIUS_API_KEY?.trim() || null;
239
+
240
+ const result: WalletNftsResponse = { evm: [], solana: null };
241
+
242
+ if (
243
+ addresses.evmAddress &&
244
+ (Boolean(alchemyKey) || rpcReadiness.managedBscRpcReady)
245
+ ) {
246
+ const evmNftsSpan = createIntegrationTelemetrySpan({
247
+ boundary: "wallet",
248
+ operation: "fetch_evm_nfts",
249
+ });
250
+ try {
251
+ result.evm = await deps.fetchEvmNfts(addresses.evmAddress, {
252
+ alchemyKey,
253
+ ankrKey,
254
+ cloudManagedAccess: rpcReadiness.cloudManagedAccess,
255
+ bscRpcUrls: rpcReadiness.bscRpcUrls,
256
+ ethereumRpcUrls: rpcReadiness.ethereumRpcUrls,
257
+ baseRpcUrls: rpcReadiness.baseRpcUrls,
258
+ avaxRpcUrls: rpcReadiness.avalancheRpcUrls,
259
+ nodeRealBscRpcUrl: process.env.NODEREAL_BSC_RPC_URL,
260
+ quickNodeBscRpcUrl: process.env.QUICKNODE_BSC_RPC_URL,
261
+ bscRpcUrl: process.env.BSC_RPC_URL,
262
+ ethereumRpcUrl: process.env.ETHEREUM_RPC_URL,
263
+ baseRpcUrl: process.env.BASE_RPC_URL,
264
+ avaxRpcUrl: process.env.AVALANCHE_RPC_URL,
265
+ });
266
+ evmNftsSpan.success();
267
+ } catch (err) {
268
+ evmNftsSpan.failure({ error: err });
269
+ logger.warn(`[wallet] EVM NFT fetch failed: ${err}`);
270
+ }
271
+ }
272
+
273
+ if (addresses.solanaAddress && heliusKey) {
274
+ const solanaNftsSpan = createIntegrationTelemetrySpan({
275
+ boundary: "wallet",
276
+ operation: "fetch_solana_nfts",
277
+ });
278
+ try {
279
+ const nfts = await deps.fetchSolanaNfts(
280
+ addresses.solanaAddress,
281
+ heliusKey,
282
+ );
283
+ result.solana = { nfts };
284
+ solanaNftsSpan.success();
285
+ } catch (err) {
286
+ solanaNftsSpan.failure({ error: err });
287
+ logger.warn(`[wallet] Solana NFT fetch failed: ${err}`);
288
+ }
289
+ }
290
+
291
+ json(res, result);
292
+ return true;
293
+ }
294
+
295
+ // POST /api/wallet/import
296
+ if (method === "POST" && pathname === "/api/wallet/import") {
297
+ const body = await readJsonBody<{ chain?: string; privateKey?: string }>(
298
+ req,
299
+ res,
300
+ );
301
+ if (!body) return true;
302
+
303
+ if (!body.privateKey?.trim()) {
304
+ error(res, "privateKey is required");
305
+ return true;
306
+ }
307
+
308
+ let chain: WalletChain;
309
+ if (body.chain === "evm" || body.chain === "solana") {
310
+ chain = body.chain;
311
+ } else if (body.chain) {
312
+ error(
313
+ res,
314
+ `Unsupported chain: ${body.chain}. Must be "evm" or "solana".`,
315
+ );
316
+ return true;
317
+ } else {
318
+ const detection = deps.validatePrivateKey(body.privateKey.trim());
319
+ chain = detection.chain;
320
+ }
321
+
322
+ const result = deps.importWallet(chain, body.privateKey.trim());
323
+
324
+ if (!result.success) {
325
+ error(res, result.error ?? "Import failed", 422);
326
+ return true;
327
+ }
328
+
329
+ if (!config.env) config.env = {};
330
+ const envKey = chain === "evm" ? "EVM_PRIVATE_KEY" : "SOLANA_PRIVATE_KEY";
331
+ (config.env as Record<string, string>)[envKey] = process.env[envKey] ?? "";
332
+
333
+ try {
334
+ saveConfig(config);
335
+ } catch (err) {
336
+ logger.warn(
337
+ `[api] Config save failed: ${err instanceof Error ? err.message : err}`,
338
+ );
339
+ }
340
+
341
+ json(res, {
342
+ ok: true,
343
+ chain,
344
+ address: result.address,
345
+ });
346
+ return true;
347
+ }
348
+
349
+ // POST /api/wallet/generate
350
+ if (method === "POST" && pathname === "/api/wallet/generate") {
351
+ const body = await readJsonBody<{ chain?: string }>(req, res);
352
+ if (!body) return true;
353
+
354
+ const chain = body.chain as string | undefined;
355
+ const validChains: Array<WalletChain | "both"> = ["evm", "solana", "both"];
356
+
357
+ if (chain && !validChains.includes(chain as WalletChain | "both")) {
358
+ error(
359
+ res,
360
+ `Unsupported chain: ${chain}. Must be "evm", "solana", or "both".`,
361
+ );
362
+ return true;
363
+ }
364
+
365
+ const targetChain = (chain ?? "both") as WalletChain | "both";
366
+
367
+ if (!config.env) config.env = {};
368
+
369
+ const generated: Array<{ chain: WalletChain; address: string }> = [];
370
+
371
+ if (targetChain === "both" || targetChain === "evm") {
372
+ const result = deps.generateWalletForChain("evm");
373
+ process.env.EVM_PRIVATE_KEY = result.privateKey;
374
+ (config.env as Record<string, string>).EVM_PRIVATE_KEY =
375
+ result.privateKey;
376
+ generated.push({ chain: "evm", address: result.address });
377
+ logger.info(`[milady-api] Generated EVM wallet: ${result.address}`);
378
+ }
379
+
380
+ if (targetChain === "both" || targetChain === "solana") {
381
+ const result = deps.generateWalletForChain("solana");
382
+ process.env.SOLANA_PRIVATE_KEY = result.privateKey;
383
+ (config.env as Record<string, string>).SOLANA_PRIVATE_KEY =
384
+ result.privateKey;
385
+ generated.push({ chain: "solana", address: result.address });
386
+ logger.info(`[milady-api] Generated Solana wallet: ${result.address}`);
387
+ }
388
+
389
+ try {
390
+ saveConfig(config);
391
+ } catch (err) {
392
+ logger.warn(
393
+ `[api] Config save failed: ${err instanceof Error ? err.message : err}`,
394
+ );
395
+ }
396
+
397
+ json(res, { ok: true, wallets: generated });
398
+ return true;
399
+ }
400
+
401
+ // GET /api/wallet/config
402
+ if (method === "GET" && pathname === "/api/wallet/config") {
403
+ const addresses = deps.getWalletAddresses();
404
+ const rpcReadiness = resolveWalletRpcReadiness(config);
405
+ const alchemyKeySet = Boolean(process.env.ALCHEMY_API_KEY?.trim());
406
+ const ankrKeySet = Boolean(process.env.ANKR_API_KEY?.trim());
407
+ const nodeRealSet = Boolean(process.env.NODEREAL_BSC_RPC_URL?.trim());
408
+ const quickNodeSet = Boolean(process.env.QUICKNODE_BSC_RPC_URL?.trim());
409
+ const configStatus: WalletConfigStatus = {
410
+ selectedRpcProviders: rpcReadiness.selectedRpcProviders,
411
+ legacyCustomChains: rpcReadiness.legacyCustomChains,
412
+ alchemyKeySet,
413
+ infuraKeySet: Boolean(process.env.INFURA_API_KEY?.trim()),
414
+ ankrKeySet,
415
+ nodeRealBscRpcSet: nodeRealSet,
416
+ quickNodeBscRpcSet: quickNodeSet,
417
+ managedBscRpcReady: rpcReadiness.managedBscRpcReady,
418
+ cloudManagedAccess: rpcReadiness.cloudManagedAccess,
419
+ evmBalanceReady: rpcReadiness.evmBalanceReady,
420
+ ethereumBalanceReady:
421
+ alchemyKeySet || rpcReadiness.ethereumRpcUrls.length > 0,
422
+ baseBalanceReady: alchemyKeySet || rpcReadiness.baseRpcUrls.length > 0,
423
+ bscBalanceReady: ankrKeySet || rpcReadiness.bscRpcUrls.length > 0,
424
+ avalancheBalanceReady:
425
+ alchemyKeySet || rpcReadiness.avalancheRpcUrls.length > 0,
426
+ solanaBalanceReady: rpcReadiness.solanaBalanceReady,
427
+ heliusKeySet: Boolean(process.env.HELIUS_API_KEY?.trim()),
428
+ birdeyeKeySet: Boolean(process.env.BIRDEYE_API_KEY?.trim()),
429
+ evmChains: [
430
+ "Ethereum",
431
+ "Base",
432
+ "Arbitrum",
433
+ "Optimism",
434
+ "Polygon",
435
+ "BSC",
436
+ "Avalanche",
437
+ ],
438
+ evmAddress: addresses.evmAddress,
439
+ solanaAddress: addresses.solanaAddress,
440
+ };
441
+ json(res, configStatus);
442
+ return true;
443
+ }
444
+
445
+ // PUT /api/wallet/config
446
+ if (method === "PUT" && pathname === "/api/wallet/config") {
447
+ const body = await readJsonBody<Record<string, unknown>>(req, res);
448
+ if (!body) return true;
449
+
450
+ const updateRequest = resolveWalletConfigUpdateRequest(
451
+ body,
452
+ getStoredWalletRpcSelections(config),
453
+ );
454
+ if (!updateRequest) {
455
+ error(res, "Invalid wallet config update");
456
+ return true;
457
+ }
458
+
459
+ applyWalletRpcConfigUpdate(config, updateRequest);
460
+
461
+ ensureWalletKeysInEnvAndConfig(config);
462
+
463
+ try {
464
+ saveConfig(config);
465
+ } catch (err) {
466
+ logger.warn(
467
+ `[api] Config save failed: ${err instanceof Error ? err.message : err}`,
468
+ );
469
+ }
470
+
471
+ json(res, { ok: true });
472
+ ctx.scheduleRuntimeRestart?.("Wallet configuration updated");
473
+ return true;
474
+ }
475
+
476
+ // POST /api/wallet/export
477
+ if (method === "POST" && pathname === "/api/wallet/export") {
478
+ const body = await readJsonBody<WalletExportRequestBody>(req, res);
479
+ if (!body) return true;
480
+
481
+ const rejection = resolveWalletExportRejection(req, body);
482
+ if (rejection) {
483
+ error(res, rejection.reason, rejection.status);
484
+ return true;
485
+ }
486
+
487
+ const evmKey = process.env.EVM_PRIVATE_KEY ?? null;
488
+ const solanaKey = process.env.SOLANA_PRIVATE_KEY ?? null;
489
+ const addresses = deps.getWalletAddresses();
490
+
491
+ logger.warn("[wallet] Private keys exported via API");
492
+
493
+ json(res, {
494
+ evm: evmKey
495
+ ? { privateKey: evmKey, address: addresses.evmAddress }
496
+ : null,
497
+ solana: solanaKey
498
+ ? { privateKey: solanaKey, address: addresses.solanaAddress }
499
+ : null,
500
+ });
501
+ return true;
502
+ }
503
+
504
+ return false;
505
+ }