@elizaos/plugin-elizacloud 2.0.0-alpha.7 → 2.0.0-beta.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.
Files changed (378) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +220 -0
  3. package/auto-enable.ts +17 -0
  4. package/dist/browser/index.browser.js +2 -21
  5. package/dist/browser/index.browser.js.map +5 -37
  6. package/dist/cjs/index.d.ts +2 -2
  7. package/dist/cjs/index.node.cjs +12173 -2271
  8. package/dist/cjs/index.node.js.map +135 -27
  9. package/dist/cloud/auth.d.ts +19 -0
  10. package/dist/cloud/auth.d.ts.map +1 -0
  11. package/dist/cloud/auth.js +330 -0
  12. package/dist/cloud/auth.js.map +12 -0
  13. package/dist/cloud/backup.d.ts +18 -0
  14. package/dist/cloud/backup.d.ts.map +1 -0
  15. package/dist/cloud/backup.js +63 -0
  16. package/dist/cloud/backup.js.map +10 -0
  17. package/dist/cloud/base-url.d.ts +3 -0
  18. package/dist/cloud/base-url.d.ts.map +1 -0
  19. package/dist/cloud/base-url.js +77 -0
  20. package/dist/cloud/base-url.js.map +10 -0
  21. package/dist/cloud/bridge-client.d.ts +126 -0
  22. package/dist/cloud/bridge-client.d.ts.map +1 -0
  23. package/dist/cloud/bridge-client.js +432 -0
  24. package/dist/cloud/bridge-client.js.map +11 -0
  25. package/dist/cloud/cloud-api-key.d.ts +26 -0
  26. package/dist/cloud/cloud-api-key.d.ts.map +1 -0
  27. package/dist/cloud/cloud-api-key.js +60 -0
  28. package/dist/cloud/cloud-api-key.js.map +10 -0
  29. package/dist/cloud/cloud-manager.d.ts +33 -0
  30. package/dist/cloud/cloud-manager.d.ts.map +1 -0
  31. package/dist/cloud/cloud-manager.js +853 -0
  32. package/dist/cloud/cloud-manager.js.map +16 -0
  33. package/dist/cloud/cloud-proxy.d.ts +20 -0
  34. package/dist/cloud/cloud-proxy.d.ts.map +1 -0
  35. package/dist/cloud/cloud-proxy.js +54 -0
  36. package/dist/cloud/cloud-proxy.js.map +10 -0
  37. package/dist/cloud/cloud-wallet.d.ts +94 -0
  38. package/dist/cloud/cloud-wallet.d.ts.map +1 -0
  39. package/dist/cloud/cloud-wallet.js +5195 -0
  40. package/dist/cloud/cloud-wallet.js.map +92 -0
  41. package/dist/cloud/index.d.ts +9 -0
  42. package/dist/cloud/index.d.ts.map +1 -0
  43. package/dist/cloud/index.js +30 -0
  44. package/dist/cloud/index.js.map +9 -0
  45. package/dist/cloud/reconnect.d.ts +26 -0
  46. package/dist/cloud/reconnect.d.ts.map +1 -0
  47. package/dist/cloud/reconnect.js +104 -0
  48. package/dist/cloud/reconnect.js.map +10 -0
  49. package/dist/cloud/validate-url.d.ts +2 -0
  50. package/dist/cloud/validate-url.d.ts.map +1 -0
  51. package/dist/cloud/validate-url.js +174 -0
  52. package/dist/cloud/validate-url.js.map +10 -0
  53. package/dist/cloud-providers/cloud-status.d.ts.map +1 -1
  54. package/dist/cloud-providers/cloud-status.js +78 -0
  55. package/dist/cloud-providers/cloud-status.js.map +10 -0
  56. package/dist/cloud-providers/container-health.d.ts.map +1 -1
  57. package/dist/cloud-providers/container-health.js +74 -0
  58. package/dist/cloud-providers/container-health.js.map +10 -0
  59. package/dist/cloud-providers/credit-balance.d.ts.map +1 -1
  60. package/dist/cloud-providers/credit-balance.js +85 -0
  61. package/dist/cloud-providers/credit-balance.js.map +10 -0
  62. package/dist/cloud-providers/index.d.ts.map +1 -1
  63. package/dist/cloud-providers/index.js +24 -0
  64. package/dist/cloud-providers/index.js.map +9 -0
  65. package/dist/cloud-providers/model-registry.d.ts.map +1 -1
  66. package/dist/cloud-providers/model-registry.js +71 -0
  67. package/dist/cloud-providers/model-registry.js.map +10 -0
  68. package/dist/index.browser.d.ts +4 -2
  69. package/dist/index.browser.d.ts.map +1 -1
  70. package/dist/index.d.ts +18 -0
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +12851 -0
  73. package/dist/index.js.map +145 -0
  74. package/dist/index.node.d.ts +15 -2
  75. package/dist/index.node.d.ts.map +1 -1
  76. package/dist/init.d.ts.map +1 -1
  77. package/dist/init.js +169 -0
  78. package/dist/init.js.map +12 -0
  79. package/dist/lib/cloud-connection.d.ts +78 -0
  80. package/dist/lib/cloud-connection.d.ts.map +1 -0
  81. package/dist/lib/cloud-connection.js +731 -0
  82. package/dist/lib/cloud-connection.js.map +14 -0
  83. package/dist/lib/cloud-secrets.d.ts +23 -0
  84. package/dist/lib/cloud-secrets.d.ts.map +1 -0
  85. package/dist/lib/cloud-secrets.js +64 -0
  86. package/dist/lib/cloud-secrets.js.map +10 -0
  87. package/dist/lib/config-env.d.ts +5 -0
  88. package/dist/lib/config-env.d.ts.map +1 -0
  89. package/dist/lib/config-env.js +191 -0
  90. package/dist/lib/config-env.js.map +11 -0
  91. package/dist/lib/config-like.d.ts +40 -0
  92. package/dist/lib/config-like.d.ts.map +1 -0
  93. package/dist/lib/config-like.js +103 -0
  94. package/dist/lib/config-like.js.map +10 -0
  95. package/dist/lib/credential-type-map.d.ts +53 -0
  96. package/dist/lib/credential-type-map.d.ts.map +1 -0
  97. package/dist/lib/credential-type-map.js +88 -0
  98. package/dist/lib/credential-type-map.js.map +10 -0
  99. package/dist/lib/feature-flags.d.ts +2 -0
  100. package/dist/lib/feature-flags.d.ts.map +1 -0
  101. package/dist/lib/feature-flags.js +40 -0
  102. package/dist/lib/feature-flags.js.map +10 -0
  103. package/dist/lib/http.d.ts +22 -0
  104. package/dist/lib/http.d.ts.map +1 -0
  105. package/dist/lib/http.js +107 -0
  106. package/dist/lib/http.js.map +10 -0
  107. package/dist/lib/server-cloud-tts.d.ts +34 -0
  108. package/dist/lib/server-cloud-tts.d.ts.map +1 -0
  109. package/dist/lib/server-cloud-tts.js +549 -0
  110. package/dist/lib/server-cloud-tts.js.map +13 -0
  111. package/dist/lib/state-paths.d.ts +4 -0
  112. package/dist/lib/state-paths.d.ts.map +1 -0
  113. package/dist/lib/state-paths.js +52 -0
  114. package/dist/lib/state-paths.js.map +10 -0
  115. package/dist/lib/tts-debug.d.ts +4 -0
  116. package/dist/lib/tts-debug.d.ts.map +1 -0
  117. package/dist/lib/tts-debug.js +57 -0
  118. package/dist/lib/tts-debug.js.map +10 -0
  119. package/dist/models/embeddings.d.ts.map +1 -1
  120. package/dist/models/embeddings.js +319 -0
  121. package/dist/models/embeddings.js.map +13 -0
  122. package/dist/models/image.d.ts.map +1 -1
  123. package/dist/models/image.js +374 -0
  124. package/dist/models/image.js.map +14 -0
  125. package/dist/models/index.d.ts +1 -2
  126. package/dist/models/index.d.ts.map +1 -1
  127. package/dist/models/index.js +1386 -0
  128. package/dist/models/index.js.map +20 -0
  129. package/dist/models/research.d.ts.map +1 -1
  130. package/dist/models/research.js +324 -0
  131. package/dist/models/research.js.map +13 -0
  132. package/dist/models/speech.d.ts.map +1 -1
  133. package/dist/models/speech.js +273 -0
  134. package/dist/models/speech.js.map +13 -0
  135. package/dist/models/text.d.ts +5 -2
  136. package/dist/models/text.d.ts.map +1 -1
  137. package/dist/models/text.js +803 -0
  138. package/dist/models/text.js.map +15 -0
  139. package/dist/models/tokenization.d.ts.map +1 -1
  140. package/dist/models/tokenization.js +65 -0
  141. package/dist/models/tokenization.js.map +10 -0
  142. package/dist/models/transcription.d.ts.map +1 -1
  143. package/dist/models/transcription.js +283 -0
  144. package/dist/models/transcription.js.map +13 -0
  145. package/dist/node/index.d.ts +2 -2
  146. package/dist/node/index.node.js +12171 -2266
  147. package/dist/node/index.node.js.map +135 -27
  148. package/dist/onboarding.d.ts +35 -0
  149. package/dist/onboarding.d.ts.map +1 -0
  150. package/dist/onboarding.js +883 -0
  151. package/dist/onboarding.js.map +14 -0
  152. package/dist/plugin.d.ts +20 -0
  153. package/dist/plugin.d.ts.map +1 -0
  154. package/dist/plugin.js +7611 -0
  155. package/dist/plugin.js.map +104 -0
  156. package/dist/providers/openai.d.ts.map +1 -1
  157. package/dist/providers/openai.js +127 -0
  158. package/dist/providers/openai.js.map +11 -0
  159. package/dist/register-routes.d.ts +2 -0
  160. package/dist/register-routes.d.ts.map +1 -0
  161. package/dist/register-routes.js +7612 -0
  162. package/dist/register-routes.js.map +105 -0
  163. package/dist/routes/cloud-billing-routes.d.ts +9 -0
  164. package/dist/routes/cloud-billing-routes.d.ts.map +1 -0
  165. package/dist/routes/cloud-billing-routes.js +807 -0
  166. package/dist/routes/cloud-billing-routes.js.map +14 -0
  167. package/dist/routes/cloud-compat-routes.d.ts +10 -0
  168. package/dist/routes/cloud-compat-routes.d.ts.map +1 -0
  169. package/dist/routes/cloud-compat-routes.js +538 -0
  170. package/dist/routes/cloud-compat-routes.js.map +14 -0
  171. package/dist/routes/cloud-features-routes.d.ts +9 -0
  172. package/dist/routes/cloud-features-routes.d.ts.map +1 -0
  173. package/dist/routes/cloud-features-routes.js +124 -0
  174. package/dist/routes/cloud-features-routes.js.map +11 -0
  175. package/dist/routes/cloud-provisioning.d.ts +14 -0
  176. package/dist/routes/cloud-provisioning.d.ts.map +1 -0
  177. package/dist/routes/cloud-provisioning.js +37 -0
  178. package/dist/routes/cloud-provisioning.js.map +10 -0
  179. package/dist/routes/cloud-relay-routes.d.ts +22 -0
  180. package/dist/routes/cloud-relay-routes.d.ts.map +1 -0
  181. package/dist/routes/cloud-relay-routes.js +60 -0
  182. package/dist/routes/cloud-relay-routes.js.map +10 -0
  183. package/dist/routes/cloud-routes-autonomous.d.ts +83 -0
  184. package/dist/routes/cloud-routes-autonomous.d.ts.map +1 -0
  185. package/dist/routes/cloud-routes-autonomous.js +6134 -0
  186. package/dist/routes/cloud-routes-autonomous.js.map +97 -0
  187. package/dist/routes/cloud-routes.d.ts +35 -0
  188. package/dist/routes/cloud-routes.d.ts.map +1 -0
  189. package/dist/routes/cloud-routes.js +6888 -0
  190. package/dist/routes/cloud-routes.js.map +100 -0
  191. package/dist/routes/cloud-status-routes-autonomous.d.ts +15 -0
  192. package/dist/routes/cloud-status-routes-autonomous.d.ts.map +1 -0
  193. package/dist/routes/cloud-status-routes-autonomous.js +396 -0
  194. package/dist/routes/cloud-status-routes-autonomous.js.map +13 -0
  195. package/dist/routes/cloud-status-routes.d.ts +4 -0
  196. package/dist/routes/cloud-status-routes.d.ts.map +1 -0
  197. package/dist/routes/cloud-status-routes.js +771 -0
  198. package/dist/routes/cloud-status-routes.js.map +15 -0
  199. package/dist/services/cloud-auth.d.ts +140 -5
  200. package/dist/services/cloud-auth.d.ts.map +1 -1
  201. package/dist/services/cloud-auth.js +363 -0
  202. package/dist/services/cloud-auth.js.map +12 -0
  203. package/dist/services/cloud-backup.d.ts.map +1 -1
  204. package/dist/services/cloud-backup.js +176 -0
  205. package/dist/services/cloud-backup.js.map +11 -0
  206. package/dist/services/cloud-bootstrap.d.ts +38 -0
  207. package/dist/services/cloud-bootstrap.d.ts.map +1 -0
  208. package/dist/services/cloud-bootstrap.js +84 -0
  209. package/dist/services/cloud-bootstrap.js.map +10 -0
  210. package/dist/services/cloud-bridge.d.ts +1 -1
  211. package/dist/services/cloud-bridge.d.ts.map +1 -1
  212. package/dist/services/cloud-bridge.js +308 -0
  213. package/dist/services/cloud-bridge.js.map +11 -0
  214. package/dist/services/cloud-container.d.ts.map +1 -1
  215. package/dist/services/cloud-container.js +241 -0
  216. package/dist/services/cloud-container.js.map +11 -0
  217. package/dist/services/cloud-credential-provider.d.ts +55 -0
  218. package/dist/services/cloud-credential-provider.d.ts.map +1 -0
  219. package/dist/services/cloud-credential-provider.js +190 -0
  220. package/dist/services/cloud-credential-provider.js.map +11 -0
  221. package/dist/services/cloud-managed-gateway-relay.d.ts +38 -0
  222. package/dist/services/cloud-managed-gateway-relay.d.ts.map +1 -0
  223. package/dist/services/cloud-managed-gateway-relay.js +479 -0
  224. package/dist/services/cloud-managed-gateway-relay.js.map +10 -0
  225. package/dist/services/cloud-model-registry.d.ts.map +1 -1
  226. package/dist/services/cloud-model-registry.js +175 -0
  227. package/dist/services/cloud-model-registry.js.map +10 -0
  228. package/dist/services/index.d.ts +3 -1
  229. package/dist/services/index.d.ts.map +1 -1
  230. package/dist/services/index.js +29 -0
  231. package/dist/services/index.js.map +9 -0
  232. package/dist/types/cloud.d.ts +41 -19
  233. package/dist/types/cloud.d.ts.map +1 -1
  234. package/dist/types/cloud.js +52 -0
  235. package/dist/types/cloud.js.map +10 -0
  236. package/dist/types/index.d.ts +1 -1
  237. package/dist/types/index.d.ts.map +1 -1
  238. package/dist/types/index.js +24 -0
  239. package/dist/types/index.js.map +9 -0
  240. package/dist/utils/cloud-api.d.ts +2 -27
  241. package/dist/utils/cloud-api.d.ts.map +1 -1
  242. package/dist/utils/cloud-api.js +33 -0
  243. package/dist/utils/cloud-api.js.map +10 -0
  244. package/dist/utils/cloud-sdk/client.d.ts +133 -0
  245. package/dist/utils/cloud-sdk/client.d.ts.map +1 -0
  246. package/dist/utils/cloud-sdk/client.js +3561 -0
  247. package/dist/utils/cloud-sdk/client.js.map +13 -0
  248. package/dist/utils/cloud-sdk/http.d.ts +37 -0
  249. package/dist/utils/cloud-sdk/http.d.ts.map +1 -0
  250. package/dist/utils/cloud-sdk/http.js +237 -0
  251. package/dist/utils/cloud-sdk/http.js.map +11 -0
  252. package/dist/utils/cloud-sdk/index.d.ts +6 -0
  253. package/dist/utils/cloud-sdk/index.d.ts.map +1 -0
  254. package/dist/utils/cloud-sdk/index.js +29 -0
  255. package/dist/utils/cloud-sdk/index.js.map +9 -0
  256. package/dist/utils/cloud-sdk/public-routes.d.ts +5377 -0
  257. package/dist/utils/cloud-sdk/public-routes.d.ts.map +1 -0
  258. package/dist/utils/cloud-sdk/public-routes.js +2950 -0
  259. package/dist/utils/cloud-sdk/public-routes.js.map +10 -0
  260. package/dist/utils/cloud-sdk/types.cloud-api.d.ts +101 -0
  261. package/dist/utils/cloud-sdk/types.cloud-api.d.ts.map +1 -0
  262. package/dist/utils/cloud-sdk/types.cloud-api.js +2 -0
  263. package/dist/utils/cloud-sdk/types.cloud-api.js.map +9 -0
  264. package/dist/utils/cloud-sdk/types.d.ts +655 -0
  265. package/dist/utils/cloud-sdk/types.d.ts.map +1 -0
  266. package/dist/utils/cloud-sdk/types.js +29 -0
  267. package/dist/utils/cloud-sdk/types.js.map +10 -0
  268. package/dist/utils/config.d.ts +7 -3
  269. package/dist/utils/config.d.ts.map +1 -1
  270. package/dist/utils/config.js +137 -0
  271. package/dist/utils/config.js.map +10 -0
  272. package/dist/utils/events.d.ts.map +1 -1
  273. package/dist/utils/events.js +43 -0
  274. package/dist/utils/events.js.map +10 -0
  275. package/dist/utils/helpers.d.ts.map +1 -1
  276. package/dist/utils/helpers.js +103 -0
  277. package/dist/utils/helpers.js.map +10 -0
  278. package/dist/utils/responses-output.d.ts +13 -0
  279. package/dist/utils/responses-output.d.ts.map +1 -0
  280. package/dist/utils/responses-output.js +102 -0
  281. package/dist/utils/responses-output.js.map +10 -0
  282. package/dist/utils/sdk-client.d.ts +5 -0
  283. package/dist/utils/sdk-client.d.ts.map +1 -0
  284. package/dist/utils/sdk-client.js +144 -0
  285. package/dist/utils/sdk-client.js.map +11 -0
  286. package/package.json +108 -19
  287. package/src/cloud/auth.ts +175 -0
  288. package/src/cloud/backup.ts +46 -0
  289. package/src/cloud/base-url.ts +62 -0
  290. package/src/cloud/bridge-client.ts +602 -0
  291. package/src/cloud/cloud-api-key.ts +80 -0
  292. package/src/cloud/cloud-manager.ts +163 -0
  293. package/src/cloud/cloud-proxy.ts +52 -0
  294. package/src/cloud/cloud-wallet.ts +341 -0
  295. package/src/cloud/index.ts +28 -0
  296. package/src/cloud/reconnect.ts +111 -0
  297. package/src/cloud/validate-url.ts +181 -0
  298. package/src/cloud-providers/cloud-status.ts +75 -0
  299. package/src/cloud-providers/container-health.ts +68 -0
  300. package/src/cloud-providers/credit-balance.ts +70 -0
  301. package/src/cloud-providers/index.ts +3 -0
  302. package/src/cloud-providers/model-registry.ts +74 -0
  303. package/src/index.browser.ts +10 -0
  304. package/src/index.node.ts +39 -0
  305. package/src/index.ts +347 -0
  306. package/src/init.ts +39 -0
  307. package/src/lib/cloud-connection.ts +663 -0
  308. package/src/lib/cloud-secrets.ts +58 -0
  309. package/src/lib/config-env.ts +168 -0
  310. package/src/lib/config-like.ts +149 -0
  311. package/src/lib/credential-type-map.ts +130 -0
  312. package/src/lib/feature-flags.ts +26 -0
  313. package/src/lib/http.ts +139 -0
  314. package/src/lib/server-cloud-tts.ts +609 -0
  315. package/src/lib/state-paths.ts +28 -0
  316. package/src/lib/tts-debug.ts +34 -0
  317. package/src/models/embeddings.ts +234 -0
  318. package/src/models/image.ts +219 -0
  319. package/src/models/index.ts +16 -0
  320. package/src/models/research.ts +265 -0
  321. package/src/models/speech.ts +78 -0
  322. package/src/models/text.ts +899 -0
  323. package/src/models/tokenization.ts +67 -0
  324. package/src/models/transcription.ts +97 -0
  325. package/src/onboarding.ts +396 -0
  326. package/src/plugin.ts +243 -0
  327. package/src/providers/openai.ts +16 -0
  328. package/src/register-routes.ts +6 -0
  329. package/src/routes/cloud-billing-routes.ts +754 -0
  330. package/src/routes/cloud-compat-routes.ts +314 -0
  331. package/src/routes/cloud-features-routes.ts +57 -0
  332. package/src/routes/cloud-provisioning.ts +37 -0
  333. package/src/routes/cloud-relay-routes.ts +89 -0
  334. package/src/routes/cloud-routes-autonomous.ts +996 -0
  335. package/src/routes/cloud-routes.ts +576 -0
  336. package/src/routes/cloud-status-routes-autonomous.ts +234 -0
  337. package/src/routes/cloud-status-routes.ts +73 -0
  338. package/src/services/cloud-auth.ts +567 -0
  339. package/src/services/cloud-backup.ts +208 -0
  340. package/src/services/cloud-bootstrap.ts +108 -0
  341. package/src/services/cloud-bridge.ts +386 -0
  342. package/src/services/cloud-container.ts +297 -0
  343. package/src/services/cloud-credential-provider.ts +210 -0
  344. package/src/services/cloud-managed-gateway-relay.ts +663 -0
  345. package/src/services/cloud-model-registry.ts +202 -0
  346. package/src/services/index.ts +17 -0
  347. package/{types → src/types}/cloud.ts +52 -29
  348. package/{types → src/types}/index.ts +6 -0
  349. package/src/utils/cloud-api.ts +10 -0
  350. package/src/utils/cloud-sdk/client.ts +735 -0
  351. package/src/utils/cloud-sdk/http.ts +291 -0
  352. package/src/utils/cloud-sdk/index.ts +23 -0
  353. package/src/utils/cloud-sdk/public-routes.ts +5070 -0
  354. package/src/utils/cloud-sdk/types.cloud-api.ts +120 -0
  355. package/src/utils/cloud-sdk/types.ts +762 -0
  356. package/src/utils/config.ts +174 -0
  357. package/src/utils/events.ts +37 -0
  358. package/src/utils/helpers.ts +107 -0
  359. package/src/utils/responses-output.ts +115 -0
  360. package/src/utils/sdk-client.ts +37 -0
  361. package/dist/actions/check-credits.d.ts +0 -6
  362. package/dist/actions/check-credits.d.ts.map +0 -1
  363. package/dist/actions/freeze-agent.d.ts +0 -9
  364. package/dist/actions/freeze-agent.d.ts.map +0 -1
  365. package/dist/actions/index.d.ts +0 -5
  366. package/dist/actions/index.d.ts.map +0 -1
  367. package/dist/actions/provision-agent.d.ts +0 -8
  368. package/dist/actions/provision-agent.d.ts.map +0 -1
  369. package/dist/actions/resume-agent.d.ts +0 -9
  370. package/dist/actions/resume-agent.d.ts.map +0 -1
  371. package/dist/build.d.ts +0 -3
  372. package/dist/build.d.ts.map +0 -1
  373. package/dist/generated/specs/specs.d.ts +0 -55
  374. package/dist/generated/specs/specs.d.ts.map +0 -1
  375. package/dist/models/object.d.ts +0 -4
  376. package/dist/models/object.d.ts.map +0 -1
  377. package/dist/utils/forwarded-settings.d.ts +0 -8
  378. package/dist/utils/forwarded-settings.d.ts.map +0 -1
