@waiaas/daemon 2.0.0-rc.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 (480) hide show
  1. package/dist/api/error-hints.d.ts +15 -0
  2. package/dist/api/error-hints.d.ts.map +1 -0
  3. package/dist/api/error-hints.js +71 -0
  4. package/dist/api/error-hints.js.map +1 -0
  5. package/dist/api/index.d.ts +11 -0
  6. package/dist/api/index.d.ts.map +1 -0
  7. package/dist/api/index.js +14 -0
  8. package/dist/api/index.js.map +1 -0
  9. package/dist/api/middleware/address-validation.d.ts +38 -0
  10. package/dist/api/middleware/address-validation.d.ts.map +1 -0
  11. package/dist/api/middleware/address-validation.js +134 -0
  12. package/dist/api/middleware/address-validation.js.map +1 -0
  13. package/dist/api/middleware/csp.d.ts +17 -0
  14. package/dist/api/middleware/csp.d.ts.map +1 -0
  15. package/dist/api/middleware/csp.js +31 -0
  16. package/dist/api/middleware/csp.js.map +1 -0
  17. package/dist/api/middleware/error-handler.d.ts +16 -0
  18. package/dist/api/middleware/error-handler.d.ts.map +1 -0
  19. package/dist/api/middleware/error-handler.js +46 -0
  20. package/dist/api/middleware/error-handler.js.map +1 -0
  21. package/dist/api/middleware/host-guard.d.ts +11 -0
  22. package/dist/api/middleware/host-guard.d.ts.map +1 -0
  23. package/dist/api/middleware/host-guard.js +25 -0
  24. package/dist/api/middleware/host-guard.js.map +1 -0
  25. package/dist/api/middleware/index.d.ts +13 -0
  26. package/dist/api/middleware/index.d.ts.map +1 -0
  27. package/dist/api/middleware/index.js +13 -0
  28. package/dist/api/middleware/index.js.map +1 -0
  29. package/dist/api/middleware/kill-switch-guard.d.ts +19 -0
  30. package/dist/api/middleware/kill-switch-guard.d.ts.map +1 -0
  31. package/dist/api/middleware/kill-switch-guard.js +49 -0
  32. package/dist/api/middleware/kill-switch-guard.js.map +1 -0
  33. package/dist/api/middleware/master-auth.d.ts +15 -0
  34. package/dist/api/middleware/master-auth.d.ts.map +1 -0
  35. package/dist/api/middleware/master-auth.js +35 -0
  36. package/dist/api/middleware/master-auth.js.map +1 -0
  37. package/dist/api/middleware/owner-auth.d.ts +30 -0
  38. package/dist/api/middleware/owner-auth.d.ts.map +1 -0
  39. package/dist/api/middleware/owner-auth.js +133 -0
  40. package/dist/api/middleware/owner-auth.js.map +1 -0
  41. package/dist/api/middleware/request-id.d.ts +10 -0
  42. package/dist/api/middleware/request-id.d.ts.map +1 -0
  43. package/dist/api/middleware/request-id.js +18 -0
  44. package/dist/api/middleware/request-id.js.map +1 -0
  45. package/dist/api/middleware/request-logger.d.ts +9 -0
  46. package/dist/api/middleware/request-logger.d.ts.map +1 -0
  47. package/dist/api/middleware/request-logger.js +18 -0
  48. package/dist/api/middleware/request-logger.js.map +1 -0
  49. package/dist/api/middleware/session-auth.d.ts +21 -0
  50. package/dist/api/middleware/session-auth.d.ts.map +1 -0
  51. package/dist/api/middleware/session-auth.js +51 -0
  52. package/dist/api/middleware/session-auth.js.map +1 -0
  53. package/dist/api/middleware/siwe-verify.d.ts +31 -0
  54. package/dist/api/middleware/siwe-verify.d.ts.map +1 -0
  55. package/dist/api/middleware/siwe-verify.js +55 -0
  56. package/dist/api/middleware/siwe-verify.js.map +1 -0
  57. package/dist/api/routes/actions.d.ts +56 -0
  58. package/dist/api/routes/actions.d.ts.map +1 -0
  59. package/dist/api/routes/actions.js +291 -0
  60. package/dist/api/routes/actions.js.map +1 -0
  61. package/dist/api/routes/admin.d.ts +99 -0
  62. package/dist/api/routes/admin.d.ts.map +1 -0
  63. package/dist/api/routes/admin.js +1304 -0
  64. package/dist/api/routes/admin.js.map +1 -0
  65. package/dist/api/routes/display-currency-helper.d.ts +26 -0
  66. package/dist/api/routes/display-currency-helper.d.ts.map +1 -0
  67. package/dist/api/routes/display-currency-helper.js +47 -0
  68. package/dist/api/routes/display-currency-helper.js.map +1 -0
  69. package/dist/api/routes/health.d.ts +14 -0
  70. package/dist/api/routes/health.d.ts.map +1 -0
  71. package/dist/api/routes/health.js +47 -0
  72. package/dist/api/routes/health.js.map +1 -0
  73. package/dist/api/routes/index.d.ts +15 -0
  74. package/dist/api/routes/index.d.ts.map +1 -0
  75. package/dist/api/routes/index.js +15 -0
  76. package/dist/api/routes/index.js.map +1 -0
  77. package/dist/api/routes/mcp.d.ts +30 -0
  78. package/dist/api/routes/mcp.d.ts.map +1 -0
  79. package/dist/api/routes/mcp.js +156 -0
  80. package/dist/api/routes/mcp.js.map +1 -0
  81. package/dist/api/routes/nonce.d.ts +20 -0
  82. package/dist/api/routes/nonce.d.ts.map +1 -0
  83. package/dist/api/routes/nonce.js +48 -0
  84. package/dist/api/routes/nonce.js.map +1 -0
  85. package/dist/api/routes/openapi-schemas.d.ts +2281 -0
  86. package/dist/api/routes/openapi-schemas.d.ts.map +1 -0
  87. package/dist/api/routes/openapi-schemas.js +770 -0
  88. package/dist/api/routes/openapi-schemas.js.map +1 -0
  89. package/dist/api/routes/policies.d.ts +29 -0
  90. package/dist/api/routes/policies.d.ts.map +1 -0
  91. package/dist/api/routes/policies.js +332 -0
  92. package/dist/api/routes/policies.js.map +1 -0
  93. package/dist/api/routes/sessions.d.ts +35 -0
  94. package/dist/api/routes/sessions.d.ts.map +1 -0
  95. package/dist/api/routes/sessions.js +347 -0
  96. package/dist/api/routes/sessions.js.map +1 -0
  97. package/dist/api/routes/skills.d.ts +9 -0
  98. package/dist/api/routes/skills.d.ts.map +1 -0
  99. package/dist/api/routes/skills.js +59 -0
  100. package/dist/api/routes/skills.js.map +1 -0
  101. package/dist/api/routes/tokens.d.ts +25 -0
  102. package/dist/api/routes/tokens.d.ts.map +1 -0
  103. package/dist/api/routes/tokens.js +161 -0
  104. package/dist/api/routes/tokens.js.map +1 -0
  105. package/dist/api/routes/transactions.d.ts +68 -0
  106. package/dist/api/routes/transactions.d.ts.map +1 -0
  107. package/dist/api/routes/transactions.js +576 -0
  108. package/dist/api/routes/transactions.js.map +1 -0
  109. package/dist/api/routes/utils.d.ts +9 -0
  110. package/dist/api/routes/utils.d.ts.map +1 -0
  111. package/dist/api/routes/utils.js +52 -0
  112. package/dist/api/routes/utils.js.map +1 -0
  113. package/dist/api/routes/wallet.d.ts +36 -0
  114. package/dist/api/routes/wallet.d.ts.map +1 -0
  115. package/dist/api/routes/wallet.js +358 -0
  116. package/dist/api/routes/wallet.js.map +1 -0
  117. package/dist/api/routes/wallets.d.ts +43 -0
  118. package/dist/api/routes/wallets.d.ts.map +1 -0
  119. package/dist/api/routes/wallets.js +630 -0
  120. package/dist/api/routes/wallets.js.map +1 -0
  121. package/dist/api/routes/wc.d.ts +46 -0
  122. package/dist/api/routes/wc.d.ts.map +1 -0
  123. package/dist/api/routes/wc.js +354 -0
  124. package/dist/api/routes/wc.js.map +1 -0
  125. package/dist/api/routes/x402.d.ts +61 -0
  126. package/dist/api/routes/x402.d.ts.map +1 -0
  127. package/dist/api/routes/x402.js +493 -0
  128. package/dist/api/routes/x402.js.map +1 -0
  129. package/dist/api/server.d.ts +81 -0
  130. package/dist/api/server.d.ts.map +1 -0
  131. package/dist/api/server.js +406 -0
  132. package/dist/api/server.js.map +1 -0
  133. package/dist/index.d.ts +35 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +43 -0
  136. package/dist/index.js.map +1 -0
  137. package/dist/infrastructure/action/action-provider-registry.d.ts +77 -0
  138. package/dist/infrastructure/action/action-provider-registry.d.ts.map +1 -0
  139. package/dist/infrastructure/action/action-provider-registry.js +239 -0
  140. package/dist/infrastructure/action/action-provider-registry.js.map +1 -0
  141. package/dist/infrastructure/action/api-key-store.d.ts +60 -0
  142. package/dist/infrastructure/action/api-key-store.d.ts.map +1 -0
  143. package/dist/infrastructure/action/api-key-store.js +130 -0
  144. package/dist/infrastructure/action/api-key-store.js.map +1 -0
  145. package/dist/infrastructure/action/index.d.ts +10 -0
  146. package/dist/infrastructure/action/index.d.ts.map +1 -0
  147. package/dist/infrastructure/action/index.js +9 -0
  148. package/dist/infrastructure/action/index.js.map +1 -0
  149. package/dist/infrastructure/adapter-pool.d.ts +50 -0
  150. package/dist/infrastructure/adapter-pool.d.ts.map +1 -0
  151. package/dist/infrastructure/adapter-pool.js +110 -0
  152. package/dist/infrastructure/adapter-pool.js.map +1 -0
  153. package/dist/infrastructure/backup/backup-service.d.ts +53 -0
  154. package/dist/infrastructure/backup/backup-service.d.ts.map +1 -0
  155. package/dist/infrastructure/backup/backup-service.js +158 -0
  156. package/dist/infrastructure/backup/backup-service.js.map +1 -0
  157. package/dist/infrastructure/backup/index.d.ts +2 -0
  158. package/dist/infrastructure/backup/index.d.ts.map +1 -0
  159. package/dist/infrastructure/backup/index.js +2 -0
  160. package/dist/infrastructure/backup/index.js.map +1 -0
  161. package/dist/infrastructure/config/index.d.ts +8 -0
  162. package/dist/infrastructure/config/index.d.ts.map +1 -0
  163. package/dist/infrastructure/config/index.js +7 -0
  164. package/dist/infrastructure/config/index.js.map +1 -0
  165. package/dist/infrastructure/config/loader.d.ts +555 -0
  166. package/dist/infrastructure/config/loader.d.ts.map +1 -0
  167. package/dist/infrastructure/config/loader.js +311 -0
  168. package/dist/infrastructure/config/loader.js.map +1 -0
  169. package/dist/infrastructure/database/checks.d.ts +19 -0
  170. package/dist/infrastructure/database/checks.d.ts.map +1 -0
  171. package/dist/infrastructure/database/checks.js +27 -0
  172. package/dist/infrastructure/database/checks.js.map +1 -0
  173. package/dist/infrastructure/database/compatibility.d.ts +36 -0
  174. package/dist/infrastructure/database/compatibility.d.ts.map +1 -0
  175. package/dist/infrastructure/database/compatibility.js +75 -0
  176. package/dist/infrastructure/database/compatibility.js.map +1 -0
  177. package/dist/infrastructure/database/connection.d.ts +36 -0
  178. package/dist/infrastructure/database/connection.d.ts.map +1 -0
  179. package/dist/infrastructure/database/connection.js +47 -0
  180. package/dist/infrastructure/database/connection.js.map +1 -0
  181. package/dist/infrastructure/database/id.d.ts +17 -0
  182. package/dist/infrastructure/database/id.d.ts.map +1 -0
  183. package/dist/infrastructure/database/id.js +20 -0
  184. package/dist/infrastructure/database/id.js.map +1 -0
  185. package/dist/infrastructure/database/index.d.ts +15 -0
  186. package/dist/infrastructure/database/index.d.ts.map +1 -0
  187. package/dist/infrastructure/database/index.js +12 -0
  188. package/dist/infrastructure/database/index.js.map +1 -0
  189. package/dist/infrastructure/database/migrate.d.ts +76 -0
  190. package/dist/infrastructure/database/migrate.d.ts.map +1 -0
  191. package/dist/infrastructure/database/migrate.js +1214 -0
  192. package/dist/infrastructure/database/migrate.js.map +1 -0
  193. package/dist/infrastructure/database/schema.d.ts +2352 -0
  194. package/dist/infrastructure/database/schema.d.ts.map +1 -0
  195. package/dist/infrastructure/database/schema.js +288 -0
  196. package/dist/infrastructure/database/schema.js.map +1 -0
  197. package/dist/infrastructure/jwt/index.d.ts +2 -0
  198. package/dist/infrastructure/jwt/index.d.ts.map +1 -0
  199. package/dist/infrastructure/jwt/index.js +2 -0
  200. package/dist/infrastructure/jwt/index.js.map +1 -0
  201. package/dist/infrastructure/jwt/jwt-secret-manager.d.ts +58 -0
  202. package/dist/infrastructure/jwt/jwt-secret-manager.d.ts.map +1 -0
  203. package/dist/infrastructure/jwt/jwt-secret-manager.js +222 -0
  204. package/dist/infrastructure/jwt/jwt-secret-manager.js.map +1 -0
  205. package/dist/infrastructure/keystore/crypto.d.ts +62 -0
  206. package/dist/infrastructure/keystore/crypto.d.ts.map +1 -0
  207. package/dist/infrastructure/keystore/crypto.js +89 -0
  208. package/dist/infrastructure/keystore/crypto.js.map +1 -0
  209. package/dist/infrastructure/keystore/index.d.ts +4 -0
  210. package/dist/infrastructure/keystore/index.d.ts.map +1 -0
  211. package/dist/infrastructure/keystore/index.js +5 -0
  212. package/dist/infrastructure/keystore/index.js.map +1 -0
  213. package/dist/infrastructure/keystore/keystore.d.ts +115 -0
  214. package/dist/infrastructure/keystore/keystore.d.ts.map +1 -0
  215. package/dist/infrastructure/keystore/keystore.js +327 -0
  216. package/dist/infrastructure/keystore/keystore.js.map +1 -0
  217. package/dist/infrastructure/keystore/memory.d.ts +45 -0
  218. package/dist/infrastructure/keystore/memory.d.ts.map +1 -0
  219. package/dist/infrastructure/keystore/memory.js +105 -0
  220. package/dist/infrastructure/keystore/memory.js.map +1 -0
  221. package/dist/infrastructure/oracle/coingecko-forex.d.ts +35 -0
  222. package/dist/infrastructure/oracle/coingecko-forex.d.ts.map +1 -0
  223. package/dist/infrastructure/oracle/coingecko-forex.js +69 -0
  224. package/dist/infrastructure/oracle/coingecko-forex.js.map +1 -0
  225. package/dist/infrastructure/oracle/coingecko-oracle.d.ts +73 -0
  226. package/dist/infrastructure/oracle/coingecko-oracle.d.ts.map +1 -0
  227. package/dist/infrastructure/oracle/coingecko-oracle.js +199 -0
  228. package/dist/infrastructure/oracle/coingecko-oracle.js.map +1 -0
  229. package/dist/infrastructure/oracle/coingecko-platform-ids.d.ts +32 -0
  230. package/dist/infrastructure/oracle/coingecko-platform-ids.d.ts.map +1 -0
  231. package/dist/infrastructure/oracle/coingecko-platform-ids.js +30 -0
  232. package/dist/infrastructure/oracle/coingecko-platform-ids.js.map +1 -0
  233. package/dist/infrastructure/oracle/forex-currencies.d.ts +36 -0
  234. package/dist/infrastructure/oracle/forex-currencies.d.ts.map +1 -0
  235. package/dist/infrastructure/oracle/forex-currencies.js +71 -0
  236. package/dist/infrastructure/oracle/forex-currencies.js.map +1 -0
  237. package/dist/infrastructure/oracle/forex-rate-service.d.ts +51 -0
  238. package/dist/infrastructure/oracle/forex-rate-service.d.ts.map +1 -0
  239. package/dist/infrastructure/oracle/forex-rate-service.js +149 -0
  240. package/dist/infrastructure/oracle/forex-rate-service.js.map +1 -0
  241. package/dist/infrastructure/oracle/index.d.ts +18 -0
  242. package/dist/infrastructure/oracle/index.d.ts.map +1 -0
  243. package/dist/infrastructure/oracle/index.js +19 -0
  244. package/dist/infrastructure/oracle/index.js.map +1 -0
  245. package/dist/infrastructure/oracle/oracle-chain.d.ts +101 -0
  246. package/dist/infrastructure/oracle/oracle-chain.d.ts.map +1 -0
  247. package/dist/infrastructure/oracle/oracle-chain.js +163 -0
  248. package/dist/infrastructure/oracle/oracle-chain.js.map +1 -0
  249. package/dist/infrastructure/oracle/oracle-errors.d.ts +42 -0
  250. package/dist/infrastructure/oracle/oracle-errors.d.ts.map +1 -0
  251. package/dist/infrastructure/oracle/oracle-errors.js +53 -0
  252. package/dist/infrastructure/oracle/oracle-errors.js.map +1 -0
  253. package/dist/infrastructure/oracle/price-age.d.ts +38 -0
  254. package/dist/infrastructure/oracle/price-age.d.ts.map +1 -0
  255. package/dist/infrastructure/oracle/price-age.js +44 -0
  256. package/dist/infrastructure/oracle/price-age.js.map +1 -0
  257. package/dist/infrastructure/oracle/price-cache.d.ts +99 -0
  258. package/dist/infrastructure/oracle/price-cache.d.ts.map +1 -0
  259. package/dist/infrastructure/oracle/price-cache.js +173 -0
  260. package/dist/infrastructure/oracle/price-cache.js.map +1 -0
  261. package/dist/infrastructure/oracle/pyth-feed-ids.d.ts +31 -0
  262. package/dist/infrastructure/oracle/pyth-feed-ids.d.ts.map +1 -0
  263. package/dist/infrastructure/oracle/pyth-feed-ids.js +44 -0
  264. package/dist/infrastructure/oracle/pyth-feed-ids.js.map +1 -0
  265. package/dist/infrastructure/oracle/pyth-oracle.d.ts +69 -0
  266. package/dist/infrastructure/oracle/pyth-oracle.d.ts.map +1 -0
  267. package/dist/infrastructure/oracle/pyth-oracle.js +149 -0
  268. package/dist/infrastructure/oracle/pyth-oracle.js.map +1 -0
  269. package/dist/infrastructure/settings/hot-reload.d.ts +71 -0
  270. package/dist/infrastructure/settings/hot-reload.d.ts.map +1 -0
  271. package/dist/infrastructure/settings/hot-reload.js +315 -0
  272. package/dist/infrastructure/settings/hot-reload.js.map +1 -0
  273. package/dist/infrastructure/settings/index.d.ts +13 -0
  274. package/dist/infrastructure/settings/index.d.ts.map +1 -0
  275. package/dist/infrastructure/settings/index.js +10 -0
  276. package/dist/infrastructure/settings/index.js.map +1 -0
  277. package/dist/infrastructure/settings/setting-keys.d.ts +28 -0
  278. package/dist/infrastructure/settings/setting-keys.d.ts.map +1 -0
  279. package/dist/infrastructure/settings/setting-keys.js +105 -0
  280. package/dist/infrastructure/settings/setting-keys.js.map +1 -0
  281. package/dist/infrastructure/settings/settings-crypto.d.ts +39 -0
  282. package/dist/infrastructure/settings/settings-crypto.d.ts.map +1 -0
  283. package/dist/infrastructure/settings/settings-crypto.js +73 -0
  284. package/dist/infrastructure/settings/settings-crypto.js.map +1 -0
  285. package/dist/infrastructure/settings/settings-service.d.ts +82 -0
  286. package/dist/infrastructure/settings/settings-service.d.ts.map +1 -0
  287. package/dist/infrastructure/settings/settings-service.js +267 -0
  288. package/dist/infrastructure/settings/settings-service.js.map +1 -0
  289. package/dist/infrastructure/telegram/index.d.ts +6 -0
  290. package/dist/infrastructure/telegram/index.d.ts.map +1 -0
  291. package/dist/infrastructure/telegram/index.js +5 -0
  292. package/dist/infrastructure/telegram/index.js.map +1 -0
  293. package/dist/infrastructure/telegram/telegram-api.d.ts +35 -0
  294. package/dist/infrastructure/telegram/telegram-api.d.ts.map +1 -0
  295. package/dist/infrastructure/telegram/telegram-api.js +82 -0
  296. package/dist/infrastructure/telegram/telegram-api.js.map +1 -0
  297. package/dist/infrastructure/telegram/telegram-auth.d.ts +57 -0
  298. package/dist/infrastructure/telegram/telegram-auth.d.ts.map +1 -0
  299. package/dist/infrastructure/telegram/telegram-auth.js +88 -0
  300. package/dist/infrastructure/telegram/telegram-auth.js.map +1 -0
  301. package/dist/infrastructure/telegram/telegram-bot-service.d.ts +95 -0
  302. package/dist/infrastructure/telegram/telegram-bot-service.d.ts.map +1 -0
  303. package/dist/infrastructure/telegram/telegram-bot-service.js +564 -0
  304. package/dist/infrastructure/telegram/telegram-bot-service.js.map +1 -0
  305. package/dist/infrastructure/telegram/telegram-keyboard.d.ts +27 -0
  306. package/dist/infrastructure/telegram/telegram-keyboard.d.ts.map +1 -0
  307. package/dist/infrastructure/telegram/telegram-keyboard.js +52 -0
  308. package/dist/infrastructure/telegram/telegram-keyboard.js.map +1 -0
  309. package/dist/infrastructure/telegram/telegram-types.d.ts +43 -0
  310. package/dist/infrastructure/telegram/telegram-types.d.ts.map +1 -0
  311. package/dist/infrastructure/telegram/telegram-types.js +8 -0
  312. package/dist/infrastructure/telegram/telegram-types.js.map +1 -0
  313. package/dist/infrastructure/token-registry/builtin-tokens.d.ts +39 -0
  314. package/dist/infrastructure/token-registry/builtin-tokens.d.ts.map +1 -0
  315. package/dist/infrastructure/token-registry/builtin-tokens.js +135 -0
  316. package/dist/infrastructure/token-registry/builtin-tokens.js.map +1 -0
  317. package/dist/infrastructure/token-registry/index.d.ts +8 -0
  318. package/dist/infrastructure/token-registry/index.d.ts.map +1 -0
  319. package/dist/infrastructure/token-registry/index.js +8 -0
  320. package/dist/infrastructure/token-registry/index.js.map +1 -0
  321. package/dist/infrastructure/token-registry/token-registry-service.d.ts +49 -0
  322. package/dist/infrastructure/token-registry/token-registry-service.d.ts.map +1 -0
  323. package/dist/infrastructure/token-registry/token-registry-service.js +93 -0
  324. package/dist/infrastructure/token-registry/token-registry-service.js.map +1 -0
  325. package/dist/infrastructure/version/index.d.ts +5 -0
  326. package/dist/infrastructure/version/index.d.ts.map +1 -0
  327. package/dist/infrastructure/version/index.js +5 -0
  328. package/dist/infrastructure/version/index.js.map +1 -0
  329. package/dist/infrastructure/version/version-check-service.d.ts +35 -0
  330. package/dist/infrastructure/version/version-check-service.d.ts.map +1 -0
  331. package/dist/infrastructure/version/version-check-service.js +92 -0
  332. package/dist/infrastructure/version/version-check-service.js.map +1 -0
  333. package/dist/lifecycle/daemon.d.ts +103 -0
  334. package/dist/lifecycle/daemon.d.ts.map +1 -0
  335. package/dist/lifecycle/daemon.js +934 -0
  336. package/dist/lifecycle/daemon.js.map +1 -0
  337. package/dist/lifecycle/index.d.ts +9 -0
  338. package/dist/lifecycle/index.d.ts.map +1 -0
  339. package/dist/lifecycle/index.js +9 -0
  340. package/dist/lifecycle/index.js.map +1 -0
  341. package/dist/lifecycle/signal-handler.d.ts +18 -0
  342. package/dist/lifecycle/signal-handler.d.ts.map +1 -0
  343. package/dist/lifecycle/signal-handler.js +37 -0
  344. package/dist/lifecycle/signal-handler.js.map +1 -0
  345. package/dist/lifecycle/workers.d.ts +46 -0
  346. package/dist/lifecycle/workers.d.ts.map +1 -0
  347. package/dist/lifecycle/workers.js +101 -0
  348. package/dist/lifecycle/workers.js.map +1 -0
  349. package/dist/notifications/channels/discord.d.ts +10 -0
  350. package/dist/notifications/channels/discord.d.ts.map +1 -0
  351. package/dist/notifications/channels/discord.js +54 -0
  352. package/dist/notifications/channels/discord.js.map +1 -0
  353. package/dist/notifications/channels/ntfy.d.ts +13 -0
  354. package/dist/notifications/channels/ntfy.d.ts.map +1 -0
  355. package/dist/notifications/channels/ntfy.js +58 -0
  356. package/dist/notifications/channels/ntfy.js.map +1 -0
  357. package/dist/notifications/channels/slack.d.ts +10 -0
  358. package/dist/notifications/channels/slack.d.ts.map +1 -0
  359. package/dist/notifications/channels/slack.js +55 -0
  360. package/dist/notifications/channels/slack.js.map +1 -0
  361. package/dist/notifications/channels/telegram.d.ts +10 -0
  362. package/dist/notifications/channels/telegram.d.ts.map +1 -0
  363. package/dist/notifications/channels/telegram.js +40 -0
  364. package/dist/notifications/channels/telegram.js.map +1 -0
  365. package/dist/notifications/index.d.ts +9 -0
  366. package/dist/notifications/index.d.ts.map +1 -0
  367. package/dist/notifications/index.js +7 -0
  368. package/dist/notifications/index.js.map +1 -0
  369. package/dist/notifications/notification-service.d.ts +75 -0
  370. package/dist/notifications/notification-service.d.ts.map +1 -0
  371. package/dist/notifications/notification-service.js +213 -0
  372. package/dist/notifications/notification-service.js.map +1 -0
  373. package/dist/notifications/templates/message-templates.d.ts +12 -0
  374. package/dist/notifications/templates/message-templates.d.ts.map +1 -0
  375. package/dist/notifications/templates/message-templates.js +22 -0
  376. package/dist/notifications/templates/message-templates.js.map +1 -0
  377. package/dist/pipeline/database-policy-engine.d.ts +286 -0
  378. package/dist/pipeline/database-policy-engine.d.ts.map +1 -0
  379. package/dist/pipeline/database-policy-engine.js +992 -0
  380. package/dist/pipeline/database-policy-engine.js.map +1 -0
  381. package/dist/pipeline/default-policy-engine.d.ts +26 -0
  382. package/dist/pipeline/default-policy-engine.d.ts.map +1 -0
  383. package/dist/pipeline/default-policy-engine.js +25 -0
  384. package/dist/pipeline/default-policy-engine.js.map +1 -0
  385. package/dist/pipeline/index.d.ts +9 -0
  386. package/dist/pipeline/index.d.ts.map +1 -0
  387. package/dist/pipeline/index.js +9 -0
  388. package/dist/pipeline/index.js.map +1 -0
  389. package/dist/pipeline/network-resolver.d.ts +22 -0
  390. package/dist/pipeline/network-resolver.d.ts.map +1 -0
  391. package/dist/pipeline/network-resolver.js +32 -0
  392. package/dist/pipeline/network-resolver.js.map +1 -0
  393. package/dist/pipeline/pipeline.d.ts +72 -0
  394. package/dist/pipeline/pipeline.d.ts.map +1 -0
  395. package/dist/pipeline/pipeline.js +87 -0
  396. package/dist/pipeline/pipeline.js.map +1 -0
  397. package/dist/pipeline/resolve-effective-amount-usd.d.ts +41 -0
  398. package/dist/pipeline/resolve-effective-amount-usd.d.ts.map +1 -0
  399. package/dist/pipeline/resolve-effective-amount-usd.js +208 -0
  400. package/dist/pipeline/resolve-effective-amount-usd.js.map +1 -0
  401. package/dist/pipeline/sign-only.d.ts +99 -0
  402. package/dist/pipeline/sign-only.d.ts.map +1 -0
  403. package/dist/pipeline/sign-only.js +267 -0
  404. package/dist/pipeline/sign-only.js.map +1 -0
  405. package/dist/pipeline/sleep.d.ts +6 -0
  406. package/dist/pipeline/sleep.d.ts.map +1 -0
  407. package/dist/pipeline/sleep.js +8 -0
  408. package/dist/pipeline/sleep.js.map +1 -0
  409. package/dist/pipeline/stages.d.ts +82 -0
  410. package/dist/pipeline/stages.d.ts.map +1 -0
  411. package/dist/pipeline/stages.js +784 -0
  412. package/dist/pipeline/stages.js.map +1 -0
  413. package/dist/services/autostop-rules.d.ts +79 -0
  414. package/dist/services/autostop-rules.d.ts.map +1 -0
  415. package/dist/services/autostop-rules.js +174 -0
  416. package/dist/services/autostop-rules.js.map +1 -0
  417. package/dist/services/autostop-service.d.ts +82 -0
  418. package/dist/services/autostop-service.d.ts.map +1 -0
  419. package/dist/services/autostop-service.js +223 -0
  420. package/dist/services/autostop-service.js.map +1 -0
  421. package/dist/services/kill-switch-service.d.ts +118 -0
  422. package/dist/services/kill-switch-service.d.ts.map +1 -0
  423. package/dist/services/kill-switch-service.js +291 -0
  424. package/dist/services/kill-switch-service.js.map +1 -0
  425. package/dist/services/monitoring/balance-monitor-service.d.ts +65 -0
  426. package/dist/services/monitoring/balance-monitor-service.d.ts.map +1 -0
  427. package/dist/services/monitoring/balance-monitor-service.js +207 -0
  428. package/dist/services/monitoring/balance-monitor-service.js.map +1 -0
  429. package/dist/services/wc-session-service.d.ts +123 -0
  430. package/dist/services/wc-session-service.d.ts.map +1 -0
  431. package/dist/services/wc-session-service.js +363 -0
  432. package/dist/services/wc-session-service.js.map +1 -0
  433. package/dist/services/wc-signing-bridge.d.ts +60 -0
  434. package/dist/services/wc-signing-bridge.d.ts.map +1 -0
  435. package/dist/services/wc-signing-bridge.js +334 -0
  436. package/dist/services/wc-signing-bridge.js.map +1 -0
  437. package/dist/services/wc-storage.d.ts +32 -0
  438. package/dist/services/wc-storage.d.ts.map +1 -0
  439. package/dist/services/wc-storage.js +64 -0
  440. package/dist/services/wc-storage.js.map +1 -0
  441. package/dist/services/x402/payment-signer.d.ts +88 -0
  442. package/dist/services/x402/payment-signer.d.ts.map +1 -0
  443. package/dist/services/x402/payment-signer.js +311 -0
  444. package/dist/services/x402/payment-signer.js.map +1 -0
  445. package/dist/services/x402/ssrf-guard.d.ts +27 -0
  446. package/dist/services/x402/ssrf-guard.d.ts.map +1 -0
  447. package/dist/services/x402/ssrf-guard.js +236 -0
  448. package/dist/services/x402/ssrf-guard.js.map +1 -0
  449. package/dist/services/x402/x402-domain-policy.d.ts +50 -0
  450. package/dist/services/x402/x402-domain-policy.d.ts.map +1 -0
  451. package/dist/services/x402/x402-domain-policy.js +78 -0
  452. package/dist/services/x402/x402-domain-policy.js.map +1 -0
  453. package/dist/services/x402/x402-handler.d.ts +71 -0
  454. package/dist/services/x402/x402-handler.d.ts.map +1 -0
  455. package/dist/services/x402/x402-handler.js +195 -0
  456. package/dist/services/x402/x402-handler.js.map +1 -0
  457. package/dist/services/x402/x402-usd-resolver.d.ts +26 -0
  458. package/dist/services/x402/x402-usd-resolver.d.ts.map +1 -0
  459. package/dist/services/x402/x402-usd-resolver.js +79 -0
  460. package/dist/services/x402/x402-usd-resolver.js.map +1 -0
  461. package/dist/workflow/approval-workflow.d.ts +103 -0
  462. package/dist/workflow/approval-workflow.d.ts.map +1 -0
  463. package/dist/workflow/approval-workflow.js +202 -0
  464. package/dist/workflow/approval-workflow.js.map +1 -0
  465. package/dist/workflow/delay-queue.d.ts +78 -0
  466. package/dist/workflow/delay-queue.d.ts.map +1 -0
  467. package/dist/workflow/delay-queue.js +174 -0
  468. package/dist/workflow/delay-queue.js.map +1 -0
  469. package/dist/workflow/index.d.ts +11 -0
  470. package/dist/workflow/index.d.ts.map +1 -0
  471. package/dist/workflow/index.js +9 -0
  472. package/dist/workflow/index.js.map +1 -0
  473. package/dist/workflow/owner-state.d.ts +97 -0
  474. package/dist/workflow/owner-state.d.ts.map +1 -0
  475. package/dist/workflow/owner-state.js +168 -0
  476. package/dist/workflow/owner-state.js.map +1 -0
  477. package/package.json +71 -0
  478. package/public/admin/assets/index-BPoUSH8W.css +1 -0
  479. package/public/admin/assets/index-CDi1qoXB.js +1 -0
  480. package/public/admin/index.html +13 -0
