dexterm-cli 0.0.0-stage → 0.1.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 (349) hide show
  1. package/LICENSE +661 -0
  2. package/dist/packages/agents/src/audit.js +122 -0
  3. package/dist/packages/agents/src/bus.js +379 -0
  4. package/dist/packages/agents/src/executor.js +177 -0
  5. package/dist/packages/agents/src/gate.js +176 -0
  6. package/dist/packages/agents/src/identity.js +179 -0
  7. package/dist/packages/agents/src/index.js +15 -0
  8. package/dist/packages/agents/src/proposals.js +153 -0
  9. package/dist/packages/agents/src/schema.js +87 -0
  10. package/dist/packages/agents/src/sdk.js +140 -0
  11. package/dist/packages/agents/src/tiers.js +114 -0
  12. package/dist/packages/agents/src/tools.js +69 -0
  13. package/dist/packages/agents/src/ws.js +158 -0
  14. package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
  15. package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
  16. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
  17. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +67 -0
  18. package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -0
  19. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
  20. package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
  21. package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
  22. package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
  23. package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
  24. package/dist/packages/cli/bin/dexterm.mjs +11 -0
  25. package/dist/packages/cli/src/agent-cmd.js +303 -0
  26. package/dist/packages/cli/src/agent-roster.js +26 -0
  27. package/dist/packages/cli/src/analyst/backend.js +526 -0
  28. package/dist/packages/cli/src/analyst/results.js +38 -0
  29. package/dist/packages/cli/src/analyst/session.js +319 -0
  30. package/dist/packages/cli/src/audit.js +51 -0
  31. package/dist/packages/cli/src/backtest-cmd.js +149 -0
  32. package/dist/packages/cli/src/boot-state.js +62 -0
  33. package/dist/packages/cli/src/custom.js +261 -0
  34. package/dist/packages/cli/src/display.js +191 -0
  35. package/dist/packages/cli/src/doctor.js +361 -0
  36. package/dist/packages/cli/src/feed.js +634 -0
  37. package/dist/packages/cli/src/fixtures.js +275 -0
  38. package/dist/packages/cli/src/follow-cmd.js +221 -0
  39. package/dist/packages/cli/src/images-config.js +59 -0
  40. package/dist/packages/cli/src/index.js +31 -0
  41. package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
  42. package/dist/packages/cli/src/larp-record.js +21 -0
  43. package/dist/packages/cli/src/live/copy.js +80 -0
  44. package/dist/packages/cli/src/live/flow.js +233 -0
  45. package/dist/packages/cli/src/live/index.js +509 -0
  46. package/dist/packages/cli/src/live/intel.js +227 -0
  47. package/dist/packages/cli/src/live/newpairs.js +334 -0
  48. package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
  49. package/dist/packages/cli/src/live/screens/token-live.js +459 -0
  50. package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
  51. package/dist/packages/cli/src/live/token.js +585 -0
  52. package/dist/packages/cli/src/live/trade.js +929 -0
  53. package/dist/packages/cli/src/live/watchlist.js +178 -0
  54. package/dist/packages/cli/src/main.js +5583 -0
  55. package/dist/packages/cli/src/narrative-cmd.js +76 -0
  56. package/dist/packages/cli/src/pack-cmd.js +323 -0
  57. package/dist/packages/cli/src/plan-cmd.js +278 -0
  58. package/dist/packages/cli/src/popup.js +810 -0
  59. package/dist/packages/cli/src/rules-cmd.js +131 -0
  60. package/dist/packages/cli/src/rules.js +339 -0
  61. package/dist/packages/cli/src/scan/command.js +57 -0
  62. package/dist/packages/cli/src/scan/engine.js +419 -0
  63. package/dist/packages/cli/src/scan/observe.js +64 -0
  64. package/dist/packages/cli/src/scan/ui.js +98 -0
  65. package/dist/packages/cli/src/screener/filter.js +241 -0
  66. package/dist/packages/cli/src/screener/presets.js +80 -0
  67. package/dist/packages/cli/src/screener/rows.js +146 -0
  68. package/dist/packages/cli/src/screener/rug.js +50 -0
  69. package/dist/packages/cli/src/screener/sort.js +52 -0
  70. package/dist/packages/cli/src/screener/view.js +183 -0
  71. package/dist/packages/cli/src/screens/analyst-session.js +343 -0
  72. package/dist/packages/cli/src/screens/analyst.js +326 -0
  73. package/dist/packages/cli/src/screens/boot.js +74 -0
  74. package/dist/packages/cli/src/screens/bubbles.js +255 -0
  75. package/dist/packages/cli/src/screens/coin.js +33 -0
  76. package/dist/packages/cli/src/screens/follows.js +201 -0
  77. package/dist/packages/cli/src/screens/kol.js +90 -0
  78. package/dist/packages/cli/src/screens/narrative.js +90 -0
  79. package/dist/packages/cli/src/screens/newpairs.js +150 -0
  80. package/dist/packages/cli/src/screens/overlay.js +257 -0
  81. package/dist/packages/cli/src/screens/plans.js +126 -0
  82. package/dist/packages/cli/src/screens/positions.js +176 -0
  83. package/dist/packages/cli/src/screens/rules.js +170 -0
  84. package/dist/packages/cli/src/screens/screener.js +459 -0
  85. package/dist/packages/cli/src/screens/social-panel.js +107 -0
  86. package/dist/packages/cli/src/screens/ticket.js +342 -0
  87. package/dist/packages/cli/src/screens/token.js +312 -0
  88. package/dist/packages/cli/src/screens/trending.js +181 -0
  89. package/dist/packages/cli/src/screens/watchlist.js +104 -0
  90. package/dist/packages/cli/src/social/demo.js +96 -0
  91. package/dist/packages/cli/src/social/view.js +250 -0
  92. package/dist/packages/cli/src/state.js +463 -0
  93. package/dist/packages/cli/src/theme-cmd.js +126 -0
  94. package/dist/packages/cli/src/trade.js +319 -0
  95. package/dist/packages/cli/src/voice-hud.js +42 -0
  96. package/dist/packages/cli/src/wallet-cmd.js +440 -0
  97. package/dist/packages/cli/src/wallet-command.js +31 -0
  98. package/dist/packages/cli/src/widgets.js +208 -0
  99. package/dist/packages/cli/src/workspace-cmd.js +68 -0
  100. package/dist/packages/cli/src/x-cmd.js +154 -0
  101. package/dist/packages/custom/src/config.js +213 -0
  102. package/dist/packages/custom/src/index.js +10 -0
  103. package/dist/packages/custom/src/keymap.js +107 -0
  104. package/dist/packages/custom/src/layout.js +235 -0
  105. package/dist/packages/custom/src/packs.js +633 -0
  106. package/dist/packages/custom/src/rules.js +704 -0
  107. package/dist/packages/custom/src/schema.js +515 -0
  108. package/dist/packages/custom/src/themes.js +176 -0
  109. package/dist/packages/custom/src/widget-html.js +222 -0
  110. package/dist/packages/custom/src/workspace.js +141 -0
  111. package/dist/packages/data/src/analyst-actions.js +133 -0
  112. package/dist/packages/data/src/budget.js +88 -0
  113. package/dist/packages/data/src/cache.js +142 -0
  114. package/dist/packages/data/src/candles.js +168 -0
  115. package/dist/packages/data/src/clock.js +64 -0
  116. package/dist/packages/data/src/credits.js +137 -0
  117. package/dist/packages/data/src/detail.js +97 -0
  118. package/dist/packages/data/src/fast-lane.js +321 -0
  119. package/dist/packages/data/src/fixtures.js +135 -0
  120. package/dist/packages/data/src/health.js +245 -0
  121. package/dist/packages/data/src/http.js +277 -0
  122. package/dist/packages/data/src/images/cache.js +141 -0
  123. package/dist/packages/data/src/images/decode-worker.js +28 -0
  124. package/dist/packages/data/src/images/decode.js +81 -0
  125. package/dist/packages/data/src/images/fetch.js +162 -0
  126. package/dist/packages/data/src/images/gif.js +214 -0
  127. package/dist/packages/data/src/images/index.js +10 -0
  128. package/dist/packages/data/src/images/jpeg.js +544 -0
  129. package/dist/packages/data/src/images/png.js +310 -0
  130. package/dist/packages/data/src/images/service.js +334 -0
  131. package/dist/packages/data/src/images/types.js +27 -0
  132. package/dist/packages/data/src/index.js +208 -0
  133. package/dist/packages/data/src/intel/helius-history.js +59 -0
  134. package/dist/packages/data/src/intel/holder-class.js +125 -0
  135. package/dist/packages/data/src/intel/rules.js +421 -0
  136. package/dist/packages/data/src/intel/scan.js +170 -0
  137. package/dist/packages/data/src/larp/evidence.js +288 -0
  138. package/dist/packages/data/src/larp/links.js +137 -0
  139. package/dist/packages/data/src/larp/robots.js +90 -0
  140. package/dist/packages/data/src/larp/safe-fetch.js +389 -0
  141. package/dist/packages/data/src/limiter.js +341 -0
  142. package/dist/packages/data/src/model.js +12 -0
  143. package/dist/packages/data/src/paper.js +331 -0
  144. package/dist/packages/data/src/rpc-config.js +126 -0
  145. package/dist/packages/data/src/rpc-pool.js +166 -0
  146. package/dist/packages/data/src/social/chain.js +83 -0
  147. package/dist/packages/data/src/social/dedupe.js +205 -0
  148. package/dist/packages/data/src/social/feed.js +462 -0
  149. package/dist/packages/data/src/social/firewall.js +110 -0
  150. package/dist/packages/data/src/social/index.js +11 -0
  151. package/dist/packages/data/src/social/kol.js +269 -0
  152. package/dist/packages/data/src/social/model.js +11 -0
  153. package/dist/packages/data/src/social/rank.js +184 -0
  154. package/dist/packages/data/src/social/rss.js +113 -0
  155. package/dist/packages/data/src/social/summary.js +104 -0
  156. package/dist/packages/data/src/social/venues.js +186 -0
  157. package/dist/packages/data/src/sources/dexscreener.js +170 -0
  158. package/dist/packages/data/src/sources/geckoterminal.js +133 -0
  159. package/dist/packages/data/src/sources/goplus.js +161 -0
  160. package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
  161. package/dist/packages/data/src/sources/jupiter.js +198 -0
  162. package/dist/packages/data/src/sources/pda.js +113 -0
  163. package/dist/packages/data/src/sources/pump-curve.js +58 -0
  164. package/dist/packages/data/src/sources/pumpportal.js +247 -0
  165. package/dist/packages/data/src/sources/rugcheck.js +71 -0
  166. package/dist/packages/data/src/sources/solana-rpc.js +299 -0
  167. package/dist/packages/data/src/sources/solana-ws.js +287 -0
  168. package/dist/packages/data/src/store.js +289 -0
  169. package/dist/packages/data/src/trade/bridge.js +464 -0
  170. package/dist/packages/data/src/trade/caps.js +323 -0
  171. package/dist/packages/data/src/trade/copy/auto.js +385 -0
  172. package/dist/packages/data/src/trade/copy/follows.js +103 -0
  173. package/dist/packages/data/src/trade/copy/index.js +9 -0
  174. package/dist/packages/data/src/trade/copy/mirror.js +91 -0
  175. package/dist/packages/data/src/trade/copy/stats.js +120 -0
  176. package/dist/packages/data/src/trade/copy/tracker.js +158 -0
  177. package/dist/packages/data/src/trade/copy/trades.js +87 -0
  178. package/dist/packages/data/src/trade/copy/types.js +6 -0
  179. package/dist/packages/data/src/trade/executor.js +933 -0
  180. package/dist/packages/data/src/trade/kill-switch.js +47 -0
  181. package/dist/packages/data/src/trade/link-store.js +52 -0
  182. package/dist/packages/data/src/trade/plans/confirm.js +157 -0
  183. package/dist/packages/data/src/trade/plans/engine.js +784 -0
  184. package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
  185. package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
  186. package/dist/packages/data/src/trade/plans/executor.js +11 -0
  187. package/dist/packages/data/src/trade/plans/index.js +12 -0
  188. package/dist/packages/data/src/trade/plans/journal.js +77 -0
  189. package/dist/packages/data/src/trade/plans/mandate.js +129 -0
  190. package/dist/packages/data/src/trade/plans/plan.js +179 -0
  191. package/dist/packages/data/src/trade/plans/service.js +168 -0
  192. package/dist/packages/data/src/trade/plans/types.js +6 -0
  193. package/dist/packages/data/src/trade/rpc-exec.js +229 -0
  194. package/dist/packages/data/src/trade/signer/client.js +197 -0
  195. package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
  196. package/dist/packages/data/src/trade/signer/host.js +97 -0
  197. package/dist/packages/data/src/trade/signer/main.js +121 -0
  198. package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
  199. package/dist/packages/data/src/trade/signer/protocol.js +92 -0
  200. package/dist/packages/data/src/trade/signer/service.js +400 -0
  201. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
  202. package/dist/packages/data/src/trade/sol-usd.js +86 -0
  203. package/dist/packages/data/src/trade/swap-build.js +108 -0
  204. package/dist/packages/data/src/trade/token-delta.js +201 -0
  205. package/dist/packages/data/src/trade/tx-decode.js +632 -0
  206. package/dist/packages/data/src/util.js +24 -0
  207. package/dist/packages/data/src/watchlist.js +176 -0
  208. package/dist/packages/data/src/x/api.js +256 -0
  209. package/dist/packages/data/src/x/budget.js +66 -0
  210. package/dist/packages/data/src/x/config.js +49 -0
  211. package/dist/packages/data/src/x/index.js +10 -0
  212. package/dist/packages/data/src/x/mock-server.js +217 -0
  213. package/dist/packages/data/src/x/oauth.js +242 -0
  214. package/dist/packages/data/src/x/pkce.js +21 -0
  215. package/dist/packages/data/src/x/source.js +285 -0
  216. package/dist/packages/data/src/x/token-store.js +134 -0
  217. package/dist/packages/data/src/x/transport.js +71 -0
  218. package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
  219. package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
  220. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
  221. package/dist/packages/mcp/assets/cases.json +1194 -0
  222. package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
  223. package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
  224. package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
  225. package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
  226. package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
  227. package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
  228. package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
  229. package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
  230. package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
  231. package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
  232. package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
  233. package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
  234. package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
  235. package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
  236. package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
  237. package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
  238. package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
  239. package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
  240. package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
  241. package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
  242. package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
  243. package/dist/packages/mcp/src/dossier.js +78 -0
  244. package/dist/packages/mcp/src/firewall.js +21 -0
  245. package/dist/packages/mcp/src/index.js +22 -0
  246. package/dist/packages/mcp/src/server.js +849 -0
  247. package/dist/packages/mcp/src/user-content.js +434 -0
  248. package/dist/packages/packs/src/backtest.js +183 -0
  249. package/dist/packages/packs/src/digest.js +95 -0
  250. package/dist/packages/packs/src/firewall.js +57 -0
  251. package/dist/packages/packs/src/index.js +19 -0
  252. package/dist/packages/packs/src/larp-eval.js +100 -0
  253. package/dist/packages/packs/src/narrative.js +309 -0
  254. package/dist/packages/packs/src/reason.js +135 -0
  255. package/dist/packages/packs/src/rug-model.js +137 -0
  256. package/dist/packages/packs/src/schema.js +54 -0
  257. package/dist/packages/packs/src/token-dossier.js +174 -0
  258. package/dist/packages/packs/src/verdicts.js +17 -0
  259. package/dist/packages/packs/src/wallet-dossier.js +105 -0
  260. package/dist/packages/popup/src/config.js +71 -0
  261. package/dist/packages/popup/src/dock.js +271 -0
  262. package/dist/packages/popup/src/host.js +210 -0
  263. package/dist/packages/popup/src/index.js +12 -0
  264. package/dist/packages/popup/src/loopback.js +53 -0
  265. package/dist/packages/popup/src/open.js +183 -0
  266. package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
  267. package/dist/packages/popup/src/pages/bubbles.css +23 -0
  268. package/dist/packages/popup/src/pages/bubbles.html +36 -0
  269. package/dist/packages/popup/src/pages/bubbles.js +214 -0
  270. package/dist/packages/popup/src/pages/chart-lib.js +262 -0
  271. package/dist/packages/popup/src/pages/chart.css +50 -0
  272. package/dist/packages/popup/src/pages/chart.html +56 -0
  273. package/dist/packages/popup/src/pages/chart.js +554 -0
  274. package/dist/packages/popup/src/pages/common.css +34 -0
  275. package/dist/packages/popup/src/pages/common.js +187 -0
  276. package/dist/packages/popup/src/pages/feeds.html +20 -0
  277. package/dist/packages/popup/src/pages/feeds.js +77 -0
  278. package/dist/packages/popup/src/pages/narrative.html +20 -0
  279. package/dist/packages/popup/src/pages/narrative.js +61 -0
  280. package/dist/packages/popup/src/pages/plans.css +30 -0
  281. package/dist/packages/popup/src/pages/plans.html +26 -0
  282. package/dist/packages/popup/src/pages/plans.js +82 -0
  283. package/dist/packages/popup/src/pages/roster.css +41 -0
  284. package/dist/packages/popup/src/pages/roster.html +25 -0
  285. package/dist/packages/popup/src/pages/roster.js +75 -0
  286. package/dist/packages/popup/src/pages/token.html +20 -0
  287. package/dist/packages/popup/src/pages/token.js +112 -0
  288. package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
  289. package/dist/packages/popup/src/pages/widget.css +5 -0
  290. package/dist/packages/popup/src/pages/widget.html +21 -0
  291. package/dist/packages/popup/src/pages/widget.js +85 -0
  292. package/dist/packages/popup/src/protocol.js +31 -0
  293. package/dist/packages/popup/src/selftest.js +156 -0
  294. package/dist/packages/popup/src/server.js +582 -0
  295. package/dist/packages/popup/src/sinkproxy.js +167 -0
  296. package/dist/packages/popup/src/widget-html.js +231 -0
  297. package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
  298. package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
  299. package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
  300. package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
  301. package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
  302. package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
  303. package/dist/packages/recorder/src/cli.js +113 -0
  304. package/dist/packages/recorder/src/export.js +71 -0
  305. package/dist/packages/recorder/src/index.js +20 -0
  306. package/dist/packages/recorder/src/labels.js +151 -0
  307. package/dist/packages/recorder/src/open.js +29 -0
  308. package/dist/packages/recorder/src/recorder.js +553 -0
  309. package/dist/packages/recorder/src/schedule.js +43 -0
  310. package/dist/packages/recorder/src/schema.js +135 -0
  311. package/dist/packages/recorder/src/snapshot.js +191 -0
  312. package/dist/packages/recorder/src/sweep.js +52 -0
  313. package/dist/packages/recorder/src/verdicts.js +52 -0
  314. package/dist/packages/tui/src/bubbles.js +415 -0
  315. package/dist/packages/tui/src/canvas.js +370 -0
  316. package/dist/packages/tui/src/chrome.js +85 -0
  317. package/dist/packages/tui/src/color.js +133 -0
  318. package/dist/packages/tui/src/flush.js +62 -0
  319. package/dist/packages/tui/src/fmt.js +188 -0
  320. package/dist/packages/tui/src/fx-trade.js +278 -0
  321. package/dist/packages/tui/src/fx.js +527 -0
  322. package/dist/packages/tui/src/images/blocks.js +141 -0
  323. package/dist/packages/tui/src/images/coin.js +22 -0
  324. package/dist/packages/tui/src/images/detect.js +140 -0
  325. package/dist/packages/tui/src/images/index.js +7 -0
  326. package/dist/packages/tui/src/images/layer.js +146 -0
  327. package/dist/packages/tui/src/images/pixels.js +157 -0
  328. package/dist/packages/tui/src/images/protocols.js +87 -0
  329. package/dist/packages/tui/src/index.js +15 -0
  330. package/dist/packages/tui/src/keymap.js +355 -0
  331. package/dist/packages/tui/src/layout.js +11 -0
  332. package/dist/packages/tui/src/sound.js +177 -0
  333. package/dist/packages/tui/src/theme.js +182 -0
  334. package/dist/packages/tui/src/widgets.js +598 -0
  335. package/dist/packages/voice/src/controller.js +395 -0
  336. package/dist/packages/voice/src/doctor.js +63 -0
  337. package/dist/packages/voice/src/engines.js +44 -0
  338. package/dist/packages/voice/src/index.js +16 -0
  339. package/dist/packages/voice/src/router.js +502 -0
  340. package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
  341. package/dist/packages/voice/src/stt/apple.js +179 -0
  342. package/dist/packages/voice/src/stt/fake.js +34 -0
  343. package/dist/packages/voice/src/stt/whisper.js +102 -0
  344. package/dist/packages/voice/src/summary.js +44 -0
  345. package/dist/packages/voice/src/tts/fake.js +30 -0
  346. package/dist/packages/voice/src/tts/say.js +96 -0
  347. package/dist/packages/voice/src/types.js +3 -0
  348. package/package.json +42 -4
  349. package/README.md +0 -3