@@ -0,0 +1,996 @@
1
+ import fs from "node:fs/promises";
2
+ import type http from "node:http";
3
+ import path from "node:path";
4
+ import {
5
+ isCloudInferenceSelectedInConfig,
6
+ migrateLegacyRuntimeConfig,
7
+ } from "@elizaos/core";
8
+ import { logger } from "@elizaos/core";
9
+ import { normalizeCloudSiteUrl } from "../cloud/base-url.js";
10
+ import type {
11
+ CloudChainType,
12
+ CloudWalletDescriptor,
13
+ CloudWalletProvider,
14
+ } from "../cloud/bridge-client.js";
15
+ import {
16
+ ELIZA_CLOUD_CLIENT_ADDRESS_KEY_ENV,
17
+ getOrCreateClientAddressKey,
18
+ persistCloudWalletCache,
19
+ provisionCloudWallets,
20
+ } from "../cloud/cloud-wallet.js";
21
+ import { validateCloudBaseUrl } from "../cloud/validate-url.js";
22
+ import { persistConfigEnv } from "../lib/config-env";
23
+ import {
24
+ applyCanonicalOnboardingConfig,
25
+ isTimeoutError,
26
+ } from "../lib/config-like";
27
+ import { isCloudWalletEnabled } from "../lib/feature-flags";
28
+ import {
29
+ readJsonBody as parseJsonBody,
30
+ sendJson,
31
+ sendJsonError,
32
+ } from "../lib/http";
33
+ import { resolveStateDir } from "../lib/state-paths";
34
+
35
+ export interface CloudConfigLike {
36
+ cloud?: {
37
+ enabled?: boolean;
38
+ apiKey?: string;
39
+ baseUrl?: string;
40
+ };
41
+ }
42
+
43
+ interface CloudClientLike {
44
+ listAgents: () => Promise<unknown>;
45
+ createAgent: (args: {
46
+ agentName: string;
47
+ agentConfig?: Record<string, unknown>;
48
+ environmentVars?: Record<string, string>;
49
+ }) => Promise<unknown>;
50
+ deleteAgent: (agentId: string) => Promise<unknown>;
51
+ getAgentWallet: (
52
+ agentId: string,
53
+ chain: CloudChainType,
54
+ ) => Promise<CloudWalletDescriptor>;
55
+ provisionWallet: (input: {
56
+ chainType: CloudChainType;
57
+ clientAddress: string;
58
+ }) => Promise<{
59
+ walletId: string;
60
+ address: string;
61
+ chainType: CloudChainType;
62
+ provider: CloudWalletProvider;
63
+ }>;
64
+ }
65
+
66
+ interface ConnectedCloudAgentLike {
67
+ agentName: string;
68
+ }
69
+
70
+ interface CloudManagerLike {
71
+ init?: () => Promise<void>;
72
+ replaceApiKey?: (apiKey: string) => Promise<void>;
73
+ getClient: () => CloudClientLike | null;
74
+ connect: (agentId: string) => Promise<ConnectedCloudAgentLike>;
75
+ disconnect: () => Promise<void>;
76
+ getStatus: () => unknown;
77
+ getActiveAgentId: () => string | null;
78
+ }
79
+
80
+ interface RuntimeLike {
81
+ agentId: string;
82
+ character?: {
83
+ secrets?: Record<string, string | number | boolean>;
84
+ };
85
+ getService?: (name: string) => unknown;
86
+ setSetting?: (key: string, value: string | null) => unknown;
87
+ updateAgent?: (
88
+ agentId: string,
89
+ update: {
90
+ secrets: Record<string, string | number | boolean>;
91
+ },
92
+ ) => Promise<unknown>;
93
+ }
94
+
95
+ interface IntegrationTelemetrySpanLike {
96
+ success: (args?: { statusCode?: number }) => void;
97
+ failure: (args?: {
98
+ statusCode?: number;
99
+ error?: unknown;
100
+ errorKind?: string;
101
+ }) => void;
102
+ }
103
+
104
+ interface CloudAuthLike {
105
+ authenticateWithApiKey?: (input: {
106
+ apiKey: string;
107
+ organizationId?: string;
108
+ userId?: string;
109
+ }) => unknown;
110
+ clearAuth?: () => unknown;
111
+ }
112
+
113
+ type CreateTelemetrySpanLike = (meta: {
114
+ boundary: "cloud";
115
+ operation: string;
116
+ timeoutMs?: number;
117
+ }) => IntegrationTelemetrySpanLike | null | undefined;
118
+
119
+ export interface CloudRouteState {
120
+ config: CloudConfigLike;
121
+ cloudManager: CloudManagerLike | null;
122
+ runtime: RuntimeLike | null;
123
+ saveConfig?: (config: CloudConfigLike) => void;
124
+ createTelemetrySpan?: CreateTelemetrySpanLike;
125
+ /**
126
+ * Optional runtime restart hook. When Phase 8 lands the cloud-wallet
127
+ * provisioning integration, the cloud-login handler will call this to
128
+ * rebind plugin-wallet to the cloud provider. Threaded
129
+ * from server.ts the same way provider-switch-routes does.
130
+ */
131
+ restartRuntime?: (reason: string) => Promise<boolean> | boolean;
132
+ }
133
+
134
+ const UUID_RE =
135
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
136
+
137
+ const CLOUD_LOGIN_CREATE_TIMEOUT_MS = 10_000;
138
+ const CLOUD_LOGIN_POLL_TIMEOUT_MS = 10_000;
139
+ const CONFIG_ENV_FILENAME = "config.env";
140
+ const CONFIG_ENV_BAK_SUFFIX = ".bak";
141
+ const CLOUD_WALLET_ROLLBACK_ENV_KEYS = [
142
+ ELIZA_CLOUD_CLIENT_ADDRESS_KEY_ENV,
143
+ "ELIZA_CLOUD_EVM_ADDRESS",
144
+ "ELIZA_CLOUD_SOLANA_ADDRESS",
145
+ "ENABLE_EVM_PLUGIN",
146
+ "WALLET_SOURCE_EVM",
147
+ "WALLET_SOURCE_SOLANA",
148
+ ] as const;
149
+
150
+ type CloudWalletRollbackEnvKey =
151
+ (typeof CLOUD_WALLET_ROLLBACK_ENV_KEYS)[number];
152
+
153
+ interface ConfigEnvRollbackSnapshot {
154
+ bakPath: string;
155
+ filePath: string;
156
+ originalRaw: string | null;
157
+ previousEnv: Partial<Record<CloudWalletRollbackEnvKey, string>>;
158
+ }
159
+
160
+ function extractAgentId(pathname: string): string | null {
161
+ const id = pathname.split("/")[4];
162
+ return id && UUID_RE.test(id) ? id : null;
163
+ }
164
+
165
+ function replaceMutableRoot<T extends object>(target: T, snapshot: T): void {
166
+ const targetRecord = target as Record<string, unknown>;
167
+ for (const key of Object.keys(targetRecord)) {
168
+ delete targetRecord[key];
169
+ }
170
+ Object.assign(
171
+ targetRecord,
172
+ structuredClone(snapshot as Record<string, unknown>),
173
+ );
174
+ }
175
+
176
+ function getCloudAuth(runtime: RuntimeLike | null): CloudAuthLike | null {
177
+ if (typeof runtime?.getService !== "function") {
178
+ return null;
179
+ }
180
+ const service = runtime.getService("CLOUD_AUTH");
181
+ return service && typeof service === "object"
182
+ ? (service as CloudAuthLike)
183
+ : null;
184
+ }
185
+
186
+ function clearCloudAuth(runtime: RuntimeLike | null): CloudAuthLike | null {
187
+ const cloudAuth = getCloudAuth(runtime);
188
+ if (typeof cloudAuth?.clearAuth === "function") {
189
+ cloudAuth.clearAuth();
190
+ }
191
+ return cloudAuth;
192
+ }
193
+
194
+ async function captureConfigEnvRollbackSnapshot(): Promise<ConfigEnvRollbackSnapshot> {
195
+ const filePath = path.join(resolveStateDir(), CONFIG_ENV_FILENAME);
196
+ const bakPath = `${filePath}${CONFIG_ENV_BAK_SUFFIX}`;
197
+
198
+ let originalRaw: string | null = null;
199
+ try {
200
+ originalRaw = await fs.readFile(filePath, "utf8");
201
+ } catch (err) {
202
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
203
+ throw err;
204
+ }
205
+ }
206
+
207
+ const previousEnv = Object.fromEntries(
208
+ CLOUD_WALLET_ROLLBACK_ENV_KEYS.flatMap((key) => {
209
+ const value = process.env[key];
210
+ return typeof value === "string" ? ([[key, value]] as const) : [];
211
+ }),
212
+ ) as Partial<Record<CloudWalletRollbackEnvKey, string>>;
213
+
214
+ return {
215
+ bakPath,
216
+ filePath,
217
+ originalRaw,
218
+ previousEnv,
219
+ };
220
+ }
221
+
222
+ async function restoreConfigEnvRollbackSnapshot(
223
+ snapshot: ConfigEnvRollbackSnapshot,
224
+ ): Promise<void> {
225
+ await fs.mkdir(path.dirname(snapshot.filePath), { recursive: true });
226
+
227
+ if (snapshot.originalRaw === null) {
228
+ await fs.rm(snapshot.filePath, { force: true });
229
+ await fs.rm(snapshot.bakPath, { force: true });
230
+ } else {
231
+ await fs.writeFile(snapshot.filePath, snapshot.originalRaw, {
232
+ encoding: "utf8",
233
+ mode: 0o600,
234
+ });
235
+ await fs.writeFile(snapshot.bakPath, snapshot.originalRaw, {
236
+ encoding: "utf8",
237
+ mode: 0o600,
238
+ });
239
+ }
240
+
241
+ for (const key of CLOUD_WALLET_ROLLBACK_ENV_KEYS) {
242
+ const previousValue = snapshot.previousEnv[key];
243
+ if (typeof previousValue === "string") {
244
+ process.env[key] = previousValue;
245
+ } else {
246
+ delete process.env[key];
247
+ }
248
+ }
249
+ }
250
+
251
+ function saveConfigOrThrow(state: CloudRouteState): void {
252
+ if (!state.saveConfig) {
253
+ throw new Error("saveConfig not available");
254
+ }
255
+ state.saveConfig(state.config);
256
+ }
257
+
258
+ async function readJsonBody<T = Record<string, unknown>>(
259
+ req: http.IncomingMessage,
260
+ res: http.ServerResponse,
261
+ ): Promise<T | null> {
262
+ return (await parseJsonBody(req, res, {
263
+ maxBytes: 1_048_576,
264
+ tooLargeMessage: "Request body too large",
265
+ destroyOnTooLarge: true,
266
+ })) as T | null;
267
+ }
268
+
269
+ function isRedirectResponse(response: Response): boolean {
270
+ return response.status >= 300 && response.status < 400;
271
+ }
272
+
273
+ function createNoopTelemetrySpan(): IntegrationTelemetrySpanLike {
274
+ return {
275
+ success: () => {},
276
+ failure: () => {},
277
+ };
278
+ }
279
+
280
+ function getTelemetrySpan(
281
+ state: CloudRouteState,
282
+ meta: {
283
+ boundary: "cloud";
284
+ operation: string;
285
+ timeoutMs?: number;
286
+ },
287
+ ): IntegrationTelemetrySpanLike {
288
+ return state.createTelemetrySpan?.(meta) ?? createNoopTelemetrySpan();
289
+ }
290
+
291
+ async function fetchWithTimeout(
292
+ input: string,
293
+ init: RequestInit,
294
+ timeoutMs: number,
295
+ ): Promise<Response> {
296
+ return fetch(input, {
297
+ ...init,
298
+ redirect: "manual",
299
+ signal: AbortSignal.timeout(timeoutMs),
300
+ });
301
+ }
302
+
303
+ export async function handleCloudRoute(
304
+ req: http.IncomingMessage,
305
+ res: http.ServerResponse,
306
+ pathname: string,
307
+ method: string,
308
+ state: CloudRouteState,
309
+ ): Promise<boolean> {
310
+ if (method === "POST" && pathname === "/api/cloud/login") {
311
+ const baseUrl = normalizeCloudSiteUrl(state.config.cloud?.baseUrl);
312
+ const urlError = await validateCloudBaseUrl(baseUrl);
313
+ if (urlError) {
314
+ sendJsonError(res, urlError);
315
+ return true;
316
+ }
317
+ const sessionId = crypto.randomUUID();
318
+ const loginCreateSpan = getTelemetrySpan(state, {
319
+ boundary: "cloud",
320
+ operation: "login_create_session",
321
+ timeoutMs: CLOUD_LOGIN_CREATE_TIMEOUT_MS,
322
+ });
323
+
324
+ let createRes: Response;
325
+ try {
326
+ createRes = await fetchWithTimeout(
327
+ `${baseUrl}/api/auth/cli-session`,
328
+ {
329
+ method: "POST",
330
+ headers: { "Content-Type": "application/json" },
331
+ body: JSON.stringify({ sessionId }),
332
+ },
333
+ CLOUD_LOGIN_CREATE_TIMEOUT_MS,
334
+ );
335
+ } catch (err) {
336
+ if (isTimeoutError(err)) {
337
+ loginCreateSpan.failure({ error: err, statusCode: 504 });
338
+ sendJsonError(res, "Eliza Cloud login request timed out", 504);
339
+ return true;
340
+ }
341
+ loginCreateSpan.failure({ error: err, statusCode: 502 });
342
+ sendJsonError(res, "Failed to reach Eliza Cloud", 502);
343
+ return true;
344
+ }
345
+
346
+ if (isRedirectResponse(createRes)) {
347
+ loginCreateSpan.failure({
348
+ statusCode: createRes.status,
349
+ errorKind: "redirect_response",
350
+ });
351
+ sendJsonError(
352
+ res,
353
+ "Eliza Cloud login request was redirected; redirects are not allowed",
354
+ 502,
355
+ );
356
+ return true;
357
+ }
358
+
359
+ if (!createRes.ok) {
360
+ loginCreateSpan.failure({
361
+ statusCode: createRes.status,
362
+ errorKind: "http_error",
363
+ });
364
+ sendJsonError(res, "Failed to create auth session with Eliza Cloud", 502);
365
+ return true;
366
+ }
367
+
368
+ loginCreateSpan.success({ statusCode: createRes.status });
369
+ sendJson(res, {
370
+ ok: true,
371
+ sessionId,
372
+ browserUrl: `${baseUrl}/auth/cli-login?session=${encodeURIComponent(sessionId)}`,
373
+ });
374
+ return true;
375
+ }
376
+
377
+ if (method === "GET" && pathname.startsWith("/api/cloud/login/status")) {
378
+ const url = new URL(
379
+ req.url ?? "/",
380
+ `http://${req.headers.host ?? "localhost"}`,
381
+ );
382
+ const sessionId = url.searchParams.get("sessionId");
383
+ if (!sessionId) {
384
+ sendJsonError(res, "sessionId query parameter is required");
385
+ return true;
386
+ }
387
+
388
+ const baseUrl = normalizeCloudSiteUrl(state.config.cloud?.baseUrl);
389
+ const urlError = await validateCloudBaseUrl(baseUrl);
390
+ if (urlError) {
391
+ sendJsonError(res, urlError);
392
+ return true;
393
+ }
394
+ const loginPollSpan = getTelemetrySpan(state, {
395
+ boundary: "cloud",
396
+ operation: "login_poll_status",
397
+ timeoutMs: CLOUD_LOGIN_POLL_TIMEOUT_MS,
398
+ });
399
+ let pollRes: Response;
400
+ try {
401
+ pollRes = await fetchWithTimeout(
402
+ `${baseUrl}/api/auth/cli-session/${encodeURIComponent(sessionId)}`,
403
+ {},
404
+ CLOUD_LOGIN_POLL_TIMEOUT_MS,
405
+ );
406
+ } catch (err) {
407
+ if (isTimeoutError(err)) {
408
+ loginPollSpan.failure({ error: err, statusCode: 504 });
409
+ sendJson(
410
+ res,
411
+ {
412
+ status: "error",
413
+ error: "Eliza Cloud status request timed out",
414
+ },
415
+ 504,
416
+ );
417
+ return true;
418
+ }
419
+ loginPollSpan.failure({ error: err, statusCode: 502 });
420
+ sendJson(
421
+ res,
422
+ {
423
+ status: "error",
424
+ error: "Failed to reach Eliza Cloud",
425
+ },
426
+ 502,
427
+ );
428
+ return true;
429
+ }
430
+
431
+ if (isRedirectResponse(pollRes)) {
432
+ loginPollSpan.failure({
433
+ statusCode: pollRes.status,
434
+ errorKind: "redirect_response",
435
+ });
436
+ sendJson(
437
+ res,
438
+ {
439
+ status: "error",
440
+ error:
441
+ "Eliza Cloud status request was redirected; redirects are not allowed",
442
+ },
443
+ 502,
444
+ );
445
+ return true;
446
+ }
447
+
448
+ if (!pollRes.ok) {
449
+ loginPollSpan.failure({
450
+ statusCode: pollRes.status,
451
+ errorKind: "http_error",
452
+ });
453
+ sendJson(
454
+ res,
455
+ pollRes.status === 404
456
+ ? { status: "expired", error: "Session not found or expired" }
457
+ : {
458
+ status: "error",
459
+ error: `Eliza Cloud returned HTTP ${pollRes.status}`,
460
+ },
461
+ );
462
+ return true;
463
+ }
464
+
465
+ let data: {
466
+ status: string;
467
+ apiKey?: string;
468
+ keyPrefix?: string;
469
+ organizationId?: string;
470
+ userId?: string;
471
+ };
472
+ try {
473
+ data = (await pollRes.json()) as {
474
+ status: string;
475
+ apiKey?: string;
476
+ keyPrefix?: string;
477
+ organizationId?: string;
478
+ userId?: string;
479
+ };
480
+ } catch (parseErr) {
481
+ loginPollSpan.failure({ error: parseErr, statusCode: pollRes.status });
482
+ sendJson(
483
+ res,
484
+ { status: "error", error: "Eliza Cloud returned invalid JSON" },
485
+ 502,
486
+ );
487
+ return true;
488
+ }
489
+ loginPollSpan.success({ statusCode: pollRes.status });
490
+
491
+ if (data.status === "authenticated" && data.apiKey) {
492
+ const organizationId =
493
+ typeof data.organizationId === "string"
494
+ ? data.organizationId.trim()
495
+ : undefined;
496
+ const userId =
497
+ typeof data.userId === "string" ? data.userId.trim() : undefined;
498
+ const cloudAuth = clearCloudAuth(state.runtime);
499
+ migrateLegacyRuntimeConfig(state.config as Record<string, unknown>);
500
+ const cloud = (state.config.cloud ?? {}) as NonNullable<
501
+ CloudConfigLike["cloud"]
502
+ >;
503
+ cloud.apiKey = data.apiKey;
504
+ (state.config as Record<string, unknown>).cloud = cloud;
505
+ applyCanonicalOnboardingConfig(state.config as never, {
506
+ linkedAccounts: {
507
+ elizacloud: {
508
+ status: "linked",
509
+ source: "api-key",
510
+ },
511
+ },
512
+ });
513
+ const cloudInferenceSelected = isCloudInferenceSelectedInConfig(
514
+ state.config as Record<string, unknown>,
515
+ );
516
+ migrateLegacyRuntimeConfig(state.config as Record<string, unknown>);
517
+ try {
518
+ if (state.saveConfig) {
519
+ state.saveConfig(state.config);
520
+ } else {
521
+ logger.warn(
522
+ "[cloud-login] saveConfig not available — config not persisted",
523
+ );
524
+ }
525
+ logger.info("[cloud-login] API key saved to config file");
526
+ } catch (saveErr) {
527
+ logger.error(`[cloud-login] Failed to save config: ${String(saveErr)}`);
528
+ sendJson(
529
+ res,
530
+ { status: "error", error: "Authenticated but failed to save config" },
531
+ 500,
532
+ );
533
+ return true;
534
+ }
535
+
536
+ process.env.ELIZAOS_CLOUD_API_KEY = data.apiKey;
537
+ if (cloudInferenceSelected) {
538
+ process.env.ELIZAOS_CLOUD_ENABLED = "true";
539
+ } else {
540
+ delete process.env.ELIZAOS_CLOUD_ENABLED;
541
+ }
542
+
543
+ if (state.runtime) {
544
+ const character = state.runtime.character ?? {};
545
+ state.runtime.character = character;
546
+ if (!character.secrets) {
547
+ character.secrets = {};
548
+ }
549
+ const secrets = character.secrets as Record<string, string>;
550
+ secrets.ELIZAOS_CLOUD_API_KEY = data.apiKey;
551
+ if (userId) {
552
+ secrets.ELIZA_CLOUD_USER_ID = userId;
553
+ } else {
554
+ delete secrets.ELIZA_CLOUD_USER_ID;
555
+ }
556
+ if (organizationId) {
557
+ secrets.ELIZA_CLOUD_ORGANIZATION_ID = organizationId;
558
+ } else {
559
+ delete secrets.ELIZA_CLOUD_ORGANIZATION_ID;
560
+ }
561
+ if (cloudInferenceSelected) {
562
+ secrets.ELIZAOS_CLOUD_ENABLED = "true";
563
+ } else {
564
+ delete secrets.ELIZAOS_CLOUD_ENABLED;
565
+ }
566
+
567
+ if (typeof state.runtime.setSetting === "function") {
568
+ state.runtime.setSetting("ELIZA_CLOUD_USER_ID", userId ?? null);
569
+ state.runtime.setSetting(
570
+ "ELIZA_CLOUD_ORGANIZATION_ID",
571
+ organizationId ?? null,
572
+ );
573
+ }
574
+
575
+ if (typeof state.runtime.updateAgent === "function") {
576
+ await state.runtime.updateAgent(state.runtime.agentId, {
577
+ secrets: { ...secrets },
578
+ });
579
+ logger.info("[cloud-login] API key persisted to agent DB record");
580
+ } else {
581
+ logger.warn(
582
+ "[cloud-login] runtime.updateAgent not available — agent DB secrets not persisted",
583
+ );
584
+ }
585
+ }
586
+
587
+ if (
588
+ state.cloudManager &&
589
+ typeof state.cloudManager.replaceApiKey === "function"
590
+ ) {
591
+ await state.cloudManager.replaceApiKey(data.apiKey);
592
+ } else if (
593
+ state.cloudManager &&
594
+ !state.cloudManager.getClient() &&
595
+ typeof state.cloudManager.init === "function"
596
+ ) {
597
+ await state.cloudManager.init();
598
+ }
599
+
600
+ if (typeof cloudAuth?.authenticateWithApiKey === "function") {
601
+ cloudAuth.authenticateWithApiKey({
602
+ apiKey: data.apiKey,
603
+ organizationId,
604
+ userId,
605
+ });
606
+ }
607
+
608
+ // Cloud-wallet remote-signing bridge (gated by ENABLE_CLOUD_WALLET).
609
+ // Failures here do NOT abort the cloud-login response — the API key
610
+ // is already saved. We log, rollback the partial wallet bind, and
611
+ // fall through so the user stays logged in.
612
+ if (isCloudWalletEnabled()) {
613
+ const rollbackConfigSnapshot = structuredClone(
614
+ state.config as Record<string, unknown>,
615
+ ) as CloudConfigLike;
616
+ const rollbackEnvSnapshot = await captureConfigEnvRollbackSnapshot();
617
+
618
+ try {
619
+ const bridge = state.cloudManager?.getClient();
620
+ const agentId = state.runtime?.agentId;
621
+ if (!bridge) {
622
+ throw new Error("cloud-wallet bridge unavailable");
623
+ }
624
+ if (!agentId) {
625
+ throw new Error("cloud-wallet runtime agentId missing");
626
+ }
627
+
628
+ const { address: clientAddress, minted } =
629
+ await getOrCreateClientAddressKey();
630
+ if (minted) {
631
+ logger.info(
632
+ `[cloud-login] cloud-wallet: minted client_address ${clientAddress}`,
633
+ );
634
+ }
635
+
636
+ const descriptors = await provisionCloudWallets(bridge, {
637
+ agentId,
638
+ clientAddress,
639
+ });
640
+
641
+ persistCloudWalletCache(
642
+ state.config as Record<string, unknown>,
643
+ descriptors,
644
+ );
645
+
646
+ const cloudCfg = (state.config.cloud ?? {}) as Record<
647
+ string,
648
+ unknown
649
+ >;
650
+ cloudCfg.clientAddressPublicKey = clientAddress;
651
+ (state.config as Record<string, unknown>).cloud = cloudCfg;
652
+ saveConfigOrThrow(state);
653
+
654
+ if (descriptors.evm?.walletAddress) {
655
+ process.env.ELIZA_CLOUD_EVM_ADDRESS = descriptors.evm.walletAddress;
656
+ await persistConfigEnv(
657
+ "ELIZA_CLOUD_EVM_ADDRESS",
658
+ descriptors.evm.walletAddress,
659
+ );
660
+ }
661
+ if (descriptors.solana?.walletAddress) {
662
+ process.env.ELIZA_CLOUD_SOLANA_ADDRESS =
663
+ descriptors.solana.walletAddress;
664
+ await persistConfigEnv(
665
+ "ELIZA_CLOUD_SOLANA_ADDRESS",
666
+ descriptors.solana.walletAddress,
667
+ );
668
+ }
669
+
670
+ await persistConfigEnv("ENABLE_EVM_PLUGIN", "1");
671
+ if (descriptors.evm) {
672
+ await persistConfigEnv("WALLET_SOURCE_EVM", "cloud");
673
+ }
674
+ if (descriptors.solana) {
675
+ await persistConfigEnv("WALLET_SOURCE_SOLANA", "cloud");
676
+ }
677
+
678
+ const wallet = ((state.config as Record<string, unknown>).wallet ??
679
+ {}) as Record<string, unknown>;
680
+ const primary = {
681
+ ...((wallet.primary ?? {}) as Record<string, string>),
682
+ };
683
+ if (descriptors.evm) primary.evm = "cloud";
684
+ if (descriptors.solana) primary.solana = "cloud";
685
+ wallet.primary = primary;
686
+ (state.config as Record<string, unknown>).wallet = wallet;
687
+ saveConfigOrThrow(state);
688
+
689
+ logger.info(
690
+ `[cloud-login] cloud-wallet: provisioned ${Object.keys(descriptors).join(", ")} — applying runtime reload`,
691
+ );
692
+
693
+ const restarted = state.restartRuntime
694
+ ? await Promise.resolve(state.restartRuntime("cloud-wallet-bound"))
695
+ : false;
696
+ if (!restarted) {
697
+ logger.warn(
698
+ "[cloud-login] cloud-wallet: restartRuntime not wired or restart declined — user must restart manually",
699
+ );
700
+ }
701
+ } catch (cloudWalletErr) {
702
+ try {
703
+ await restoreConfigEnvRollbackSnapshot(rollbackEnvSnapshot);
704
+ } catch (rollbackErr) {
705
+ logger.error(
706
+ `[cloud-login] cloud-wallet rollback failed: ${String(
707
+ rollbackErr,
708
+ )}`,
709
+ );
710
+ }
711
+
712
+ replaceMutableRoot(state.config, rollbackConfigSnapshot);
713
+ try {
714
+ saveConfigOrThrow(state);
715
+ } catch (saveRollbackErr) {
716
+ logger.error(
717
+ `[cloud-login] cloud-wallet config rollback failed: ${String(
718
+ saveRollbackErr,
719
+ )}`,
720
+ );
721
+ }
722
+
723
+ logger.error(
724
+ `[cloud-login] cloud-wallet provision failed: ${String(
725
+ cloudWalletErr,
726
+ )}`,
727
+ );
728
+ }
729
+ }
730
+
731
+ // Return the cloud API key to the renderer so it can populate
732
+ // `globalThis.__ELIZA_CLOUD_AUTH_TOKEN__` and use the direct cloud
733
+ // path (`/api/v1/eliza/agents`) for agent creation/provisioning.
734
+ // Without this, every cloud op falls back to the proxy compat path,
735
+ // which creates agents in a namespace whose queue never drains
736
+ // (agents stay `status: "queued"` forever — onboarding hangs).
737
+ //
738
+ // ## Security trade-off — token in HTTP response body
739
+ //
740
+ // Sending an API key in the response body is a deliberate choice
741
+ // for our architecture, NOT an oversight:
742
+ // - The agent process and the renderer (Electrobun WebView) live
743
+ // on the same machine. They communicate over loopback HTTP
744
+ // (`127.0.0.1`). Any process that can sniff the loopback
745
+ // interface (tcpdump, attached debugger) already has the
746
+ // ambient privilege to read the renderer's memory or the
747
+ // vault directly — the HTTP body is not the weakest link.
748
+ // - The key IS the user's own secret (not server-owned), and
749
+ // the response only goes to the renderer the user is
750
+ // actively using.
751
+ // - Returning a short-lived "handle" that the renderer
752
+ // exchanges for the real token would just shift the same
753
+ // cleartext transit one hop, not eliminate it.
754
+ //
755
+ // The principled fix is to push the token over an Electrobun RPC
756
+ // channel (the same IPC the api-base-owner module uses for
757
+ // `pushApiBaseToRenderer`). That requires routing the cloud
758
+ // login outcome from the agent process to the Electrobun main
759
+ // process and back to the renderer — three-process choreography
760
+ // that doesn't fit this PR. Tracked as a follow-up.
761
+ logger.info(
762
+ `[cloud-login] sending API key to loopback renderer (single-user desktop trust model)`,
763
+ );
764
+ sendJson(res, {
765
+ status: "authenticated",
766
+ token: data.apiKey,
767
+ keyPrefix: data.keyPrefix,
768
+ organizationId,
769
+ userId,
770
+ });
771
+ } else {
772
+ sendJson(res, { status: data.status });
773
+ }
774
+ return true;
775
+ }
776
+
777
+ if (method === "GET" && pathname === "/api/cloud/agents") {
778
+ const client = state.cloudManager?.getClient();
779
+ if (!client) {
780
+ sendJsonError(res, "Not connected to Eliza Cloud", 401);
781
+ return true;
782
+ }
783
+ sendJson(res, { ok: true, agents: await client.listAgents() });
784
+ return true;
785
+ }
786
+
787
+ if (method === "POST" && pathname === "/api/cloud/agents") {
788
+ const client = state.cloudManager?.getClient();
789
+ if (!client) {
790
+ sendJsonError(res, "Not connected to Eliza Cloud", 401);
791
+ return true;
792
+ }
793
+
794
+ const body = await readJsonBody<{
795
+ agentName?: string;
796
+ agentConfig?: Record<string, unknown>;
797
+ environmentVars?: Record<string, string>;
798
+ }>(req, res);
799
+ if (!body) return true;
800
+
801
+ if (!body.agentName?.trim()) {
802
+ sendJsonError(res, "agentName is required");
803
+ return true;
804
+ }
805
+
806
+ let agent: unknown;
807
+ try {
808
+ agent = await client.createAgent({
809
+ agentName: body.agentName,
810
+ agentConfig: body.agentConfig,
811
+ environmentVars: body.environmentVars,
812
+ });
813
+ } catch (err) {
814
+ logger.error(`[cloud] createAgent failed: ${String(err)}`);
815
+ sendJson(
816
+ res,
817
+ { ok: false, error: `Cloud createAgent failed: ${String(err)}` },
818
+ 502,
819
+ );
820
+ return true;
821
+ }
822
+ sendJson(res, { ok: true, agent }, 201);
823
+ return true;
824
+ }
825
+
826
+ if (
827
+ method === "POST" &&
828
+ pathname.startsWith("/api/cloud/agents/") &&
829
+ pathname.endsWith("/provision")
830
+ ) {
831
+ const agentId = extractAgentId(pathname);
832
+ if (!agentId || !state.cloudManager) {
833
+ sendJsonError(res, "Invalid agent ID or cloud not connected", 400);
834
+ return true;
835
+ }
836
+ let proxy: { agentName?: string };
837
+ try {
838
+ proxy = await state.cloudManager.connect(agentId);
839
+ } catch (err) {
840
+ logger.error(`[cloud] provision/connect failed: ${String(err)}`);
841
+ sendJson(
842
+ res,
843
+ { ok: false, error: `Cloud provision failed: ${String(err)}` },
844
+ 502,
845
+ );
846
+ return true;
847
+ }
848
+ sendJson(res, {
849
+ ok: true,
850
+ agentId,
851
+ agentName: proxy.agentName,
852
+ status: state.cloudManager.getStatus(),
853
+ });
854
+ return true;
855
+ }
856
+
857
+ if (
858
+ method === "POST" &&
859
+ pathname.startsWith("/api/cloud/agents/") &&
860
+ pathname.endsWith("/shutdown")
861
+ ) {
862
+ const agentId = extractAgentId(pathname);
863
+ if (!agentId || !state.cloudManager) {
864
+ sendJsonError(res, "Invalid agent ID or cloud not connected", 400);
865
+ return true;
866
+ }
867
+ const client = state.cloudManager.getClient();
868
+ if (!client) {
869
+ sendJsonError(res, "Not connected to Eliza Cloud", 401);
870
+ return true;
871
+ }
872
+ try {
873
+ if (state.cloudManager.getActiveAgentId() === agentId) {
874
+ await state.cloudManager.disconnect();
875
+ }
876
+ await client.deleteAgent(agentId);
877
+ } catch (err) {
878
+ logger.error(`[cloud] shutdown/deleteAgent failed: ${String(err)}`);
879
+ sendJson(
880
+ res,
881
+ { ok: false, error: `Cloud shutdown failed: ${String(err)}` },
882
+ 502,
883
+ );
884
+ return true;
885
+ }
886
+ sendJson(res, { ok: true, agentId, status: "stopped" });
887
+ return true;
888
+ }
889
+
890
+ if (
891
+ method === "POST" &&
892
+ pathname.startsWith("/api/cloud/agents/") &&
893
+ pathname.endsWith("/connect")
894
+ ) {
895
+ const agentId = extractAgentId(pathname);
896
+ if (!agentId || !state.cloudManager) {
897
+ sendJsonError(res, "Invalid agent ID or cloud not connected", 400);
898
+ return true;
899
+ }
900
+ let proxy: { agentName?: string };
901
+ try {
902
+ if (state.cloudManager.getActiveAgentId()) {
903
+ await state.cloudManager.disconnect();
904
+ }
905
+ proxy = await state.cloudManager.connect(agentId);
906
+ } catch (err) {
907
+ logger.error(`[cloud] connect failed: ${String(err)}`);
908
+ sendJson(
909
+ res,
910
+ { ok: false, error: `Cloud connect failed: ${String(err)}` },
911
+ 502,
912
+ );
913
+ return true;
914
+ }
915
+ sendJson(res, {
916
+ ok: true,
917
+ agentId,
918
+ agentName: proxy.agentName,
919
+ status: state.cloudManager.getStatus(),
920
+ });
921
+ return true;
922
+ }
923
+
924
+ if (method === "POST" && pathname === "/api/cloud/disconnect") {
925
+ if (state.cloudManager) {
926
+ await state.cloudManager.disconnect();
927
+ }
928
+ const cloud = (state.config.cloud ?? {}) as NonNullable<
929
+ CloudConfigLike["cloud"]
930
+ >;
931
+ delete cloud.apiKey;
932
+ (state.config as Record<string, unknown>).cloud = cloud;
933
+ applyCanonicalOnboardingConfig(state.config as never, {
934
+ deploymentTarget: { runtime: "local" },
935
+ linkedAccounts: {
936
+ elizacloud: {
937
+ status: "unlinked",
938
+ source: "api-key",
939
+ },
940
+ },
941
+ clearRoutes: ["llmText", "tts", "media", "embeddings", "rpc"],
942
+ });
943
+ migrateLegacyRuntimeConfig(state.config as Record<string, unknown>);
944
+
945
+ try {
946
+ if (state.saveConfig) {
947
+ state.saveConfig(state.config);
948
+ } else {
949
+ logger.warn(
950
+ "[cloud-disconnect] saveConfig not available — config not persisted",
951
+ );
952
+ }
953
+ } catch (saveErr) {
954
+ logger.error(
955
+ `[cloud-disconnect] Failed to save config: ${String(saveErr)}`,
956
+ );
957
+ sendJson(
958
+ res,
959
+ { ok: false, error: "Disconnected but failed to save config" },
960
+ 500,
961
+ );
962
+ return true;
963
+ }
964
+
965
+ delete process.env.ELIZAOS_CLOUD_API_KEY;
966
+ delete process.env.ELIZAOS_CLOUD_ENABLED;
967
+
968
+ if (state.runtime) {
969
+ const character = state.runtime.character ?? {};
970
+ state.runtime.character = character;
971
+ if (!character.secrets) {
972
+ character.secrets = {};
973
+ }
974
+ const secrets = character.secrets as Record<
975
+ string,
976
+ string | number | boolean
977
+ >;
978
+ delete secrets.ELIZAOS_CLOUD_API_KEY;
979
+ delete secrets.ELIZAOS_CLOUD_ENABLED;
980
+ if (typeof state.runtime.updateAgent === "function") {
981
+ await state.runtime.updateAgent(state.runtime.agentId, {
982
+ secrets: { ...secrets },
983
+ });
984
+ } else {
985
+ logger.warn(
986
+ "[cloud-disconnect] updateAgent not available — runtime secrets not persisted",
987
+ );
988
+ }
989
+ }
990
+
991
+ sendJson(res, { ok: true, status: "disconnected" });
992
+ return true;
993
+ }
994
+
995
+ return false;
996
+ }