dexterm-cli 0.1.2 → 0.1.4

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 (327) hide show
  1. package/dist/packages/agents/src/bus.js +37 -1
  2. package/dist/packages/agents/src/follow.js +105 -0
  3. package/dist/packages/agents/src/index.js +5 -0
  4. package/dist/packages/agents/src/spectator-server.js +153 -0
  5. package/dist/packages/agents/src/spectator.js +212 -0
  6. package/dist/packages/cli/assets/claude/analyst-system.md +7 -1
  7. package/dist/packages/cli/src/agent-cmd.js +71 -7
  8. package/dist/packages/cli/src/agent-spectator.js +37 -0
  9. package/dist/packages/cli/src/alerts/format.js +25 -0
  10. package/dist/packages/cli/src/app-help-gen.js +365 -0
  11. package/dist/packages/cli/src/docs-kb-gen.js +228 -0
  12. package/dist/packages/cli/src/doctor.js +28 -12
  13. package/dist/packages/cli/src/follow-cmd.js +107 -1
  14. package/dist/packages/cli/src/help-cmd.js +48 -0
  15. package/dist/packages/cli/src/index.js +1 -1
  16. package/dist/packages/cli/src/kb-skill-cmd.js +3 -0
  17. package/dist/packages/cli/src/larp-detected.js +209 -0
  18. package/dist/packages/cli/src/live/index.js +64 -3
  19. package/dist/packages/cli/src/live/screens/token-live.js +24 -0
  20. package/dist/packages/cli/src/live/signals.js +109 -0
  21. package/dist/packages/cli/src/live/token.js +38 -1
  22. package/dist/packages/cli/src/live/trade.js +24 -1
  23. package/dist/packages/cli/src/main.js +495 -11
  24. package/dist/packages/cli/src/mcp-cmd.js +2 -7
  25. package/dist/packages/cli/src/mcp-follow.js +225 -0
  26. package/dist/packages/cli/src/pnl-app.js +221 -0
  27. package/dist/packages/cli/src/pnl-card.js +615 -0
  28. package/dist/packages/cli/src/record-cmd.js +455 -0
  29. package/dist/packages/cli/src/rules.js +50 -2
  30. package/dist/packages/cli/src/screens/follows.js +91 -2
  31. package/dist/packages/cli/src/screens/overlay.js +3 -0
  32. package/dist/packages/cli/src/screens/positions.js +1 -1
  33. package/dist/packages/cli/src/screens/rules.js +1 -1
  34. package/dist/packages/cli/src/spectate-cmd.js +115 -0
  35. package/dist/packages/cli/src/state.js +1 -1
  36. package/dist/packages/cli/src/version.js +1 -1
  37. package/dist/packages/cli/src/voice-cmd.js +86 -11
  38. package/dist/packages/cli/src/voice-hud.js +1 -1
  39. package/dist/packages/cli/src/x-cmd.js +11 -5
  40. package/dist/packages/custom/src/rules.js +2 -1
  41. package/dist/packages/data/src/analyst-actions.js +130 -3
  42. package/dist/packages/data/src/fast-lane.js +16 -2
  43. package/dist/packages/data/src/index.js +1 -0
  44. package/dist/packages/data/src/intel/live-signals.js +185 -0
  45. package/dist/packages/data/src/trade/copy/index.js +1 -1
  46. package/dist/packages/data/src/trade/copy/stats.js +36 -2
  47. package/dist/packages/data/src/trade/copy/tracker.js +3 -2
  48. package/dist/packages/mcp/assets/kb/app-help/app-help-command.md +35 -0
  49. package/dist/packages/mcp/assets/kb/app-help/ask-the-analyst.md +43 -0
  50. package/dist/packages/mcp/assets/kb/app-help/backtest.md +33 -0
  51. package/dist/packages/mcp/assets/kb/app-help/bubble-map.md +35 -0
  52. package/dist/packages/mcp/assets/kb/app-help/calm-mode.md +31 -0
  53. package/dist/packages/mcp/assets/kb/app-help/change-voice-speed.md +31 -0
  54. package/dist/packages/mcp/assets/kb/app-help/change-voice.md +30 -0
  55. package/dist/packages/mcp/assets/kb/app-help/chart-popup-selftest.md +32 -0
  56. package/dist/packages/mcp/assets/kb/app-help/coin-images.md +29 -0
  57. package/dist/packages/mcp/assets/kb/app-help/configure-rpc-key.md +28 -0
  58. package/dist/packages/mcp/assets/kb/app-help/connect-an-agent-registry.md +35 -0
  59. package/dist/packages/mcp/assets/kb/app-help/connect-an-agent.md +39 -0
  60. package/dist/packages/mcp/assets/kb/app-help/custom-keymap.md +32 -0
  61. package/dist/packages/mcp/assets/kb/app-help/edit-kb-or-skill.md +36 -0
  62. package/dist/packages/mcp/assets/kb/app-help/edit-rules.md +34 -0
  63. package/dist/packages/mcp/assets/kb/app-help/export-a-view.md +36 -0
  64. package/dist/packages/mcp/assets/kb/app-help/follow-a-wallet.md +33 -0
  65. package/dist/packages/mcp/assets/kb/app-help/getting-started.md +37 -0
  66. package/dist/packages/mcp/assets/kb/app-help/kol-lists.md +34 -0
  67. package/dist/packages/mcp/assets/kb/app-help/larp-check.md +36 -0
  68. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +41 -0
  69. package/dist/packages/mcp/assets/kb/app-help/login-with-x.md +33 -0
  70. package/dist/packages/mcp/assets/kb/app-help/narrative-board.md +32 -0
  71. package/dist/packages/mcp/assets/kb/app-help/open-a-coin.md +43 -0
  72. package/dist/packages/mcp/assets/kb/app-help/open-chart-popup.md +33 -0
  73. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +33 -0
  74. package/dist/packages/mcp/assets/kb/app-help/paper-trade.md +36 -0
  75. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +38 -0
  76. package/dist/packages/mcp/assets/kb/app-help/record-headless.md +34 -0
  77. package/dist/packages/mcp/assets/kb/app-help/recorder-history.md +39 -0
  78. package/dist/packages/mcp/assets/kb/app-help/rug-check.md +39 -0
  79. package/dist/packages/mcp/assets/kb/app-help/run-doctor.md +32 -0
  80. package/dist/packages/mcp/assets/kb/app-help/scan-mode.md +37 -0
  81. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +181 -0
  82. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +190 -0
  83. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +181 -0
  84. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +180 -0
  85. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +199 -0
  86. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +240 -0
  87. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +192 -0
  88. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +181 -0
  89. package/dist/packages/mcp/assets/kb/app-help/set-a-filter.md +36 -0
  90. package/dist/packages/mcp/assets/kb/app-help/setup-neural-voice.md +36 -0
  91. package/dist/packages/mcp/assets/kb/app-help/share-packs.md +34 -0
  92. package/dist/packages/mcp/assets/kb/app-help/sort-the-screener.md +34 -0
  93. package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +39 -0
  94. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +33 -0
  95. package/dist/packages/mcp/assets/kb/app-help/switch-theme.md +42 -0
  96. package/dist/packages/mcp/assets/kb/app-help/turn-on-alerts.md +37 -0
  97. package/dist/packages/mcp/assets/kb/app-help/update-dexterm.md +30 -0
  98. package/dist/packages/mcp/assets/kb/app-help/use-presets.md +42 -0
  99. package/dist/packages/mcp/assets/kb/app-help/use-voice-mode.md +36 -0
  100. package/dist/packages/mcp/assets/kb/app-help/watch-a-coin.md +34 -0
  101. package/dist/packages/mcp/assets/kb/app-help/watch-headless.md +35 -0
  102. package/dist/packages/mcp/assets/kb/app-help/what-dexterm-shows-agents.md +31 -0
  103. package/dist/packages/mcp/assets/kb/app-help/workspaces.md +38 -0
  104. package/dist/packages/mcp/assets/kb/docs/docs-agents-capability-tiers-enforced-server-side.md +38 -0
  105. package/dist/packages/mcp/assets/kb/docs/docs-agents-install-in-one-command.md +49 -0
  106. package/dist/packages/mcp/assets/kb/docs/docs-agents-per-agent-mandates.md +35 -0
  107. package/dist/packages/mcp/assets/kb/docs/docs-agents-recipes.md +43 -0
  108. package/dist/packages/mcp/assets/kb/docs/docs-agents-red-team-coverage.md +30 -0
  109. package/dist/packages/mcp/assets/kb/docs/docs-agents-two-ways-to-connect.md +37 -0
  110. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +36 -0
  111. package/dist/packages/mcp/assets/kb/docs/docs-agents-watch-an-agent-work-spectator-view.md +35 -0
  112. package/dist/packages/mcp/assets/kb/docs/docs-agents.md +26 -0
  113. package/dist/packages/mcp/assets/kb/docs/docs-analyst-answer-style.md +35 -0
  114. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +25 -0
  115. package/dist/packages/mcp/assets/kb/docs/docs-analyst-it-knows-the-app.md +33 -0
  116. package/dist/packages/mcp/assets/kb/docs/docs-analyst-mcp-tools.md +33 -0
  117. package/dist/packages/mcp/assets/kb/docs/docs-analyst-plain-language-filters.md +25 -0
  118. package/dist/packages/mcp/assets/kb/docs/docs-analyst-skills.md +31 -0
  119. package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +33 -0
  120. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-can-change-three-tiers.md +33 -0
  121. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +26 -0
  122. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-the-analyst-can-never-do.md +31 -0
  123. package/dist/packages/mcp/assets/kb/docs/docs-analyst.md +23 -0
  124. package/dist/packages/mcp/assets/kb/docs/docs-beginners-1-start-dexterm.md +35 -0
  125. package/dist/packages/mcp/assets/kb/docs/docs-beginners-2-open-a-real-coin-by-contract-address.md +43 -0
  126. package/dist/packages/mcp/assets/kb/docs/docs-beginners-3-read-the-safety-panel.md +40 -0
  127. package/dist/packages/mcp/assets/kb/docs/docs-beginners-4-read-the-holder-panel.md +35 -0
  128. package/dist/packages/mcp/assets/kb/docs/docs-beginners-5-optionally-ask-the-analyst.md +39 -0
  129. package/dist/packages/mcp/assets/kb/docs/docs-beginners-6-export-or-share-what-you-see.md +34 -0
  130. package/dist/packages/mcp/assets/kb/docs/docs-beginners-7-quit-safely.md +34 -0
  131. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +33 -0
  132. package/dist/packages/mcp/assets/kb/docs/docs-beginners-check-the-installation-with-doctor.md +38 -0
  133. package/dist/packages/mcp/assets/kb/docs/docs-beginners-choose-a-setup-path.md +30 -0
  134. package/dist/packages/mcp/assets/kb/docs/docs-beginners-common-mistakes-and-recovery.md +30 -0
  135. package/dist/packages/mcp/assets/kb/docs/docs-beginners-copy-and-paste-without-surprises.md +39 -0
  136. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-data-says-unavailable-stale-or-rate-limited.md +35 -0
  137. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-dexterm-says-command-not-found.md +36 -0
  138. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-optional-help-is-unavailable.md +33 -0
  139. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-paste-does-nothing-or-pastes-twice.md +35 -0
  140. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-the-screen-looks-broken.md +33 -0
  141. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-you-typed-the-wrong-thing.md +33 -0
  142. package/dist/packages/mcp/assets/kb/docs/docs-beginners-install-node-js-first.md +36 -0
  143. package/dist/packages/mcp/assets/kb/docs/docs-beginners-linux.md +30 -0
  144. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-homebrew.md +46 -0
  145. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-or-linux-npm.md +39 -0
  146. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos.md +30 -0
  147. package/dist/packages/mcp/assets/kb/docs/docs-beginners-open-the-terminal.md +29 -0
  148. package/dist/packages/mcp/assets/kb/docs/docs-beginners-reopen-dexterm-later.md +36 -0
  149. package/dist/packages/mcp/assets/kb/docs/docs-beginners-ten-minute-first-win.md +30 -0
  150. package/dist/packages/mcp/assets/kb/docs/docs-beginners-terminal-shell-prompt-command-output-and-path.md +40 -0
  151. package/dist/packages/mcp/assets/kb/docs/docs-beginners-where-to-go-next.md +32 -0
  152. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-expectations.md +29 -0
  153. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-powershell-experimental.md +39 -0
  154. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows.md +30 -0
  155. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +29 -0
  156. package/dist/packages/mcp/assets/kb/docs/docs-cli-dexterm-help.md +31 -0
  157. package/dist/packages/mcp/assets/kb/docs/docs-cli-filter-grammar.md +37 -0
  158. package/dist/packages/mcp/assets/kb/docs/docs-cli-flags.md +27 -0
  159. package/dist/packages/mcp/assets/kb/docs/docs-cli-invocations.md +43 -0
  160. package/dist/packages/mcp/assets/kb/docs/docs-cli-presets.md +26 -0
  161. package/dist/packages/mcp/assets/kb/docs/docs-cli-scan-commands.md +26 -0
  162. package/dist/packages/mcp/assets/kb/docs/docs-cli-sort-keys.md +27 -0
  163. package/dist/packages/mcp/assets/kb/docs/docs-cli-subcommands.md +26 -0
  164. package/dist/packages/mcp/assets/kb/docs/docs-cli.md +25 -0
  165. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +45 -0
  166. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +41 -0
  167. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +44 -0
  168. package/dist/packages/mcp/assets/kb/docs/docs-configuration-history-recorder.md +33 -0
  169. package/dist/packages/mcp/assets/kb/docs/docs-configuration-packs.md +38 -0
  170. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +35 -0
  171. package/dist/packages/mcp/assets/kb/docs/docs-configuration-push-alerts-and-dexterm-watch.md +45 -0
  172. package/dist/packages/mcp/assets/kb/docs/docs-configuration-rules-engine.md +36 -0
  173. package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-analyst-s-launch.md +27 -0
  174. package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-optional-free-rpc-key.md +42 -0
  175. package/dist/packages/mcp/assets/kb/docs/docs-configuration-themes-and-keymaps.md +29 -0
  176. package/dist/packages/mcp/assets/kb/docs/docs-configuration-themes-and-look.md +27 -0
  177. package/dist/packages/mcp/assets/kb/docs/docs-configuration-widgets.md +31 -0
  178. package/dist/packages/mcp/assets/kb/docs/docs-configuration-workspaces.md +30 -0
  179. package/dist/packages/mcp/assets/kb/docs/docs-configuration.md +25 -0
  180. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-1-follow-a-wallet-see-what-it-does.md +51 -0
  181. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +56 -0
  182. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +47 -0
  183. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +36 -0
  184. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +31 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-cache.md +32 -0
  186. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-health-registry.md +29 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-rate-limits-and-pacing.md +32 -0
  188. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-request-budget-meter.md +30 -0
  189. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +47 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-the-helius-credit-meter.md +30 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-data-sources.md +27 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-dexterm-doctor.md +33 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +34 -0
  194. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +71 -0
  195. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-next.md +32 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +35 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +34 -0
  198. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-supported-platforms.md +44 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-updating.md +28 -0
  200. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +27 -0
  201. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-global-keys.md +26 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-keys-handled-by-the-loop.md +29 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-per-screen-keys.md +26 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-what-shows.md +27 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-keyboard.md +26 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +55 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-larp-open-link-keys.md +26 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-larp-paste-a-coin-on-the-web.md +35 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +29 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-larp-timestamped-follow-up-drafts.md +35 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +33 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-larp.md +24 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-degraded-states-you-will-see.md +36 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-free-vs-paid-x-api-tiers-your-bill-your-limits.md +37 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-kol-lists-and-the-per-handle-filter.md +28 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-tests.md +25 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-what-is-read-and-when.md +35 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-what-you-set-up-once.md +40 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-where-the-tokens-live.md +27 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x.md +26 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +42 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +34 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +35 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +33 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +32 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +30 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +31 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +44 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +31 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +37 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +41 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +34 -0
  233. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +31 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +34 -0
  235. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +32 -0
  236. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +37 -0
  237. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +41 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +32 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-bubble-map-popup.md +27 -0
  240. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-build-the-native-window.md +33 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-config-dexterm-popup-json.md +38 -0
  242. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-docking-macos.md +27 -0
  243. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-keys-in-the-popup.md +30 -0
  244. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +28 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-security-model.md +33 -0
  246. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-token-and-feeds-popups.md +38 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +30 -0
  248. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart.md +27 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-scan-cost-control.md +28 -0
  250. package/dist/packages/mcp/assets/kb/docs/docs-scan-keys-flag-command.md +34 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-scan-safety-four-layers.md +32 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-scan-states.md +27 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +25 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-scan-triggers-and-score.md +37 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-scan.md +24 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-frozen-canary.md +30 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-frozen-task-1401-evaluation.md +35 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-latest-skill-care-run.md +33 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-links-only-larp-baseline-where-it-is-weak.md +35 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-not-measured.md +29 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-rug-verdict-rules.md +33 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-source-and-rebuild.md +31 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-scorecard.md +27 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-screens-1-screener.md +35 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-screens-2-new-pairs.md +28 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-screens-3-watchlist.md +27 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-screens-4-analyst-tab.md +28 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-screens-bubble-map.md +32 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-screens-coin-images.md +32 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-screens-live-holder-and-rug-signals.md +51 -0
  271. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +26 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-screens-paper-ticket-and-positions.md +29 -0
  273. package/dist/packages/mcp/assets/kb/docs/docs-screens-the-look.md +29 -0
  274. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +36 -0
  275. package/dist/packages/mcp/assets/kb/docs/docs-screens-trending-social-and-kol-lists.md +34 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-screens.md +26 -0
  277. package/dist/packages/mcp/assets/kb/docs/docs-spectator-1-the-agent-activity-window-follow-mode.md +56 -0
  278. package/dist/packages/mcp/assets/kb/docs/docs-spectator-2-the-read-only-stream-spectate.md +42 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-spectator-3-save-and-replay-record-file-dexterm-spectate.md +41 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +39 -0
  281. package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +36 -0
  282. package/dist/packages/mcp/assets/kb/docs/docs-spectator-quick-start.md +37 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-spectator-security-model.md +35 -0
  284. package/dist/packages/mcp/assets/kb/docs/docs-spectator-see-also.md +29 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-spectator-streaming-it.md +32 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +37 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-spectator-what-is-redacted.md +35 -0
  288. package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +29 -0
  289. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +33 -0
  290. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +36 -0
  291. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-in-app-messages.md +42 -0
  292. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-reporting-a-problem.md +25 -0
  293. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting.md +23 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-voice-doctor.md +26 -0
  295. package/dist/packages/mcp/assets/kb/docs/docs-voice-it-clarifies-it-never-guesses.md +36 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-voice-latency.md +38 -0
  297. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +32 -0
  298. package/dist/packages/mcp/assets/kb/docs/docs-voice-privacy.md +26 -0
  299. package/dist/packages/mcp/assets/kb/docs/docs-voice-still-unverified.md +27 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-voice-the-voice-neural-by-default.md +48 -0
  301. package/dist/packages/mcp/assets/kb/docs/docs-voice-what-it-can-never-do.md +27 -0
  302. package/dist/packages/mcp/assets/kb/docs/docs-voice-what-you-can-say.md +38 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +25 -0
  304. package/dist/packages/mcp/src/app-help.js +258 -0
  305. package/dist/packages/mcp/src/firewall.js +2 -2
  306. package/dist/packages/mcp/src/index.js +5 -1
  307. package/dist/packages/mcp/src/server.js +129 -4
  308. package/dist/packages/mcp/src/spectator-sink.js +144 -0
  309. package/dist/packages/packs/src/index.js +2 -0
  310. package/dist/packages/packs/src/skill-care.js +206 -0
  311. package/dist/packages/popup/src/pages/spectator.css +45 -0
  312. package/dist/packages/popup/src/pages/spectator.html +25 -0
  313. package/dist/packages/popup/src/pages/spectator.js +74 -0
  314. package/dist/packages/popup/src/selftest.js +19 -0
  315. package/dist/packages/popup/src/server.js +7 -1
  316. package/dist/packages/tui/src/keymap.js +10 -0
  317. package/dist/packages/voice/src/controller.js +42 -1
  318. package/dist/packages/voice/src/doctor.js +25 -8
  319. package/dist/packages/voice/src/engines.js +8 -1
  320. package/dist/packages/voice/src/index.js +3 -1
  321. package/dist/packages/voice/src/speakable.js +111 -0
  322. package/dist/packages/voice/src/stt/whisper.js +4 -2
  323. package/dist/packages/voice/src/tts/piper-setup.js +229 -0
  324. package/dist/packages/voice/src/tts/piper.js +309 -66
  325. package/dist/packages/voice/src/tts/say.js +3 -2
  326. package/dist/packages/voice/src/tts/voices.js +8 -5
  327. package/package.json +6 -5
