dexterm-cli 0.0.0-stage → 0.1.2
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.
- package/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +394 -0
- package/dist/packages/agents/src/executor.js +202 -0
- package/dist/packages/agents/src/gate.js +179 -0
- package/dist/packages/agents/src/identity.js +218 -0
- package/dist/packages/agents/src/index.js +15 -0
- package/dist/packages/agents/src/proposals.js +153 -0
- package/dist/packages/agents/src/schema.js +87 -0
- package/dist/packages/agents/src/sdk.js +140 -0
- package/dist/packages/agents/src/tiers.js +119 -0
- package/dist/packages/agents/src/tools.js +72 -0
- package/dist/packages/agents/src/ws.js +158 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
- package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +75 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -0
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
- package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
- package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
- package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
- package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
- package/dist/packages/cli/bin/dexterm.mjs +11 -0
- package/dist/packages/cli/src/agent-cmd.js +336 -0
- package/dist/packages/cli/src/agent-roster.js +26 -0
- package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
- package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
- package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
- package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
- package/dist/packages/cli/src/alerts/creds.js +83 -0
- package/dist/packages/cli/src/alerts/format.js +41 -0
- package/dist/packages/cli/src/alerts/index.js +10 -0
- package/dist/packages/cli/src/alerts/notifier.js +53 -0
- package/dist/packages/cli/src/alerts/route.js +19 -0
- package/dist/packages/cli/src/alerts/types.js +1 -0
- package/dist/packages/cli/src/alerts-cmd.js +212 -0
- package/dist/packages/cli/src/analyst/backend.js +526 -0
- package/dist/packages/cli/src/analyst/results.js +38 -0
- package/dist/packages/cli/src/analyst/session.js +319 -0
- package/dist/packages/cli/src/audit.js +51 -0
- package/dist/packages/cli/src/backtest-cmd.js +208 -0
- package/dist/packages/cli/src/boot-state.js +62 -0
- package/dist/packages/cli/src/coin-target.js +2 -0
- package/dist/packages/cli/src/custom.js +261 -0
- package/dist/packages/cli/src/display.js +191 -0
- package/dist/packages/cli/src/doctor.js +375 -0
- package/dist/packages/cli/src/export.js +235 -0
- package/dist/packages/cli/src/feed.js +634 -0
- package/dist/packages/cli/src/fixtures.js +275 -0
- package/dist/packages/cli/src/follow-cmd.js +221 -0
- package/dist/packages/cli/src/images-config.js +59 -0
- package/dist/packages/cli/src/index.js +31 -0
- package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
- package/dist/packages/cli/src/larp-record.js +21 -0
- package/dist/packages/cli/src/live/copy.js +80 -0
- package/dist/packages/cli/src/live/flow.js +233 -0
- package/dist/packages/cli/src/live/index.js +511 -0
- package/dist/packages/cli/src/live/intel.js +227 -0
- package/dist/packages/cli/src/live/newpairs.js +334 -0
- package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
- package/dist/packages/cli/src/live/screens/token-live.js +459 -0
- package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
- package/dist/packages/cli/src/live/token.js +585 -0
- package/dist/packages/cli/src/live/trade.js +929 -0
- package/dist/packages/cli/src/live/watchlist.js +178 -0
- package/dist/packages/cli/src/main.js +5785 -0
- package/dist/packages/cli/src/mcp-cmd.js +326 -0
- package/dist/packages/cli/src/narrative-cmd.js +76 -0
- package/dist/packages/cli/src/pack-cmd.js +323 -0
- package/dist/packages/cli/src/plan-cmd.js +278 -0
- package/dist/packages/cli/src/popup.js +810 -0
- package/dist/packages/cli/src/rules-cmd.js +131 -0
- package/dist/packages/cli/src/rules.js +358 -0
- package/dist/packages/cli/src/scan/command.js +57 -0
- package/dist/packages/cli/src/scan/engine.js +419 -0
- package/dist/packages/cli/src/scan/observe.js +64 -0
- package/dist/packages/cli/src/scan/ui.js +98 -0
- package/dist/packages/cli/src/screener/filter.js +241 -0
- package/dist/packages/cli/src/screener/presets.js +80 -0
- package/dist/packages/cli/src/screener/rows.js +146 -0
- package/dist/packages/cli/src/screener/rug.js +50 -0
- package/dist/packages/cli/src/screener/sort.js +52 -0
- package/dist/packages/cli/src/screener/view.js +183 -0
- package/dist/packages/cli/src/screens/analyst-session.js +343 -0
- package/dist/packages/cli/src/screens/analyst.js +326 -0
- package/dist/packages/cli/src/screens/boot.js +74 -0
- package/dist/packages/cli/src/screens/bubbles.js +255 -0
- package/dist/packages/cli/src/screens/coin.js +33 -0
- package/dist/packages/cli/src/screens/follows.js +201 -0
- package/dist/packages/cli/src/screens/kol.js +90 -0
- package/dist/packages/cli/src/screens/narrative.js +90 -0
- package/dist/packages/cli/src/screens/newpairs.js +150 -0
- package/dist/packages/cli/src/screens/overlay.js +257 -0
- package/dist/packages/cli/src/screens/plans.js +126 -0
- package/dist/packages/cli/src/screens/positions.js +176 -0
- package/dist/packages/cli/src/screens/rules.js +170 -0
- package/dist/packages/cli/src/screens/screener.js +459 -0
- package/dist/packages/cli/src/screens/social-panel.js +107 -0
- package/dist/packages/cli/src/screens/ticket.js +342 -0
- package/dist/packages/cli/src/screens/token.js +312 -0
- package/dist/packages/cli/src/screens/trending.js +181 -0
- package/dist/packages/cli/src/screens/watchlist.js +104 -0
- package/dist/packages/cli/src/skill-eval-build.js +109 -0
- package/dist/packages/cli/src/social/demo.js +96 -0
- package/dist/packages/cli/src/social/view.js +250 -0
- package/dist/packages/cli/src/state.js +463 -0
- package/dist/packages/cli/src/theme-cmd.js +126 -0
- package/dist/packages/cli/src/trade.js +319 -0
- package/dist/packages/cli/src/update.js +294 -0
- package/dist/packages/cli/src/version.js +3 -0
- package/dist/packages/cli/src/voice-cmd.js +119 -0
- package/dist/packages/cli/src/voice-hud.js +42 -0
- package/dist/packages/cli/src/wallet-cmd.js +440 -0
- package/dist/packages/cli/src/wallet-command.js +31 -0
- package/dist/packages/cli/src/watch-cmd.js +190 -0
- package/dist/packages/cli/src/widgets.js +208 -0
- package/dist/packages/cli/src/workspace-cmd.js +68 -0
- package/dist/packages/cli/src/x-cmd.js +154 -0
- package/dist/packages/custom/src/config.js +213 -0
- package/dist/packages/custom/src/index.js +10 -0
- package/dist/packages/custom/src/keymap.js +107 -0
- package/dist/packages/custom/src/layout.js +235 -0
- package/dist/packages/custom/src/packs.js +633 -0
- package/dist/packages/custom/src/rules.js +704 -0
- package/dist/packages/custom/src/schema.js +538 -0
- package/dist/packages/custom/src/themes.js +176 -0
- package/dist/packages/custom/src/widget-html.js +222 -0
- package/dist/packages/custom/src/workspace.js +141 -0
- package/dist/packages/data/src/analyst-actions.js +133 -0
- package/dist/packages/data/src/budget.js +88 -0
- package/dist/packages/data/src/cache.js +142 -0
- package/dist/packages/data/src/candles.js +168 -0
- package/dist/packages/data/src/clock.js +64 -0
- package/dist/packages/data/src/coin-target.js +82 -0
- package/dist/packages/data/src/credits.js +137 -0
- package/dist/packages/data/src/detail.js +97 -0
- package/dist/packages/data/src/fast-lane.js +321 -0
- package/dist/packages/data/src/fixtures.js +135 -0
- package/dist/packages/data/src/health.js +245 -0
- package/dist/packages/data/src/http.js +277 -0
- package/dist/packages/data/src/images/cache.js +141 -0
- package/dist/packages/data/src/images/decode-worker.js +28 -0
- package/dist/packages/data/src/images/decode.js +81 -0
- package/dist/packages/data/src/images/fetch.js +162 -0
- package/dist/packages/data/src/images/gif.js +214 -0
- package/dist/packages/data/src/images/index.js +10 -0
- package/dist/packages/data/src/images/jpeg.js +544 -0
- package/dist/packages/data/src/images/png.js +310 -0
- package/dist/packages/data/src/images/service.js +334 -0
- package/dist/packages/data/src/images/types.js +27 -0
- package/dist/packages/data/src/index.js +211 -0
- package/dist/packages/data/src/intel/helius-history.js +70 -0
- package/dist/packages/data/src/intel/holder-class.js +125 -0
- package/dist/packages/data/src/intel/rules.js +421 -0
- package/dist/packages/data/src/intel/scan.js +170 -0
- package/dist/packages/data/src/larp/evidence.js +288 -0
- package/dist/packages/data/src/larp/link-baseline.js +196 -0
- package/dist/packages/data/src/larp/links.js +137 -0
- package/dist/packages/data/src/larp/robots.js +90 -0
- package/dist/packages/data/src/larp/safe-fetch.js +389 -0
- package/dist/packages/data/src/limiter.js +341 -0
- package/dist/packages/data/src/model.js +12 -0
- package/dist/packages/data/src/paper.js +331 -0
- package/dist/packages/data/src/rpc-config.js +126 -0
- package/dist/packages/data/src/rpc-pool.js +166 -0
- package/dist/packages/data/src/social/chain.js +83 -0
- package/dist/packages/data/src/social/dedupe.js +205 -0
- package/dist/packages/data/src/social/feed.js +462 -0
- package/dist/packages/data/src/social/firewall.js +110 -0
- package/dist/packages/data/src/social/index.js +11 -0
- package/dist/packages/data/src/social/kol.js +269 -0
- package/dist/packages/data/src/social/model.js +11 -0
- package/dist/packages/data/src/social/rank.js +184 -0
- package/dist/packages/data/src/social/rss.js +113 -0
- package/dist/packages/data/src/social/summary.js +104 -0
- package/dist/packages/data/src/social/venues.js +186 -0
- package/dist/packages/data/src/sources/dexscreener.js +184 -0
- package/dist/packages/data/src/sources/geckoterminal.js +133 -0
- package/dist/packages/data/src/sources/goplus.js +161 -0
- package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
- package/dist/packages/data/src/sources/jupiter.js +198 -0
- package/dist/packages/data/src/sources/pda.js +113 -0
- package/dist/packages/data/src/sources/pump-curve.js +58 -0
- package/dist/packages/data/src/sources/pumpportal.js +252 -0
- package/dist/packages/data/src/sources/rugcheck.js +71 -0
- package/dist/packages/data/src/sources/solana-rpc.js +299 -0
- package/dist/packages/data/src/sources/solana-ws.js +287 -0
- package/dist/packages/data/src/store.js +289 -0
- package/dist/packages/data/src/trade/bridge.js +464 -0
- package/dist/packages/data/src/trade/caps.js +323 -0
- package/dist/packages/data/src/trade/copy/auto.js +385 -0
- package/dist/packages/data/src/trade/copy/follows.js +103 -0
- package/dist/packages/data/src/trade/copy/index.js +9 -0
- package/dist/packages/data/src/trade/copy/mirror.js +91 -0
- package/dist/packages/data/src/trade/copy/stats.js +120 -0
- package/dist/packages/data/src/trade/copy/tracker.js +158 -0
- package/dist/packages/data/src/trade/copy/trades.js +87 -0
- package/dist/packages/data/src/trade/copy/types.js +6 -0
- package/dist/packages/data/src/trade/executor.js +933 -0
- package/dist/packages/data/src/trade/kill-switch.js +47 -0
- package/dist/packages/data/src/trade/link-store.js +52 -0
- package/dist/packages/data/src/trade/plans/confirm.js +157 -0
- package/dist/packages/data/src/trade/plans/engine.js +784 -0
- package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
- package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
- package/dist/packages/data/src/trade/plans/executor.js +11 -0
- package/dist/packages/data/src/trade/plans/index.js +12 -0
- package/dist/packages/data/src/trade/plans/journal.js +77 -0
- package/dist/packages/data/src/trade/plans/mandate.js +129 -0
- package/dist/packages/data/src/trade/plans/plan.js +179 -0
- package/dist/packages/data/src/trade/plans/service.js +168 -0
- package/dist/packages/data/src/trade/plans/types.js +6 -0
- package/dist/packages/data/src/trade/rpc-exec.js +229 -0
- package/dist/packages/data/src/trade/signer/client.js +197 -0
- package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
- package/dist/packages/data/src/trade/signer/host.js +97 -0
- package/dist/packages/data/src/trade/signer/main.js +121 -0
- package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
- package/dist/packages/data/src/trade/signer/protocol.js +92 -0
- package/dist/packages/data/src/trade/signer/service.js +400 -0
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
- package/dist/packages/data/src/trade/sol-usd.js +86 -0
- package/dist/packages/data/src/trade/swap-build.js +108 -0
- package/dist/packages/data/src/trade/token-delta.js +201 -0
- package/dist/packages/data/src/trade/tx-decode.js +632 -0
- package/dist/packages/data/src/util.js +24 -0
- package/dist/packages/data/src/watchlist.js +176 -0
- package/dist/packages/data/src/x/api.js +256 -0
- package/dist/packages/data/src/x/budget.js +66 -0
- package/dist/packages/data/src/x/config.js +49 -0
- package/dist/packages/data/src/x/index.js +10 -0
- package/dist/packages/data/src/x/mock-server.js +217 -0
- package/dist/packages/data/src/x/oauth.js +242 -0
- package/dist/packages/data/src/x/pkce.js +21 -0
- package/dist/packages/data/src/x/source.js +285 -0
- package/dist/packages/data/src/x/token-store.js +134 -0
- package/dist/packages/data/src/x/transport.js +71 -0
- package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
- package/dist/packages/mcp/assets/cases.json +1194 -0
- package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
- package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
- package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
- package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
- package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
- package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
- package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
- package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
- package/dist/packages/mcp/src/dossier.js +78 -0
- package/dist/packages/mcp/src/firewall.js +21 -0
- package/dist/packages/mcp/src/index.js +22 -0
- package/dist/packages/mcp/src/server.js +936 -0
- package/dist/packages/mcp/src/user-content.js +434 -0
- package/dist/packages/packs/src/backtest.js +183 -0
- package/dist/packages/packs/src/digest.js +95 -0
- package/dist/packages/packs/src/firewall.js +57 -0
- package/dist/packages/packs/src/index.js +20 -0
- package/dist/packages/packs/src/larp-eval.js +100 -0
- package/dist/packages/packs/src/narrative.js +309 -0
- package/dist/packages/packs/src/reason.js +135 -0
- package/dist/packages/packs/src/rug-model.js +137 -0
- package/dist/packages/packs/src/schema.js +54 -0
- package/dist/packages/packs/src/skill-backtest.js +300 -0
- package/dist/packages/packs/src/token-dossier.js +174 -0
- package/dist/packages/packs/src/verdicts.js +17 -0
- package/dist/packages/packs/src/wallet-dossier.js +105 -0
- package/dist/packages/popup/src/config.js +71 -0
- package/dist/packages/popup/src/dock.js +271 -0
- package/dist/packages/popup/src/host.js +210 -0
- package/dist/packages/popup/src/index.js +12 -0
- package/dist/packages/popup/src/loopback.js +53 -0
- package/dist/packages/popup/src/open.js +183 -0
- package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
- package/dist/packages/popup/src/pages/bubbles.css +23 -0
- package/dist/packages/popup/src/pages/bubbles.html +36 -0
- package/dist/packages/popup/src/pages/bubbles.js +214 -0
- package/dist/packages/popup/src/pages/chart-lib.js +262 -0
- package/dist/packages/popup/src/pages/chart.css +50 -0
- package/dist/packages/popup/src/pages/chart.html +56 -0
- package/dist/packages/popup/src/pages/chart.js +554 -0
- package/dist/packages/popup/src/pages/common.css +34 -0
- package/dist/packages/popup/src/pages/common.js +187 -0
- package/dist/packages/popup/src/pages/feeds.html +20 -0
- package/dist/packages/popup/src/pages/feeds.js +77 -0
- package/dist/packages/popup/src/pages/narrative.html +20 -0
- package/dist/packages/popup/src/pages/narrative.js +61 -0
- package/dist/packages/popup/src/pages/plans.css +30 -0
- package/dist/packages/popup/src/pages/plans.html +26 -0
- package/dist/packages/popup/src/pages/plans.js +82 -0
- package/dist/packages/popup/src/pages/roster.css +41 -0
- package/dist/packages/popup/src/pages/roster.html +25 -0
- package/dist/packages/popup/src/pages/roster.js +75 -0
- package/dist/packages/popup/src/pages/token.html +20 -0
- package/dist/packages/popup/src/pages/token.js +112 -0
- package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
- package/dist/packages/popup/src/pages/widget.css +5 -0
- package/dist/packages/popup/src/pages/widget.html +21 -0
- package/dist/packages/popup/src/pages/widget.js +85 -0
- package/dist/packages/popup/src/protocol.js +31 -0
- package/dist/packages/popup/src/selftest.js +156 -0
- package/dist/packages/popup/src/server.js +582 -0
- package/dist/packages/popup/src/sinkproxy.js +167 -0
- package/dist/packages/popup/src/widget-html.js +231 -0
- package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
- package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
- package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
- package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
- package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
- package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
- package/dist/packages/recorder/src/cli.js +113 -0
- package/dist/packages/recorder/src/export.js +71 -0
- package/dist/packages/recorder/src/index.js +20 -0
- package/dist/packages/recorder/src/labels.js +151 -0
- package/dist/packages/recorder/src/open.js +29 -0
- package/dist/packages/recorder/src/recorder.js +553 -0
- package/dist/packages/recorder/src/schedule.js +43 -0
- package/dist/packages/recorder/src/schema.js +135 -0
- package/dist/packages/recorder/src/snapshot.js +191 -0
- package/dist/packages/recorder/src/sweep.js +52 -0
- package/dist/packages/recorder/src/verdicts.js +52 -0
- package/dist/packages/tui/src/bubbles.js +415 -0
- package/dist/packages/tui/src/canvas.js +370 -0
- package/dist/packages/tui/src/chrome.js +85 -0
- package/dist/packages/tui/src/color.js +133 -0
- package/dist/packages/tui/src/flush.js +62 -0
- package/dist/packages/tui/src/fmt.js +188 -0
- package/dist/packages/tui/src/fx-trade.js +278 -0
- package/dist/packages/tui/src/fx.js +527 -0
- package/dist/packages/tui/src/images/blocks.js +141 -0
- package/dist/packages/tui/src/images/coin.js +22 -0
- package/dist/packages/tui/src/images/detect.js +140 -0
- package/dist/packages/tui/src/images/index.js +7 -0
- package/dist/packages/tui/src/images/layer.js +146 -0
- package/dist/packages/tui/src/images/pixels.js +157 -0
- package/dist/packages/tui/src/images/protocols.js +87 -0
- package/dist/packages/tui/src/index.js +18 -0
- package/dist/packages/tui/src/keymap.js +357 -0
- package/dist/packages/tui/src/layout.js +11 -0
- package/dist/packages/tui/src/raster-font.js +195 -0
- package/dist/packages/tui/src/raster.js +213 -0
- package/dist/packages/tui/src/sound.js +177 -0
- package/dist/packages/tui/src/theme.js +182 -0
- package/dist/packages/tui/src/widgets.js +598 -0
- package/dist/packages/voice/src/controller.js +407 -0
- package/dist/packages/voice/src/doctor.js +131 -0
- package/dist/packages/voice/src/download.js +184 -0
- package/dist/packages/voice/src/engines.js +84 -0
- package/dist/packages/voice/src/index.js +20 -0
- package/dist/packages/voice/src/router.js +502 -0
- package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
- package/dist/packages/voice/src/stt/apple.js +179 -0
- package/dist/packages/voice/src/stt/fake.js +34 -0
- package/dist/packages/voice/src/stt/whisper.js +112 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/espeak.js +131 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/piper.js +206 -0
- package/dist/packages/voice/src/tts/say.js +132 -0
- package/dist/packages/voice/src/tts/voices.js +206 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +38 -4
- 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
|
+
}
|