@echoclaw/echo-0g 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +1175 -0
  2. package/dist/0g-compute/account.d.ts +36 -0
  3. package/dist/0g-compute/account.d.ts.map +1 -0
  4. package/dist/0g-compute/account.js +85 -0
  5. package/dist/0g-compute/account.js.map +1 -0
  6. package/dist/0g-compute/bridge.d.ts +16 -0
  7. package/dist/0g-compute/bridge.d.ts.map +1 -0
  8. package/dist/0g-compute/bridge.js +40 -0
  9. package/dist/0g-compute/bridge.js.map +1 -0
  10. package/dist/0g-compute/broker-factory.d.ts +19 -0
  11. package/dist/0g-compute/broker-factory.d.ts.map +1 -0
  12. package/dist/0g-compute/broker-factory.js +65 -0
  13. package/dist/0g-compute/broker-factory.js.map +1 -0
  14. package/dist/0g-compute/constants.d.ts +10 -0
  15. package/dist/0g-compute/constants.d.ts.map +1 -0
  16. package/dist/0g-compute/constants.js +12 -0
  17. package/dist/0g-compute/constants.js.map +1 -0
  18. package/dist/0g-compute/monitor.d.ts +43 -0
  19. package/dist/0g-compute/monitor.d.ts.map +1 -0
  20. package/dist/0g-compute/monitor.js +302 -0
  21. package/dist/0g-compute/monitor.js.map +1 -0
  22. package/dist/0g-compute/pricing.d.ts +43 -0
  23. package/dist/0g-compute/pricing.d.ts.map +1 -0
  24. package/dist/0g-compute/pricing.js +53 -0
  25. package/dist/0g-compute/pricing.js.map +1 -0
  26. package/dist/0g-compute/sdk-bridge.cjs +17 -0
  27. package/dist/0g-compute/sdk-bridge.cjs.map +1 -0
  28. package/dist/0g-compute/sdk-bridge.d.cts +9 -0
  29. package/dist/0g-compute/sdk-bridge.d.cts.map +1 -0
  30. package/dist/0g-compute/smoke-test.d.ts +11 -0
  31. package/dist/0g-compute/smoke-test.d.ts.map +1 -0
  32. package/dist/0g-compute/smoke-test.js +172 -0
  33. package/dist/0g-compute/smoke-test.js.map +1 -0
  34. package/dist/bot/daemon.d.ts +34 -0
  35. package/dist/bot/daemon.d.ts.map +1 -0
  36. package/dist/bot/daemon.js +386 -0
  37. package/dist/bot/daemon.js.map +1 -0
  38. package/dist/bot/executor.d.ts +14238 -0
  39. package/dist/bot/executor.d.ts.map +1 -0
  40. package/dist/bot/executor.js +183 -0
  41. package/dist/bot/executor.js.map +1 -0
  42. package/dist/bot/nonce-queue.d.ts +20 -0
  43. package/dist/bot/nonce-queue.d.ts.map +1 -0
  44. package/dist/bot/nonce-queue.js +41 -0
  45. package/dist/bot/nonce-queue.js.map +1 -0
  46. package/dist/bot/notify.d.ts +15 -0
  47. package/dist/bot/notify.d.ts.map +1 -0
  48. package/dist/bot/notify.js +98 -0
  49. package/dist/bot/notify.js.map +1 -0
  50. package/dist/bot/orders.d.ts +30 -0
  51. package/dist/bot/orders.d.ts.map +1 -0
  52. package/dist/bot/orders.js +172 -0
  53. package/dist/bot/orders.js.map +1 -0
  54. package/dist/bot/state.d.ts +14 -0
  55. package/dist/bot/state.d.ts.map +1 -0
  56. package/dist/bot/state.js +109 -0
  57. package/dist/bot/state.js.map +1 -0
  58. package/dist/bot/stream.d.ts +28 -0
  59. package/dist/bot/stream.d.ts.map +1 -0
  60. package/dist/bot/stream.js +96 -0
  61. package/dist/bot/stream.js.map +1 -0
  62. package/dist/bot/triggers.d.ts +17 -0
  63. package/dist/bot/triggers.d.ts.map +1 -0
  64. package/dist/bot/triggers.js +95 -0
  65. package/dist/bot/triggers.js.map +1 -0
  66. package/dist/bot/types.d.ts +199 -0
  67. package/dist/bot/types.d.ts.map +1 -0
  68. package/dist/bot/types.js +12 -0
  69. package/dist/bot/types.js.map +1 -0
  70. package/dist/chainscan/client.d.ts +28 -0
  71. package/dist/chainscan/client.d.ts.map +1 -0
  72. package/dist/chainscan/client.js +361 -0
  73. package/dist/chainscan/client.js.map +1 -0
  74. package/dist/chainscan/constants.d.ts +15 -0
  75. package/dist/chainscan/constants.d.ts.map +1 -0
  76. package/dist/chainscan/constants.js +15 -0
  77. package/dist/chainscan/constants.js.map +1 -0
  78. package/dist/chainscan/types.d.ts +148 -0
  79. package/dist/chainscan/types.d.ts.map +1 -0
  80. package/dist/chainscan/types.js +2 -0
  81. package/dist/chainscan/types.js.map +1 -0
  82. package/dist/chainscan/validation.d.ts +35 -0
  83. package/dist/chainscan/validation.d.ts.map +1 -0
  84. package/dist/chainscan/validation.js +97 -0
  85. package/dist/chainscan/validation.js.map +1 -0
  86. package/dist/cli.d.ts +7 -0
  87. package/dist/cli.d.ts.map +1 -0
  88. package/dist/cli.js +328 -0
  89. package/dist/cli.js.map +1 -0
  90. package/dist/commands/0g-compute.d.ts +21 -0
  91. package/dist/commands/0g-compute.d.ts.map +1 -0
  92. package/dist/commands/0g-compute.js +850 -0
  93. package/dist/commands/0g-compute.js.map +1 -0
  94. package/dist/commands/chainscan.d.ts +17 -0
  95. package/dist/commands/chainscan.d.ts.map +1 -0
  96. package/dist/commands/chainscan.js +605 -0
  97. package/dist/commands/chainscan.js.map +1 -0
  98. package/dist/commands/config.d.ts +3 -0
  99. package/dist/commands/config.d.ts.map +1 -0
  100. package/dist/commands/config.js +251 -0
  101. package/dist/commands/config.js.map +1 -0
  102. package/dist/commands/echobook.d.ts +17 -0
  103. package/dist/commands/echobook.d.ts.map +1 -0
  104. package/dist/commands/echobook.js +905 -0
  105. package/dist/commands/echobook.js.map +1 -0
  106. package/dist/commands/jaine-subgraph.d.ts +3 -0
  107. package/dist/commands/jaine-subgraph.d.ts.map +1 -0
  108. package/dist/commands/jaine-subgraph.js +565 -0
  109. package/dist/commands/jaine-subgraph.js.map +1 -0
  110. package/dist/commands/jaine.d.ts +3 -0
  111. package/dist/commands/jaine.d.ts.map +1 -0
  112. package/dist/commands/jaine.js +1415 -0
  113. package/dist/commands/jaine.js.map +1 -0
  114. package/dist/commands/marketmaker.d.ts +6 -0
  115. package/dist/commands/marketmaker.d.ts.map +1 -0
  116. package/dist/commands/marketmaker.js +451 -0
  117. package/dist/commands/marketmaker.js.map +1 -0
  118. package/dist/commands/send.d.ts +3 -0
  119. package/dist/commands/send.d.ts.map +1 -0
  120. package/dist/commands/send.js +229 -0
  121. package/dist/commands/send.js.map +1 -0
  122. package/dist/commands/setup.d.ts +3 -0
  123. package/dist/commands/setup.d.ts.map +1 -0
  124. package/dist/commands/setup.js +263 -0
  125. package/dist/commands/setup.js.map +1 -0
  126. package/dist/commands/slop-app.d.ts +9 -0
  127. package/dist/commands/slop-app.d.ts.map +1 -0
  128. package/dist/commands/slop-app.js +708 -0
  129. package/dist/commands/slop-app.js.map +1 -0
  130. package/dist/commands/slop-stream.d.ts +9 -0
  131. package/dist/commands/slop-stream.d.ts.map +1 -0
  132. package/dist/commands/slop-stream.js +99 -0
  133. package/dist/commands/slop-stream.js.map +1 -0
  134. package/dist/commands/slop.d.ts +3 -0
  135. package/dist/commands/slop.d.ts.map +1 -0
  136. package/dist/commands/slop.js +1053 -0
  137. package/dist/commands/slop.js.map +1 -0
  138. package/dist/commands/wallet.d.ts +13 -0
  139. package/dist/commands/wallet.d.ts.map +1 -0
  140. package/dist/commands/wallet.js +748 -0
  141. package/dist/commands/wallet.js.map +1 -0
  142. package/dist/config/paths.d.ts +13 -0
  143. package/dist/config/paths.d.ts.map +1 -0
  144. package/dist/config/paths.js +33 -0
  145. package/dist/config/paths.js.map +1 -0
  146. package/dist/config/store.d.ts +48 -0
  147. package/dist/config/store.d.ts.map +1 -0
  148. package/dist/config/store.js +113 -0
  149. package/dist/config/store.js.map +1 -0
  150. package/dist/constants/chain.d.ts +57 -0
  151. package/dist/constants/chain.d.ts.map +1 -0
  152. package/dist/constants/chain.js +51 -0
  153. package/dist/constants/chain.js.map +1 -0
  154. package/dist/echobook/api.d.ts +38 -0
  155. package/dist/echobook/api.d.ts.map +1 -0
  156. package/dist/echobook/api.js +86 -0
  157. package/dist/echobook/api.js.map +1 -0
  158. package/dist/echobook/auth.d.ts +31 -0
  159. package/dist/echobook/auth.d.ts.map +1 -0
  160. package/dist/echobook/auth.js +93 -0
  161. package/dist/echobook/auth.js.map +1 -0
  162. package/dist/echobook/comments.d.ts +26 -0
  163. package/dist/echobook/comments.d.ts.map +1 -0
  164. package/dist/echobook/comments.js +20 -0
  165. package/dist/echobook/comments.js.map +1 -0
  166. package/dist/echobook/follows.d.ts +19 -0
  167. package/dist/echobook/follows.d.ts.map +1 -0
  168. package/dist/echobook/follows.js +21 -0
  169. package/dist/echobook/follows.js.map +1 -0
  170. package/dist/echobook/jwtCache.d.ts +15 -0
  171. package/dist/echobook/jwtCache.d.ts.map +1 -0
  172. package/dist/echobook/jwtCache.js +63 -0
  173. package/dist/echobook/jwtCache.js.map +1 -0
  174. package/dist/echobook/notifications.d.ts +30 -0
  175. package/dist/echobook/notifications.d.ts.map +1 -0
  176. package/dist/echobook/notifications.js +26 -0
  177. package/dist/echobook/notifications.js.map +1 -0
  178. package/dist/echobook/points.d.ts +35 -0
  179. package/dist/echobook/points.d.ts.map +1 -0
  180. package/dist/echobook/points.js +20 -0
  181. package/dist/echobook/points.js.map +1 -0
  182. package/dist/echobook/posts.d.ts +46 -0
  183. package/dist/echobook/posts.d.ts.map +1 -0
  184. package/dist/echobook/posts.js +43 -0
  185. package/dist/echobook/posts.js.map +1 -0
  186. package/dist/echobook/profile.d.ts +29 -0
  187. package/dist/echobook/profile.d.ts.map +1 -0
  188. package/dist/echobook/profile.js +14 -0
  189. package/dist/echobook/profile.js.map +1 -0
  190. package/dist/echobook/submolts.d.ts +22 -0
  191. package/dist/echobook/submolts.d.ts.map +1 -0
  192. package/dist/echobook/submolts.js +24 -0
  193. package/dist/echobook/submolts.js.map +1 -0
  194. package/dist/echobook/tradeProof.d.ts +21 -0
  195. package/dist/echobook/tradeProof.d.ts.map +1 -0
  196. package/dist/echobook/tradeProof.js +14 -0
  197. package/dist/echobook/tradeProof.js.map +1 -0
  198. package/dist/echobook/votes.d.ts +17 -0
  199. package/dist/echobook/votes.d.ts.map +1 -0
  200. package/dist/echobook/votes.js +20 -0
  201. package/dist/echobook/votes.js.map +1 -0
  202. package/dist/errors.d.ts +125 -0
  203. package/dist/errors.d.ts.map +1 -0
  204. package/dist/errors.js +147 -0
  205. package/dist/errors.js.map +1 -0
  206. package/dist/intents/store.d.ts +22 -0
  207. package/dist/intents/store.d.ts.map +1 -0
  208. package/dist/intents/store.js +76 -0
  209. package/dist/intents/store.js.map +1 -0
  210. package/dist/intents/types.d.ts +21 -0
  211. package/dist/intents/types.d.ts.map +1 -0
  212. package/dist/intents/types.js +2 -0
  213. package/dist/intents/types.js.map +1 -0
  214. package/dist/jaine/abi/erc20.d.ts +90 -0
  215. package/dist/jaine/abi/erc20.d.ts.map +1 -0
  216. package/dist/jaine/abi/erc20.js +65 -0
  217. package/dist/jaine/abi/erc20.js.map +1 -0
  218. package/dist/jaine/abi/factory.d.ts +38 -0
  219. package/dist/jaine/abi/factory.d.ts.map +1 -0
  220. package/dist/jaine/abi/factory.js +26 -0
  221. package/dist/jaine/abi/factory.js.map +1 -0
  222. package/dist/jaine/abi/index.d.ts +11 -0
  223. package/dist/jaine/abi/index.d.ts.map +1 -0
  224. package/dist/jaine/abi/index.js +11 -0
  225. package/dist/jaine/abi/index.js.map +1 -0
  226. package/dist/jaine/abi/nftManager.d.ts +282 -0
  227. package/dist/jaine/abi/nftManager.d.ts.map +1 -0
  228. package/dist/jaine/abi/nftManager.js +182 -0
  229. package/dist/jaine/abi/nftManager.js.map +1 -0
  230. package/dist/jaine/abi/pool.d.ts +77 -0
  231. package/dist/jaine/abi/pool.d.ts.map +1 -0
  232. package/dist/jaine/abi/pool.js +56 -0
  233. package/dist/jaine/abi/pool.js.map +1 -0
  234. package/dist/jaine/abi/quoter.d.ts +84 -0
  235. package/dist/jaine/abi/quoter.d.ts.map +1 -0
  236. package/dist/jaine/abi/quoter.js +53 -0
  237. package/dist/jaine/abi/quoter.js.map +1 -0
  238. package/dist/jaine/abi/router.d.ts +135 -0
  239. package/dist/jaine/abi/router.d.ts.map +1 -0
  240. package/dist/jaine/abi/router.js +88 -0
  241. package/dist/jaine/abi/router.js.map +1 -0
  242. package/dist/jaine/abi/w0g.d.ts +41 -0
  243. package/dist/jaine/abi/w0g.d.ts.map +1 -0
  244. package/dist/jaine/abi/w0g.js +34 -0
  245. package/dist/jaine/abi/w0g.js.map +1 -0
  246. package/dist/jaine/allowance.d.ts +48 -0
  247. package/dist/jaine/allowance.d.ts.map +1 -0
  248. package/dist/jaine/allowance.js +192 -0
  249. package/dist/jaine/allowance.js.map +1 -0
  250. package/dist/jaine/coreTokens.d.ts +32 -0
  251. package/dist/jaine/coreTokens.d.ts.map +1 -0
  252. package/dist/jaine/coreTokens.js +91 -0
  253. package/dist/jaine/coreTokens.js.map +1 -0
  254. package/dist/jaine/pathEncoding.d.ts +39 -0
  255. package/dist/jaine/pathEncoding.d.ts.map +1 -0
  256. package/dist/jaine/pathEncoding.js +98 -0
  257. package/dist/jaine/pathEncoding.js.map +1 -0
  258. package/dist/jaine/paths.d.ts +11 -0
  259. package/dist/jaine/paths.d.ts.map +1 -0
  260. package/dist/jaine/paths.js +20 -0
  261. package/dist/jaine/paths.js.map +1 -0
  262. package/dist/jaine/poolCache.d.ts +47 -0
  263. package/dist/jaine/poolCache.d.ts.map +1 -0
  264. package/dist/jaine/poolCache.js +195 -0
  265. package/dist/jaine/poolCache.js.map +1 -0
  266. package/dist/jaine/routing.d.ts +41 -0
  267. package/dist/jaine/routing.d.ts.map +1 -0
  268. package/dist/jaine/routing.js +247 -0
  269. package/dist/jaine/routing.js.map +1 -0
  270. package/dist/jaine/subgraph/client.d.ts +26 -0
  271. package/dist/jaine/subgraph/client.d.ts.map +1 -0
  272. package/dist/jaine/subgraph/client.js +201 -0
  273. package/dist/jaine/subgraph/client.js.map +1 -0
  274. package/dist/jaine/subgraph/constants.d.ts +9 -0
  275. package/dist/jaine/subgraph/constants.d.ts.map +1 -0
  276. package/dist/jaine/subgraph/constants.js +9 -0
  277. package/dist/jaine/subgraph/constants.js.map +1 -0
  278. package/dist/jaine/subgraph/queries.d.ts +21 -0
  279. package/dist/jaine/subgraph/queries.d.ts.map +1 -0
  280. package/dist/jaine/subgraph/queries.js +304 -0
  281. package/dist/jaine/subgraph/queries.js.map +1 -0
  282. package/dist/jaine/subgraph/types.d.ts +209 -0
  283. package/dist/jaine/subgraph/types.d.ts.map +1 -0
  284. package/dist/jaine/subgraph/types.js +7 -0
  285. package/dist/jaine/subgraph/types.js.map +1 -0
  286. package/dist/jaine/userTokens.d.ts +27 -0
  287. package/dist/jaine/userTokens.d.ts.map +1 -0
  288. package/dist/jaine/userTokens.js +89 -0
  289. package/dist/jaine/userTokens.js.map +1 -0
  290. package/dist/openclaw/config.d.ts +43 -0
  291. package/dist/openclaw/config.d.ts.map +1 -0
  292. package/dist/openclaw/config.js +231 -0
  293. package/dist/openclaw/config.js.map +1 -0
  294. package/dist/openclaw/hooks-client.d.ts +24 -0
  295. package/dist/openclaw/hooks-client.d.ts.map +1 -0
  296. package/dist/openclaw/hooks-client.js +119 -0
  297. package/dist/openclaw/hooks-client.js.map +1 -0
  298. package/dist/slop/abi/factory.d.ts +128 -0
  299. package/dist/slop/abi/factory.d.ts.map +1 -0
  300. package/dist/slop/abi/factory.js +70 -0
  301. package/dist/slop/abi/factory.js.map +1 -0
  302. package/dist/slop/abi/feeCollector.d.ts +95 -0
  303. package/dist/slop/abi/feeCollector.d.ts.map +1 -0
  304. package/dist/slop/abi/feeCollector.js +71 -0
  305. package/dist/slop/abi/feeCollector.js.map +1 -0
  306. package/dist/slop/abi/index.d.ts +5 -0
  307. package/dist/slop/abi/index.d.ts.map +1 -0
  308. package/dist/slop/abi/index.js +5 -0
  309. package/dist/slop/abi/index.js.map +1 -0
  310. package/dist/slop/abi/registry.d.ts +135 -0
  311. package/dist/slop/abi/registry.d.ts.map +1 -0
  312. package/dist/slop/abi/registry.js +90 -0
  313. package/dist/slop/abi/registry.js.map +1 -0
  314. package/dist/slop/abi/token.d.ts +320 -0
  315. package/dist/slop/abi/token.d.ts.map +1 -0
  316. package/dist/slop/abi/token.js +251 -0
  317. package/dist/slop/abi/token.js.map +1 -0
  318. package/dist/slop/auth.d.ts +19 -0
  319. package/dist/slop/auth.d.ts.map +1 -0
  320. package/dist/slop/auth.js +92 -0
  321. package/dist/slop/auth.js.map +1 -0
  322. package/dist/slop/jwtCache.d.ts +27 -0
  323. package/dist/slop/jwtCache.d.ts.map +1 -0
  324. package/dist/slop/jwtCache.js +91 -0
  325. package/dist/slop/jwtCache.js.map +1 -0
  326. package/dist/slop/quote.d.ts +80 -0
  327. package/dist/slop/quote.d.ts.map +1 -0
  328. package/dist/slop/quote.js +174 -0
  329. package/dist/slop/quote.js.map +1 -0
  330. package/dist/utils/canonicalJson.d.ts +8 -0
  331. package/dist/utils/canonicalJson.d.ts.map +1 -0
  332. package/dist/utils/canonicalJson.js +20 -0
  333. package/dist/utils/canonicalJson.js.map +1 -0
  334. package/dist/utils/env.d.ts +11 -0
  335. package/dist/utils/env.d.ts.map +1 -0
  336. package/dist/utils/env.js +20 -0
  337. package/dist/utils/env.js.map +1 -0
  338. package/dist/utils/http.d.ts +19 -0
  339. package/dist/utils/http.d.ts.map +1 -0
  340. package/dist/utils/http.js +61 -0
  341. package/dist/utils/http.js.map +1 -0
  342. package/dist/utils/logger.d.ts +4 -0
  343. package/dist/utils/logger.d.ts.map +1 -0
  344. package/dist/utils/logger.js +21 -0
  345. package/dist/utils/logger.js.map +1 -0
  346. package/dist/utils/output.d.ts +29 -0
  347. package/dist/utils/output.d.ts.map +1 -0
  348. package/dist/utils/output.js +51 -0
  349. package/dist/utils/output.js.map +1 -0
  350. package/dist/utils/rateLimit.d.ts +22 -0
  351. package/dist/utils/rateLimit.d.ts.map +1 -0
  352. package/dist/utils/rateLimit.js +58 -0
  353. package/dist/utils/rateLimit.js.map +1 -0
  354. package/dist/utils/respond.d.ts +19 -0
  355. package/dist/utils/respond.d.ts.map +1 -0
  356. package/dist/utils/respond.js +25 -0
  357. package/dist/utils/respond.js.map +1 -0
  358. package/dist/utils/ui.d.ts +38 -0
  359. package/dist/utils/ui.d.ts.map +1 -0
  360. package/dist/utils/ui.js +126 -0
  361. package/dist/utils/ui.js.map +1 -0
  362. package/dist/wallet/client.d.ts +4 -0
  363. package/dist/wallet/client.d.ts.map +1 -0
  364. package/dist/wallet/client.js +53 -0
  365. package/dist/wallet/client.js.map +1 -0
  366. package/dist/wallet/keystore.d.ts +22 -0
  367. package/dist/wallet/keystore.d.ts.map +1 -0
  368. package/dist/wallet/keystore.js +111 -0
  369. package/dist/wallet/keystore.js.map +1 -0
  370. package/package.json +63 -0
  371. package/skills/echo/SKILL.md +1121 -0