@@ -0,0 +1,209 @@
1
+ // Local-only LARP/high-risk follow-up draft pipeline (task-1433). History mode reads the recorder
2
+ // without network access, scores only the point-in-time t0 row, joins only later outcome labels, and
3
+ // writes drafts/cards for human review. Live mode performs one read-only enrichment for one mint.
4
+ // Nothing here has X credentials, opens a composer, schedules work, trades, arms, signs or posts.
5
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
6
+ import { join, resolve } from 'node:path';
7
+ import { createDataLayer, defaultDbPath } from '../../data/src/index.js';
8
+ import { openHistoryReadOnly, exportRecorded } from '../../recorder/src/index.js';
9
+ import { h1Model, rugFeaturesFromRecord, rugFeaturesFromToken } from '../../packs/src/index.js';
10
+ import { Canvas, THEMES } from '../../tui/src/index.js';
11
+ import { writeExport, fmtAge } from "./export.js";
12
+ export const LARP_DRAFT_MIN_SAMPLE = 50;
13
+ export const LARP_DRAFT_HORIZON = 259_200;
14
+ export const LARP_DRAFT_THRESHOLD = 0.5;
15
+ const isoMinute = (t) => new Date(t).toISOString().replace(/:\d\d\.\d{3}Z$/, 'Z');
16
+ const shortMint = (mint) => `${mint.slice(0, 4)}...${mint.slice(-4)}`;
17
+ const pct = (n, d) => `${d ? ((n / d) * 100).toFixed(1) : '0.0'}%`;
18
+ /** One point-in-time flag per mint: earliest flagged t0 with an observed outcome at this horizon. */
19
+ export function buildFollowUpDataset(records, horizonSec = LARP_DRAFT_HORIZON, threshold = LARP_DRAFT_THRESHOLD) {
20
+ const model = h1Model();
21
+ const byMint = new Map();
22
+ for (const r of records) {
23
+ const outcome = r.outcomes[`${horizonSec}`];
24
+ if (!outcome || byMint.has(r.mint))
25
+ continue;
26
+ const score = model.score(rugFeaturesFromRecord(r.features));
27
+ if (score < threshold)
28
+ continue;
29
+ byMint.set(r.mint, { mint: r.mint, t0: r.t0, score, model: model.id, outcome });
30
+ }
31
+ const flagged = [...byMint.values()];
32
+ const collapsed = flagged.filter((r) => r.outcome.positive);
33
+ return { horizonSec, threshold, flagged, collapsed, didNotCollapse: flagged.length - collapsed.length };
34
+ }
35
+ function outcomeText(o) {
36
+ if (o.label === 'rug')
37
+ return 'recorder label: liquidity-pull condition';
38
+ if (o.label === 'dead')
39
+ return 'recorder label: inactive at follow-up';
40
+ return `recorder label: ${o.label}`;
41
+ }
42
+ function datasetLine(d) {
43
+ return `Same-dataset follow-up: ${d.collapsed.length}/${d.flagged.length} met the condition (${pct(d.collapsed.length, d.flagged.length)}); ${d.didNotCollapse}/${d.flagged.length} did not.`;
44
+ }
45
+ function cardCanvas(row, d, now) {
46
+ const c = new Canvas(92, 25, THEMES.cyberspace);
47
+ const t = c.theme;
48
+ c.fill(0, 0, c.w, c.h, ' ', { bg: t.bg });
49
+ c.fill(0, 0, c.w, 3, ' ', { bg: t.headBg });
50
+ c.text(3, 1, 'LARP DETECTED · RECORDED FOLLOW-UP', { fg: t.warn, bg: t.headBg, b: true });
51
+ c.textR(0, 1, c.w - 3, shortMint(row.mint), { fg: t.muted, bg: t.headBg });
52
+ c.text(4, 5, 'AT THE RECORDED MOMENT', { fg: t.muted, b: true });
53
+ c.text(4, 7, `HIGH RISK ${Math.round(row.score * 100)}/100`, { fg: t.down, b: true });
54
+ c.text(4, 9, `${row.model} heuristic · model output · risk flag, not a certificate`, { fg: t.text });
55
+ c.text(4, 10, `${isoMinute(row.t0)} · local recorder · t0 evidence age ${fmtAge((now - row.t0) / 1000)}`, { fg: t.muted });
56
+ c.text(4, 13, 'LATER OBSERVED', { fg: t.muted, b: true });
57
+ c.text(4, 15, outcomeText(row.outcome).toUpperCase(), { fg: t.warn, b: true });
58
+ c.text(4, 17, `${fmtAge(d.horizonSec)} follow-up · ${row.outcome.tier} · labeler evidence`, { fg: t.text });
59
+ c.text(4, 20, datasetLine(d), { fg: t.muted }, c.w - 8);
60
+ c.text(4, 22, 'Sources: local dexterm recorder · DexScreener + Jupiter · draft only', { fg: t.muted }, c.w - 8);
61
+ c.text(4, 23, 'Not financial advice.', { fg: t.muted });
62
+ return c;
63
+ }
64
+ function historyDraft(row, d, now) {
65
+ return [
66
+ 'LARP DETECTED — recorded follow-up',
67
+ '',
68
+ `Here is what the data showed: graded HIGH RISK ${Math.round(row.score * 100)}/100 at ${isoMinute(row.t0)} by ${row.model} (heuristic model output); ${outcomeText(row.outcome)} at the ${fmtAge(d.horizonSec)} follow-up.`,
69
+ '',
70
+ datasetLine(d),
71
+ '',
72
+ `Evidence: local dexterm recorder; DexScreener + Jupiter; t0 data age ${fmtAge((now - row.t0) / 1000)} at export; ${row.outcome.tier}; coin ${shortMint(row.mint)}.`,
73
+ '',
74
+ 'A risk flag is not a certificate. Draft only; a human decides whether to post. Not financial advice.',
75
+ ].join('\n') + '\n';
76
+ }
77
+ export function renderHistoryDrafts(d, opts) {
78
+ const now = opts.now ?? Date.now();
79
+ const min = opts.minSample ?? LARP_DRAFT_MIN_SAMPLE;
80
+ if (d.flagged.length < min)
81
+ return { drafts: [], cards: [] };
82
+ mkdirSync(opts.outDir, { recursive: true, mode: 0o700 });
83
+ const rows = [...d.collapsed].sort((a, b) => b.t0 - a.t0).slice(0, opts.limit ?? 3);
84
+ const drafts = [], cards = [];
85
+ for (let i = 0; i < rows.length; i++) {
86
+ const row = rows[i];
87
+ const stem = `${String(i + 1).padStart(2, '0')}-${row.mint.slice(0, 4)}-${row.mint.slice(-4)}`;
88
+ const draft = join(opts.outDir, `${stem}.md`);
89
+ writeFileSync(draft, historyDraft(row, d, now), { mode: 0o600 });
90
+ drafts.push(draft);
91
+ const view = {
92
+ kind: 'larp-detected', title: `RECORDED FOLLOW-UP · ${shortMint(row.mint)}`, label: 'replay', canvas: cardCanvas(row, d, now),
93
+ ages: [
94
+ { name: 't0 evidence', source: 'local recorder · DexScreener + Jupiter', ageSec: (now - row.t0) / 1000 },
95
+ { name: 'follow-up', source: `${row.outcome.tier} recorder outcome`, ageSec: d.horizonSec },
96
+ ],
97
+ report: historyDraft(row, d, now).trimEnd().split('\n'),
98
+ };
99
+ const saved = writeExport(view, { dir: opts.outDir, now: now + i, subject: stem });
100
+ cards.push(saved.png);
101
+ }
102
+ return { drafts, cards };
103
+ }
104
+ function parse(argv) {
105
+ const out = {};
106
+ for (let i = 0; i < argv.length; i++) {
107
+ const a = argv[i];
108
+ if (!a.startsWith('--'))
109
+ continue;
110
+ const value = argv[i + 1];
111
+ if (value !== undefined && !value.startsWith('--')) {
112
+ out[a.slice(2)] = value;
113
+ i++;
114
+ }
115
+ else
116
+ out[a.slice(2)] = true;
117
+ }
118
+ return out;
119
+ }
120
+ export const LARP_DETECTED_HELP = `dexterm larp-detected: local draft/card pipeline; never posts or schedules
121
+
122
+ dexterm larp-detected --out DIR [--db PATH] [--horizon SEC] [--limit 3]
123
+ dexterm larp-detected --live MINT --out DIR
124
+
125
+ History mode is cron-ready and offline: it reads recorder t0 rows plus later labels, requires at least
126
+ ${LARP_DRAFT_MIN_SAMPLE} unique flagged coins, and otherwise writes no drafts. Live mode performs one read-only
127
+ keyless lookup and writes a graded-now card. All output is for human review.`;
128
+ function liveFeatures(row) {
129
+ return rugFeaturesFromToken({
130
+ liquidity_usd: row.pair.liquidityUsd, mcap: row.mcap?.value ?? row.pair.mcap, age_sec: row.token.createdAt === null ? null : Math.max(0, (Date.now() - row.token.createdAt) / 1000),
131
+ top10_pct: row.audit?.top10Pct ?? null, dev_pct: row.audit?.devPct ?? null, organic_score: row.stats?.organicScore ?? null,
132
+ holder_count: row.stats?.holderCount ?? null, mint_authority: row.audit?.mintAuthority ?? null, freeze_authority: row.audit?.freezeAuthority ?? null,
133
+ });
134
+ }
135
+ async function liveCard(mint, outDir, dbPath, io) {
136
+ if (!/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(mint)) {
137
+ io.stderr.write('dexterm larp-detected: --live needs a Solana mint\n');
138
+ return 2;
139
+ }
140
+ const dl = createDataLayer({ dbPath });
141
+ try {
142
+ const result = await dl.store.enrich([mint]);
143
+ const row = result.rows[0];
144
+ if (!row) {
145
+ io.stderr.write('dexterm larp-detected: no current market record found for that mint\n');
146
+ return 2;
147
+ }
148
+ const now = Date.now(), model = h1Model(), score = model.score(liveFeatures(row));
149
+ const c = new Canvas(92, 20, THEMES.cyberspace), t = c.theme;
150
+ c.fill(0, 0, c.w, c.h, ' ', { bg: t.bg });
151
+ c.fill(0, 0, c.w, 3, ' ', { bg: t.headBg });
152
+ c.text(3, 1, 'LARP CHECK · GRADED NOW', { fg: t.warn, bg: t.headBg, b: true });
153
+ c.textR(0, 1, c.w - 3, shortMint(mint), { fg: t.muted, bg: t.headBg });
154
+ c.text(4, 6, `${score >= LARP_DRAFT_THRESHOLD ? 'HIGH RISK' : 'BELOW HIGH-RISK LINE'} ${Math.round(score * 100)}/100`, { fg: score >= LARP_DRAFT_THRESHOLD ? t.down : t.up, b: true });
155
+ c.text(4, 9, `${model.id} heuristic · model output · risk flag, not a certificate`, { fg: t.text });
156
+ c.text(4, 11, `${isoMinute(now)} · LIVE DATA · DexScreener + Jupiter`, { fg: t.muted });
157
+ c.text(4, 14, 'No later outcome yet. No person, project or wallet is identified.', { fg: t.text });
158
+ c.text(4, 17, 'Draft only · human review · not financial advice', { fg: t.muted });
159
+ const view = { kind: 'larp-now', title: `GRADED NOW · ${shortMint(mint)}`, label: 'live', canvas: c,
160
+ ages: [{ name: 'market', source: 'DexScreener + Jupiter', ageSec: Math.max(0, (now - Math.max(row.token.fetchedAt, row.pair.fetchedAt)) / 1000) }],
161
+ report: [`coin ${shortMint(mint)}`, `graded now ${Math.round(score * 100)}/100 · ${model.id} heuristic model output`, 'No later outcome yet. Risk flag, not a certificate. Draft only.'],
162
+ };
163
+ const saved = writeExport(view, { dir: outDir, now, subject: `${mint.slice(0, 4)}-${mint.slice(-4)}` });
164
+ io.stdout.write(`graded-now card: ${saved.png}\n`);
165
+ return 0;
166
+ }
167
+ finally {
168
+ dl.close();
169
+ }
170
+ }
171
+ export async function larpDetectedCommand(argv, io) {
172
+ const o = parse(argv);
173
+ if (o.help || o.h) {
174
+ io.stdout.write(LARP_DETECTED_HELP + '\n');
175
+ return 0;
176
+ }
177
+ const dbPath = typeof o.db === 'string' ? resolve(o.db) : (io.env.DEXTERM_DB_PATH || defaultDbPath(io.env));
178
+ const outDir = typeof o.out === 'string' ? resolve(o.out) : resolve('marketing', 'larp-detected', new Date().toISOString().slice(0, 10));
179
+ if (typeof o.live === 'string')
180
+ return liveCard(o.live, outDir, dbPath, io);
181
+ if (!existsSync(dbPath)) {
182
+ io.stderr.write(`dexterm larp-detected: no recorder history at ${dbPath}\n`);
183
+ return 2;
184
+ }
185
+ const horizon = typeof o.horizon === 'string' ? Number(o.horizon) : LARP_DRAFT_HORIZON;
186
+ if (!Number.isFinite(horizon) || horizon <= 0) {
187
+ io.stderr.write('dexterm larp-detected: --horizon must be positive seconds\n');
188
+ return 2;
189
+ }
190
+ const db = openHistoryReadOnly(dbPath);
191
+ if (!db) {
192
+ io.stderr.write('dexterm larp-detected: recorder database could not be opened read-only\n');
193
+ return 2;
194
+ }
195
+ try {
196
+ const dataset = buildFollowUpDataset(exportRecorded(db, { labelledOnly: true }).records, horizon);
197
+ if (dataset.flagged.length < LARP_DRAFT_MIN_SAMPLE) {
198
+ io.stdout.write(`thin sample: ${dataset.flagged.length} unique flagged coins (< ${LARP_DRAFT_MIN_SAMPLE}); wrote no drafts\n`);
199
+ return 0;
200
+ }
201
+ const limit = typeof o.limit === 'string' ? Math.max(0, Math.floor(Number(o.limit))) : 3;
202
+ const saved = renderHistoryDrafts(dataset, { outDir, limit: Number.isFinite(limit) ? limit : 3 });
203
+ io.stdout.write(`${datasetLine(dataset)}\nwrote ${saved.drafts.length} draft(s) and ${saved.cards.length} card(s) to ${outDir}\n`);
204
+ return 0;
205
+ }
206
+ finally {
207
+ db.close();
208
+ }
209
+ }
@@ -10,15 +10,24 @@ import { NewPairsLive } from "./newpairs.js";
10
10
  import { WatchlistLive } from "./watchlist.js";