@@ -0,0 +1,227 @@
1
+ // dexterm — live binding for WALLET INTEL (task-1340): runs the data layer's WalletIntel scan for the token on
2
+ // screen (light tier) or a watchlist coin (deep tier), keeps the result per mint, and builds the two view models
3
+ // the bubble map is drawn from: the terminal screen's (tui BubbleVM) and the popup's (popup BubbleVM, text-only,
4
+ // no keys). Every state is explicit: loading → ok (possibly degraded: keyless) → unavailable (with the reason and
5
+ // the fix), never a fake picture. Read-only: nothing here trades or signs.
6
+ import { WalletIntel, HIDDEN_LABELS, visibleLabels, EX_POOL_TAGS, BIG_HOLDER_PCT } from '../../../data/src/index.js';
7
+ import { bubbleVM, fmtNum } from '../../../tui/src/index.js';
8
+ /** Delay before an opened token is scanned, so flipping through tokens never spends history credits. */
9
+ export const INTEL_AUTOSCAN_DELAY_MS = 1_500;
10
+ export class IntelLive {
11
+ intel;
12
+ now;
13
+ onChange;
14
+ isWatched;
15
+ showHidden;
16
+ entries = new Map();
17
+ gen = new Map();
18
+ autoscan = null;
19
+ constructor(dl, opts = {}) {
20
+ this.intel = opts.intel ?? new WalletIntel({ cache: dl.cache, http: dl.http, pool: dl.rpcPool, detail: dl.detail, health: dl.health, credits: dl.credits, clock: dl.clock });
21
+ this.now = opts.now ?? (() => Date.now());
22
+ this.onChange = opts.onChange ?? (() => { });
23
+ this.isWatched = opts.isWatched ?? (() => false);
24
+ this.showHidden = opts.showHidden ?? false;
25
+ }
26
+ /** The depth a mint gets: deep when watched, else light. */
27
+ depthFor(mint) { return this.isWatched(mint) ? 'deep' : 'light'; }
28
+ /** Whether the history leg (funders + launch window) can run; the screen prints the reason when not. */
29
+ historyAvailable() { return this.intel.historyAvailable(); }
30
+ entry(mint) { return this.entries.get(mint) ?? null; }
31
+ /** Run (or re-run) the scan for a mint. Resolves when it settles; `view()` is readable throughout. */
32
+ async scan(seed, opts = {}) {
33
+ const depth = opts.depth ?? this.depthFor(seed.mint);
34
+ const my = (this.gen.get(seed.mint) ?? 0) + 1;
35
+ this.gen.set(seed.mint, my);
36
+ const prev = this.entries.get(seed.mint);
37
+ const e = { mint: seed.mint, symbol: seed.symbol, name: seed.name ?? prev?.name ?? '', holderCount: seed.holderCount ?? prev?.holderCount ?? null, state: 'loading', result: prev?.result ?? null, bubbles: prev?.bubbles ?? null, at: this.now(), error: null };
38
+ this.entries.set(seed.mint, e);
39
+ this.onChange();
40
+ try {
41
+ const r = await this.intel.scan(seed.mint, depth, { force: opts.force });
42
+ if (this.gen.get(seed.mint) !== my)
43
+ return this.entries.get(seed.mint) ?? e;
44
+ e.result = r;
45
+ e.at = this.now();
46
+ if (r.state === 'ok') {
47
+ e.state = 'ok';
48
+ const a = this.showHidden ? r.analysis : hideLabels(r.analysis);
49
+ e.bubbles = bubbleVM(a, { symbol: e.symbol, name: e.name, mint: e.mint, holderCount: e.holderCount });
50
+ }
51
+ else {
52
+ e.state = 'unavailable';
53
+ e.bubbles = null;
54
+ }
55
+ }
56
+ catch (err) {
57
+ if (this.gen.get(seed.mint) !== my)
58
+ return this.entries.get(seed.mint) ?? e;
59
+ e.state = 'unavailable';
60
+ e.error = err instanceof Error ? err.message : String(err);
61
+ e.bubbles = null;
62
+ }
63
+ finally {
64
+ if (this.gen.get(seed.mint) === my)
65
+ this.onChange();
66
+ }
67
+ return e;
68
+ }
69
+ /** Schedule a light scan for an opened token after INTEL_AUTOSCAN_DELAY_MS (cancelled by the next open). */
70
+ autoscanLater(seed, delayMs = INTEL_AUTOSCAN_DELAY_MS) {
71
+ if (this.autoscan)
72
+ clearTimeout(this.autoscan);
73
+ const h = setTimeout(() => { this.autoscan = null; void this.scan(seed).catch(() => { }); }, delayMs);
74
+ h.unref?.();
75
+ this.autoscan = h;
76
+ }
77
+ /**
78
+ * task-1359: the NEW PAIRS list scans the row the cursor rests on (same delay as a token open, cancelled by
79
+ * the next move), but only when the history leg can run: without a Helius key a scan cannot read the
80
+ * launch window, so it would spend public-RPC calls to still print a dash. A mint already scanned or in
81
+ * flight is left alone. Returns whether a scan was scheduled.
82
+ */
83
+ scanSelectedLater(seed, delayMs = INTEL_AUTOSCAN_DELAY_MS) {
84
+ if (!this.intel.historyAvailable().ok)
85
+ return false;
86
+ const e = this.entries.get(seed.mint);
87
+ if (e && (e.state === 'loading' || e.state === 'ok'))
88
+ return false;
89
+ this.autoscanLater(seed, delayMs);
90
+ return true;
91
+ }
92
+ /** Cancel a pending autoscan (token closed). */
93
+ cancelAutoscan() { if (this.autoscan) {
94
+ clearTimeout(this.autoscan);
95
+ this.autoscan = null;
96
+ } }
97
+ /** The analysis for a mint when the scan is ok, else null (the token screen's ON-CHAIN rows read this). */
98
+ analysis(mint) {
99
+ const e = this.entries.get(mint);
100
+ return e && e.state === 'ok' && e.result?.state === 'ok' ? e.result.analysis : null;
101
+ }
102
+ close() { this.cancelAutoscan(); }
103
+ }
104
+ /** Drop the hidden-by-default labels from every holder (the renderer never sees a cabal-class label). */
105
+ export function hideLabels(a) {
106
+ return { ...a, holders: a.holders.map((h) => ({ ...h, labels: visibleLabels(h.labels, false) })), clusters: a.clusters.map((c) => ({ ...c, labels: c.labels.filter((l) => !HIDDEN_LABELS.has(l)) })) };
107
+ }
108
+ /**
109
+ * Snipers / bundled supply for one mint, from the wallet-intel scan only (never a guess). Shared by the token
110
+ * screen's ON-CHAIN rows and the live NEW PAIRS columns + spotlight. Before a scan has read the launch window
111
+ * both cells say why (scanning / needs a free Helius key / not scanned yet) and the short form is a dash.
112
+ */
113
+ export function launchIntelCells(intel, mint, notScanned = 'not scanned yet') {
114
+ const a = intel?.analysis(mint) ?? null;
115
+ const I = a && a.insights.coverage.launchTxs > 0 ? a.insights : null;
116
+ if (I) {
117
+ const s = I.snipers, b = I.bundle;
118
+ return {
119
+ measured: true,
120
+ snipers: { text: `${s.count} · ${s.launchWindowSharePct.toFixed(1)}% at launch`, short: String(s.count), level: s.launchWindowSharePct > 20 ? 'bad' : s.count > 3 ? 'warn' : 'ok' },
121
+ bundle: { text: `${b.bundlePctOfTopSample.toFixed(1)}% · ${b.sameSlotCohorts} cohorts`, short: `${b.bundlePctOfTopSample.toFixed(0)}%`, level: b.bundlePctOfTopSample > 15 ? 'bad' : b.bundlePctOfTopSample > 5 ? 'warn' : 'ok' },
122
+ };
123
+ }
124
+ const e = intel?.entry(mint) ?? null;
125
+ const loading = e?.state === 'loading';
126
+ const hist = intel ? intel.historyAvailable() : { ok: false, why: 'live mode only' };
127
+ const r = e?.result?.state === 'ok' ? e.result : null;
128
+ const why = loading ? 'scanning…' : a && r ? noLaunchWhy(r, hist) : !hist.ok ? historyWhy(hist.why, !intel) : notScanned;
129
+ const na = { text: why, short: loading ? '…' : '—', level: 'na' };
130
+ return { measured: false, snipers: na, bundle: { ...na } };
131
+ }
132
+ /**
133
+ * task-1367: why an ok scan has no launch window. Before, every case printed "needs Helius key" — also with a key
134
+ * configured, when Helius was rate-limited, the daily credit cap was hit or the history call failed. Now the
135
+ * reason is the scan's own: only a missing key says "needs Helius key".
136
+ */
137
+ function noLaunchWhy(r, hist) {
138
+ if (r.via.history === 'helius')
139
+ return r.degraded ? 'history failed · r' : 'no launch txs read';
140
+ return historyWhy(r.degraded ?? (hist.ok ? '' : hist.why), false, hist.ok);
141
+ }
142
+ function historyWhy(why, noBinding, nowOk = false) {
143
+ if (noBinding || /Helius key/i.test(why))
144
+ return 'needs Helius key';
145
+ if (/credit budget/i.test(why))
146
+ return 'Helius cap today';
147
+ if (/Helius is/i.test(why))
148
+ return 'Helius paused · r';
149
+ return nowOk ? 'not read · r retry' : 'needs Helius key';
150
+ }
151
+ /** Whether `r` on the token screen should re-run this mint's scan (an ok scan whose history leg did not land). */
152
+ export function launchRetryable(intel, mint) {
153
+ const e = intel?.entry(mint);
154
+ if (!intel || !e || e.state !== 'ok' || e.result?.state !== 'ok')
155
+ return false;
156
+ return e.result.analysis.insights.coverage.launchTxs === 0 && intel.historyAvailable().ok;
157
+ }
158
+ const shortA = (a) => `${a.slice(0, 4)}…${a.slice(-4)}`;
159
+ /** The token screen's holder classes + concentration for a mint (pure over the IntelLive state). */
160
+ export function holderTags(intel, mint) {
161
+ const a = intel?.analysis(mint) ?? null;
162
+ const byOwner = new Map();
163
+ if (!a)
164
+ return { byOwner, top10: null, note: null };
165
+ for (const h of a.holders)
166
+ if (h.cls)
167
+ byOwner.set(h.owner, h.cls);
168
+ const T = a.insights.top10;
169
+ const top10 = typeof T.exPool === 'number' ? { raw: T.byTokenAccount, exPool: T.exPool, dropped: T.exPoolDropped ?? 0, droppedPct: T.exPoolDroppedPct ?? 0 } : null;
170
+ let note = null;
171
+ const big = a.insights.bigHolder ?? null;
172
+ if (big) {
173
+ note = { text: `main risk: ${big.tag === 'DEV' ? 'CREATOR' : big.tag} ${shortA(big.owner)} holds ${big.pct.toFixed(1)}%`, level: 'bad' };
174
+ }
175
+ else {
176
+ const top = a.holders[0];
177
+ if (top?.cls && top.pct > BIG_HOLDER_PCT && EX_POOL_TAGS.has(top.cls.tag)) {
178
+ const c = top.cls;
179
+ const what = c.lock ? `${c.lock.program} escrow ${shortA(c.lock.escrow)}, ${c.lock.unlock ?? 'unlock not decoded'}` : c.program ?? 'burn address';
180
+ note = { text: `top holder ${top.pct.toFixed(1)}% is ${c.tag} (${what}): not counted in ex-pool`, level: 'info' };
181
+ }
182
+ }
183
+ return { byOwner, top10, note };
184
+ }
185
+ /** "unavailable: <reason> — <fix>" for the screen / popup banner. */
186
+ export function unavailableText(e) {
187
+ if (!e)
188
+ return { reason: 'no scan yet', fix: null };
189
+ if (e.error)
190
+ return { reason: e.error, fix: null };
191
+ if (e.result?.state === 'unavailable')
192
+ return { reason: e.result.reason, fix: e.result.fix };
193
+ return { reason: 'no holder source answered', fix: null };
194
+ }
195
+ /** Build the popup's text-only view model from an entry (never a key, never a URL). */
196
+ export function popupBubbleVM(e, seed, selected, image = null) {
197
+ const base = { symbol: seed.symbol, name: seed.name ?? e?.name ?? '', mint: seed.mint, state: 'loading', reason: null, fix: null, depth: null, degraded: null, holders: [], clusters: [], insights: null, selected, via: null, updatedAt: e?.at ?? null, image };
198
+ if (!e || e.state === 'loading')
199
+ return base;
200
+ if (e.state !== 'ok' || !e.result || e.result.state !== 'ok' || !e.bubbles) {
201
+ const u = unavailableText(e);
202
+ return { ...base, state: 'unavailable', reason: u.reason, fix: u.fix };
203
+ }
204
+ const r = e.result;
205
+ const I = r.analysis.insights;
206
+ const clsOf = new Map(r.analysis.holders.map((h) => [h.owner, h.cls]));
207
+ const insights = {
208
+ top10Raw: I.top10.byTokenAccount, top10Adjusted: I.top10.clusterAdjusted, excludedCount: I.top10.excludedCount,
209
+ top10ExPool: I.top10.exPool ?? null, exPoolDropped: I.top10.exPoolDropped ?? 0,
210
+ bigHolder: I.bigHolder ? { owner: I.bigHolder.owner, pct: I.bigHolder.pct, tag: I.bigHolder.tag } : null,
211
+ snipers: I.coverage.launchTxs > 0 ? { count: I.snipers.count, launchSharePct: I.snipers.launchWindowSharePct } : null,
212
+ bundlePct: I.coverage.launchTxs > 0 ? I.bundle.bundlePctOfTopSample : null,
213
+ cohorts: I.coverage.launchTxs > 0 ? I.bundle.sameSlotCohorts : null,
214
+ devPct: I.dev.creatorPctNow, devUpperBoundPct: I.dev.creatorUpperBoundPct,
215
+ freshPct: I.coverage.fundingLookups > 0 ? I.fresh.freshAtBuyPctOfTopSample : null, freshMeasuredPct: I.coverage.fundingLookups > 0 ? I.fresh.measuredPct : null,
216
+ sample: `sample top ${I.coverage.topHoldersFetched} accts${e.holderCount ? ` of ${fmtNum(e.holderCount)} holders` : ''} · ${I.coverage.supplyCoveredPct}% supply · funders ${I.coverage.fundingLookups}/${I.coverage.topHoldersFetched} · launch ${I.coverage.launchTxs} txs${I.coverage.launchMorePages ? '+' : ''}`,
217
+ };
218
+ return {
219
+ ...base, state: 'ok', depth: r.depth, degraded: r.degraded, via: r.via.history ? `${r.via.holders} + helius history` : r.via.holders,
220
+ holders: e.bubbles.holders.map((h) => ({ owner: h.owner, pct: h.pct, kind: h.kind, labels: h.labels.map((l) => ({ label: l.label, rule: l.rule, confidence: l.confidence })), funder: h.funder ? { address: h.funder.address, label: h.funder.label, blockTime: h.funder.blockTime } : null, walletFirstSeen: h.walletFirstSeen, cluster: h.cluster, cls: clsVM(clsOf.get(h.owner)) })),
221
+ clusters: e.bubbles.clusters.filter((c) => c.members.length > 1).map((c) => ({ name: c.name, kind: c.kind, members: c.members.slice(), pct: c.pct, evidence: c.evidence.map((v) => ({ a: v.a, b: v.b, rule: v.rule, linked: v.linked })) })),
222
+ insights,
223
+ };
224
+ }
225
+ function clsVM(c) {
226
+ return c ? { tag: c.tag, reason: c.reason, lock: c.lock ? { escrow: c.lock.escrow, program: c.lock.program, unlock: c.lock.unlock, note: c.lock.note } : null } : null;
227
+ }
@@ -0,0 +1,334 @@
1
+ import { SOL_MINT } from "../state.js";
2
+ import { cleanText, deriveFlags, impliedSpark } from "../screener/rows.js";
3
+ export const RECENT_POLL_MS = 15_000;
4
+ export const NEW_TOKENS_POLL_MS = 30_000;
5
+ export const ENRICH_POLL_MS = 10_000;
6
+ export const MAX_ROWS = 200;
7
+ /** pump.fun graduates at ~$69K market cap (SPEC §4.4). */
8
+ export const PUMP_GRADUATION_USD = 69_000;
9
+ export function percentile(xs, p) {
10
+ if (!xs.length)
11
+ return null;
12
+ const s = [...xs].sort((a, b) => a - b);
13
+ const i = Math.min(s.length - 1, Math.max(0, Math.ceil((p / 100) * s.length) - 1));
14
+ return s[i];
15
+ }
16
+ export function latencyStats(xs) {
17
+ return { n: xs.length, p50: percentile(xs, 50), p95: percentile(xs, 95), max: xs.length ? Math.max(...xs) : null };
18
+ }
19
+ export class NewPairsLive {
20
+ dl;
21
+ now;
22
+ onChange;
23
+ limit;
24
+ polls;
25
+ rows = new Map();
26
+ client = null;
27
+ mode = 'off';
28
+ lastRecent = 0;
29
+ lastNewTokens = 0;
30
+ lastEnrich = 0;
31
+ createTimes = [];
32
+ latencies = [];
33
+ renderLatencies = [];
34
+ solPrice = null;
35
+ errors = {};
36
+ refreshing = false;
37
+ /** Creator / authority facts from RugCheck by mint (kept even for mints not yet in `rows`). */
38
+ fresh = new Map();
39
+ constructor(dl, opts = {}) {
40
+ this.dl = dl;
41
+ this.now = opts.now ?? (() => Date.now());
42
+ this.onChange = opts.onChange;
43
+ this.limit = opts.limit ?? MAX_ROWS;
44
+ this.polls = { recent: true, newTokens: true, enrich: true, ...opts.polls };
45
+ }
46
+ /** Seed the SOL price (e.g. from the screener feed) so SOL-denominated market caps show before the first poll. */
47
+ setSolPrice(price) {
48
+ if (price && price > 0) {
49
+ this.solPrice = price;
50
+ for (const row of this.rows.values())
51
+ if (row.mcap === null && row.marketCapSol !== null)
52
+ row.mcap = row.marketCapSol * price;
53
+ }
54
+ }
55
+ /** Attach a PumpPortal client (live or for replay). The client is started by start(). */
56
+ attach(client, mode = 'live') {
57
+ this.client = client;
58
+ this.mode = mode;
59
+ }
60
+ /** Route one stream event (the client's onEvent). */
61
+ handleEvent(ev) {
62
+ if (ev.kind === 'create')
63
+ this.addCreate(ev.create);
64
+ else if (ev.kind === 'migrate' && ev.mint) {
65
+ const row = this.rows.get(ev.mint);
66
+ if (row) {
67
+ row.migrated = true;
68
+ row.bonding = 100;
69
+ this.onChange?.('migrate');
70
+ }
71
+ }
72
+ }
73
+ start() {
74
+ if (this.mode === 'live')
75
+ this.client?.start();
76
+ }
77
+ stop() {
78
+ this.client?.stop();
79
+ }
80
+ /** Replay a recorded stream through the same path (tests / --replay). */
81
+ replay(rec, speed = 0) {
82
+ if (!this.client)
83
+ throw new Error('no client attached for replay');
84
+ this.mode = 'replay';
85
+ return this.client.replay(rec, speed);
86
+ }
87
+ addCreate(c) {
88
+ const rowAt = this.now();
89
+ const existing = this.rows.get(c.mint);
90
+ const fresh = this.fresh.get(c.mint);
91
+ const mcap = c.marketCapSol !== null && this.solPrice ? c.marketCapSol * this.solPrice : existing?.mcap ?? null;
92
+ const row = existing ?? {
93
+ mint: c.mint, symbol: cleanText(c.symbol, 12) || '?', name: cleanText(c.name, 40), launchpad: c.launchpad, creator: c.creator || fresh?.creator || null,
94
+ createdAt: c.receivedAt, rowAt, receivedAt: c.receivedAt, latencyMs: Math.max(0, rowAt - c.receivedAt), drawnAt: null,
95
+ bonding: c.bondingPct, migrated: false, marketCapSol: c.marketCapSol, mcap, price: null, liq: null, vol: null, chg: { m5: null, h1: null },
96
+ holders: null, buys: null, sells: null, dev: null, org: null,
97
+ mintAuth: fresh?.mintAuthority ?? null, freezeAuth: fresh?.freezeAuthority ?? null,
98
+ initialBuySol: c.initialBuySol, spark: undefined, flags: [], socials: { x: false, tg: false, web: false }, image: null, via: ['pumpportal'],
99
+ };
100
+ if (existing) {
101
+ existing.bonding = c.bondingPct ?? existing.bonding;
102
+ existing.marketCapSol = c.marketCapSol ?? existing.marketCapSol;
103
+ if (!existing.via.includes('pumpportal'))
104
+ existing.via.push('pumpportal');
105
+ }
106
+ else {
107
+ this.rows.set(c.mint, row);
108
+ this.latencies.push(row.latencyMs);
109
+ if (this.latencies.length > 500)
110
+ this.latencies.shift();
111
+ }
112
+ this.createTimes.push(rowAt);
113
+ this.createTimes = this.createTimes.filter((t) => t > rowAt - 60_000);
114
+ this.trim();
115
+ this.onChange?.('create');
116
+ }
117
+ trim() {
118
+ if (this.rows.size <= this.limit)
119
+ return;
120
+ const sorted = this.list();
121
+ for (const r of sorted.slice(this.limit))
122
+ this.rows.delete(r.mint);
123
+ }
124
+ /** Newest first. */
125
+ list() {
126
+ return [...this.rows.values()].sort((a, b) => b.createdAt - a.createdAt);
127
+ }
128
+ /** Mark rows as drawn (called by the UI after a flush) and record receive→draw latency once per row. */
129
+ markDrawn(at = this.now()) {
130
+ for (const r of this.rows.values()) {
131
+ if (r.drawnAt === null && r.receivedAt !== null) {
132
+ r.drawnAt = at;
133
+ this.renderLatencies.push(Math.max(0, at - r.receivedAt));
134
+ if (this.renderLatencies.length > 500)
135
+ this.renderLatencies.shift();
136
+ }
137
+ }
138
+ }
139
+ /** Run whichever polls are due. Never throws; errors land in `errors`. */
140
+ async refresh(force = false) {
141
+ if (this.refreshing)
142
+ return;
143
+ this.refreshing = true;
144
+ try {
145
+ const now = this.now();
146
+ const jobs = [];
147
+ const pushDown = !this.dl.health.isAvailable('pumpportal') || (this.mode === 'live' && this.client?.getStatus().state !== 'open');
148
+ if (this.polls.recent && (force || now - this.lastRecent >= RECENT_POLL_MS || (pushDown && now - this.lastRecent >= RECENT_POLL_MS / 3))) {
149
+ this.lastRecent = now;
150
+ jobs.push(this.pollRecent(pushDown));
151
+ }
152
+ if (this.polls.newTokens && (force || now - this.lastNewTokens >= NEW_TOKENS_POLL_MS)) {
153
+ this.lastNewTokens = now;
154
+ jobs.push(this.pollNewTokens());
155
+ }
156
+ if (this.polls.enrich && (force || now - this.lastEnrich >= ENRICH_POLL_MS)) {
157
+ this.lastEnrich = now;
158
+ jobs.push(this.pollEnrich());
159
+ }
160
+ await Promise.all(jobs);
161
+ if (jobs.length)
162
+ this.onChange?.('poll');
163
+ }
164
+ finally {
165
+ this.refreshing = false;
166
+ }
167
+ }
168
+ async pollRecent(addRows) {
169
+ try {
170
+ const views = await this.dl.jupiter.recent();
171
+ delete this.errors['jupiter-tokens'];
172
+ const now = this.now();
173
+ for (const v of views) {
174
+ let row = this.rows.get(v.token.mint);
175
+ if (!row) {
176
+ if (!addRows)
177
+ continue;
178
+ const created = v.token.createdAt ?? now;
179
+ row = {
180
+ mint: v.token.mint, symbol: cleanText(v.token.symbol, 12) || '?', name: cleanText(v.token.name, 40), launchpad: v.token.launchpad, creator: null,
181
+ createdAt: created, rowAt: now, receivedAt: null, latencyMs: null, drawnAt: null, bonding: null, migrated: false, marketCapSol: null,
182
+ mcap: null, price: null, liq: null, vol: null, chg: { m5: null, h1: null }, holders: null, buys: null, sells: null, dev: null, org: null,
183
+ mintAuth: null, freezeAuth: null, initialBuySol: null, spark: undefined, flags: [], socials: { x: false, tg: false, web: false }, image: null, via: [],
184
+ };
185
+ this.rows.set(row.mint, row);
186
+ }
187
+ this.applyJup(row, v);
188
+ }
189
+ this.trim();
190
+ }
191
+ catch (err) {
192
+ this.errors['jupiter-tokens'] = msg(err);
193
+ }
194
+ }
195
+ applyJup(row, v) {
196
+ if (row.launchpad === 'unknown')
197
+ row.launchpad = v.token.launchpad;
198
+ row.holders = v.stats.holderCount ?? row.holders;
199
+ row.org = v.stats.organicScore ?? row.org;
200
+ row.dev = v.audit.devPct ?? row.dev;
201
+ if (row.mintAuth === null)
202
+ row.mintAuth = v.audit.mintAuthority;
203
+ if (row.freezeAuth === null)
204
+ row.freezeAuth = v.audit.freezeAuthority;
205
+ if (v.pair.priceUsd > 0)
206
+ row.price = v.pair.priceUsd;
207
+ row.mcap = v.pair.mcap ?? v.pair.fdv ?? row.mcap;
208
+ row.liq = v.pair.liquidityUsd ?? row.liq;
209
+ row.vol = v.pair.volume.h24 ?? row.vol;
210
+ row.buys = v.pair.txns.m5.buys;
211
+ row.sells = v.pair.txns.m5.sells;
212
+ row.chg = { m5: v.pair.priceChange.m5 ?? row.chg.m5, h1: v.pair.priceChange.h1 ?? row.chg.h1 };
213
+ row.socials = { x: !!v.token.socials.x, tg: !!v.token.socials.tg, web: !!v.token.socials.web };
214
+ if (row.image === null && typeof v.token.image === 'string' && /^https?:\/\//i.test(v.token.image))
215
+ row.image = v.token.image;
216
+ row.spark = impliedSpark(row.price, { m5: row.chg.m5, h1: row.chg.h1, h6: null, h24: null }, 12);
217
+ if (!row.via.includes('jupiter-tokens'))
218
+ row.via.push('jupiter-tokens');
219
+ row.flags = deriveFlags({ mintAuth: row.mintAuth, freezeAuth: row.freezeAuth, top10: v.audit.top10Pct, dev: row.dev, bundled: null, lpLocked: row.launchpad === 'pump.fun' ? 100 : null });
220
+ }
221
+ async pollNewTokens() {
222
+ try {
223
+ const list = await this.dl.rugcheck.newTokens();
224
+ delete this.errors.rugcheck;
225
+ for (const t of list) {
226
+ this.fresh.set(t.mint, t);
227
+ const row = this.rows.get(t.mint);
228
+ if (!row)
229
+ continue;
230
+ row.creator = row.creator ?? t.creator;
231
+ if (t.mintAuthority !== null)
232
+ row.mintAuth = t.mintAuthority;
233
+ if (t.freezeAuthority !== null)
234
+ row.freezeAuth = t.freezeAuthority;
235
+ if (!row.via.includes('rugcheck'))
236
+ row.via.push('rugcheck');
237
+ row.flags = deriveFlags({ mintAuth: row.mintAuth, freezeAuth: row.freezeAuth, top10: null, dev: row.dev, bundled: null, lpLocked: row.launchpad === 'pump.fun' ? 100 : null });
238
+ }
239
+ if (this.fresh.size > 500)
240
+ for (const k of [...this.fresh.keys()].slice(0, this.fresh.size - 500))
241
+ this.fresh.delete(k);
242
+ }
243
+ catch (err) {
244
+ this.errors.rugcheck = msg(err);
245
+ }
246
+ }
247
+ async pollEnrich() {
248
+ const visible = this.list().slice(0, 29).map((r) => r.mint);
249
+ try {
250
+ const res = await this.dl.store.enrich([...visible, SOL_MINT], { price: false });
251
+ delete this.errors.dexscreener;
252
+ for (const r of res.rows) {
253
+ if (r.mint === SOL_MINT) {
254
+ if (r.pair.priceUsd > 0)
255
+ this.solPrice = r.pair.priceUsd;
256
+ continue;
257
+ }
258
+ const row = this.rows.get(r.mint);
259
+ if (!row)
260
+ continue;
261
+ const p = r.pair;
262
+ if (p.priceUsd > 0)
263
+ row.price = p.priceUsd;
264
+ row.liq = p.liquidityUsd ?? row.liq;
265
+ row.mcap = r.mcap?.value ?? p.mcap ?? p.fdv ?? row.mcap;
266
+ row.vol = p.volume.h24 ?? row.vol;
267
+ row.chg = { m5: p.priceChange.m5 ?? row.chg.m5, h1: p.priceChange.h1 ?? row.chg.h1 };
268
+ row.buys = p.txns.m5.buys;
269
+ row.sells = p.txns.m5.sells;
270
+ if (r.stats) {
271
+ row.holders = r.stats.holderCount ?? row.holders;
272
+ row.org = r.stats.organicScore ?? row.org;
273
+ }
274
+ if (r.audit) {
275
+ row.dev = r.audit.devPct ?? row.dev;
276
+ if (row.mintAuth === null)
277
+ row.mintAuth = r.audit.mintAuthority;
278
+ if (row.freezeAuth === null)
279
+ row.freezeAuth = r.audit.freezeAuthority;
280
+ }
281
+ if (row.image === null && typeof r.token.image === 'string' && /^https?:\/\//i.test(r.token.image))
282
+ row.image = r.token.image;
283
+ row.spark = impliedSpark(row.price, { m5: row.chg.m5, h1: row.chg.h1, h6: p.priceChange.h6, h24: p.priceChange.h24 }, 12);
284
+ for (const s of r.sources)
285
+ if (!row.via.includes(s))
286
+ row.via.push(s);
287
+ row.flags = deriveFlags({ mintAuth: row.mintAuth, freezeAuth: row.freezeAuth, top10: r.audit?.top10Pct ?? null, dev: row.dev, bundled: null, lpLocked: row.launchpad === 'pump.fun' ? 100 : null });
288
+ }
289
+ Object.assign(this.errors, res.errors);
290
+ // SOL price arrived: convert the SOL-denominated market caps of rows nobody else priced yet
291
+ if (this.solPrice)
292
+ for (const row of this.rows.values())
293
+ if (row.mcap === null && row.marketCapSol !== null)
294
+ row.mcap = row.marketCapSol * this.solPrice;
295
+ }
296
+ catch (err) {
297
+ this.errors.dexscreener = msg(err);
298
+ }
299
+ }
300
+ status() {
301
+ const stream = this.client?.getStatus() ?? null;
302
+ const h = this.dl.health;
303
+ const perMin = this.createTimes.filter((t) => t > this.now() - 60_000).length;
304
+ const parts = [];
305
+ let headerStatus;
306
+ if (this.mode === 'off')
307
+ parts.push('push off');
308
+ else if (this.mode === 'replay')
309
+ parts.push(`replay: pumpportal ${stream?.creates ?? 0} creates`);
310
+ else if (stream?.state === 'open')
311
+ parts.push(`ws: pumpportal ${perMin}/min`);
312
+ else {
313
+ const wait = stream?.nextRetryMs ? ` in ${Math.round(stream.nextRetryMs / 1000)}s` : '';
314
+ parts.push(`pumpportal ${stream?.state ?? 'off'}${wait} · polling jup recent`);
315
+ headerStatus = '◐ degraded: pumpportal';
316
+ }
317
+ if (this.polls.recent)
318
+ parts.push(h.isAvailable('jupiter-tokens') ? 'jup recent' : 'jup degraded');
319
+ if (this.polls.newTokens)
320
+ parts.push(h.isAvailable('rugcheck') ? 'rugcheck' : 'rugcheck degraded');
321
+ if (this.polls.enrich && !h.isAvailable('dexscreener'))
322
+ parts.push('dexscreener degraded');
323
+ const degraded = ['jupiter-tokens', 'rugcheck', 'dexscreener'].filter((s) => !h.isAvailable(s));
324
+ if (!headerStatus && degraded.length)
325
+ headerStatus = `◐ degraded: ${degraded.map((s) => (s === 'jupiter-tokens' ? 'jup' : s)).join(', ')}`;
326
+ return {
327
+ stream, mode: this.mode, caption: parts.join(' · '), headerStatus, createsPerMin: this.mode === 'live' ? perMin : null,
328
+ latency: latencyStats(this.latencies), renderLatency: latencyStats(this.renderLatencies), errors: this.errors,
329
+ };
330
+ }
331
+ }
332
+ function msg(err) {
333
+ return err instanceof Error ? err.message : String(err);
334
+ }