@@ -0,0 +1,708 @@
1
+ /**
2
+ * slop-app commands - Interact with slop.money production APIs
3
+ * - Profile registration with Echo badge
4
+ * - Image upload/generate via proxy
5
+ * - Chat messaging via Socket.IO
6
+ */
7
+ import { Command } from "commander";
8
+ import { readFileSync } from "node:fs";
9
+ import { basename } from "node:path";
10
+ import { io } from "socket.io-client";
11
+ import { loadConfig } from "../config/store.js";
12
+ import { loadKeystore, decryptPrivateKey } from "../wallet/keystore.js";
13
+ import { requireKeystorePassword } from "../utils/env.js";
14
+ import { EchoError, ErrorCodes } from "../errors.js";
15
+ import { isHeadless, writeJsonSuccess } from "../utils/output.js";
16
+ import { spinner, successBox, infoBox, colors } from "../utils/ui.js";
17
+ import { fetchJson, fetchWithTimeout } from "../utils/http.js";
18
+ import { requireSlopAuth } from "../slop/auth.js";
19
+ // ============ HELPERS ============
20
+ function requireWalletAndKeystore() {
21
+ const cfg = loadConfig();
22
+ if (!cfg.wallet.address) {
23
+ throw new EchoError(ErrorCodes.WALLET_NOT_CONFIGURED, "No wallet configured.", "Run: echo wallet create --json");
24
+ }
25
+ const password = requireKeystorePassword();
26
+ const keystore = loadKeystore();
27
+ if (!keystore) {
28
+ throw new EchoError(ErrorCodes.KEYSTORE_NOT_FOUND, "Keystore not found.", "Run: echo wallet create --json");
29
+ }
30
+ const privateKey = decryptPrivateKey(keystore, password);
31
+ return { address: cfg.wallet.address, privateKey };
32
+ }
33
+ // ============ COMMAND FACTORY ============
34
+ export function createSlopAppCommand() {
35
+ const slopApp = new Command("slop-app")
36
+ .description("Interact with slop.money production APIs")
37
+ .exitOverride();
38
+ // ============ PROFILE SUBCOMMAND ============
39
+ const profile = new Command("profile")
40
+ .description("Profile management")
41
+ .exitOverride();
42
+ profile
43
+ .command("nonce")
44
+ .description("[Deprecated] Nonce endpoint removed — auth uses JWT now")
45
+ .action(async () => {
46
+ throw new EchoError(ErrorCodes.SLOP_AUTH_FAILED, "Profile nonce endpoint has been removed. Auth now uses JWT tokens.", "Use 'echo slop-app profile register' which handles auth automatically");
47
+ });
48
+ profile
49
+ .command("register")
50
+ .description("Register agent profile")
51
+ .requiredOption("--username <name>", "Username (3-15 chars, alphanumeric + underscore)")
52
+ .option("--twitter <url>", "X.com URL (https://x.com/username)")
53
+ .option("--avatar-cid <cid>", "IPFS CID from image upload")
54
+ .option("--avatar-gateway <url>", "Gateway URL from image upload")
55
+ .requiredOption("--yes", "Confirm registration")
56
+ .action(async (options) => {
57
+ if (!options.yes) {
58
+ throw new EchoError(ErrorCodes.CONFIRMATION_REQUIRED, "Add --yes to confirm");
59
+ }
60
+ // Validate username
61
+ if (!/^[a-zA-Z0-9_]{3,15}$/.test(options.username)) {
62
+ throw new EchoError(ErrorCodes.INVALID_USERNAME, "Username must be 3-15 characters, alphanumeric and underscore only");
63
+ }
64
+ // Validate Twitter URL if provided
65
+ if (options.twitter && !/^https:\/\/x\.com\/[A-Za-z0-9_]{1,15}$/.test(options.twitter)) {
66
+ throw new EchoError(ErrorCodes.INVALID_USERNAME, "Invalid X.com URL format. Must be https://x.com/username");
67
+ }
68
+ // Validate avatar flags: both must be provided together
69
+ if ((options.avatarCid && !options.avatarGateway) || (!options.avatarCid && options.avatarGateway)) {
70
+ throw new EchoError(ErrorCodes.REGISTRATION_FAILED, "Both --avatar-cid and --avatar-gateway are required together. Use 'echo slop-app image upload' to get both values.");
71
+ }
72
+ const { address, privateKey } = requireWalletAndKeystore();
73
+ const cfg = loadConfig();
74
+ const spin = spinner("Registering profile...");
75
+ spin.start();
76
+ try {
77
+ // 1. Authenticate (JWT)
78
+ spin.text = "Authenticating...";
79
+ const accessToken = await requireSlopAuth(privateKey, address, cfg.services.backendApiUrl);
80
+ // 2. Register with isEchoBot=true (Bearer auth)
81
+ spin.text = "Registering profile...";
82
+ const registerResponse = await fetchJson(`${cfg.services.backendApiUrl}/profiles/register`, {
83
+ method: "POST",
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ "Authorization": `Bearer ${accessToken}`,
87
+ },
88
+ body: JSON.stringify({
89
+ username: options.username,
90
+ twitterUrl: options.twitter || null,
91
+ avatarCid: options.avatarCid || null,
92
+ avatarGateway: options.avatarGateway || null,
93
+ avatarIpfs: options.avatarCid ? `ipfs://${options.avatarCid}` : null,
94
+ isEchoBot: true,
95
+ }),
96
+ });
97
+ if (!registerResponse.success || !registerResponse.data) {
98
+ throw new EchoError(ErrorCodes.REGISTRATION_FAILED, registerResponse.error || "Registration failed");
99
+ }
100
+ spin.succeed("Profile registered");
101
+ const profileData = registerResponse.data;
102
+ if (isHeadless()) {
103
+ writeJsonSuccess({
104
+ walletAddress: profileData.walletAddress,
105
+ username: profileData.username,
106
+ isEchoBot: true,
107
+ avatarUrl: profileData.avatarUrl,
108
+ twitterUrl: profileData.twitterUrl,
109
+ createdAt: profileData.createdAt,
110
+ });
111
+ }
112
+ else {
113
+ successBox("Profile Registered", `Username: ${colors.info(profileData.username)}\n` +
114
+ `Wallet: ${colors.address(profileData.walletAddress)}\n` +
115
+ `Badge: ${colors.success("(ECHO)")}\n` +
116
+ (profileData.twitterUrl ? `Twitter: ${profileData.twitterUrl}\n` : ""));
117
+ }
118
+ }
119
+ catch (err) {
120
+ spin.fail("Registration failed");
121
+ if (err instanceof EchoError)
122
+ throw err;
123
+ throw new EchoError(ErrorCodes.REGISTRATION_FAILED, `Registration failed: ${err instanceof Error ? err.message : err}`);
124
+ }
125
+ });
126
+ profile
127
+ .command("show")
128
+ .description("Show profile by address")
129
+ .argument("[address]", "Wallet address (default: configured wallet)")
130
+ .action(async (addressArg) => {
131
+ const cfg = loadConfig();
132
+ let targetAddress;
133
+ if (addressArg) {
134
+ targetAddress = addressArg;
135
+ }
136
+ else if (cfg.wallet.address) {
137
+ targetAddress = cfg.wallet.address;
138
+ }
139
+ else {
140
+ throw new EchoError(ErrorCodes.WALLET_NOT_CONFIGURED, "No address specified and no wallet configured", "Provide an address or configure a wallet");
141
+ }
142
+ const spin = spinner("Fetching profile...");
143
+ spin.start();
144
+ try {
145
+ const response = await fetchJson(`${cfg.services.backendApiUrl}/profiles/${targetAddress}`);
146
+ if (!response.success || !response.data) {
147
+ throw new EchoError(ErrorCodes.PROFILE_NOT_FOUND, response.error || "Profile not found");
148
+ }
149
+ spin.succeed("Profile loaded");
150
+ const profile = response.data;
151
+ if (isHeadless()) {
152
+ writeJsonSuccess({
153
+ walletAddress: profile.walletAddress,
154
+ username: profile.username,
155
+ isEchoBot: profile.isEchoBot || false,
156
+ avatarUrl: profile.avatarUrl,
157
+ twitterUrl: profile.twitterUrl,
158
+ createdAt: profile.createdAt,
159
+ });
160
+ }
161
+ else {
162
+ const badgeStr = profile.isEchoBot ? ` ${colors.success("(ECHO)")}` : "";
163
+ infoBox(`${profile.username}${badgeStr}`, `Wallet: ${colors.address(profile.walletAddress)}\n` +
164
+ (profile.avatarUrl ? `Avatar: ${profile.avatarUrl}\n` : "") +
165
+ (profile.twitterUrl ? `Twitter: ${profile.twitterUrl}\n` : "") +
166
+ `Created: ${new Date(profile.createdAt).toLocaleString()}`);
167
+ }
168
+ }
169
+ catch (err) {
170
+ spin.fail("Failed to fetch profile");
171
+ if (err instanceof EchoError)
172
+ throw err;
173
+ throw new EchoError(ErrorCodes.PROFILE_NOT_FOUND, `Profile fetch failed: ${err instanceof Error ? err.message : err}`);
174
+ }
175
+ });
176
+ slopApp.addCommand(profile);
177
+ // ============ IMAGE SUBCOMMAND ============
178
+ const image = new Command("image")
179
+ .description("Image upload and generation")
180
+ .exitOverride();
181
+ image
182
+ .command("upload")
183
+ .description("Upload image to IPFS via proxy")
184
+ .requiredOption("--file <path>", "Path to image file")
185
+ .action(async (options) => {
186
+ const cfg = loadConfig();
187
+ // Read file
188
+ let fileBuffer;
189
+ let filename;
190
+ try {
191
+ fileBuffer = readFileSync(options.file);
192
+ filename = basename(options.file);
193
+ }
194
+ catch (err) {
195
+ throw new EchoError(ErrorCodes.IMAGE_UPLOAD_FAILED, `Failed to read file: ${options.file}`);
196
+ }
197
+ // Check file size (5MB limit)
198
+ if (fileBuffer.length > 5 * 1024 * 1024) {
199
+ throw new EchoError(ErrorCodes.IMAGE_TOO_LARGE, "Image too large (max 5MB)");
200
+ }
201
+ // Detect mime type
202
+ const mimeTypes = {
203
+ jpg: "image/jpeg",
204
+ jpeg: "image/jpeg",
205
+ png: "image/png",
206
+ gif: "image/gif",
207
+ };
208
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
209
+ const mimeType = mimeTypes[ext];
210
+ if (!mimeType) {
211
+ throw new EchoError(ErrorCodes.IMAGE_INVALID_FORMAT, "Invalid image format. Allowed: jpg, jpeg, png, gif");
212
+ }
213
+ const spin = spinner("Uploading image...");
214
+ spin.start();
215
+ try {
216
+ // Create FormData
217
+ const formData = new FormData();
218
+ const blob = new Blob([new Uint8Array(fileBuffer)], { type: mimeType });
219
+ formData.append("image", blob, filename);
220
+ const response = await fetchWithTimeout(`${cfg.services.proxyApiUrl}/upload-image`, {
221
+ method: "POST",
222
+ body: formData,
223
+ });
224
+ const result = (await response.json());
225
+ if (!result.success) {
226
+ throw new EchoError(ErrorCodes.IMAGE_UPLOAD_FAILED, result.error || "Upload failed");
227
+ }
228
+ spin.succeed("Image uploaded");
229
+ if (isHeadless()) {
230
+ writeJsonSuccess({
231
+ ipfsHash: result.ipfsHash,
232
+ gatewayUrl: result.gatewayUrl,
233
+ filename,
234
+ });
235
+ }
236
+ else {
237
+ successBox("Image Uploaded", `File: ${filename}\n` +
238
+ `IPFS Hash: ${colors.info(result.ipfsHash)}\n` +
239
+ `Gateway URL: ${colors.muted(result.gatewayUrl)}`);
240
+ }
241
+ }
242
+ catch (err) {
243
+ spin.fail("Upload failed");
244
+ if (err instanceof EchoError)
245
+ throw err;
246
+ throw new EchoError(ErrorCodes.IMAGE_UPLOAD_FAILED, `Upload failed: ${err instanceof Error ? err.message : err}`);
247
+ }
248
+ });
249
+ image
250
+ .command("generate")
251
+ .description("Generate AI image from prompt")
252
+ .requiredOption("--prompt <text>", "Image generation prompt")
253
+ .option("--upload", "Upload generated image to IPFS")
254
+ .action(async (options) => {
255
+ const cfg = loadConfig();
256
+ if (options.prompt.length > 1000) {
257
+ throw new EchoError(ErrorCodes.IMAGE_GENERATION_FAILED, "Prompt too long (max 1000 characters)");
258
+ }
259
+ const spin = spinner("Generating image...");
260
+ spin.start();
261
+ try {
262
+ const response = await fetchJson(`${cfg.services.proxyApiUrl}/generate-image`, {
263
+ method: "POST",
264
+ headers: { "Content-Type": "application/json" },
265
+ body: JSON.stringify({
266
+ prompt: options.prompt,
267
+ uploadToIPFS: options.upload || false,
268
+ }),
269
+ timeoutMs: 120000, // 2 min for generation
270
+ });
271
+ if (!response.success) {
272
+ throw new EchoError(ErrorCodes.IMAGE_GENERATION_FAILED, response.error || "Generation failed");
273
+ }
274
+ spin.succeed("Image generated");
275
+ if (isHeadless()) {
276
+ writeJsonSuccess({
277
+ imageUrl: response.imageUrl,
278
+ ipfsHash: response.ipfsHash || null,
279
+ gatewayUrl: response.gatewayUrl || null,
280
+ });
281
+ }
282
+ else {
283
+ const lines = [`Image URL: ${colors.muted(response.imageUrl || "N/A")}`];
284
+ if (response.ipfsHash) {
285
+ lines.push(`IPFS Hash: ${colors.info(response.ipfsHash)}`);
286
+ lines.push(`Gateway: ${colors.muted(response.gatewayUrl || "")}`);
287
+ }
288
+ successBox("Image Generated", lines.join("\n"));
289
+ }
290
+ }
291
+ catch (err) {
292
+ spin.fail("Generation failed");
293
+ if (err instanceof EchoError)
294
+ throw err;
295
+ throw new EchoError(ErrorCodes.IMAGE_GENERATION_FAILED, `Generation failed: ${err instanceof Error ? err.message : err}`);
296
+ }
297
+ });
298
+ slopApp.addCommand(image);
299
+ // ============ CHAT SUBCOMMAND ============
300
+ const chat = new Command("chat")
301
+ .description("Global chat operations")
302
+ .exitOverride();
303
+ chat
304
+ .command("post")
305
+ .description("Post a message to global chat")
306
+ .requiredOption("--message <text>", "Message content")
307
+ .option("--gif <url>", "GIF URL")
308
+ .action(async (options) => {
309
+ if (!options.message || !options.message.trim()) {
310
+ throw new EchoError(ErrorCodes.CHAT_MESSAGE_EMPTY, "Message cannot be empty");
311
+ }
312
+ if (options.message.length > 500) {
313
+ throw new EchoError(ErrorCodes.CHAT_MESSAGE_TOO_LONG, "Message too long (max 500 characters)");
314
+ }
315
+ const { address, privateKey } = requireWalletAndKeystore();
316
+ const cfg = loadConfig();
317
+ const spin = spinner("Authenticating...");
318
+ spin.start();
319
+ // 1. Get JWT access token
320
+ const accessToken = await requireSlopAuth(privateKey, address, cfg.services.backendApiUrl);
321
+ spin.text = "Connecting to chat...";
322
+ // Socket.IO promise wrapper
323
+ const postMessage = () => {
324
+ return new Promise((resolve, reject) => {
325
+ const socket = io(cfg.services.chatWsUrl, {
326
+ transports: ["websocket"],
327
+ timeout: 30000,
328
+ });
329
+ let authenticated = false;
330
+ const timeoutHandle = setTimeout(() => {
331
+ socket.disconnect();
332
+ reject(new EchoError(ErrorCodes.HTTP_TIMEOUT, "Chat connection timed out"));
333
+ }, 60000);
334
+ socket.on("connect", () => {
335
+ spin.text = "Authenticating with JWT...";
336
+ socket.emit("chat:auth", { accessToken });
337
+ });
338
+ socket.on("chat:auth_ok", (data) => {
339
+ authenticated = true;
340
+ spin.text = "Sending message...";
341
+ socket.emit("chat:send", {
342
+ content: options.message.trim(),
343
+ gifUrl: options.gif || null,
344
+ });
345
+ });
346
+ socket.on("chat:auth_failed", (data) => {
347
+ clearTimeout(timeoutHandle);
348
+ socket.disconnect();
349
+ reject(new EchoError(ErrorCodes.CHAT_NOT_AUTHENTICATED, data.error || "Authentication failed"));
350
+ });
351
+ socket.on("chat:new", (msg) => {
352
+ // Wait for our own message echo
353
+ if (authenticated && msg.senderAddress?.toLowerCase() === address.toLowerCase() && msg.content === options.message.trim()) {
354
+ clearTimeout(timeoutHandle);
355
+ socket.disconnect();
356
+ resolve({ messageId: msg.id, timestamp: msg.timestamp });
357
+ }
358
+ });
359
+ socket.on("chat:error", (data) => {
360
+ clearTimeout(timeoutHandle);
361
+ socket.disconnect();
362
+ reject(new EchoError(ErrorCodes.CHAT_SEND_FAILED, data.error || "Chat error"));
363
+ });
364
+ socket.on("connect_error", (err) => {
365
+ clearTimeout(timeoutHandle);
366
+ socket.disconnect();
367
+ reject(new EchoError(ErrorCodes.HTTP_REQUEST_FAILED, `Connection failed: ${err.message}`));
368
+ });
369
+ socket.on("disconnect", (reason) => {
370
+ if (!authenticated) {
371
+ clearTimeout(timeoutHandle);
372
+ reject(new EchoError(ErrorCodes.CHAT_SEND_FAILED, `Disconnected: ${reason}`));
373
+ }
374
+ });
375
+ });
376
+ };
377
+ try {
378
+ const result = await postMessage();
379
+ spin.succeed("Message posted");
380
+ if (isHeadless()) {
381
+ writeJsonSuccess({
382
+ messageId: result.messageId,
383
+ timestamp: result.timestamp,
384
+ content: options.message.trim(),
385
+ });
386
+ }
387
+ else {
388
+ successBox("Message Posted", `ID: ${colors.info(result.messageId)}\n` +
389
+ `Content: ${options.message.trim().substring(0, 50)}${options.message.length > 50 ? "..." : ""}`);
390
+ }
391
+ }
392
+ catch (err) {
393
+ spin.fail("Failed to post message");
394
+ throw err;
395
+ }
396
+ });
397
+ chat
398
+ .command("read")
399
+ .description("Read recent chat messages (no auth required)")
400
+ .option("--limit <n>", "Number of messages (1-250, default: server default 25)")
401
+ .action(async (options) => {
402
+ const cfg = loadConfig();
403
+ // Parse and validate limit
404
+ let limitNum;
405
+ if (options.limit) {
406
+ limitNum = parseInt(options.limit, 10);
407
+ if (isNaN(limitNum) || limitNum < 1 || limitNum > 250) {
408
+ throw new EchoError(ErrorCodes.CHAT_SEND_FAILED, "Limit must be 1-250");
409
+ }
410
+ }
411
+ const spin = spinner("Connecting to chat...");
412
+ spin.start();
413
+ const readHistory = () => {
414
+ return new Promise((resolve, reject) => {
415
+ const query = {};
416
+ if (limitNum)
417
+ query.historyLimit = String(limitNum);
418
+ const socket = io(cfg.services.chatWsUrl, {
419
+ transports: ["websocket"],
420
+ timeout: 15000,
421
+ query,
422
+ });
423
+ const timeoutHandle = setTimeout(() => {
424
+ socket.disconnect();
425
+ reject(new EchoError(ErrorCodes.HTTP_TIMEOUT, "Chat history request timed out"));
426
+ }, 15000);
427
+ socket.on("chat:history", (messages) => {
428
+ clearTimeout(timeoutHandle);
429
+ socket.disconnect();
430
+ resolve(messages);
431
+ });
432
+ socket.on("connect_error", (err) => {
433
+ clearTimeout(timeoutHandle);
434
+ socket.disconnect();
435
+ reject(new EchoError(ErrorCodes.HTTP_REQUEST_FAILED, `Connection failed: ${err.message}`));
436
+ });
437
+ socket.on("chat:error", (data) => {
438
+ clearTimeout(timeoutHandle);
439
+ socket.disconnect();
440
+ reject(new EchoError(ErrorCodes.CHAT_SEND_FAILED, data.error || "Chat error"));
441
+ });
442
+ });
443
+ };
444
+ try {
445
+ const messages = await readHistory();
446
+ spin.succeed(`Received ${messages.length} messages`);
447
+ if (isHeadless()) {
448
+ writeJsonSuccess({
449
+ count: messages.length,
450
+ messages,
451
+ });
452
+ }
453
+ else {
454
+ if (messages.length === 0) {
455
+ infoBox("Chat", "No messages.");
456
+ }
457
+ else {
458
+ for (const msg of messages) {
459
+ const sender = msg.senderDisplayName || msg.senderAddress || "unknown";
460
+ const time = msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : "";
461
+ const content = String(msg.content || "");
462
+ const badge = msg.isAgent ? " [BOT]" : msg.senderIsEchoBot ? " [ECHO]" : "";
463
+ console.log(`${colors.muted(time)} ${colors.info(String(sender))}${badge}: ${content}`);
464
+ }
465
+ }
466
+ }
467
+ }
468
+ catch (err) {
469
+ spin.fail("Failed to read chat history");
470
+ throw err;
471
+ }
472
+ });
473
+ slopApp.addCommand(chat);
474
+ // ============ AGENTS SUBCOMMAND ============
475
+ const agents = new Command("agents")
476
+ .description("Query tokens via Agent DSL")
477
+ .exitOverride();
478
+ // --- Helpers ---
479
+ function normalizeQuery(query) {
480
+ const normalized = {
481
+ source: query.source,
482
+ orderBy: query.orderBy ?? { field: "created_at_ms", direction: "desc" },
483
+ limit: query.limit ?? 50,
484
+ };
485
+ if (query.filters && query.filters.length > 0) {
486
+ normalized.filters = query.filters;
487
+ }
488
+ if (query.offset && query.offset > 0) {
489
+ normalized.offset = query.offset;
490
+ }
491
+ return normalized;
492
+ }
493
+ async function executeAgentQuery(query) {
494
+ const { address, privateKey } = requireWalletAndKeystore();
495
+ const cfg = loadConfig();
496
+ const normalized = normalizeQuery(query);
497
+ // 1. Authenticate (JWT)
498
+ const accessToken = await requireSlopAuth(privateKey, address, cfg.services.backendApiUrl);
499
+ // 2. Execute query with Bearer auth
500
+ const response = await fetchWithTimeout(`${cfg.services.backendApiUrl}/agents/query`, {
501
+ method: "POST",
502
+ headers: {
503
+ "Content-Type": "application/json",
504
+ "Authorization": `Bearer ${accessToken}`,
505
+ },
506
+ body: JSON.stringify({ query: normalized }),
507
+ });
508
+ const body = (await response.json());
509
+ if (!response.ok) {
510
+ const status = response.status;
511
+ if (status === 400) {
512
+ throw new EchoError(ErrorCodes.AGENT_QUERY_INVALID, body.error || "Invalid query");
513
+ }
514
+ if (status === 401) {
515
+ throw new EchoError(ErrorCodes.SLOP_AUTH_FAILED, body.error || "Authentication failed");
516
+ }
517
+ if (status === 403) {
518
+ throw new EchoError(ErrorCodes.PROFILE_NOT_FOUND, body.error || "Profile required", "Register profile first: echo slop-app profile register --username <name> --yes --json");
519
+ }
520
+ if (status === 429) {
521
+ throw new EchoError(ErrorCodes.AGENT_QUERY_FAILED, "Rate limited, try again later");
522
+ }
523
+ if (status === 504) {
524
+ throw new EchoError(ErrorCodes.AGENT_QUERY_TIMEOUT, "Query too complex, simplify filters");
525
+ }
526
+ throw new EchoError(ErrorCodes.AGENT_QUERY_FAILED, body.error || `Query failed (HTTP ${status})`);
527
+ }
528
+ if (!body.success) {
529
+ throw new EchoError(ErrorCodes.AGENT_QUERY_FAILED, body.error || "Query failed");
530
+ }
531
+ const tokens = body.data || [];
532
+ return { tokens, count: tokens.length, cached: body.cached ?? false };
533
+ }
534
+ function formatAgentTable(tokens) {
535
+ if (tokens.length === 0)
536
+ return "No tokens found.";
537
+ const header = `${"Symbol".padEnd(12)} ${"Name".padEnd(20)} ${"Price".padEnd(14)} ${"Vol 24h".padEnd(14)} ${"Status".padEnd(10)}`;
538
+ const sep = "-".repeat(header.length);
539
+ const rows = tokens.map((t) => {
540
+ const sym = String(t.symbol ?? "").slice(0, 11).padEnd(12);
541
+ const name = String(t.name ?? "").slice(0, 19).padEnd(20);
542
+ const price = t.actual_price != null ? Number(t.actual_price).toFixed(6).padEnd(14) : "N/A".padEnd(14);
543
+ const vol = t.volume_24h != null ? Number(t.volume_24h).toFixed(2).padEnd(14) : "N/A".padEnd(14);
544
+ const status = String(t.status ?? "").padEnd(10);
545
+ return `${sym} ${name} ${price} ${vol} ${status}`;
546
+ });
547
+ return [header, sep, ...rows].join("\n");
548
+ }
549
+ function collect(val, acc) {
550
+ acc.push(val);
551
+ return acc;
552
+ }
553
+ // --- Commands ---
554
+ agents
555
+ .command("query")
556
+ .description("Execute agent query with full DSL")
557
+ .requiredOption("--source <source>", "Data source (tokens)")
558
+ .option("--filter <json>", "Filter as JSON (repeatable)", collect, [])
559
+ .option("--order-by <field>", "Order by field")
560
+ .option("--order-dir <dir>", "Order direction (asc|desc)")
561
+ .option("--limit <n>", "Result limit (1-200)")
562
+ .option("--offset <n>", "Result offset")
563
+ .action(async (options) => {
564
+ // Parse filters
565
+ const filters = [];
566
+ for (const raw of options.filter) {
567
+ try {
568
+ const parsed = JSON.parse(raw);
569
+ if (!parsed.field || !parsed.op) {
570
+ throw new Error("Filter must have 'field' and 'op'");
571
+ }
572
+ filters.push(parsed);
573
+ }
574
+ catch (err) {
575
+ throw new EchoError(ErrorCodes.AGENT_QUERY_INVALID, `Invalid filter JSON: ${raw}`, 'Expected format: \'{"field":"status","op":"=","value":"active"}\'');
576
+ }
577
+ }
578
+ const query = {
579
+ source: options.source,
580
+ };
581
+ if (filters.length > 0)
582
+ query.filters = filters;
583
+ if (options.orderBy) {
584
+ query.orderBy = {
585
+ field: options.orderBy,
586
+ direction: options.orderDir || "desc",
587
+ };
588
+ }
589
+ if (options.limit) {
590
+ const limit = parseInt(options.limit, 10);
591
+ if (isNaN(limit) || limit < 1 || limit > 200) {
592
+ throw new EchoError(ErrorCodes.AGENT_QUERY_INVALID, "Limit must be 1-200");
593
+ }
594
+ query.limit = limit;
595
+ }
596
+ if (options.offset) {
597
+ const offset = parseInt(options.offset, 10);
598
+ if (isNaN(offset) || offset < 0) {
599
+ throw new EchoError(ErrorCodes.AGENT_QUERY_INVALID, "Offset must be >= 0");
600
+ }
601
+ query.offset = offset;
602
+ }
603
+ const spin = spinner("Querying agents API...");
604
+ spin.start();
605
+ try {
606
+ const result = await executeAgentQuery(query);
607
+ spin.succeed(`Query returned ${result.count} tokens`);
608
+ if (isHeadless()) {
609
+ writeJsonSuccess({ tokens: result.tokens, count: result.count, cached: result.cached });
610
+ }
611
+ else {
612
+ console.log(formatAgentTable(result.tokens));
613
+ }
614
+ }
615
+ catch (err) {
616
+ spin.fail("Query failed");
617
+ throw err;
618
+ }
619
+ });
620
+ agents
621
+ .command("trending")
622
+ .description("Top tokens by 24h volume")
623
+ .option("--limit <n>", "Result limit (default: 20)")
624
+ .action(async (options) => {
625
+ const limit = options.limit ? parseInt(options.limit, 10) : 20;
626
+ const spin = spinner("Fetching trending tokens...");
627
+ spin.start();
628
+ try {
629
+ const result = await executeAgentQuery({
630
+ source: "tokens",
631
+ orderBy: { field: "volume_24h", direction: "desc" },
632
+ limit,
633
+ });
634
+ spin.succeed(`Trending: ${result.count} tokens`);
635
+ if (isHeadless()) {
636
+ writeJsonSuccess({ tokens: result.tokens, count: result.count, cached: result.cached });
637
+ }
638
+ else {
639
+ console.log(formatAgentTable(result.tokens));
640
+ }
641
+ }
642
+ catch (err) {
643
+ spin.fail("Failed to fetch trending tokens");
644
+ throw err;
645
+ }
646
+ });
647
+ agents
648
+ .command("newest")
649
+ .description("Newest tokens by creation time")
650
+ .option("--limit <n>", "Result limit (default: 20)")
651
+ .action(async (options) => {
652
+ const limit = options.limit ? parseInt(options.limit, 10) : 20;
653
+ const spin = spinner("Fetching newest tokens...");
654
+ spin.start();
655
+ try {
656
+ const result = await executeAgentQuery({
657
+ source: "tokens",
658
+ orderBy: { field: "created_at_ms", direction: "desc" },
659
+ limit,
660
+ });
661
+ spin.succeed(`Newest: ${result.count} tokens`);
662
+ if (isHeadless()) {
663
+ writeJsonSuccess({ tokens: result.tokens, count: result.count, cached: result.cached });
664
+ }
665
+ else {
666
+ console.log(formatAgentTable(result.tokens));
667
+ }
668
+ }
669
+ catch (err) {
670
+ spin.fail("Failed to fetch newest tokens");
671
+ throw err;
672
+ }
673
+ });
674
+ agents
675
+ .command("search")
676
+ .description("Search tokens by name (ILIKE)")
677
+ .requiredOption("--name <pattern>", "Name search pattern")
678
+ .option("--limit <n>", "Result limit (default: 20)")
679
+ .action(async (options) => {
680
+ const limit = options.limit ? parseInt(options.limit, 10) : 20;
681
+ if (options.name.length > 100) {
682
+ throw new EchoError(ErrorCodes.AGENT_QUERY_INVALID, "Search pattern too long (max 100 characters)");
683
+ }
684
+ const spin = spinner(`Searching for "${options.name}"...`);
685
+ spin.start();
686
+ try {
687
+ const result = await executeAgentQuery({
688
+ source: "tokens",
689
+ filters: [{ field: "name", op: "like", value: options.name }],
690
+ limit,
691
+ });
692
+ spin.succeed(`Found ${result.count} tokens`);
693
+ if (isHeadless()) {
694
+ writeJsonSuccess({ tokens: result.tokens, count: result.count, cached: result.cached });
695
+ }
696
+ else {
697
+ console.log(formatAgentTable(result.tokens));
698
+ }
699
+ }
700
+ catch (err) {
701
+ spin.fail("Search failed");
702
+ throw err;
703
+ }
704
+ });
705
+ slopApp.addCommand(agents);
706
+ return slopApp;
707
+ }
708
+ //# sourceMappingURL=slop-app.js.map