11
11
  import { TradeTicketLive, PositionsLive, LivePositions, CombinedPositions, POSITIONS_REFRESH_MS } from "./trade.js";
12
12
  import { IntelLive } from "./intel.js";
13
+ import { LiveSignalsLive } from "./signals.js";
13
14
  import { CopyLive } from "./copy.js";
14
15
  export { TokenLive, INTERVALS, panelCaption, emptyView } from "./token.js";
15
16
  export { NewPairsLive, latencyStats, percentile, RECENT_POLL_MS, NEW_TOKENS_POLL_MS, ENRICH_POLL_MS } from "./newpairs.js";
16
17
  export { WatchlistLive, WATCH_REFRESH_MS } from "./watchlist.js";
17
18
  export { TradeTicketLive, PositionsLive, LivePositions, CombinedPositions, POSITIONS_REFRESH_MS } from "./trade.js";
18
19
  export { IntelLive, popupBubbleVM, unavailableText, INTEL_AUTOSCAN_DELAY_MS } from "./intel.js";
20
+ export { LiveSignalsLive, buildObservation, SIGNALS_LOG_MAX } from "./signals.js";
19
21
  export { renderTokenLive } from "./screens/token-live.js";
20
22
  export { renderNewPairsLive } from "./screens/newpairs-live.js";