@@ -0,0 +1,1214 @@
1
+ /**
2
+ * Schema push + incremental migration runner for daemon SQLite database.
3
+ *
4
+ * Creates all 15 tables with indexes, foreign keys, and CHECK constraints
5
+ * using CREATE TABLE IF NOT EXISTS statements. After initial schema creation,
6
+ * runs incremental migrations via runMigrations() for ALTER TABLE changes.
7
+ *
8
+ * v1.4+: DB schema changes MUST use ALTER TABLE incremental migrations (MIG-01~06).
9
+ * DB deletion and recreation is prohibited.
10
+ *
11
+ * v1.4.2: agents table renamed to wallets (v3 migration). DDL uses latest
12
+ * names (wallets, wallet_id). pushSchema records LATEST_SCHEMA_VERSION so
13
+ * migrations are only needed for existing (pre-v3) databases.
14
+ *
15
+ * v1.4.6: Environment model migration:
16
+ * v6a (version 6): Add network column to transactions with backfill from wallets
17
+ * v6b (version 7): Replace wallets.network with environment + default_network (12-step)
18
+ * v8 (version 8): Add network column to policies (12-step)
19
+ *
20
+ * @see docs/25-sqlite-schema.md
21
+ * @see docs/65-migration-strategy.md
22
+ * @see docs/69-db-migration-v6-design.md
23
+ * @see docs/71-policy-engine-network-extension-design.md
24
+ */
25
+ import { WALLET_STATUSES, CHAIN_TYPES, NETWORK_TYPES, ENVIRONMENT_TYPES, TRANSACTION_STATUSES, TRANSACTION_TYPES, POLICY_TYPES, POLICY_TIERS, NOTIFICATION_LOG_STATUSES, } from '@waiaas/core';
26
+ // ---------------------------------------------------------------------------
27
+ // Utility: build CHECK IN clause from SSoT arrays
28
+ // ---------------------------------------------------------------------------
29
+ const inList = (values) => values.map((v) => `'${v}'`).join(', ');
30
+ // ---------------------------------------------------------------------------
31
+ // DDL statements for all 15 tables (latest schema: wallets + wallet_id + token_registry + settings + api_keys + telegram_users + wc_sessions + wc_store)
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * The latest schema version that getCreateTableStatements() represents.
35
+ * pushSchema() records this version for fresh databases so migrations are skipped.
36
+ * Increment this whenever DDL statements are updated to match a new migration.
37
+ */
38
+ export const LATEST_SCHEMA_VERSION = 16;
39
+ function getCreateTableStatements() {
40
+ return [
41
+ // Table 1: wallets (renamed from agents in v3, environment model in v6b)
42
+ `CREATE TABLE IF NOT EXISTS wallets (
43
+ id TEXT PRIMARY KEY,
44
+ name TEXT NOT NULL,
45
+ chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
46
+ environment TEXT NOT NULL CHECK (environment IN (${inList(ENVIRONMENT_TYPES)})),
47
+ default_network TEXT CHECK (default_network IS NULL OR default_network IN (${inList(NETWORK_TYPES)})),
48
+ public_key TEXT NOT NULL,
49
+ status TEXT NOT NULL DEFAULT 'CREATING' CHECK (status IN (${inList(WALLET_STATUSES)})),
50
+ owner_address TEXT,
51
+ owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
52
+ created_at INTEGER NOT NULL,
53
+ updated_at INTEGER NOT NULL,
54
+ suspended_at INTEGER,
55
+ suspension_reason TEXT
56
+ )`,
57
+ // Table 2: sessions
58
+ `CREATE TABLE IF NOT EXISTS sessions (
59
+ id TEXT PRIMARY KEY,
60
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
61
+ token_hash TEXT NOT NULL,
62
+ expires_at INTEGER NOT NULL,
63
+ constraints TEXT,
64
+ usage_stats TEXT,
65
+ revoked_at INTEGER,
66
+ renewal_count INTEGER NOT NULL DEFAULT 0,
67
+ max_renewals INTEGER NOT NULL DEFAULT 30,
68
+ last_renewed_at INTEGER,
69
+ absolute_expires_at INTEGER NOT NULL,
70
+ created_at INTEGER NOT NULL
71
+ )`,
72
+ // Table 3: transactions
73
+ `CREATE TABLE IF NOT EXISTS transactions (
74
+ id TEXT PRIMARY KEY,
75
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
76
+ session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
77
+ chain TEXT NOT NULL,
78
+ tx_hash TEXT,
79
+ type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
80
+ amount TEXT,
81
+ to_address TEXT,
82
+ token_mint TEXT,
83
+ contract_address TEXT,
84
+ method_signature TEXT,
85
+ spender_address TEXT,
86
+ approved_amount TEXT,
87
+ parent_id TEXT REFERENCES transactions(id) ON DELETE CASCADE,
88
+ batch_index INTEGER,
89
+ status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
90
+ tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
91
+ queued_at INTEGER,
92
+ executed_at INTEGER,
93
+ created_at INTEGER NOT NULL,
94
+ reserved_amount TEXT,
95
+ amount_usd REAL,
96
+ reserved_amount_usd REAL,
97
+ error TEXT,
98
+ metadata TEXT,
99
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)}))
100
+ )`,
101
+ // Table 4: policies (network column added in v8)
102
+ `CREATE TABLE IF NOT EXISTS policies (
103
+ id TEXT PRIMARY KEY,
104
+ wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
105
+ type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
106
+ rules TEXT NOT NULL,
107
+ priority INTEGER NOT NULL DEFAULT 0,
108
+ enabled INTEGER NOT NULL DEFAULT 1,
109
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
110
+ created_at INTEGER NOT NULL,
111
+ updated_at INTEGER NOT NULL
112
+ )`,
113
+ // Table 5: pending_approvals (approval_channel added in v16)
114
+ `CREATE TABLE IF NOT EXISTS pending_approvals (
115
+ id TEXT PRIMARY KEY,
116
+ tx_id TEXT NOT NULL REFERENCES transactions(id) ON DELETE CASCADE,
117
+ required_by INTEGER NOT NULL,
118
+ expires_at INTEGER NOT NULL,
119
+ approved_at INTEGER,
120
+ rejected_at INTEGER,
121
+ owner_signature TEXT,
122
+ approval_channel TEXT DEFAULT 'rest_api',
123
+ created_at INTEGER NOT NULL
124
+ )`,
125
+ // Table 6: audit_log
126
+ `CREATE TABLE IF NOT EXISTS audit_log (
127
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
128
+ timestamp INTEGER NOT NULL,
129
+ event_type TEXT NOT NULL,
130
+ actor TEXT NOT NULL,
131
+ wallet_id TEXT,
132
+ session_id TEXT,
133
+ tx_id TEXT,
134
+ details TEXT NOT NULL,
135
+ severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
136
+ ip_address TEXT
137
+ )`,
138
+ // Table 7: key_value_store
139
+ `CREATE TABLE IF NOT EXISTS key_value_store (
140
+ key TEXT PRIMARY KEY,
141
+ value TEXT NOT NULL,
142
+ updated_at INTEGER NOT NULL
143
+ )`,
144
+ // Table 8: notification_logs
145
+ `CREATE TABLE IF NOT EXISTS notification_logs (
146
+ id TEXT PRIMARY KEY,
147
+ event_type TEXT NOT NULL,
148
+ wallet_id TEXT,
149
+ channel TEXT NOT NULL,
150
+ status TEXT NOT NULL CHECK (status IN (${inList(NOTIFICATION_LOG_STATUSES)})),
151
+ error TEXT,
152
+ message TEXT,
153
+ created_at INTEGER NOT NULL
154
+ )`,
155
+ // Table 9: token_registry
156
+ `CREATE TABLE IF NOT EXISTS token_registry (
157
+ id TEXT PRIMARY KEY,
158
+ network TEXT NOT NULL,
159
+ address TEXT NOT NULL,
160
+ symbol TEXT NOT NULL,
161
+ name TEXT NOT NULL,
162
+ decimals INTEGER NOT NULL,
163
+ source TEXT NOT NULL DEFAULT 'custom' CHECK (source IN ('builtin', 'custom')),
164
+ created_at INTEGER NOT NULL
165
+ )`,
166
+ // Table 10: settings
167
+ `CREATE TABLE IF NOT EXISTS settings (
168
+ key TEXT PRIMARY KEY,
169
+ value TEXT NOT NULL,
170
+ encrypted INTEGER NOT NULL DEFAULT 0 CHECK (encrypted IN (0, 1)),
171
+ category TEXT NOT NULL,
172
+ updated_at INTEGER NOT NULL
173
+ )`,
174
+ // Table 11: api_keys (Action Provider API key encrypted storage, v1.5)
175
+ `CREATE TABLE IF NOT EXISTS api_keys (
176
+ provider_name TEXT PRIMARY KEY,
177
+ encrypted_key TEXT NOT NULL,
178
+ created_at INTEGER NOT NULL,
179
+ updated_at INTEGER NOT NULL
180
+ )`,
181
+ // Table 12: schema_version
182
+ `CREATE TABLE IF NOT EXISTS schema_version (
183
+ version INTEGER PRIMARY KEY,
184
+ applied_at INTEGER NOT NULL,
185
+ description TEXT NOT NULL
186
+ )`,
187
+ // Table 13: telegram_users (Telegram Bot user management, v1.6)
188
+ `CREATE TABLE IF NOT EXISTS telegram_users (
189
+ chat_id INTEGER PRIMARY KEY,
190
+ username TEXT,
191
+ role TEXT NOT NULL DEFAULT 'PENDING' CHECK (role IN ('PENDING', 'ADMIN', 'READONLY')),
192
+ registered_at INTEGER NOT NULL,
193
+ approved_at INTEGER
194
+ )`,
195
+ // Table 14: wc_sessions (WalletConnect session metadata, v1.6.1)
196
+ `CREATE TABLE IF NOT EXISTS wc_sessions (
197
+ wallet_id TEXT PRIMARY KEY REFERENCES wallets(id) ON DELETE CASCADE,
198
+ topic TEXT NOT NULL UNIQUE,
199
+ peer_meta TEXT,
200
+ chain_id TEXT NOT NULL,
201
+ owner_address TEXT NOT NULL,
202
+ namespaces TEXT,
203
+ expiry INTEGER NOT NULL,
204
+ created_at INTEGER NOT NULL
205
+ )`,
206
+ // Table 15: wc_store (WalletConnect IKeyValueStorage, v1.6.1)
207
+ `CREATE TABLE IF NOT EXISTS wc_store (
208
+ key TEXT PRIMARY KEY,
209
+ value TEXT NOT NULL
210
+ )`,
211
+ ];
212
+ }
213
+ // ---------------------------------------------------------------------------
214
+ // Index creation statements
215
+ // ---------------------------------------------------------------------------
216
+ function getCreateIndexStatements() {
217
+ return [
218
+ // wallets indexes (renamed from agents in v3)
219
+ 'CREATE UNIQUE INDEX IF NOT EXISTS idx_wallets_public_key ON wallets(public_key)',
220
+ 'CREATE INDEX IF NOT EXISTS idx_wallets_status ON wallets(status)',
221
+ 'CREATE INDEX IF NOT EXISTS idx_wallets_chain_environment ON wallets(chain, environment)',
222
+ 'CREATE INDEX IF NOT EXISTS idx_wallets_owner_address ON wallets(owner_address)',
223
+ // sessions indexes
224
+ 'CREATE INDEX IF NOT EXISTS idx_sessions_wallet_id ON sessions(wallet_id)',
225
+ 'CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at)',
226
+ 'CREATE INDEX IF NOT EXISTS idx_sessions_token_hash ON sessions(token_hash)',
227
+ // transactions indexes
228
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_wallet_status ON transactions(wallet_id, status)',
229
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_session_id ON transactions(session_id)',
230
+ 'CREATE UNIQUE INDEX IF NOT EXISTS idx_transactions_tx_hash ON transactions(tx_hash)',
231
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_queued_at ON transactions(queued_at)',
232
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_created_at ON transactions(created_at)',
233
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(type)',
234
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_contract_address ON transactions(contract_address)',
235
+ 'CREATE INDEX IF NOT EXISTS idx_transactions_parent_id ON transactions(parent_id)',
236
+ // policies indexes
237
+ 'CREATE INDEX IF NOT EXISTS idx_policies_wallet_enabled ON policies(wallet_id, enabled)',
238
+ 'CREATE INDEX IF NOT EXISTS idx_policies_type ON policies(type)',
239
+ 'CREATE INDEX IF NOT EXISTS idx_policies_network ON policies(network)',
240
+ // pending_approvals indexes
241
+ 'CREATE INDEX IF NOT EXISTS idx_pending_approvals_tx_id ON pending_approvals(tx_id)',
242
+ 'CREATE INDEX IF NOT EXISTS idx_pending_approvals_expires_at ON pending_approvals(expires_at)',
243
+ // audit_log indexes
244
+ 'CREATE INDEX IF NOT EXISTS idx_audit_log_timestamp ON audit_log(timestamp)',
245
+ 'CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)',
246
+ 'CREATE INDEX IF NOT EXISTS idx_audit_log_wallet_id ON audit_log(wallet_id)',
247
+ 'CREATE INDEX IF NOT EXISTS idx_audit_log_severity ON audit_log(severity)',
248
+ 'CREATE INDEX IF NOT EXISTS idx_audit_log_wallet_timestamp ON audit_log(wallet_id, timestamp)',
249
+ // notification_logs indexes
250
+ 'CREATE INDEX IF NOT EXISTS idx_notification_logs_event_type ON notification_logs(event_type)',
251
+ 'CREATE INDEX IF NOT EXISTS idx_notification_logs_wallet_id ON notification_logs(wallet_id)',
252
+ 'CREATE INDEX IF NOT EXISTS idx_notification_logs_status ON notification_logs(status)',
253
+ 'CREATE INDEX IF NOT EXISTS idx_notification_logs_created_at ON notification_logs(created_at)',
254
+ // token_registry indexes
255
+ 'CREATE UNIQUE INDEX IF NOT EXISTS idx_token_registry_network_address ON token_registry(network, address)',
256
+ 'CREATE INDEX IF NOT EXISTS idx_token_registry_network ON token_registry(network)',
257
+ // settings indexes
258
+ 'CREATE INDEX IF NOT EXISTS idx_settings_category ON settings(category)',
259
+ // telegram_users indexes
260
+ 'CREATE INDEX IF NOT EXISTS idx_telegram_users_role ON telegram_users(role)',
261
+ // wc_sessions indexes
262
+ 'CREATE INDEX IF NOT EXISTS idx_wc_sessions_topic ON wc_sessions(topic)',
263
+ ];
264
+ }
265
+ /**
266
+ * Global migration registry. v1.4 migrations will be added here in subsequent phases.
267
+ * Each migration's version must be unique and greater than 1.
268
+ */
269
+ export const MIGRATIONS = [];
270
+ // ---------------------------------------------------------------------------
271
+ // v2: Expand agents.network CHECK to include EVM networks
272
+ // ---------------------------------------------------------------------------
273
+ // SQLite cannot ALTER CHECK constraints, so we use 12-step table recreation.
274
+ // This requires PRAGMA foreign_keys=OFF (handled by managesOwnTransaction).
275
+ MIGRATIONS.push({
276
+ version: 2,
277
+ description: 'Expand agents network CHECK to include EVM networks',
278
+ managesOwnTransaction: true,
279
+ up: (sqlite) => {
280
+ // Step 1: Begin transaction (foreign_keys already OFF via runner)
281
+ sqlite.exec('BEGIN');
282
+ try {
283
+ // Step 2: Create new agents table with expanded CHECK (uses SSoT arrays)
284
+ sqlite.exec(`CREATE TABLE agents_new (
285
+ id TEXT PRIMARY KEY,
286
+ name TEXT NOT NULL,
287
+ chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
288
+ network TEXT NOT NULL CHECK (network IN (${inList(NETWORK_TYPES)})),
289
+ public_key TEXT NOT NULL,
290
+ status TEXT NOT NULL DEFAULT 'CREATING' CHECK (status IN (${inList(WALLET_STATUSES)})),
291
+ owner_address TEXT,
292
+ owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
293
+ created_at INTEGER NOT NULL,
294
+ updated_at INTEGER NOT NULL,
295
+ suspended_at INTEGER,
296
+ suspension_reason TEXT
297
+ )`);
298
+ // Step 3: Copy existing data
299
+ sqlite.exec('INSERT INTO agents_new SELECT * FROM agents');
300
+ // Step 4: Drop old table
301
+ sqlite.exec('DROP TABLE agents');
302
+ // Step 5: Rename new table
303
+ sqlite.exec('ALTER TABLE agents_new RENAME TO agents');
304
+ // Step 6: Recreate indexes
305
+ sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_public_key ON agents(public_key)');
306
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status)');
307
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agents_chain_network ON agents(chain, network)');
308
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agents_owner_address ON agents(owner_address)');
309
+ // Step 7: Commit transaction
310
+ sqlite.exec('COMMIT');
311
+ }
312
+ catch (err) {
313
+ sqlite.exec('ROLLBACK');
314
+ throw err;
315
+ }
316
+ // Step 8: Re-enable foreign keys to run integrity check
317
+ sqlite.pragma('foreign_keys = ON');
318
+ // Step 9: Verify FK integrity
319
+ const fkErrors = sqlite.pragma('foreign_key_check');
320
+ if (fkErrors.length > 0) {
321
+ throw new Error(`FK integrity check failed after v2 migration: ${JSON.stringify(fkErrors)}`);
322
+ }
323
+ // Note: Runner will also set foreign_keys = ON after we return,
324
+ // but we set it here to run the integrity check with FK enabled.
325
+ },
326
+ });
327
+ // ---------------------------------------------------------------------------
328
+ // v3: Rename agents to wallets (table, FK columns, indexes, enum data)
329
+ // ---------------------------------------------------------------------------
330
+ // Renames agents table to wallets, agent_id columns to wallet_id in 5 tables,
331
+ // recreates all affected indexes, and updates AGENT_* enum data to WALLET_*.
332
+ // Requires PRAGMA foreign_keys=OFF for table recreation (managesOwnTransaction).
333
+ MIGRATIONS.push({
334
+ version: 3,
335
+ description: 'Rename agents to wallets (table, FK columns, indexes, enum data)',
336
+ managesOwnTransaction: true,
337
+ up: (sqlite) => {
338
+ sqlite.exec('BEGIN');
339
+ try {
340
+ // Step 1: Rename agents table to wallets
341
+ sqlite.exec('ALTER TABLE agents RENAME TO wallets');
342
+ // Step 1b: Drop old agents indexes (ALTER TABLE RENAME doesn't rename indexes)
343
+ sqlite.exec('DROP INDEX IF EXISTS idx_agents_public_key');
344
+ sqlite.exec('DROP INDEX IF EXISTS idx_agents_status');
345
+ sqlite.exec('DROP INDEX IF EXISTS idx_agents_chain_network');
346
+ sqlite.exec('DROP INDEX IF EXISTS idx_agents_owner_address');
347
+ // Step 2: Recreate sessions with wallet_id instead of agent_id
348
+ sqlite.exec(`CREATE TABLE sessions_new (
349
+ id TEXT PRIMARY KEY,
350
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
351
+ token_hash TEXT NOT NULL,
352
+ expires_at INTEGER NOT NULL,
353
+ constraints TEXT,
354
+ usage_stats TEXT,
355
+ revoked_at INTEGER,
356
+ renewal_count INTEGER NOT NULL DEFAULT 0,
357
+ max_renewals INTEGER NOT NULL DEFAULT 30,
358
+ last_renewed_at INTEGER,
359
+ absolute_expires_at INTEGER NOT NULL,
360
+ created_at INTEGER NOT NULL
361
+ )`);
362
+ sqlite.exec(`INSERT INTO sessions_new (id, wallet_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at)
363
+ SELECT id, agent_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at FROM sessions`);
364
+ sqlite.exec('DROP TABLE sessions');
365
+ sqlite.exec('ALTER TABLE sessions_new RENAME TO sessions');
366
+ // Step 3: Recreate transactions with wallet_id instead of agent_id
367
+ sqlite.exec(`CREATE TABLE transactions_new (
368
+ id TEXT PRIMARY KEY,
369
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
370
+ session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
371
+ chain TEXT NOT NULL,
372
+ tx_hash TEXT,
373
+ type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
374
+ amount TEXT,
375
+ to_address TEXT,
376
+ token_mint TEXT,
377
+ contract_address TEXT,
378
+ method_signature TEXT,
379
+ spender_address TEXT,
380
+ approved_amount TEXT,
381
+ parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
382
+ batch_index INTEGER,
383
+ status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
384
+ tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
385
+ queued_at INTEGER,
386
+ executed_at INTEGER,
387
+ created_at INTEGER NOT NULL,
388
+ reserved_amount TEXT,
389
+ error TEXT,
390
+ metadata TEXT
391
+ )`);
392
+ sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata)
393
+ SELECT id, agent_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata FROM transactions`);
394
+ sqlite.exec('DROP TABLE transactions');
395
+ sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
396
+ // Step 4: Recreate policies with wallet_id instead of agent_id
397
+ sqlite.exec(`CREATE TABLE policies_new (
398
+ id TEXT PRIMARY KEY,
399
+ wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
400
+ type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
401
+ rules TEXT NOT NULL,
402
+ priority INTEGER NOT NULL DEFAULT 0,
403
+ enabled INTEGER NOT NULL DEFAULT 1,
404
+ created_at INTEGER NOT NULL,
405
+ updated_at INTEGER NOT NULL
406
+ )`);
407
+ sqlite.exec(`INSERT INTO policies_new (id, wallet_id, type, rules, priority, enabled, created_at, updated_at)
408
+ SELECT id, agent_id, type, rules, priority, enabled, created_at, updated_at FROM policies`);
409
+ sqlite.exec('DROP TABLE policies');
410
+ sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
411
+ // Step 5: Recreate audit_log with wallet_id instead of agent_id (no FK constraint)
412
+ sqlite.exec(`CREATE TABLE audit_log_new (
413
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
414
+ timestamp INTEGER NOT NULL,
415
+ event_type TEXT NOT NULL,
416
+ actor TEXT NOT NULL,
417
+ wallet_id TEXT,
418
+ session_id TEXT,
419
+ tx_id TEXT,
420
+ details TEXT NOT NULL,
421
+ severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
422
+ ip_address TEXT
423
+ )`);
424
+ sqlite.exec(`INSERT INTO audit_log_new (id, timestamp, event_type, actor, wallet_id, session_id, tx_id, details, severity, ip_address)
425
+ SELECT id, timestamp, event_type, actor, agent_id, session_id, tx_id, details, severity, ip_address FROM audit_log`);
426
+ sqlite.exec('DROP TABLE audit_log');
427
+ sqlite.exec('ALTER TABLE audit_log_new RENAME TO audit_log');
428
+ // Step 6: Recreate notification_logs with wallet_id instead of agent_id (no FK constraint)
429
+ sqlite.exec(`CREATE TABLE notification_logs_new (
430
+ id TEXT PRIMARY KEY,
431
+ event_type TEXT NOT NULL,
432
+ wallet_id TEXT,
433
+ channel TEXT NOT NULL,
434
+ status TEXT NOT NULL CHECK (status IN (${inList(NOTIFICATION_LOG_STATUSES)})),
435
+ error TEXT,
436
+ created_at INTEGER NOT NULL
437
+ )`);
438
+ sqlite.exec(`INSERT INTO notification_logs_new (id, event_type, wallet_id, channel, status, error, created_at)
439
+ SELECT id, event_type, agent_id, channel, status, error, created_at FROM notification_logs`);
440
+ sqlite.exec('DROP TABLE notification_logs');
441
+ sqlite.exec('ALTER TABLE notification_logs_new RENAME TO notification_logs');
442
+ // Step 7: Recreate all indexes with wallet naming
443
+ // wallets table indexes
444
+ sqlite.exec('CREATE UNIQUE INDEX idx_wallets_public_key ON wallets(public_key)');
445
+ sqlite.exec('CREATE INDEX idx_wallets_status ON wallets(status)');
446
+ sqlite.exec('CREATE INDEX idx_wallets_chain_network ON wallets(chain, network)');
447
+ sqlite.exec('CREATE INDEX idx_wallets_owner_address ON wallets(owner_address)');
448
+ // sessions indexes (all recreated because table was dropped/recreated)
449
+ sqlite.exec('CREATE INDEX idx_sessions_wallet_id ON sessions(wallet_id)');
450
+ sqlite.exec('CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)');
451
+ sqlite.exec('CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)');
452
+ // transactions indexes (all recreated because table was dropped/recreated)
453
+ sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
454
+ sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
455
+ sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
456
+ sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
457
+ sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
458
+ sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
459
+ sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
460
+ sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
461
+ // policies indexes (all recreated because table was dropped/recreated)
462
+ sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
463
+ sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
464
+ // audit_log indexes (all recreated because table was dropped/recreated)
465
+ sqlite.exec('CREATE INDEX idx_audit_log_timestamp ON audit_log(timestamp)');
466
+ sqlite.exec('CREATE INDEX idx_audit_log_event_type ON audit_log(event_type)');
467
+ sqlite.exec('CREATE INDEX idx_audit_log_wallet_id ON audit_log(wallet_id)');
468
+ sqlite.exec('CREATE INDEX idx_audit_log_severity ON audit_log(severity)');
469
+ sqlite.exec('CREATE INDEX idx_audit_log_wallet_timestamp ON audit_log(wallet_id, timestamp)');
470
+ // notification_logs indexes (all recreated because table was dropped/recreated)
471
+ sqlite.exec('CREATE INDEX idx_notification_logs_event_type ON notification_logs(event_type)');
472
+ sqlite.exec('CREATE INDEX idx_notification_logs_wallet_id ON notification_logs(wallet_id)');
473
+ sqlite.exec('CREATE INDEX idx_notification_logs_status ON notification_logs(status)');
474
+ sqlite.exec('CREATE INDEX idx_notification_logs_created_at ON notification_logs(created_at)');
475
+ // Step 8: Update audit_log.event_type AGENT_* values to WALLET_*
476
+ sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_CREATED' WHERE event_type = 'AGENT_CREATED'");
477
+ sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_ACTIVATED' WHERE event_type = 'AGENT_ACTIVATED'");
478
+ sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_SUSPENDED' WHERE event_type = 'AGENT_SUSPENDED'");
479
+ sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_TERMINATED' WHERE event_type = 'AGENT_TERMINATED'");
480
+ // Step 9: Update notification_logs.event_type AGENT_SUSPENDED to WALLET_SUSPENDED
481
+ sqlite.exec("UPDATE notification_logs SET event_type = 'WALLET_SUSPENDED' WHERE event_type = 'AGENT_SUSPENDED'");
482
+ sqlite.exec('COMMIT');
483
+ }
484
+ catch (err) {
485
+ sqlite.exec('ROLLBACK');
486
+ throw err;
487
+ }
488
+ // Re-enable FK and verify integrity
489
+ sqlite.pragma('foreign_keys = ON');
490
+ const fkErrors = sqlite.pragma('foreign_key_check');
491
+ if (fkErrors.length > 0) {
492
+ throw new Error(`FK integrity check failed after v3 migration: ${JSON.stringify(fkErrors)}`);
493
+ }
494
+ },
495
+ });
496
+ // ---------------------------------------------------------------------------
497
+ // v4: Create token_registry table for EVM token management
498
+ // ---------------------------------------------------------------------------
499
+ MIGRATIONS.push({
500
+ version: 4,
501
+ description: 'Create token_registry table for EVM token management',
502
+ up: (sqlite) => {
503
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS token_registry (
504
+ id TEXT PRIMARY KEY,
505
+ network TEXT NOT NULL,
506
+ address TEXT NOT NULL,
507
+ symbol TEXT NOT NULL,
508
+ name TEXT NOT NULL,
509
+ decimals INTEGER NOT NULL,
510
+ source TEXT NOT NULL DEFAULT 'custom' CHECK (source IN ('builtin', 'custom')),
511
+ created_at INTEGER NOT NULL
512
+ )`);
513
+ sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_token_registry_network_address ON token_registry(network, address)');
514
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_token_registry_network ON token_registry(network)');
515
+ },
516
+ });
517
+ // ---------------------------------------------------------------------------
518
+ // v5: Create settings table for operational config DB storage
519
+ // ---------------------------------------------------------------------------
520
+ MIGRATIONS.push({
521
+ version: 5,
522
+ description: 'Create settings table for operational config DB storage',
523
+ up: (sqlite) => {
524
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS settings (
525
+ key TEXT PRIMARY KEY,
526
+ value TEXT NOT NULL,
527
+ encrypted INTEGER NOT NULL DEFAULT 0 CHECK (encrypted IN (0, 1)),
528
+ category TEXT NOT NULL,
529
+ updated_at INTEGER NOT NULL
530
+ )`);
531
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_settings_category ON settings(category)');
532
+ },
533
+ });
534
+ // ---------------------------------------------------------------------------
535
+ // v6a: Add network column to transactions with backfill from wallets
536
+ // ---------------------------------------------------------------------------
537
+ // Standard migration (managesOwnTransaction: false). Adds nullable network
538
+ // column to transactions and backfills from wallets.network via FK relationship.
539
+ // Must run BEFORE v6b which removes wallets.network.
540
+ MIGRATIONS.push({
541
+ version: 6,
542
+ description: 'Add network column to transactions with backfill from wallets',
543
+ managesOwnTransaction: false,
544
+ up: (sqlite) => {
545
+ // SQL 1: Add nullable network column
546
+ sqlite.exec('ALTER TABLE transactions ADD COLUMN network TEXT');
547
+ // SQL 2: Backfill from wallets.network via FK relationship
548
+ sqlite.exec(`UPDATE transactions SET network = (
549
+ SELECT w.network FROM wallets w WHERE w.id = transactions.wallet_id
550
+ )`);
551
+ },
552
+ });
553
+ // ---------------------------------------------------------------------------
554
+ // v6b: Replace wallets.network with environment + default_network (12-step)
555
+ // ---------------------------------------------------------------------------
556
+ // 12-step table recreation. Converts wallets.network to environment + default_network.
557
+ // Recreates FK dependent tables (sessions, transactions, policies, audit_log).
558
+ // Requires PRAGMA foreign_keys=OFF (handled by managesOwnTransaction).
559
+ // @see docs/69-db-migration-v6-design.md section 3
560
+ MIGRATIONS.push({
561
+ version: 7,
562
+ description: 'Replace wallets.network with environment + default_network (12-step recreation)',
563
+ managesOwnTransaction: true,
564
+ up: (sqlite) => {
565
+ // Step 1: Begin transaction
566
+ sqlite.exec('BEGIN');
567
+ try {
568
+ // Step 2: Create wallets_new with environment + default_network
569
+ sqlite.exec(`CREATE TABLE wallets_new (
570
+ id TEXT PRIMARY KEY,
571
+ name TEXT NOT NULL,
572
+ chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
573
+ environment TEXT NOT NULL CHECK (environment IN (${inList(ENVIRONMENT_TYPES)})),
574
+ default_network TEXT CHECK (default_network IS NULL OR default_network IN (${inList(NETWORK_TYPES)})),
575
+ public_key TEXT NOT NULL,
576
+ status TEXT NOT NULL DEFAULT 'CREATING' CHECK (status IN (${inList(WALLET_STATUSES)})),
577
+ owner_address TEXT,
578
+ owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
579
+ created_at INTEGER NOT NULL,
580
+ updated_at INTEGER NOT NULL,
581
+ suspended_at INTEGER,
582
+ suspension_reason TEXT
583
+ )`);
584
+ // Step 3: Data transformation INSERT with 13 CASE WHEN branches
585
+ // Maps network -> environment using deriveEnvironment() logic (docs/68 section 3.3)
586
+ // Preserves original network as default_network
587
+ sqlite.exec(`INSERT INTO wallets_new (
588
+ id, name, chain, environment, default_network,
589
+ public_key, status, owner_address, owner_verified,
590
+ created_at, updated_at, suspended_at, suspension_reason
591
+ )
592
+ SELECT
593
+ id, name, chain,
594
+ CASE
595
+ WHEN network = 'mainnet' THEN 'mainnet'
596
+ WHEN network = 'devnet' THEN 'testnet'
597
+ WHEN network = 'testnet' THEN 'testnet'
598
+ WHEN network = 'ethereum-mainnet' THEN 'mainnet'
599
+ WHEN network = 'polygon-mainnet' THEN 'mainnet'
600
+ WHEN network = 'arbitrum-mainnet' THEN 'mainnet'
601
+ WHEN network = 'optimism-mainnet' THEN 'mainnet'
602
+ WHEN network = 'base-mainnet' THEN 'mainnet'
603
+ WHEN network = 'ethereum-sepolia' THEN 'testnet'
604
+ WHEN network = 'polygon-amoy' THEN 'testnet'
605
+ WHEN network = 'arbitrum-sepolia' THEN 'testnet'
606
+ WHEN network = 'optimism-sepolia' THEN 'testnet'
607
+ WHEN network = 'base-sepolia' THEN 'testnet'
608
+ ELSE 'testnet'
609
+ END AS environment,
610
+ network AS default_network,
611
+ public_key, status, owner_address, owner_verified,
612
+ created_at, updated_at, suspended_at, suspension_reason
613
+ FROM wallets`);
614
+ // Step 4: Drop old wallets table
615
+ sqlite.exec('DROP TABLE wallets');
616
+ // Step 5: Rename new table
617
+ sqlite.exec('ALTER TABLE wallets_new RENAME TO wallets');
618
+ // Step 6: Recreate wallets indexes
619
+ sqlite.exec('DROP INDEX IF EXISTS idx_wallets_chain_network');
620
+ sqlite.exec('CREATE UNIQUE INDEX idx_wallets_public_key ON wallets(public_key)');
621
+ sqlite.exec('CREATE INDEX idx_wallets_status ON wallets(status)');
622
+ sqlite.exec('CREATE INDEX idx_wallets_chain_environment ON wallets(chain, environment)');
623
+ sqlite.exec('CREATE INDEX idx_wallets_owner_address ON wallets(owner_address)');
624
+ // Step 7: Recreate sessions (FK reconnection, no schema change)
625
+ sqlite.exec(`CREATE TABLE sessions_new (
626
+ id TEXT PRIMARY KEY,
627
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
628
+ token_hash TEXT NOT NULL,
629
+ expires_at INTEGER NOT NULL,
630
+ constraints TEXT,
631
+ usage_stats TEXT,
632
+ revoked_at INTEGER,
633
+ renewal_count INTEGER NOT NULL DEFAULT 0,
634
+ max_renewals INTEGER NOT NULL DEFAULT 30,
635
+ last_renewed_at INTEGER,
636
+ absolute_expires_at INTEGER NOT NULL,
637
+ created_at INTEGER NOT NULL
638
+ )`);
639
+ sqlite.exec(`INSERT INTO sessions_new (id, wallet_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at)
640
+ SELECT id, wallet_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at FROM sessions`);
641
+ sqlite.exec('DROP TABLE sessions');
642
+ sqlite.exec('ALTER TABLE sessions_new RENAME TO sessions');
643
+ sqlite.exec('CREATE INDEX idx_sessions_wallet_id ON sessions(wallet_id)');
644
+ sqlite.exec('CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)');
645
+ sqlite.exec('CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)');
646
+ // Step 8: Recreate transactions (network column with CHECK, FK reconnection)
647
+ sqlite.exec(`CREATE TABLE transactions_new (
648
+ id TEXT PRIMARY KEY,
649
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
650
+ session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
651
+ chain TEXT NOT NULL,
652
+ tx_hash TEXT,
653
+ type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
654
+ amount TEXT,
655
+ to_address TEXT,
656
+ token_mint TEXT,
657
+ contract_address TEXT,
658
+ method_signature TEXT,
659
+ spender_address TEXT,
660
+ approved_amount TEXT,
661
+ parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
662
+ batch_index INTEGER,
663
+ status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
664
+ tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
665
+ queued_at INTEGER,
666
+ executed_at INTEGER,
667
+ created_at INTEGER NOT NULL,
668
+ reserved_amount TEXT,
669
+ error TEXT,
670
+ metadata TEXT,
671
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)}))
672
+ )`);
673
+ sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network)
674
+ SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network FROM transactions`);
675
+ sqlite.exec('DROP TABLE transactions');
676
+ sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
677
+ sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
678
+ sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
679
+ sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
680
+ sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
681
+ sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
682
+ sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
683
+ sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
684
+ sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
685
+ // Step 9: Recreate policies (FK reconnection, no schema change -- v8 adds network)
686
+ sqlite.exec(`CREATE TABLE policies_new (
687
+ id TEXT PRIMARY KEY,
688
+ wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
689
+ type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
690
+ rules TEXT NOT NULL,
691
+ priority INTEGER NOT NULL DEFAULT 0,
692
+ enabled INTEGER NOT NULL DEFAULT 1,
693
+ created_at INTEGER NOT NULL,
694
+ updated_at INTEGER NOT NULL
695
+ )`);
696
+ sqlite.exec(`INSERT INTO policies_new (id, wallet_id, type, rules, priority, enabled, created_at, updated_at)
697
+ SELECT id, wallet_id, type, rules, priority, enabled, created_at, updated_at FROM policies`);
698
+ sqlite.exec('DROP TABLE policies');
699
+ sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
700
+ sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
701
+ sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
702
+ // Step 10: Recreate audit_log (consistency with v3 pattern)
703
+ sqlite.exec(`CREATE TABLE audit_log_new (
704
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
705
+ timestamp INTEGER NOT NULL,
706
+ event_type TEXT NOT NULL,
707
+ actor TEXT NOT NULL,
708
+ wallet_id TEXT,
709
+ session_id TEXT,
710
+ tx_id TEXT,
711
+ details TEXT NOT NULL,
712
+ severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
713
+ ip_address TEXT
714
+ )`);
715
+ sqlite.exec(`INSERT INTO audit_log_new (id, timestamp, event_type, actor, wallet_id, session_id, tx_id, details, severity, ip_address)
716
+ SELECT id, timestamp, event_type, actor, wallet_id, session_id, tx_id, details, severity, ip_address FROM audit_log`);
717
+ sqlite.exec('DROP TABLE audit_log');
718
+ sqlite.exec('ALTER TABLE audit_log_new RENAME TO audit_log');
719
+ sqlite.exec('CREATE INDEX idx_audit_log_timestamp ON audit_log(timestamp)');
720
+ sqlite.exec('CREATE INDEX idx_audit_log_event_type ON audit_log(event_type)');
721
+ sqlite.exec('CREATE INDEX idx_audit_log_wallet_id ON audit_log(wallet_id)');
722
+ sqlite.exec('CREATE INDEX idx_audit_log_severity ON audit_log(severity)');
723
+ sqlite.exec('CREATE INDEX idx_audit_log_wallet_timestamp ON audit_log(wallet_id, timestamp)');
724
+ // Step 11: Commit transaction
725
+ sqlite.exec('COMMIT');
726
+ }
727
+ catch (err) {
728
+ sqlite.exec('ROLLBACK');
729
+ throw err;
730
+ }
731
+ // Step 12: Re-enable foreign keys and verify integrity
732
+ sqlite.pragma('foreign_keys = ON');
733
+ const fkErrors = sqlite.pragma('foreign_key_check');
734
+ if (fkErrors.length > 0) {
735
+ throw new Error(`FK integrity violation after v6b: ${JSON.stringify(fkErrors)}`);
736
+ }
737
+ },
738
+ });
739
+ // ---------------------------------------------------------------------------
740
+ // v8: Add network column to policies (12-step recreation)
741
+ // ---------------------------------------------------------------------------
742
+ // Adds nullable network column and updates type CHECK to include ALLOWED_NETWORKS.
743
+ // Requires 12-step recreation because SQLite cannot ALTER CHECK constraints.
744
+ // @see docs/71-policy-engine-network-extension-design.md section 6
745
+ MIGRATIONS.push({
746
+ version: 8,
747
+ description: 'Add network column to policies and ALLOWED_NETWORKS type support',
748
+ managesOwnTransaction: true,
749
+ up: (sqlite) => {
750
+ // Step 1: Begin transaction
751
+ sqlite.exec('BEGIN');
752
+ try {
753
+ // Step 2: Create policies_new with network column + updated CHECK
754
+ sqlite.exec(`CREATE TABLE policies_new (
755
+ id TEXT PRIMARY KEY,
756
+ wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
757
+ type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
758
+ rules TEXT NOT NULL,
759
+ priority INTEGER NOT NULL DEFAULT 0,
760
+ enabled INTEGER NOT NULL DEFAULT 1,
761
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
762
+ created_at INTEGER NOT NULL,
763
+ updated_at INTEGER NOT NULL
764
+ )`);
765
+ // Step 3: Copy existing policies with network=NULL
766
+ sqlite.exec(`INSERT INTO policies_new (
767
+ id, wallet_id, type, rules, priority, enabled, network, created_at, updated_at
768
+ )
769
+ SELECT
770
+ id, wallet_id, type, rules, priority, enabled, NULL, created_at, updated_at
771
+ FROM policies`);
772
+ // Step 4: Drop old table
773
+ sqlite.exec('DROP TABLE policies');
774
+ // Step 5: Rename new table
775
+ sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
776
+ // Step 6: Recreate existing indexes
777
+ sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
778
+ sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
779
+ // Step 7: Create new network index
780
+ sqlite.exec('CREATE INDEX idx_policies_network ON policies(network)');
781
+ // Step 8: Commit
782
+ sqlite.exec('COMMIT');
783
+ }
784
+ catch (err) {
785
+ sqlite.exec('ROLLBACK');
786
+ throw err;
787
+ }
788
+ // Step 9: Re-enable foreign keys and verify integrity
789
+ sqlite.pragma('foreign_keys = ON');
790
+ const fkErrors = sqlite.pragma('foreign_key_check');
791
+ if (fkErrors.length > 0) {
792
+ throw new Error(`FK integrity violation after v8: ${JSON.stringify(fkErrors)}`);
793
+ }
794
+ },
795
+ });
796
+ // ---------------------------------------------------------------------------
797
+ // v9: Add SIGNED status and SIGN type to transactions CHECK constraints
798
+ // ---------------------------------------------------------------------------
799
+ // SSoT arrays TRANSACTION_STATUSES/TRANSACTION_TYPES에 새 값이 추가되었으므로
800
+ // transactions 테이블의 CHECK 제약을 갱신해야 한다. SQLite는 ALTER CHECK 불가 -> 12-step 재생성.
801
+ // 이 마이그레이션은 transactions 테이블만 재생성한다 (다른 테이블에 영향 없음).
802
+ MIGRATIONS.push({
803
+ version: 9,
804
+ description: 'Add SIGNED status and SIGN type to transactions CHECK constraints',
805
+ managesOwnTransaction: true,
806
+ up: (sqlite) => {
807
+ // Step 1: Begin transaction
808
+ sqlite.exec('BEGIN');
809
+ try {
810
+ // Step 2: Create transactions_new with updated CHECK constraints
811
+ sqlite.exec(`CREATE TABLE transactions_new (
812
+ id TEXT PRIMARY KEY,
813
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
814
+ session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
815
+ chain TEXT NOT NULL,
816
+ tx_hash TEXT,
817
+ type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
818
+ amount TEXT,
819
+ to_address TEXT,
820
+ token_mint TEXT,
821
+ contract_address TEXT,
822
+ method_signature TEXT,
823
+ spender_address TEXT,
824
+ approved_amount TEXT,
825
+ parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
826
+ batch_index INTEGER,
827
+ status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
828
+ tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
829
+ queued_at INTEGER,
830
+ executed_at INTEGER,
831
+ created_at INTEGER NOT NULL,
832
+ reserved_amount TEXT,
833
+ error TEXT,
834
+ metadata TEXT,
835
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)}))
836
+ )`);
837
+ // Step 3: Copy existing data
838
+ sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network)
839
+ SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network FROM transactions`);
840
+ // Step 4: Drop old table
841
+ sqlite.exec('DROP TABLE transactions');
842
+ // Step 5: Rename new table
843
+ sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
844
+ // Step 6: Recreate all 8 existing indexes
845
+ sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
846
+ sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
847
+ sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
848
+ sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
849
+ sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
850
+ sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
851
+ sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
852
+ sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
853
+ // Step 7: Commit
854
+ sqlite.exec('COMMIT');
855
+ }
856
+ catch (err) {
857
+ sqlite.exec('ROLLBACK');
858
+ throw err;
859
+ }
860
+ // Step 8: Re-enable foreign keys and verify integrity
861
+ sqlite.pragma('foreign_keys = ON');
862
+ const fkErrors = sqlite.pragma('foreign_key_check');
863
+ if (fkErrors.length > 0) {
864
+ throw new Error(`FK integrity violation after v9: ${JSON.stringify(fkErrors)}`);
865
+ }
866
+ },
867
+ });
868
+ // ---------------------------------------------------------------------------
869
+ // v10: Add message column to notification_logs
870
+ // ---------------------------------------------------------------------------
871
+ // Simple ALTER TABLE ADD COLUMN -- no CHECK constraint changes, no table recreation needed.
872
+ MIGRATIONS.push({
873
+ version: 10,
874
+ description: 'Add message column to notification_logs',
875
+ up: (sqlite) => {
876
+ sqlite.exec('ALTER TABLE notification_logs ADD COLUMN message TEXT');
877
+ },
878
+ });
879
+ // ---------------------------------------------------------------------------
880
+ // v11: Create api_keys table for Action Provider API key storage
881
+ // ---------------------------------------------------------------------------
882
+ MIGRATIONS.push({
883
+ version: 11,
884
+ description: 'Add api_keys table for Action Provider API key storage',
885
+ up: (sqlite) => {
886
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS api_keys (
887
+ provider_name TEXT PRIMARY KEY,
888
+ encrypted_key TEXT NOT NULL,
889
+ created_at INTEGER NOT NULL,
890
+ updated_at INTEGER NOT NULL
891
+ )`);
892
+ },
893
+ });
894
+ // ---------------------------------------------------------------------------
895
+ // v12: Add X402_PAYMENT to transactions and X402_ALLOWED_DOMAINS to policies CHECK constraints
896
+ // ---------------------------------------------------------------------------
897
+ // x402 결제 지원을 위해 transactions.type에 X402_PAYMENT,
898
+ // policies.type에 X402_ALLOWED_DOMAINS가 CHECK 제약에 포함되어야 한다.
899
+ // SQLite는 ALTER CHECK 불가 -> transactions와 policies 모두 12-step 재생성.
900
+ MIGRATIONS.push({
901
+ version: 12,
902
+ description: 'Add X402_PAYMENT to transactions and X402_ALLOWED_DOMAINS to policies CHECK constraints',
903
+ managesOwnTransaction: true,
904
+ up: (sqlite) => {
905
+ sqlite.exec('BEGIN');
906
+ try {
907
+ // ── Part 1: transactions 테이블 재생성 ──
908
+ // v9과 동일 DDL. TRANSACTION_TYPES SSoT에 X402_PAYMENT이 이미 포함됨.
909
+ sqlite.exec(`CREATE TABLE transactions_new (
910
+ id TEXT PRIMARY KEY,
911
+ wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
912
+ session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
913
+ chain TEXT NOT NULL,
914
+ tx_hash TEXT,
915
+ type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
916
+ amount TEXT,
917
+ to_address TEXT,
918
+ token_mint TEXT,
919
+ contract_address TEXT,
920
+ method_signature TEXT,
921
+ spender_address TEXT,
922
+ approved_amount TEXT,
923
+ parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
924
+ batch_index INTEGER,
925
+ status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
926
+ tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
927
+ queued_at INTEGER,
928
+ executed_at INTEGER,
929
+ created_at INTEGER NOT NULL,
930
+ reserved_amount TEXT,
931
+ error TEXT,
932
+ metadata TEXT,
933
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)}))
934
+ )`);
935
+ sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network)
936
+ SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network FROM transactions`);
937
+ sqlite.exec('DROP TABLE transactions');
938
+ sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
939
+ // Recreate all 8 indexes (same as v9)
940
+ sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
941
+ sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
942
+ sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
943
+ sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
944
+ sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
945
+ sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
946
+ sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
947
+ sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
948
+ // ── Part 2: policies 테이블 재생성 ──
949
+ // v8과 동일 DDL. POLICY_TYPES SSoT에 X402_ALLOWED_DOMAINS가 이미 포함됨.
950
+ sqlite.exec(`CREATE TABLE policies_new (
951
+ id TEXT PRIMARY KEY,
952
+ wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
953
+ type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
954
+ rules TEXT NOT NULL,
955
+ priority INTEGER NOT NULL DEFAULT 0,
956
+ enabled INTEGER NOT NULL DEFAULT 1,
957
+ network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
958
+ created_at INTEGER NOT NULL,
959
+ updated_at INTEGER NOT NULL
960
+ )`);
961
+ sqlite.exec(`INSERT INTO policies_new (id, wallet_id, type, rules, priority, enabled, network, created_at, updated_at)
962
+ SELECT id, wallet_id, type, rules, priority, enabled, network, created_at, updated_at FROM policies`);
963
+ sqlite.exec('DROP TABLE policies');
964
+ sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
965
+ // Recreate 3 indexes (same as v8)
966
+ sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
967
+ sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
968
+ sqlite.exec('CREATE INDEX idx_policies_network ON policies(network)');
969
+ sqlite.exec('COMMIT');
970
+ }
971
+ catch (err) {
972
+ sqlite.exec('ROLLBACK');
973
+ throw err;
974
+ }
975
+ // Re-enable foreign keys and verify integrity
976
+ sqlite.pragma('foreign_keys = ON');
977
+ const fkErrors = sqlite.pragma('foreign_key_check');
978
+ if (fkErrors.length > 0) {
979
+ throw new Error(`FK integrity violation after v12: ${JSON.stringify(fkErrors)}`);
980
+ }
981
+ },
982
+ });
983
+ // ---------------------------------------------------------------------------
984
+ // v13: Add amount_usd and reserved_amount_usd columns to transactions
985
+ // ---------------------------------------------------------------------------
986
+ // Simple ALTER TABLE ADD COLUMN -- no CHECK constraint changes, no table recreation needed.
987
+ // These nullable REAL columns store USD-denominated amounts for cumulative spending limit evaluation.
988
+ // amount_usd: confirmed USD amount (persists after CONFIRMED), reserved_amount_usd: cleared on release.
989
+ MIGRATIONS.push({
990
+ version: 13,
991
+ description: 'Add amount_usd and reserved_amount_usd columns to transactions',
992
+ up: (sqlite) => {
993
+ sqlite.exec('ALTER TABLE transactions ADD COLUMN amount_usd REAL');
994
+ sqlite.exec('ALTER TABLE transactions ADD COLUMN reserved_amount_usd REAL');
995
+ },
996
+ });
997
+ // ---------------------------------------------------------------------------
998
+ // v14: Migrate kill_switch_state values: NORMAL->ACTIVE, ACTIVATED->SUSPENDED
999
+ // ---------------------------------------------------------------------------
1000
+ // Kill Switch 3-state machine migration.
1001
+ // Old 2-state values (NORMAL, ACTIVATED, RECOVERING) are converted to
1002
+ // new 3-state values (ACTIVE, SUSPENDED, LOCKED).
1003
+ // Simple UPDATE on key_value_store -- no schema changes, no table recreation.
1004
+ MIGRATIONS.push({
1005
+ version: 14,
1006
+ description: 'Migrate kill_switch_state values: NORMAL->ACTIVE, ACTIVATED->SUSPENDED',
1007
+ up: (sqlite) => {
1008
+ const now = Math.floor(Date.now() / 1000);
1009
+ // NORMAL -> ACTIVE
1010
+ sqlite
1011
+ .prepare("UPDATE key_value_store SET value = 'ACTIVE', updated_at = ? WHERE key = 'kill_switch_state' AND value = 'NORMAL'")
1012
+ .run(now);
1013
+ // ACTIVATED -> SUSPENDED
1014
+ sqlite
1015
+ .prepare("UPDATE key_value_store SET value = 'SUSPENDED', updated_at = ? WHERE key = 'kill_switch_state' AND value = 'ACTIVATED'")
1016
+ .run(now);
1017
+ // RECOVERING -> ACTIVE (v0.10 design removed RECOVERING state)
1018
+ sqlite
1019
+ .prepare("UPDATE key_value_store SET value = 'ACTIVE', updated_at = ? WHERE key = 'kill_switch_state' AND value = 'RECOVERING'")
1020
+ .run(now);
1021
+ },
1022
+ });
1023
+ // ---------------------------------------------------------------------------
1024
+ // v15: Create telegram_users table for Telegram Bot user management
1025
+ // ---------------------------------------------------------------------------
1026
+ MIGRATIONS.push({
1027
+ version: 15,
1028
+ description: 'Create telegram_users table for Telegram Bot user management',
1029
+ up: (sqlite) => {
1030
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS telegram_users (
1031
+ chat_id INTEGER PRIMARY KEY,
1032
+ username TEXT,
1033
+ role TEXT NOT NULL DEFAULT 'PENDING' CHECK (role IN ('PENDING', 'ADMIN', 'READONLY')),
1034
+ registered_at INTEGER NOT NULL,
1035
+ approved_at INTEGER
1036
+ )`);
1037
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_telegram_users_role ON telegram_users(role)');
1038
+ },
1039
+ });
1040
+ // ---------------------------------------------------------------------------
1041
+ // v16: Add WC infra: wc_sessions table, wc_store table, pending_approvals.approval_channel
1042
+ // ---------------------------------------------------------------------------
1043
+ MIGRATIONS.push({
1044
+ version: 16,
1045
+ description: 'Add WC infra: wc_sessions table, wc_store table, pending_approvals.approval_channel',
1046
+ up: (sqlite) => {
1047
+ // 1. pending_approvals.approval_channel 추가
1048
+ sqlite.exec("ALTER TABLE pending_approvals ADD COLUMN approval_channel TEXT DEFAULT 'rest_api'");
1049
+ // 2. wc_sessions 테이블 생성
1050
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS wc_sessions (
1051
+ wallet_id TEXT PRIMARY KEY REFERENCES wallets(id) ON DELETE CASCADE,
1052
+ topic TEXT NOT NULL UNIQUE,
1053
+ peer_meta TEXT,
1054
+ chain_id TEXT NOT NULL,
1055
+ owner_address TEXT NOT NULL,
1056
+ namespaces TEXT,
1057
+ expiry INTEGER NOT NULL,
1058
+ created_at INTEGER NOT NULL
1059
+ )`);
1060
+ sqlite.exec('CREATE INDEX IF NOT EXISTS idx_wc_sessions_topic ON wc_sessions(topic)');
1061
+ // 3. wc_store 테이블 생성 (IKeyValueStorage용)
1062
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS wc_store (
1063
+ key TEXT PRIMARY KEY,
1064
+ value TEXT NOT NULL
1065
+ )`);
1066
+ },
1067
+ });
1068
+ /**
1069
+ * Run incremental migrations against the database.
1070
+ *
1071
+ * - Reads the current max version from schema_version table
1072
+ * - Executes each migration with version > current in ascending order
1073
+ * - Each migration runs in its own transaction (BEGIN/COMMIT)
1074
+ * - On failure: ROLLBACK the failed migration, throw error, skip remaining
1075
+ *
1076
+ * @param sqlite - Raw better-sqlite3 database instance.
1077
+ * @param migrations - Migration list to apply. Defaults to global MIGRATIONS array.
1078
+ * @returns Count of applied and skipped migrations.
1079
+ */
1080
+ export function runMigrations(sqlite, migrations = MIGRATIONS) {
1081
+ // Get current schema version (pushSchema always inserts version 1)
1082
+ const row = sqlite
1083
+ .prepare('SELECT MAX(version) AS max_version FROM schema_version')
1084
+ .get();
1085
+ const currentVersion = row?.max_version ?? 1;
1086
+ // Sort migrations by version ascending to guarantee order
1087
+ const sorted = [...migrations].sort((a, b) => a.version - b.version);
1088
+ let applied = 0;
1089
+ let skipped = 0;
1090
+ for (const migration of sorted) {
1091
+ if (migration.version <= currentVersion) {
1092
+ skipped++;
1093
+ continue;
1094
+ }
1095
+ if (migration.managesOwnTransaction) {
1096
+ // Migration manages its own PRAGMA + transaction (e.g. 12-step table recreation)
1097
+ // Disable foreign keys so the migration can DROP/RENAME tables
1098
+ sqlite.pragma('foreign_keys = OFF');
1099
+ try {
1100
+ migration.up(sqlite);
1101
+ // Record successful migration (up() must have committed its own transaction)
1102
+ sqlite
1103
+ .prepare('INSERT INTO schema_version (version, applied_at, description) VALUES (?, ?, ?)')
1104
+ .run(migration.version, Math.floor(Date.now() / 1000), migration.description);
1105
+ applied++;
1106
+ }
1107
+ catch (err) {
1108
+ // Ensure foreign_keys is restored even on failure
1109
+ try {
1110
+ sqlite.pragma('foreign_keys = ON');
1111
+ }
1112
+ catch {
1113
+ /* best effort */
1114
+ }
1115
+ throw new Error(`Migration v${migration.version} (${migration.description}) failed: ${err instanceof Error ? err.message : String(err)}`);
1116
+ }
1117
+ // Re-enable foreign keys after successful migration
1118
+ sqlite.pragma('foreign_keys = ON');
1119
+ }
1120
+ else {
1121
+ // Standard migration: wrap in BEGIN/COMMIT
1122
+ sqlite.exec('BEGIN');
1123
+ try {
1124
+ migration.up(sqlite);
1125
+ // Record successful migration
1126
+ sqlite
1127
+ .prepare('INSERT INTO schema_version (version, applied_at, description) VALUES (?, ?, ?)')
1128
+ .run(migration.version, Math.floor(Date.now() / 1000), migration.description);
1129
+ sqlite.exec('COMMIT');
1130
+ applied++;
1131
+ }
1132
+ catch (err) {
1133
+ sqlite.exec('ROLLBACK');
1134
+ throw new Error(`Migration v${migration.version} (${migration.description}) failed: ${err instanceof Error ? err.message : String(err)}`);
1135
+ }
1136
+ }
1137
+ }
1138
+ return { applied, skipped };
1139
+ }
1140
+ // ---------------------------------------------------------------------------
1141
+ // Public API
1142
+ // ---------------------------------------------------------------------------
1143
+ /**
1144
+ * Push the full schema to the database (CREATE TABLE IF NOT EXISTS + indexes).
1145
+ * Then run any pending incremental migrations.
1146
+ * Safe to call on every daemon startup -- idempotent.
1147
+ *
1148
+ * @param sqlite - Raw better-sqlite3 database instance (PRAGMAs must already be applied).
1149
+ */
1150
+ export function pushSchema(sqlite) {
1151
+ const tables = getCreateTableStatements();
1152
+ const indexes = getCreateIndexStatements();
1153
+ // Step 1: Create tables + record schema version (NO indexes yet)
1154
+ // Indexes reference latest-schema columns (e.g. wallets.environment) that may
1155
+ // not exist in pre-migration databases. Creating indexes before migrations
1156
+ // causes "no such column" errors on existing DBs. (MIGR-01 fix)
1157
+ //
1158
+ // Pre-v3 databases have an `agents` table that gets renamed to `wallets` by
1159
+ // v3 migration. We must skip creating `wallets` if `agents` still exists,
1160
+ // otherwise v3 migration fails with "table already exists". (MIGR-01b fix)
1161
+ const hasAgentsTable = sqlite
1162
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'")
1163
+ .get() !== undefined;
1164
+ sqlite.exec('BEGIN');
1165
+ try {
1166
+ for (const stmt of tables) {
1167
+ // Skip wallets table creation if agents table exists (v3 migration will handle rename)
1168
+ if (hasAgentsTable && stmt.includes('CREATE TABLE IF NOT EXISTS wallets')) {
1169
+ continue;
1170
+ }
1171
+ sqlite.exec(stmt);
1172
+ }
1173
+ // Record schema version 1 if not already recorded (for existing DBs that only had v1)
1174
+ const existing = sqlite
1175
+ .prepare('SELECT version FROM schema_version WHERE version = 1')
1176
+ .get();
1177
+ if (!existing) {
1178
+ // Fresh DB: record all versions up to LATEST_SCHEMA_VERSION so migrations are skipped.
1179
+ // The DDL above already represents the latest schema state.
1180
+ const ts = Math.floor(Date.now() / 1000);
1181
+ sqlite
1182
+ .prepare('INSERT INTO schema_version (version, applied_at, description) VALUES (?, ?, ?)')
1183
+ .run(1, ts, 'Initial schema (15 tables)');
1184
+ // Record all migration versions as already applied (DDL is up-to-date)
1185
+ for (const migration of MIGRATIONS) {
1186
+ sqlite
1187
+ .prepare('INSERT OR IGNORE INTO schema_version (version, applied_at, description) VALUES (?, ?, ?)')
1188
+ .run(migration.version, ts, `${migration.description} (via pushSchema)`);
1189
+ }
1190
+ }
1191
+ sqlite.exec('COMMIT');
1192
+ }
1193
+ catch (err) {
1194
+ sqlite.exec('ROLLBACK');
1195
+ throw err;
1196
+ }
1197
+ // Step 2: Run incremental migrations (adds/transforms columns in existing DBs)
1198
+ // Fresh DBs have all versions recorded above, so migrations are skipped.
1199
+ runMigrations(sqlite);
1200
+ // Step 3: Create indexes AFTER migrations complete
1201
+ // All columns are now guaranteed to exist (e.g. wallets.environment from v7).
1202
+ sqlite.exec('BEGIN');
1203
+ try {
1204
+ for (const stmt of indexes) {
1205
+ sqlite.exec(stmt);
1206
+ }
1207
+ sqlite.exec('COMMIT');
1208
+ }
1209
+ catch (err) {
1210
+ sqlite.exec('ROLLBACK');
1211
+ throw err;
1212
+ }
1213
+ }
1214
+ //# sourceMappingURL=migrate.js.map