21
23
  export { renderWatchlistLive } from "./screens/watchlist-live.js";
24
+ /** task-1404: how often the open token's credit-metered wallet-intel leg (holders/bundle/sniper/fresh) is re-scanned. */
25
+ export const INTEL_RESCAN_MS = 45_000;
26
+ /** A transient one-liner for a fired live signal (⚑ SYMBOL-less; the token screen shows the full strip). */
27
+ function signalNotice(s) {
28
+ const age = s.dataAgeMs >= 1000 ? ` · ${Math.round(s.dataAgeMs / 1000)}s` : '';
29
+ return `⚑ ${s.title}: ${s.text} (${s.source}${age})`;
30
+ }
22
31
  export class LiveContext {
23
32
  dl;
24
33
  mode;
@@ -54,6 +63,8 @@ export class LiveContext {
54
63
  token = null;
55
64
  /** task-1340 wallet intel: the holder scan per mint (light on open, deep for watched coins) behind the bubble map. */
56
65
  intel;
66
+ /** task-1404 LIVE holder/rug signals: turns the open token's repeated holder/safety reads into signal events. */
67
+ signals;
57
68
  /** task-1360 copy trading Tier 1: the follow store + wallet tracker (alerts on a followed wallet's buys/sells). */
58
69
  copy;
59
70
  /**
@@ -86,6 +97,8 @@ export class LiveContext {
86
97
  onLog;
87
98
  lastFastLogAt = 0;
88
99
  lastFastLog = '';
100
+ /** task-1404: last time the open token's wallet-intel leg was re-scanned (slow cadence; see refreshOpenIntel). */
101
+ lastIntelRescanAt = 0;
89
102
  constructor(dl, opts) {
90
103
  this.dl = dl;
91
104
  this.mode = opts.mode;
@@ -124,7 +137,7 @@ export class LiveContext {
124
137
  endpoints: opts.fastEndpoints,
125
138
  solUsd: () => this.solUsd,
126
139
  ws: opts.ws,
127
- onPrice: (_p) => emit('fastprice'),
140
+ onPrice: (_p) => { this.observeSignals(); emit('fastprice'); },
128
141
  onStatus: () => { emit('fastlane:status'); this.logFast(); },
129
142
  })
130
143
  : null;
@@ -166,7 +179,10 @@ export class LiveContext {
166
179
  ? new LivePositions(this.trade.paths?.liveLedger ?? defaultLiveLedgerPath(), { now: this.now, mark: (m) => this.positions.markOf(m), solUsd: () => this.positions.solUsd() ?? this.solUsd })
167
180
  : null;
168
181
  this.positionSource = new CombinedPositions(this.positions, this.livePositions);
169
- this.intel = new IntelLive(dl, { now: this.now, onChange: () => { this.syncIntelRows(); emit('intel'); }, isWatched: (mint) => this.watch.has(mint) });
182
+ this.intel = new IntelLive(dl, { now: this.now, onChange: () => { this.syncIntelRows(); this.observeSignals(); emit('intel'); }, isWatched: (mint) => this.watch.has(mint) });
183
+ // task-1404: the live holder/rug signals engine. A fired signal sets a transient notice and redraws; the
184
+ // token screen shows the recent signals, and the rules engine reads `signals.fields()` so each is an alert trigger.
185
+ this.signals = new LiveSignalsLive({ now: this.now, onSignal: (s) => { this.notice = signalNotice(s); emit('signals'); } });
170
186
  // task-1360: followed wallets are polled only in a real live run (replay never spends credits); the store is always readable.
171
187
  this.copy = new CopyLive(opts.mode === 'live' ? dl : null, { onChange: (r) => emit(r), solUsd: () => this.solUsd, env: opts.copy?.env, followsPath: opts.copy?.followsPath, history: opts.copy?.history, pollMs: opts.copy?.pollMs });
172
188
  }
@@ -376,7 +392,7 @@ export class LiveContext {
376
392
  }
377
393
  /** Open a token: a fresh loader, first load kicked off; the view is readable immediately (row fields). */
378
394
  openToken(row) {
379
- const t = new TokenLive(this.dl, row, { interval: this.token?.view.interval ?? this.interval, now: this.now, onChange: () => { this.syncChainRows(t); this.onChange('token'); } });
395
+ const t = new TokenLive(this.dl, row, { interval: this.token?.view.interval ?? this.interval, now: this.now, onChange: () => { this.syncChainRows(t); this.observeSignals(); this.onChange('token'); } });
380
396
  this.token = t;
381
397
  this.syncFastSet(); // promote the opened token to the fast lane (slow→fast, no seam)
382
398
  this.x?.setFocus({ mint: row.mint, symbol: row.symbol }); // task-1357: the open token's X search (60 s, cached 15 min)
@@ -423,6 +439,51 @@ export class LiveContext {
423
439
  if (sigs.length)
424
440
  this.social.ingest(sigs.map((s) => chainSignalToRaw(s, this.now())));
425
441
  }
442
+ /** The current inputs for the live-signals engine: the open token's view + its settled intel scan + fast-lane curve. */
443
+ signalInputs() {
444
+ if (!this.token)
445
+ return null;
446
+ const mint = this.token.view.mint;
447
+ const result = this.intel.entry(mint)?.result;
448
+ const ok = result && result.state === 'ok' ? result : null;
449
+ return {
450
+ view: this.token.view,
451
+ intel: ok ? { analysis: ok.analysis, fetchedAt: ok.fetchedAt, via: ok.via.history ? 'helius' : ok.via.holders } : null,
452
+ curve: this.fastLane?.curve(mint) ?? null,
453
+ };
454
+ }
455
+ /**
456
+ * task-1404: feed one moment of the open token's holder/rug state to the live-signals engine. Called whenever the
457
+ * token, the intel scan or a fast-lane push changed something; the engine only emits on a real threshold-crossing
458
+ * change, so this is cheap to call on every redraw. Nothing fires until a second observation of the same field.
459
+ */
460
+ observeSignals() {
461
+ if (this.closed)
462
+ return;
463
+ const inputs = this.signalInputs();
464
+ if (inputs)
465
+ this.signals.observe(inputs);
466
+ }
467
+ /**
468
+ * task-1404: re-run the open token's wallet-intel scan on a slow cadence so holder/concentration/bundle/sniper
469
+ * signals stay LIVE, not computed once on open. Gated: live mode, the history leg can actually run (else a scan
470
+ * would only spend public-RPC calls to still print a dash), not already in flight, and at most once per
471
+ * INTEL_RESCAN_MS. The cheap on-chain reads (authorities, LP, liq, price) already refresh with token.load()
472
+ * every 10 s and bonding streams sub-second; this covers the credit-metered Helius leg.
473
+ */
474
+ refreshOpenIntel(now) {
475
+ if (this.mode !== 'live' || !this.token)
476
+ return;
477
+ if (now - this.lastIntelRescanAt < INTEL_RESCAN_MS)
478
+ return;
479
+ if (!this.intel.historyAvailable().ok)
480
+ return;
481
+ const v = this.token.view;
482
+ if (this.intel.entry(v.mint)?.state === 'loading')
483
+ return;
484
+ this.lastIntelRescanAt = now;
485
+ void this.intel.scan({ mint: v.mint, symbol: v.symbol, name: v.name, holderCount: v.holders ?? null }, { force: true }).catch(() => { });
486
+ }
426
487
  /** The token screen's `1`..`6` keys. */
427
488
  setTimeframe(n) {
428
489
  const iv = INTERVALS[n - 1];
@@ -214,6 +214,30 @@ export function renderTokenLive(c, state, v) {
214
214
  sy += 1;
215
215
  }
216
216
  }
217
+ // task-1404: LIVE holder/rug signals — the changes seen since the token was opened (dev sell, concentration
218
+ // shift, LP unlock, authority flip, new bundle/sniper, fresh inflow, bonding, migration). Each is a risk flag,
219
+ // not a verdict, and carries its SOURCE and the AGE of the data it read; the `stale` mark shows honestly.
220
+ const sigs = state.live?.signals?.recent(v.mint, 4) ?? [];
221
+ if (sigs.length && sy < sb.y + sb.h) {
222
+ sy += sy + 1 < sb.y + sb.h ? 1 : 0;
223
+ if (sy < sb.y + sb.h) {
224
+ c.text(sb.x + 1, sy, 'LIVE SIGNALS', { fg: t.dim, bg: sb.bg });
225
+ sy += 1;
226
+ }
227
+ const staleAfter = 60_000;
228
+ for (const s of sigs) {
229
+ if (sy >= sb.y + sb.h)
230
+ break;
231
+ const color = s.level === 'warn' ? t.warn : s.level === 'down' ? t.down : s.level === 'up' ? t.up : t.muted;
232
+ const ageMs = Math.max(0, now - s.at);
233
+ const stale = ageMs > staleAfter;
234
+ const meta = `${s.source} · ${ageMs >= 1000 ? `${Math.round(ageMs / 1000)}s` : 'now'}${stale ? ' · stale' : ''}`;
235
+ const metaClip = clip(meta, Math.max(8, Math.floor((sb.w - 2) / 2)));
236
+ c.text(sb.x + 1, sy, `⚑ ${s.title}`, { fg: color, bg: sb.bg, b: s.level === 'warn' || s.level === 'down' }, sb.w - 2 - metaClip.length - 1);
237
+ c.textR(sb.x + 1, sy, sb.w - 2, metaClip, { fg: stale ? t.warn : t.dim, bg: sb.bg, i: true });
238
+ sy += 1;
239
+ }
240
+ }
217
241
  if (bp !== 'compact') {
218
242
  const hx = statsW;
219
243
  const hw = mainW - statsW;
@@ -0,0 +1,109 @@
1
+ // dexterm — LIVE holder/rug SIGNALS binding (task-1404). Turns the open token's repeatedly-refreshed holder and
2
+ // safety reads (market/authorities/rugcheck panels + the wallet-intel scan + the fast-lane bonding curve) into
3
+ // SIGNAL events through the pure @dexterm/data LiveSignalsEngine, keeps a small per-mint log for the token screen,
4
+ // and exposes the latest numeric values as RULE FIELDS so every signal is an alert trigger (task-1398 pattern).
5
+ //
6
+ // It owns no timers and does no I/O: the UI loop / LiveContext calls observe() whenever the token, the intel scan
7
+ // or a fast-lane push changed something. The engine dedups (only a threshold-crossing change emits), so calling it
8
+ // on every redraw is cheap. Read-only: nothing here trades or signs.
9
+ import { LiveSignalsEngine, bondingProgress, } from '../../../data/src/index.js';
10
+ /** Max signals kept per mint for the token-screen strip (newest first). */
11
+ export const SIGNALS_LOG_MAX = 40;
12
+ function fromPanel(p, value, fallbackSource) {
13
+ if (!p || value == null || !Number.isFinite(value) || p.fetchedAt == null)
14
+ return null;
15
+ return { value, source: p.via ?? fallbackSource, fetchedAt: p.fetchedAt, stale: p.stale || p.state === 'error' };
16
+ }
17
+ function authFromPanel(p, present) {
18
+ if (!p || present == null || p.fetchedAt == null)
19
+ return null;
20
+ return { present, source: p.via ?? 'authorities', fetchedAt: p.fetchedAt, stale: p.stale || p.state === 'error' };
21
+ }
22
+ function fromIntel(value, intel) {
23
+ if (value == null || !Number.isFinite(value) || !intel || intel.fetchedAt == null)
24
+ return null;
25
+ return { value, source: intel.via ?? 'helius', fetchedAt: intel.fetchedAt, stale: false };
26
+ }
27
+ /**
28
+ * Build one observation from the token view + intel scan + fast-lane curve. Each field keeps its OWN source and
29
+ * fetch time so a signal is honest about where it came from and how old the data is. The richer wallet-intel
30
+ * figures (ex-pool top-10, creator %, bundled, snipers, fresh, cluster) are preferred when the scan has landed;
31
+ * otherwise the panel reads (Jupiter audit top-10/dev) stand in. Pure + exported for the tests.
32
+ */
33
+ export function buildObservation(inputs, now) {
34
+ const v = inputs.view;
35
+ const I = inputs.intel?.analysis?.insights ?? null;
36
+ const panels = v.panels ?? {};
37
+ const obs = { mint: v.mint, at: now };
38
+ // top-10 concentration (pools excluded): the scan's ex-pool figure first, else the holders panel's top-10.
39
+ obs.top10 = (I && typeof I.top10.exPool === 'number' ? fromIntel(I.top10.exPool, inputs.intel) : null) ?? fromPanel(panels.holders, v.top10, 'holders');
40
+ // creator / dev share: the scan's creatorPctNow first, else the market panel's Jupiter audit devPct.
41
+ obs.dev = (I ? fromIntel(I.dev.creatorPctNow, inputs.intel) : null) ?? fromPanel(panels.market, v.dev, 'jupiter');
42
+ // bundled / snipers / fresh / cluster only exist once the launch window / funders were read (Helius).
43
+ if (I && I.coverage.launchTxs > 0) {
44
+ obs.bundled = fromIntel(I.bundle.bundlePctOfTopSample, inputs.intel);
45
+ obs.snipers = fromIntel(I.snipers.count, inputs.intel);
46
+ }
47
+ if (I && I.coverage.fundingLookups > 0)
48
+ obs.fresh = fromIntel(I.fresh.freshAtBuyPctOfTopSample, inputs.intel);
49
+ const cluster = inputs.intel?.analysis?.clusters?.[0]?.pct ?? null;
50
+ obs.cluster = fromIntel(cluster, inputs.intel);
51
+ // LP locked (RugCheck), liquidity (market), mint/freeze authority (authorities panel).
52
+ obs.lpLocked = fromPanel(panels.rugcheck, v.lpLocked, 'rugcheck');
53
+ obs.liq = fromPanel(panels.market, v.liq, 'market');
54
+ obs.mintAuth = authFromPanel(panels.authorities, v.mintAuth);
55
+ obs.freezeAuth = authFromPanel(panels.authorities, v.freezeAuth);
56
+ // bonding-curve progress + migration from the on-chain fast-lane push (sub-second), when streaming.
57
+ if (inputs.curve) {
58
+ const bp = bondingProgress(inputs.curve.curve.vToken);
59
+ if (bp != null)
60
+ obs.bonding = { value: bp, source: 'onchain-curve', fetchedAt: inputs.curve.at, stale: false };
61
+ obs.migrated = { value: inputs.curve.curve.complete, source: 'onchain-curve', fetchedAt: inputs.curve.at, stale: false };
62
+ }
63
+ return obs;
64
+ }
65
+ export class LiveSignalsLive {
66
+ engine;
67
+ now;
68
+ onSignal;
69
+ logByMint = new Map();
70
+ /** Signals emitted this session (all mints), newest last — a cheap counter/feed for the smoke script + tests. */
71
+ emitted = [];
72
+ constructor(opts = {}) {
73
+ this.engine = new LiveSignalsEngine({ thresholds: opts.thresholds });
74
+ this.now = opts.now ?? (() => Date.now());
75
+ this.onSignal = opts.onSignal;
76
+ }
77
+ /** Observe one moment of a token's state; records and returns any new signals. */
78
+ observe(inputs) {
79
+ const obs = buildObservation(inputs, this.now());
80
+ const sigs = this.engine.observe(obs);
81
+ for (const s of sigs) {
82
+ const arr = this.logByMint.get(s.mint) ?? [];
83
+ arr.unshift(s);
84
+ if (arr.length > SIGNALS_LOG_MAX)
85
+ arr.length = SIGNALS_LOG_MAX;
86
+ this.logByMint.set(s.mint, arr);
87
+ this.emitted.push(s);
88
+ this.onSignal?.(s);
89
+ }
90
+ return sigs;
91
+ }
92
+ /** Recent signals for a mint, newest first (token-screen LIVE SIGNALS strip). */
93
+ recent(mint, limit = 8) {
94
+ return (this.logByMint.get(mint) ?? []).slice(0, limit);
95
+ }
96
+ /** Latest numeric fields for the rules engine (merged into the open-token RuleRow in ruleRowsOf). */
97
+ fields(mint) {
98
+ return this.engine.fields(mint);
99
+ }
100
+ /** Per-field snapshot (value + source + age + stale) for the strip header. */
101
+ snapshot(mint, now = this.now()) {
102
+ return this.engine.snapshot(mint, now);
103
+ }
104
+ /** Drop a mint the UI no longer tracks. */
105
+ forget(mint) {
106
+ this.engine.forget(mint);
107
+ this.logByMint.delete(mint);
108
+ }
109
+ }
@@ -473,6 +473,9 @@ export class CandlePrefetcher {
473
473
  timer = null;
474
474
  busy = false;
475
475
  closed = false;
476
+ settledPromise = Promise.resolve();
477
+ resolveSettled = null;
478
+ waitingForSettled = false;
476
479
  stamps = [];
477
480
  /** Requests actually sent (granted by the limiter). */
478
481
  requests = 0;
@@ -494,9 +497,25 @@ export class CandlePrefetcher {
494
497
  return;
495
498
  if (t && this.target && t.mint === this.target.mint && t.pair === this.target.pair)
496
499
  return; // same row: keep waiting
500
+ this.finishSettled(); // a replaced target can no longer do useful work for its caller
497
501
  this.target = t;
502
+ if (t?.pair)
503
+ this.settledPromise = new Promise((resolve) => { this.resolveSettled = resolve; });
498
504
  this.schedule(this.opts.debounceMs);
499
505
  }
506
+ /**
507
+ * Resolve after the current hover pass has warmed every immediately available base, explicitly
508
+ * deferred the next attempt, failed a real request, been replaced, or been cancelled by close(). This
509
+ * is the completion contract for callers that need to observe the pass result; a debounce timer firing
510
+ * alone does not mean its sequential requests have finished.
511
+ */
512
+ settled() {
513
+ if (this.resolveSettled) {
514
+ this.waitingForSettled = true;
515
+ this.timer?.ref?.();
516
+ }
517
+ return this.settledPromise;
518
+ }
500
519
  /** Requests sent in the trailing 60 s. */
501
520
  rate() {
502
521
  const now = this.opts.now();
@@ -511,6 +530,7 @@ export class CandlePrefetcher {
511
530
  clearTimeout(this.timer);
512
531
  this.timer = null;
513
532
  }
533
+ this.finishSettled();
514
534
  }
515
535
  schedule(ms) {
516
536
  if (this.timer) {
@@ -521,7 +541,10 @@ export class CandlePrefetcher {
521
541
  if (!t || !t.pair || this.closed)
522
542
  return;
523
543
  this.timer = setTimeout(() => { this.timer = null; void this.step(t); }, Math.max(0, ms));
524
- this.timer.unref?.();
544
+ if (this.waitingForSettled)
545
+ this.timer.ref?.();
546
+ else
547
+ this.timer.unref?.();
525
548
  }
526
549
  bases() {
527
550
  const iv = this.opts.interval();
@@ -541,6 +564,7 @@ export class CandlePrefetcher {
541
564
  continue;
542
565
  if (this.rate() >= this.opts.maxPerMin) {
543
566
  this.schedule(this.stamps[0] + 60_000 - this.opts.now() + 50);
567
+ this.finishSettled(t);
544
568
  return;
545
569
  }
546
570
  this.busy = true;
@@ -554,9 +578,11 @@ export class CandlePrefetcher {
554
578
  // Try again when the bucket could be idle again (still only if the cursor stays here).
555
579
  const wait = this.dl.limiter.bucket('geckoterminal').delayFor('idle', this.opts.now());
556
580
  this.schedule(Number.isFinite(wait) ? Math.max(250, wait) : 60_000);
581
+ this.finishSettled(t);
557
582
  return;
558
583
  }
559
584
  this.sent(); // a real request that failed (HTTP error) still counts against the budget
585
+ this.finishSettled(t);
560
586
  return;
561
587
  }
562
588
  finally {
@@ -565,6 +591,17 @@ export class CandlePrefetcher {
565
591
  if (this.closed || this.target !== t)
566
592
  return; // the cursor moved on: its own debounce takes over
567
593
  }
594
+ this.finishSettled(t);
595
+ }
596
+ finishSettled(t) {
597
+ if (t && this.target !== t)
598
+ return;
599
+ const resolve = this.resolveSettled;
600
+ this.resolveSettled = null;
601
+ if (this.waitingForSettled)
602
+ this.timer?.unref?.();
603
+ this.waitingForSettled = false;
604
+ resolve?.();
568
605
  }
569
606
  sent() {
570
607
  this.requests++;
@@ -680,6 +680,8 @@ export class PositionsLive {
680
680
  now;
681
681
  /** mint → current price in SOL per whole token. */
682
682
  marks = new Map();
683
+ /** task-1407: when each mark was taken (ms), so a PnL card can print the mark's age. */
684
+ markTimes = new Map();
683
685
  solPriceUsd = null;
684
686
  loading = false;
685
687
  heldMemo = new Map();
@@ -703,6 +705,10 @@ export class PositionsLive {
703
705
  markOf(mint) {
704
706
  return this.marks.get(mint) ?? null;
705
707
  }
708
+ /** task-1407: when the mark for a mint was taken (ms), or null when there is none. */
709
+ markAt(mint) {
710
+ return this.marks.has(mint) ? this.markTimes.get(mint) ?? null : null;
711
+ }
706
712
  /**
707
713
  * PositionSource: the open paper position for a mint, or null. One indexed SQLite read, reused for
708
714
  * HELD_MEMO_MS so a 30 fps render loop does not hit the ledger every frame. A closed wallet reads as flat.
@@ -758,8 +764,10 @@ export class PositionsLive {
758
764
  continue;
759
765
  }
760
766
  const usd = byMint.get(m)?.pair.priceUsd ?? null;
761
- if (usd && usd > 0)
767
+ if (usd && usd > 0) {
762
768
  this.marks.set(m, usd / this.solPriceUsd);
769
+ this.markTimes.set(m, this.now());
770
+ }
763
771
  }
764
772
  }
765
773
  }
@@ -865,6 +873,21 @@ export class LivePositions {
865
873
  this.rowsMemo = { at: now, rows };
866
874
  return rows;
867
875
  }
876
+ /** task-1407: the raw ledger rows (newest first, swaps only) with network + signature, for the LIVE PnL card. */
877
+ fills(mint, limit = 500) {
878
+ try {
879
+ const l = new LiveLedger({ path: this.path, readOnly: true });
880
+ try {
881
+ return l.trades(limit * 2).filter((r) => r.kind === 'swap' && r.mint === mint).slice(0, limit);
882
+ }
883
+ finally {
884
+ l.close();
885
+ }
886
+ }
887
+ catch {
888
+ return [];
889
+ }
890
+ }
868
891
  /** Confirmed live fills (newest first) for the popup markers / history; probes excluded. */
869
892
  records(limit = 100, mint) {
870
893
  try {