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,33 @@
|
|
|
1
|
+
// dexterm — coin images on the screens (task-1326): the thin glue between a screen's cell box and the
|
|
2
|
+
// ImageService + the TUI's coinImage(). Everything here is synchronous: the service answers from memory
|
|
3
|
+
// (and queues what it does not have), the TUI draws pixels-slot / block art / placeholder accordingly.
|
|
4
|
+
import { coinImage } from '../../../tui/src/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* True when this run shows coin images at all: live/replay with the service on. Never on fixtures — fixture rows
|
|
7
|
+
* carry no image URL, so the column would only ever paint placeholder initials, and the fixture frames (goldens,
|
|
8
|
+
* `--fixtures` one-shots) must stay byte for byte what they were before images existed.
|
|
9
|
+
*/
|
|
10
|
+
export function imagesOn(state) { return !!state.images && state.screener.source !== 'fixtures'; }
|
|
11
|
+
/** Draw a coin image box (or its placeholder) if images are on; returns true when something was drawn. */
|
|
12
|
+
export function drawCoin(c, state, b) {
|
|
13
|
+
const svc = state.images;
|
|
14
|
+
if (!svc || b.w <= 0 || b.h <= 0)
|
|
15
|
+
return false;
|
|
16
|
+
const st = svc.get(b.url ?? null, b.prio ?? 1);
|
|
17
|
+
const ready = st.state === 'ready';
|
|
18
|
+
coinImage(c, {
|
|
19
|
+
x: b.x, y: b.y, w: b.w, h: b.h, symbol: b.symbol,
|
|
20
|
+
img: ready ? st.img : null, png: ready ? st.png : null, key: ready ? st.key : null,
|
|
21
|
+
pending: st.state === 'pending',
|
|
22
|
+
depth: state.colorDepth, bg: b.bg, mode: b.mode ?? state.imageBlocks ?? 'half',
|
|
23
|
+
});
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
/** The popup's same-origin image path for a coin, when the thumbnail is already cached. */
|
|
27
|
+
export function popupImagePath(state, url) {
|
|
28
|
+
const svc = state.images;
|
|
29
|
+
if (!svc || !url)
|
|
30
|
+
return null;
|
|
31
|
+
const st = svc.get(url, 2);
|
|
32
|
+
return st.state === 'ready' ? `/img/${st.key}.png` : null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// dexterm — FOLLOWED WALLETS screen (key U, task-1360 copy trading Tier 1). The user's own follow list with each
|
|
2
|
+
// wallet's decoded activity (trades, FIFO realised PnL in SOL with a USD view at today's rate, win rate with its n,
|
|
3
|
+
// early-entry score when a launch window was scanned, open positions) and the live ALERTS tape (a followed
|
|
4
|
+
// wallet's buys and sells as the tracker saw them, with the lag). Every figure prints its sample. Labels are the
|
|
5
|
+
// user's own words, never inferred. Nothing on this screen trades, signs or proposes: Tier 2 adds the one-key
|
|
6
|
+
// mirror PROPOSAL and Tier 3 the capped auto-mirror, both behind the plan engine's own keypress gates.
|
|
7
|
+
import { Canvas, header, footer, breakpoint, fmtAgeSec } from '../../../tui/src/index.js';
|
|
8
|
+
import { headerModel } from "../state.js";
|
|
9
|
+
import { shortAddr, autoSizeCap } from '../../../data/src/trade/copy/index.js';
|
|
10
|
+
function fmtSol(n) { const s = n >= 0 ? '+' : '−'; return `${s}${Math.abs(n).toFixed(Math.abs(n) >= 10 ? 1 : 3)} SOL`; }
|
|
11
|
+
function fmtUsdSigned(n) { const s = n >= 0 ? '+' : '−'; const a = Math.abs(n); return `${s}$${a >= 1000 ? (a / 1000).toFixed(1) + 'k' : a.toFixed(a >= 100 ? 0 : 2)}`; }
|
|
12
|
+
function fmtUsdAbs(n) { if (n === null)
|
|
13
|
+
return '—'; return n >= 1000 ? `$${(n / 1000).toFixed(1)}k` : `$${n.toFixed(n >= 100 ? 0 : 2)}`; }
|
|
14
|
+
export const FOLLOW_INPUT_MAX = 80;
|
|
15
|
+
export function renderFollows(c, state) {
|
|
16
|
+
const t = c.theme;
|
|
17
|
+
const bp = breakpoint(c.w);
|
|
18
|
+
header(c, headerModel(state));
|
|
19
|
+
const ui = state.copyUi ?? null;
|
|
20
|
+
const svc = state.copy ?? null;
|
|
21
|
+
const follows = svc ? svc.follows() : [];
|
|
22
|
+
const status = svc ? svc.status() : null;
|
|
23
|
+
const now = state.now.getTime();
|
|
24
|
+
const top = 2, bottom = c.h - 1;
|
|
25
|
+
const stLabel = !status ? 'no binding' : status.state === 'ok' ? `polling · ${status.polls} polls` : status.state === 'idle' ? 'idle' : status.state.toUpperCase();
|
|
26
|
+
const title = `FOLLOWED WALLETS · ${follows.length} · ${stLabel} · ${status?.alerts ?? 0} alerts`;
|
|
27
|
+
const caption = bp === 'compact' ? undefined : 'your own list, your own wallet · labels are yours, never inferred · PnL = FIFO over fetched history · not a recommendation';
|
|
28
|
+
const alertsH = Math.max(5, Math.min(12, Math.floor((bottom - top) / 3)));
|
|
29
|
+
const typing = ui?.input !== null && ui?.input !== undefined;
|
|
30
|
+
const listH = bottom - top - alertsH;
|
|
31
|
+
const box = c.box(0, top, c.w, listH, { title, caption, focus: !typing });
|
|
32
|
+
const bg = box.bg;
|
|
33
|
+
let cy = box.y;
|
|
34
|
+
if (status && status.state !== 'ok' && status.state !== 'idle' && status.why) {
|
|
35
|
+
c.text(box.x + 1, cy, `⚠ ${status.why}`, { fg: status.state === 'unavailable' ? t.warn : t.down, bg }, box.w - 2);
|
|
36
|
+
cy += 1;
|
|
37
|
+
}
|
|
38
|
+
if (!follows.length) {
|
|
39
|
+
c.text(box.x + 1, cy + 1, 'no wallets followed yet.', { fg: t.muted, bg });
|
|
40
|
+
cy += 3;
|
|
41
|
+
c.text(box.x + 1, cy, 'i type a wallet address (and an optional label) to follow it', { fg: t.dim, bg });
|
|
42
|
+
cy += 1;
|
|
43
|
+
c.text(box.x + 1, cy, 'dexterm follow add <addr> --name x the same from a shell · follow list | stats <addr> | purge', { fg: t.dim, bg });
|
|
44
|
+
cy += 1;
|
|
45
|
+
c.text(box.x + 1, cy, 'alerts on its buys/sells appear as toasts anywhere · the tracker polls Helius history (free key)', { fg: t.dim, bg });
|
|
46
|
+
cy += 2;
|
|
47
|
+
c.text(box.x + 1, cy, 'self-directed only: you pick the wallet, your caps, your keys · no pooled copy, no leader lists, no fees', { fg: t.dim, bg, i: true });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const wide = bp !== 'compact';
|
|
51
|
+
const cols = wide ? ['WALLET', 'LABEL', 'AUTO', 'TRADES', 'REALISED', 'WIN RATE', 'EES', 'OPEN', 'LAST', 'SAMPLE'] : ['WALLET', 'LABEL', 'AUTO', 'TRADES', 'REALISED', 'WIN', 'LAST'];
|
|
52
|
+
const xs = wide ? [1, 13, 26, 34, 43, 62, 76, 83, 89, 97] : [1, 13, 24, 30, 38, 52, 60];
|
|
53
|
+
const autoOf = (addr) => svc?.auto?.policy(addr) ?? null;
|
|
54
|
+
cols.forEach((h, i) => c.text(box.x + xs[i], cy, h, { fg: t.dim, bg }));
|
|
55
|
+
cy += 1;
|
|
56
|
+
const sel = Math.max(0, Math.min(follows.length - 1, ui?.selected ?? 0));
|
|
57
|
+
const scroll = Math.max(0, sel - (box.h - 4));
|
|
58
|
+
for (let i = scroll; i < follows.length; i++) {
|
|
59
|
+
if (cy >= box.y + box.h - 1)
|
|
60
|
+
break;
|
|
61
|
+
const f = follows[i];
|
|
62
|
+
const s = svc ? svc.stats(f.addr) : null;
|
|
63
|
+
const selected = i === sel;
|
|
64
|
+
const rb = selected ? t.selBg : bg;
|
|
65
|
+
c.fill(box.x, cy, box.w, 1, ' ', { bg: rb });
|
|
66
|
+
if (selected)
|
|
67
|
+
c.put(box.x, cy, '▎', { fg: t.accent, bg: rb, soft: true });
|
|
68
|
+
c.text(box.x + xs[0], cy, shortAddr(f.addr), { fg: selected ? t.borderFocus : t.text, bg: rb });
|
|
69
|
+
c.text(box.x + xs[1], cy, f.name ?? '—', { fg: f.name ? t.accent : t.dim, bg: rb }, xs[2] - xs[1] - 1);
|
|
70
|
+
const ap = autoOf(f.addr);
|
|
71
|
+
c.text(box.x + xs[2], cy, ap?.enabled ? 'ON' : 'off', { fg: ap?.enabled ? t.warn : t.dim, bg: rb, b: !!ap?.enabled });
|
|
72
|
+
const err = svc?.tracker.walletError(f.addr) ?? null;
|
|
73
|
+
if (!s || (s.trades === 0 && !err)) {
|
|
74
|
+
c.text(box.x + xs[3], cy, status?.state === 'unavailable' ? 'needs a free Helius key' : status?.polls ? 'no swaps in fetched history' : 'loading…', { fg: t.dim, bg: rb, i: true });
|
|
75
|
+
cy += 1;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (err) {
|
|
79
|
+
c.text(box.x + xs[3], cy, `poll failed: ${err}`, { fg: t.down, bg: rb }, box.w - xs[3] - 2);
|
|
80
|
+
cy += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
c.text(box.x + xs[3], cy, `${s.buys}b/${s.sells}s`, { fg: t.text, bg: rb });
|
|
84
|
+
const pnlFg = s.realisedSol > 0 ? t.up : s.realisedSol < 0 ? t.down : t.muted;
|
|
85
|
+
c.text(box.x + xs[4], cy, `${fmtSol(s.realisedSol)}${s.realisedUsd !== null ? ` ${fmtUsdSigned(s.realisedUsd)}` : ''}`, { fg: pnlFg, bg: rb }, xs[5] - xs[4] - 1);
|
|
86
|
+
const wr = s.winRate === null ? `n<10 (${s.wins}/${s.closed})` : `${Math.round(s.winRate * 100)}% (${s.wins}/${s.closed})`;
|
|
87
|
+
c.text(box.x + xs[5], cy, wr, { fg: s.winRate === null ? t.dim : t.text, bg: rb }, (xs[6] ?? box.w) - xs[5] - 1);
|
|
88
|
+
if (wide) {
|
|
89
|
+
c.text(box.x + xs[6], cy, s.earlyEntry.score === null ? '—' : s.earlyEntry.score.toFixed(2), { fg: s.earlyEntry.score === null ? t.dim : t.text, bg: rb });
|
|
90
|
+
c.text(box.x + xs[7], cy, String(s.open), { fg: t.text, bg: rb });
|
|
91
|
+
c.text(box.x + xs[8], cy, s.lastT ? fmtAgeSec(Math.max(0, Math.round((now - s.lastT) / 1000))) : '—', { fg: t.muted, bg: rb });
|
|
92
|
+
c.text(box.x + xs[9], cy, s.sample.note, { fg: t.dim, bg: rb }, box.w - xs[9] - 1);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
c.text(box.x + xs[6], cy, s.lastT ? fmtAgeSec(Math.max(0, Math.round((now - s.lastT) / 1000))) : '—', { fg: t.muted, bg: rb });
|
|
96
|
+
}
|
|
97
|
+
cy += 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (ui?.notice && !typing)
|
|
101
|
+
c.textR(box.x + 1, box.y + box.h - 1, box.w - 2, ui.notice, { fg: t.warn, bg, i: true });
|
|
102
|
+
// ALERTS tape
|
|
103
|
+
const abox = c.box(0, top + listH, c.w, alertsH, { title: `ALERTS · a followed wallet bought or sold · ${status ? `${status.alerts} seen` : ''}`, caption: bp === 'compact' ? undefined : 'information only · lag = seen − block time · USD at the SOL price when seen', focus: false });
|
|
104
|
+
const abg = abox.bg;
|
|
105
|
+
const alerts = svc ? svc.alerts(alertsH - 1) : [];
|
|
106
|
+
if (!alerts.length)
|
|
107
|
+
c.text(abox.x + 1, abox.y, status?.state === 'ok' ? 'nothing new since the first look (history is not news)' : 'no alerts yet', { fg: t.dim, bg: abg, i: true });
|
|
108
|
+
let ay = abox.y;
|
|
109
|
+
for (const a of alerts) {
|
|
110
|
+
if (ay >= abox.y + abox.h - (typing ? 1 : 0))
|
|
111
|
+
break;
|
|
112
|
+
const tr = a.trade;
|
|
113
|
+
const sideFg = tr.side === 'buy' ? t.up : t.down;
|
|
114
|
+
let cx = c.text(abox.x + 1, ay, fmtAgeSec(Math.max(0, Math.round((now - a.t) / 1000))).padEnd(5), { fg: t.dim, bg: abg });
|
|
115
|
+
cx = c.text(cx + 1, ay, (a.name ?? shortAddr(a.wallet)).padEnd(12).slice(0, 12), { fg: t.accent, bg: abg });
|
|
116
|
+
cx = c.text(cx + 1, ay, tr.side.toUpperCase().padEnd(4), { fg: sideFg, bg: abg, b: true });
|
|
117
|
+
cx = c.text(cx + 1, ay, shortAddr(tr.mint), { fg: t.text, bg: abg });
|
|
118
|
+
cx = c.text(cx + 1, ay, fmtUsdAbs(tr.usd).padStart(7), { fg: t.text, bg: abg });
|
|
119
|
+
cx = c.text(cx + 1, ay, tr.solDelta !== null ? `${Math.abs(tr.solDelta).toFixed(3)} SOL` : 'unpriced', { fg: t.muted, bg: abg });
|
|
120
|
+
c.text(cx + 1, ay, a.lagMs === null ? 'lag ?' : `lag ${(a.lagMs / 1000).toFixed(0)}s`, { fg: a.lagMs !== null && a.lagMs > 30_000 ? t.warn : t.dim, bg: abg });
|
|
121
|
+
ay += 1;
|
|
122
|
+
}
|
|
123
|
+
if (typing) {
|
|
124
|
+
const iy = abox.y + abox.h - 1;
|
|
125
|
+
c.hline(abox.x, iy - 1, abox.w, { fg: t.border, bg: abg });
|
|
126
|
+
const label = 'follow: ';
|
|
127
|
+
c.text(abox.x + 1, iy, label, { fg: t.accent, bg: abg, b: true });
|
|
128
|
+
c.text(abox.x + 1 + label.length, iy, `${ui.input}█`, { fg: t.text, bg: abg }, abox.w - label.length - 3);
|
|
129
|
+
c.textR(abox.x + 1, iy, abox.w - 2, '<address> [label] ⏎ · esc cancel', { fg: t.dim, bg: abg });
|
|
130
|
+
}
|
|
131
|
+
// Tier 2: the MIRROR TICKET preview (proposal only — nothing here arms; the O screen's keypress gate does).
|
|
132
|
+
if (ui?.mirror) {
|
|
133
|
+
const mr = ui.mirror;
|
|
134
|
+
const mw = Math.min(c.w - 4, 96), mh = 15;
|
|
135
|
+
const mx = Math.floor((c.w - mw) / 2), my = top + Math.max(1, Math.floor((bottom - top - mh) / 2));
|
|
136
|
+
c.noRain(mx, my, mw, mh);
|
|
137
|
+
const mbox = c.box(mx, my, mw, mh, { title: 'MIRROR TICKET — proposal only', caption: 'sized by YOUR cap · armed only on the O screen by keypress / Phantom · not a recommendation', focus: true, bg: t.panelAlt });
|
|
138
|
+
const mbg = mbox.bg;
|
|
139
|
+
const tr = mr.alert.trade;
|
|
140
|
+
let y = mbox.y;
|
|
141
|
+
c.text(mbox.x + 1, y, `${mr.alert.name ?? shortAddr(mr.alert.wallet)} BOUGHT ${mr.market?.symbol ?? shortAddr(tr.mint)} (${shortAddr(tr.mint)}) for ${fmtUsdAbs(tr.usd)}${tr.solDelta !== null ? ` · ${Math.abs(tr.solDelta).toFixed(3)} SOL` : ''}`, { fg: t.text, bg: mbg, b: true }, mbox.w - 2);
|
|
142
|
+
y += 2;
|
|
143
|
+
if (mr.pending || !mr.result) {
|
|
144
|
+
c.text(mbox.x + 1, y, 'resolving price, liquidity, decimals and age…', { fg: t.muted, bg: mbg, i: true });
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
for (const ck of mr.result.checks) {
|
|
148
|
+
const mark = ck.ok === true ? '✓' : ck.ok === false ? '✗' : '?';
|
|
149
|
+
const fg = ck.ok === true ? t.up : ck.ok === false ? t.down : t.warn;
|
|
150
|
+
let cx = c.text(mbox.x + 1, y, mark, { fg, bg: mbg, b: true });
|
|
151
|
+
cx = c.text(cx + 1, y, ck.name.padEnd(12), { fg: t.text, bg: mbg });
|
|
152
|
+
c.text(cx + 1, y, ck.detail, { fg: ck.ok === null ? t.warn : t.muted, bg: mbg }, mbox.w - (cx + 1 - mbox.x) - 1);
|
|
153
|
+
y += 1;
|
|
154
|
+
}
|
|
155
|
+
y += 1;
|
|
156
|
+
if (mr.result.ok) {
|
|
157
|
+
c.text(mbox.x + 1, y, `size ${fmtUsdAbs(mr.result.sizeUsd)} = ${mr.result.sizeWhy}`, { fg: t.accent, bg: mbg, b: true }, mbox.w - 2);
|
|
158
|
+
y += 1;
|
|
159
|
+
c.text(mbox.x + 1, y, 'buy now · no ladder · 3% slippage · expires in 10 min · the mandate\'s per-trade cap applies at arming', { fg: t.muted, bg: mbg }, mbox.w - 2);
|
|
160
|
+
y += 1;
|
|
161
|
+
c.text(mbox.x + 1, y, 'm again = PROPOSE (inert order plan on the O screen) · esc = close', { fg: t.accent, bg: mbg }, mbox.w - 2);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
c.text(mbox.x + 1, y, `REFUSED: ${mr.result.message}`, { fg: t.down, bg: mbg, b: true }, mbox.w - 2);
|
|
165
|
+
y += 1;
|
|
166
|
+
c.text(mbox.x + 1, y, 'm or esc = close · nothing was proposed', { fg: t.muted, bg: mbg }, mbox.w - 2);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Tier 3: the AUTO-MIRROR modal (enable = captured physical keypress in the loop; this only paints it).
|
|
171
|
+
if (ui?.autoModal) {
|
|
172
|
+
const w = ui.autoModal.wallet;
|
|
173
|
+
const pol = svc?.auto?.policy(w) ?? null;
|
|
174
|
+
const st = svc?.auto?.status(w) ?? null;
|
|
175
|
+
const mw = Math.min(c.w - 4, 92), mh = 14;
|
|
176
|
+
const mx = Math.floor((c.w - mw) / 2), my = top + Math.max(1, Math.floor((bottom - top - mh) / 2));
|
|
177
|
+
c.noRain(mx, my, mw, mh);
|
|
178
|
+
const mbox = c.box(mx, my, mw, mh, { title: '⚠ AUTO-MIRROR — capped, default OFF, less safe', caption: 'your wallet, your caps, your keys · paper/devnet only · not a recommendation', focus: true, bg: t.panelAlt });
|
|
179
|
+
const mbg = mbox.bg;
|
|
180
|
+
let y = mbox.y;
|
|
181
|
+
const name = follows.find((f) => f.addr === w)?.name ?? null;
|
|
182
|
+
c.text(mbox.x + 1, y, `Mirror every BUY by ${name ?? shortAddr(w)} (${shortAddr(w)}) automatically?`, { fg: t.text, bg: mbg, b: true }, mbox.w - 2);
|
|
183
|
+
y += 2;
|
|
184
|
+
if (pol) {
|
|
185
|
+
c.text(mbox.x + 1, y, `per mirror ≤ $${autoSizeCap(pol).toFixed(2)} (¼ of the $${Math.min(pol.perTradeUsd, pol.dailyUsd)} per-trade cap: auto fires at the hands-free tier) · $${pol.dailyUsd}/day · max ${pol.maxOpen} open · ${Math.round(pol.cooldownMs / 1000)} s cooldown`, { fg: t.muted, bg: mbg }, mbox.w - 2);
|
|
186
|
+
y += 1;
|
|
187
|
+
c.text(mbox.x + 1, y, `size factor ${pol.k}× the leader · auto-disarm after ${pol.autoDisarmStreak} refused/failed mirrors · caps expire in ${Math.round(pol.ttlMs / 3600_000)} h · change caps: dexterm follow auto <addr> …`, { fg: t.muted, bg: mbg }, mbox.w - 2);
|
|
188
|
+
y += 2;
|
|
189
|
+
}
|
|
190
|
+
c.text(mbox.x + 1, y, 'every mirror still passes the lag / drift / liquidity / age / leader checks, and the plan engine re-checks every cap signer-side', { fg: t.muted, bg: mbg }, mbox.w - 2);
|
|
191
|
+
y += 1;
|
|
192
|
+
c.text(mbox.x + 1, y, 'mirror-SELL is not built: you close every mirror yourself · auto is OFF again after every restart', { fg: t.warn, bg: mbg }, mbox.w - 2);
|
|
193
|
+
y += 2;
|
|
194
|
+
const gateOk = st?.gateActive ?? false;
|
|
195
|
+
c.text(mbox.x + 1, y, gateOk ? '✓ the "less safe" hands-free popup is accepted (O screen, G)' : '✗ needs the "less safe" hands-free popup first: O screen, then G (keypress only, expires in 24 h)', { fg: gateOk ? t.up : t.down, bg: mbg }, mbox.w - 2);
|
|
196
|
+
y += 2;
|
|
197
|
+
c.text(mbox.x + 1, y, gateOk ? 'Hold and release [enter] or [y] to turn auto ON (physical keypress) · [esc] leaves it OFF · a pipe/agent cannot' : '[esc] closes · accept the popup, then A again', { fg: t.accent, bg: mbg }, mbox.w - 2);
|
|
198
|
+
}
|
|
199
|
+
const autoHint = (() => { const f = follows[Math.max(0, Math.min(follows.length - 1, ui?.selected ?? 0))]; const p = f ? svc?.auto?.policy(f.addr) : null; return p?.enabled ? 'auto OFF' : 'auto-mirror'; })();
|
|
200
|
+
footer(c, [['↑↓', 'move'], ['i', 'follow a wallet'], ['d', 'unfollow'], ['m', ui?.mirror ? (ui.mirror.result?.ok ? 'PROPOSE mirror' : 'close') : 'mirror ticket'], ['A', autoHint], ['r', 'poll now'], ['esc', 'back'], ['q', 'quit']], t.name);
|
|
201
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// dexterm — KOL LISTS screen (key K, task-1330; INTEGRATIONS-DESIGN §5.4). Own lists, manual handles and
|
|
2
|
+
// signed packs: list, enable/disable per list (space), mute/unmute a handle (m), remove a handle from a manual
|
|
3
|
+
// list (d), add a handle by typing (i … ⏎). Packs are read-only here (`dexterm kol import <dir>`). Credibility
|
|
4
|
+
// is never shown as a property of a list: a pack grants membership, the user's own observations grant trust.
|
|
5
|
+
import { Canvas, header, footer, badge, breakpoint } from '../../../tui/src/index.js';
|
|
6
|
+
import { headerModel } from "../state.js";
|
|
7
|
+
import { kolOf, kolRows } from "../social/view.js";
|
|
8
|
+
export function renderKol(c, state) {
|
|
9
|
+
const t = c.theme;
|
|
10
|
+
const bp = breakpoint(c.w);
|
|
11
|
+
header(c, headerModel(state));
|
|
12
|
+
const kol = kolOf(state);
|
|
13
|
+
const top = 2, bottom = c.h - 1;
|
|
14
|
+
const rows = kol ? kolRows(kol) : [];
|
|
15
|
+
const sel = Math.max(0, Math.min(rows.length - 1, state.kolSelected ?? 0));
|
|
16
|
+
const title = `KOL LISTS · ${kol ? kol.summary() : 'unavailable'}`;
|
|
17
|
+
const box = c.box(0, top, c.w, bottom - top, { title, caption: bp === 'compact' ? undefined : 'packs carry handles, never posts · a list grants membership, never credibility', focus: true });
|
|
18
|
+
const bg = box.bg;
|
|
19
|
+
let cy = box.y;
|
|
20
|
+
if (!kol) {
|
|
21
|
+
c.textC(box.x, cy + 1, box.w, 'KOL lists need live, replay or fixture mode', { fg: t.muted, bg, i: true });
|
|
22
|
+
footer(c, [['esc', 'back'], ['q', 'quit']], t.name);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (rows.length === 0) {
|
|
26
|
+
c.text(box.x + 1, cy, 'no KOL lists yet.', { fg: t.muted, bg });
|
|
27
|
+
cy += 2;
|
|
28
|
+
c.text(box.x + 1, cy, 'dexterm kol create <name> make a manual list', { fg: t.dim, bg });
|
|
29
|
+
cy += 1;
|
|
30
|
+
c.text(box.x + 1, cy, 'dexterm kol add <list> @handle add a handle (or press i here once a list exists)', { fg: t.dim, bg });
|
|
31
|
+
cy += 1;
|
|
32
|
+
c.text(box.x + 1, cy, 'dexterm kol import <dir> import a hash-locked pack (pack.json + pack.lock[.sig])', { fg: t.dim, bg });
|
|
33
|
+
cy += 1;
|
|
34
|
+
c.text(box.x + 1, cy, 'X (here) or dexterm kol import-x import your OWN X lists after `dexterm x login` (handles only, never posts)', { fg: t.dim, bg });
|
|
35
|
+
cy += 1;
|
|
36
|
+
}
|
|
37
|
+
const scroll = Math.max(0, sel - (box.h - 3));
|
|
38
|
+
const typing = state.kolInput !== null && state.kolInput !== undefined;
|
|
39
|
+
for (let i = scroll; i < rows.length; i++) {
|
|
40
|
+
if (cy >= box.y + box.h - (typing ? 2 : 0))
|
|
41
|
+
break;
|
|
42
|
+
const r = rows[i];
|
|
43
|
+
const selected = i === sel;
|
|
44
|
+
const rb = selected ? t.selBg : bg;
|
|
45
|
+
c.fill(box.x, cy, box.w, 1, ' ', { bg: rb });
|
|
46
|
+
if (selected)
|
|
47
|
+
c.put(box.x, cy, '▎', { fg: t.accent, bg: rb, soft: true });
|
|
48
|
+
if (r.kind === 'list') {
|
|
49
|
+
const l = r.list;
|
|
50
|
+
let cx = c.text(box.x + 1, cy, l.enabled ? '◉' : '◌', { fg: l.enabled ? t.up : t.dim, bg: rb, b: true });
|
|
51
|
+
cx = c.text(cx + 1, cy, l.name, { fg: selected ? t.borderFocus : t.text, bg: rb, b: true }, 32);
|
|
52
|
+
cx = badge(c, cx + 1, cy, l.source === 'pack' ? 'PACK' : l.source === 'own-x-list' ? 'X LIST' : 'MANUAL', l.source === 'pack' ? t.accent : t.muted, { on: rb }) + 1;
|
|
53
|
+
cx = badge(c, cx, cy, l.tier === 'kol-a' ? 'KOL·A' : 'KOL·B', t.muted, { on: rb }) + 1;
|
|
54
|
+
if (l.signed)
|
|
55
|
+
cx = badge(c, cx, cy, l.signed.verified ? 'SIGNED ✓' : 'UNSIGNED', l.signed.verified ? t.up : t.warn, { on: rb }) + 1;
|
|
56
|
+
const tail = `${l.handles.length} handle${l.handles.length === 1 ? '' : 's'}${l.wallets.length ? ` · ${l.wallets.length} wallets` : ''}${l.signed ? ` · publisher ${l.signed.publisher} · ${l.signed.hash.slice(0, 8)}` : ''}${l.enabled ? '' : ' · OFF for trending'}`;
|
|
57
|
+
c.textR(box.x + 1, cy, box.w - 2, tail, { fg: t.dim, bg: rb });
|
|
58
|
+
}
|
|
59
|
+
else if (r.kind === 'empty') {
|
|
60
|
+
c.text(box.x + 4, cy, 'empty · press i to add a handle', { fg: t.dim, bg: rb, i: true });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const muted = kol.isMuted(r.handle);
|
|
64
|
+
let cx = c.text(box.x + 4, cy, `@${r.handle}`, { fg: muted ? t.dim : selected ? t.borderFocus : t.text, bg: rb, d: muted });
|
|
65
|
+
if (muted)
|
|
66
|
+
cx = badge(c, cx + 1, cy, 'MUTED', t.warn, { on: rb }) + 1;
|
|
67
|
+
if (r.list.source === 'pack' && r.list.handles.indexOf(r.handle) === 11 && r.list.handles.length > 12)
|
|
68
|
+
c.textR(box.x + 1, cy, box.w - 2, `… ${r.list.handles.length - 12} more in the pack`, { fg: t.dim, bg: rb, i: true });
|
|
69
|
+
}
|
|
70
|
+
cy += 1;
|
|
71
|
+
}
|
|
72
|
+
if (typing) {
|
|
73
|
+
const iy = box.y + box.h - 1;
|
|
74
|
+
const list = rows[sel]?.list;
|
|
75
|
+
c.hline(box.x, iy - 1, box.w, { fg: t.border, bg });
|
|
76
|
+
c.text(box.x + 1, iy, `add to ${list?.name ?? '?'}: @`, { fg: t.accent, bg, b: true });
|
|
77
|
+
const px = box.x + 1 + `add to ${list?.name ?? '?'}: @`.length;
|
|
78
|
+
c.text(px, iy, `${state.kolInput}█`, { fg: t.text, bg }, box.w - (px - box.x) - 2);
|
|
79
|
+
c.textR(box.x + 1, iy, box.w - 2, '⏎ add · esc cancel', { fg: t.dim, bg });
|
|
80
|
+
}
|
|
81
|
+
else if (cy + 1 < box.y + box.h)
|
|
82
|
+
c.rainZone(box.x + 1, cy + 1, box.w - 2, box.y + box.h - cy - 1, 2);
|
|
83
|
+
if (state.kolNotice && !typing) {
|
|
84
|
+
c.textR(box.x + 1, box.y + box.h - 1, box.w - 2, state.kolNotice, { fg: t.warn, bg, i: true });
|
|
85
|
+
}
|
|
86
|
+
else if (state.kolImporting && !typing) {
|
|
87
|
+
c.textR(box.x + 1, box.y + box.h - 1, box.w - 2, 'importing your X lists… (owned_lists + members; handles only)', { fg: t.accent, bg, i: true });
|
|
88
|
+
}
|
|
89
|
+
footer(c, [['j/k', 'move'], ['space', 'enable/disable'], ['m', 'mute'], ['d', 'remove'], ['i', 'add handle'], ['o', state.socialAuthor ? `only @${state.socialAuthor} (clear)` : 'only this handle'], ['X', 'import X lists'], ['T', 'trending'], ['esc', 'back'], ['q', 'quit']], t.name);
|
|
90
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// dexterm — NARRATIVE BOARD screen (key N, task-1356; AI-SUITE-DESIGN §7). What is running and what is
|
|
2
|
+
// working across the tokens dexterm has recorded: deterministic lexicon clustering, per-narrative volume /
|
|
3
|
+
// new launches / runners / pace / "what is working" score. No model in the hot path; the board carries
|
|
4
|
+
// its own limits (name-based, X/TG-only narratives missed, honest unclassified share). w flips 1h/24h,
|
|
5
|
+
// M opens the HUD popup. The board is computed in state.ts (computeNarrativeView) on entry and the tick.
|
|
6
|
+
import { Canvas, header, footer, badge, breakpoint } from '../../../tui/src/index.js';
|
|
7
|
+
import { headerModel } from "../state.js";
|
|
8
|
+
const fmtUsd = (v) => { if (v == null)
|
|
9
|
+
return '—'; const a = Math.abs(v); if (a >= 1e9)
|
|
10
|
+
return (v / 1e9).toFixed(2) + 'B'; if (a >= 1e6)
|
|
11
|
+
return (v / 1e6).toFixed(2) + 'M'; if (a >= 1e3)
|
|
12
|
+
return (v / 1e3).toFixed(1) + 'K'; return v.toFixed(0); };
|
|
13
|
+
const fmtPct = (v) => (v == null ? '—' : (v > 0 ? '+' : '') + v.toFixed(1) + '%');
|
|
14
|
+
export function renderNarrative(c, state) {
|
|
15
|
+
const t = c.theme;
|
|
16
|
+
const bp = breakpoint(c.w);
|
|
17
|
+
header(c, headerModel(state));
|
|
18
|
+
const view = state.narrative ?? null;
|
|
19
|
+
const board = view?.board ?? null;
|
|
20
|
+
const top = 2, bottom = c.h - 1;
|
|
21
|
+
const win = view?.window ?? '1h';
|
|
22
|
+
const srcLabel = view?.source === 'recorded' ? 'recorded history' : view?.source === 'screen' ? 'visible rows' : 'no data';
|
|
23
|
+
const title = `NARRATIVE BOARD · ${win} · ${board ? `${board.meta.tokens_total} tokens · ${(100 * board.meta.unclassified_share).toFixed(0)}% unclassified` : srcLabel}`;
|
|
24
|
+
const caption = bp === 'compact' ? undefined : `model output: none (deterministic lexicon v${board?.meta.lexicon_version ?? '—'}) · name-based · source: ${srcLabel}`;
|
|
25
|
+
const box = c.box(0, top, c.w, bottom - top, { title, caption, focus: true });
|
|
26
|
+
const bg = box.bg;
|
|
27
|
+
if (!board || board.narratives.every((n) => n.tokens === 0)) {
|
|
28
|
+
c.textC(box.x, box.y + 1, box.w, 'collecting narratives — run the app with the recorder on, or open the screener first', { fg: t.muted, bg, i: true });
|
|
29
|
+
c.textC(box.x, box.y + 3, box.w, view?.error ? `(${view.error})` : 'clusters come from token names against a lexicon (owner: maintainers); X/Telegram-only narratives are missed', { fg: t.dim, bg });
|
|
30
|
+
footer(c, [['w', '1h/24h'], ['esc', 'back'], ['q', 'quit']], t.name);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const shown = board.narratives.filter((n) => n.tokens > 0);
|
|
34
|
+
const sel = Math.max(0, Math.min(shown.length - 1, state.selected ?? 0));
|
|
35
|
+
// column header
|
|
36
|
+
let cy = box.y;
|
|
37
|
+
const wide = bp !== 'compact';
|
|
38
|
+
c.text(box.x + 1, cy, '#', { fg: t.dim, bg });
|
|
39
|
+
c.text(box.x + 4, cy, 'NARRATIVE', { fg: t.dim, bg });
|
|
40
|
+
c.text(box.x + 22, cy, 'TOK', { fg: t.dim, bg });
|
|
41
|
+
c.text(box.x + 28, cy, 'NEW 1h/24h', { fg: t.dim, bg });
|
|
42
|
+
c.text(box.x + 40, cy, `VOL ${win}`, { fg: t.dim, bg });
|
|
43
|
+
c.text(box.x + 50, cy, 'PACE', { fg: t.dim, bg });
|
|
44
|
+
c.text(box.x + 57, cy, 'MED1h', { fg: t.dim, bg });
|
|
45
|
+
c.text(box.x + 65, cy, 'SCORE', { fg: t.dim, bg });
|
|
46
|
+
if (wide)
|
|
47
|
+
c.text(box.x + 72, cy, 'TOP RUNNER (liq≥$10K)', { fg: t.dim, bg });
|
|
48
|
+
cy += 1;
|
|
49
|
+
c.hline(box.x, cy, box.w, { fg: t.border, bg });
|
|
50
|
+
cy += 1;
|
|
51
|
+
let rank = 0;
|
|
52
|
+
const scroll = Math.max(0, sel - (box.h - 6));
|
|
53
|
+
for (let i = scroll; i < shown.length; i++) {
|
|
54
|
+
if (cy >= box.y + box.h - 2)
|
|
55
|
+
break;
|
|
56
|
+
const n = shown[i];
|
|
57
|
+
if (n.score != null)
|
|
58
|
+
rank++;
|
|
59
|
+
const selected = i === sel;
|
|
60
|
+
const rb = selected ? t.selBg : bg;
|
|
61
|
+
c.fill(box.x, cy, box.w, 1, ' ', { bg: rb });
|
|
62
|
+
if (selected)
|
|
63
|
+
c.put(box.x, cy, '▎', { fg: t.accent, bg: rb, soft: true });
|
|
64
|
+
c.text(box.x + 1, cy, n.score == null ? '·' : String(rank), { fg: n.score == null ? t.dim : t.accent, bg: rb, b: true });
|
|
65
|
+
c.text(box.x + 4, cy, n.narrative, { fg: n.narrative === 'unclassified' ? t.dim : selected ? t.borderFocus : t.text, bg: rb, b: n.score != null }, 17);
|
|
66
|
+
c.text(box.x + 22, cy, String(n.tokens), { fg: t.text, bg: rb });
|
|
67
|
+
c.text(box.x + 28, cy, `${n.new_1h}/${n.new_24h}`, { fg: t.muted, bg: rb });
|
|
68
|
+
c.text(box.x + 40, cy, fmtUsd(win === '24h' ? n.vol_h24 : n.vol_h1), { fg: t.text, bg: rb });
|
|
69
|
+
c.text(box.x + 50, cy, n.pace_mature != null ? n.pace_mature.toFixed(1) + 'x' : n.pace == null ? '—' : n.pace.toFixed(1) + '*', { fg: t.muted, bg: rb });
|
|
70
|
+
const med = win === '24h' ? n.med_chg_h24 : n.med_chg_h1;
|
|
71
|
+
c.text(box.x + 57, cy, fmtPct(med), { fg: med == null ? t.dim : med >= 0 ? t.up : t.down, bg: rb });
|
|
72
|
+
c.text(box.x + 65, cy, n.score == null ? '—' : n.score.toFixed(0), { fg: n.score == null ? t.dim : t.accent, bg: rb, b: true });
|
|
73
|
+
if (wide) {
|
|
74
|
+
const r = n.runners[0];
|
|
75
|
+
if (r) {
|
|
76
|
+
let cx = c.text(box.x + 72, cy, r.symbol, { fg: t.text, bg: rb }, 12);
|
|
77
|
+
cx = c.text(cx + 1, cy, fmtPct(r.chg_h1), { fg: (r.chg_h1 ?? 0) >= 0 ? t.up : t.down, bg: rb });
|
|
78
|
+
c.text(cx + 1, cy, fmtUsd(r.mcap), { fg: t.muted, bg: rb });
|
|
79
|
+
}
|
|
80
|
+
else
|
|
81
|
+
c.text(box.x + 72, cy, '—', { fg: t.dim, bg: rb });
|
|
82
|
+
}
|
|
83
|
+
cy += 1;
|
|
84
|
+
}
|
|
85
|
+
// limits line
|
|
86
|
+
const ly = box.y + box.h - 1;
|
|
87
|
+
if (wide)
|
|
88
|
+
c.textC(box.x, ly, box.w, `model output: none (deterministic lexicon v${board.meta.lexicon_version}) · ${board.meta.copycat_tokens} copycat symbols · name-based · pace '*' = all tokens · compute ${board.meta.compute_ms}ms`, { fg: t.dim, bg });
|
|
89
|
+
footer(c, [['↑↓', 'move'], ['w', win === '1h' ? '→24h' : '→1h'], ['M', 'popup'], ['esc', 'back'], ['q', 'quit']], t.name);
|
|
90
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// dexterm — New pairs feed (pump.fun / DBC / Bonk launches, bonding progress, risk flags) + spotlight.
|
|
2
|
+
// Cyberspace styling (task-1297): a pair younger than a minute gets the magenta NEW treatment (its
|
|
3
|
+
// arrival fires the glitch via pulse in the loop); rain zones in the sub-tab gap, the gutter and the
|
|
4
|
+
// spotlight's blank middle; ▲/▼ on directions; the claude one-liner in coral.
|
|
5
|
+
import { Canvas, table, sparkline, sparkArea, heatPct, launchpadBadge, bar, badge, statGrid, ratioBar, fmtUsd, fmtPrice, fmtAge, fmtNum, fmtPctDir, header, footer, breakpoint, mix, isCalm, } from '../../../tui/src/index.js';
|
|
6
|
+
import { headerModel } from "../state.js";
|
|
7
|
+
import { renderNewPairsLive } from "../live/screens/newpairs-live.js";
|
|
8
|
+
import { layoutFor } from "../custom.js";
|
|
9
|
+
import { priceCell, priceModeOf, MCAP_FALLBACK_NOTE } from "../display.js";
|
|
10
|
+
export function renderNewPairs(c, state) {
|
|
11
|
+
if (state.live && state.screener.source !== 'fixtures')
|
|
12
|
+
return renderNewPairsLive(c, state, state.live.newpairs); // task-1298: live data path
|
|
13
|
+
const t = c.theme;
|
|
14
|
+
const bp = breakpoint(c.w);
|
|
15
|
+
header(c, headerModel(state));
|
|
16
|
+
const top = 2, bottom = c.h - 1;
|
|
17
|
+
const sideW = layoutFor(state.custom, c.w, c.h).pairSpotlightW; // task-1358: from the workspace layout (46 at wide by default)
|
|
18
|
+
const tableW = c.w - sideW - (sideW ? 1 : 0);
|
|
19
|
+
const sel = state.selected || 0;
|
|
20
|
+
const pairs = state.fx.newPairs;
|
|
21
|
+
// sub-tabs
|
|
22
|
+
const fy = top;
|
|
23
|
+
let x = 1;
|
|
24
|
+
const tabs = [['New', pairs.length], ['Graduating', 4], ['Migrated 1h', 7], ['Trenches', 3]];
|
|
25
|
+
for (const [lab, n] of tabs) {
|
|
26
|
+
const active = lab === 'New';
|
|
27
|
+
x = c.text(x, fy, ` ${lab} `, active ? { fg: t.borderFocus, bg: t.tabActiveBg, b: true } : { fg: t.muted, bg: t.bg });
|
|
28
|
+
x = c.text(x, fy, `${n} `, { fg: active ? t.accent : t.dim, bg: active ? t.tabActiveBg : t.bg }) + 1;
|
|
29
|
+
}
|
|
30
|
+
x = c.text(x + 2, fy, 'min liq 5K · dev < 5% · no mint auth', { fg: t.dim, bg: t.bg, i: true }, tableW - x - 4);
|
|
31
|
+
c.rainZone(x + 1, fy, Math.max(0, tableW - x - 2), 1, 1);
|
|
32
|
+
const box = c.box(0, top + 1, tableW, bottom - top - 1, { title: 'NEW PAIRS', caption: 'ws: pumpportal newToken · jup tokens/v2/recent' });
|
|
33
|
+
const fresh = (r) => r.ageMin < 1;
|
|
34
|
+
const cols = [
|
|
35
|
+
{ key: 'age', label: 'AGE', w: 5, align: 'right', prio: 0, render: (cv, x, y, w, r, bg) => {
|
|
36
|
+
if (fresh(r)) {
|
|
37
|
+
cv.fill(x, y, w, 1, ' ', { bg: mix(bg, t.pump, 0.35) });
|
|
38
|
+
cv.textR(x, y, w, 'NEW', { fg: '#ffffff', bg: mix(bg, t.pump, 0.35), b: true });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
cv.textR(x, y, w, fmtAge(r.ageMin), { fg: r.ageMin < 2 ? t.accent : t.muted, bg, b: r.ageMin < 2 });
|
|
42
|
+
} },
|
|
43
|
+
{ key: 'token', label: 'TOKEN', w: bp === 'compact' ? 12 : 20, prio: 0, flex: bp !== 'compact', render: (cv, x, y, w, r, bg, m) => {
|
|
44
|
+
const cx = cv.text(x, y, r.symbol, { fg: fresh(r) ? t.pump : m.selected ? t.borderFocus : t.text, bg, b: true }, Math.min(10, w));
|
|
45
|
+
if (w > 14)
|
|
46
|
+
cv.text(cx + 1, y, r.name, { fg: t.muted, bg }, w - (cx - x) - 1);
|
|
47
|
+
} },
|
|
48
|
+
{ key: 'lp', label: 'LAUNCH', w: 6, prio: 1, render: (cv, x, y, _w, r, bg) => { launchpadBadge(cv, x, y, r.launchpad, { on: bg }); } },
|
|
49
|
+
{ key: 'bond', label: 'BONDING', w: 14, prio: 1, render: (cv, x, y, w, r, bg) => {
|
|
50
|
+
if (r.bonding == null) {
|
|
51
|
+
cv.text(x, y, 'migrated', { fg: t.dim, bg });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
bar(cv, x, y, w - 5, r.bonding, { bg, color: r.bonding > 80 ? t.warn : t.pump });
|
|
55
|
+
cv.textR(x + w - 4, y, 4, `${Math.round(r.bonding)}%`, { fg: r.bonding > 80 ? t.warn : t.muted, bg });
|
|
56
|
+
} },
|
|
57
|
+
{ key: 'mcap', label: 'MCAP', w: 8, align: 'right', prio: 0, render: (cv, x, y, w, r, bg) => { cv.textR(x, y, w, fmtUsd(r.mcap, { digits: 1 }), { fg: t.text, bg }); } },
|
|
58
|
+
{ key: 'liq', label: 'LIQ', w: 7, align: 'right', prio: 2, render: (cv, x, y, w, r, bg) => { cv.textR(x, y, w, fmtUsd(r.liq, { digits: 1 }), { fg: t.text, bg }); } },
|
|
59
|
+
{ key: 'm5', label: '5M', w: 7, align: 'right', prio: 0, render: (cv, x, y, w, r, bg) => heatPct(cv, x, y, w, r.chg.m5, bg) },
|
|
60
|
+
{ key: 'h1', label: '1H', w: 7, align: 'right', prio: 3, render: (cv, x, y, w, r, bg) => heatPct(cv, x, y, w, r.chg.h1, bg) },
|
|
61
|
+
{ key: 'holders', label: 'HLD', w: 5, align: 'right', prio: 2, render: (cv, x, y, w, r, bg) => { cv.textR(x, y, w, fmtNum(r.holders), { fg: t.text, bg }); } },
|
|
62
|
+
{ key: 'bs', label: 'B/S 5M', w: 8, prio: 4, render: (cv, x, y, w, r, bg) => ratioBar(cv, x, y, w, Math.max(1, r.buys % 37), Math.max(1, r.sells % 29), { bg }) },
|
|
63
|
+
{ key: 'dev', label: 'DEV', w: 5, align: 'right', prio: 3, render: (cv, x, y, w, r, bg) => { cv.textR(x, y, w, `${r.dev.toFixed(1)}%`, { fg: r.dev > 5 ? t.down : t.muted, bg }); } },
|
|
64
|
+
{ key: 'snipe', label: 'SNIPE', w: 5, align: 'right', prio: 4, render: (cv, x, y, w, r, bg) => { cv.textR(x, y, w, String(r.snipers), { fg: r.snipers > 10 ? t.down : r.snipers > 4 ? t.warn : t.muted, bg }); } },
|
|
65
|
+
{ key: 'bund', label: 'BUNDLE', w: 6, align: 'right', prio: 4, render: (cv, x, y, w, r, bg) => { cv.textR(x, y, w, `${r.bundled.toFixed(0)}%`, { fg: r.bundled > 15 ? t.down : t.muted, bg }); } },
|
|
66
|
+
{ key: 'spark', label: 'TREND', w: 12, prio: 2, render: (cv, x, y, w, r, bg) => sparkline(cv, x, y, w, r.spark, { bg }) },
|
|
67
|
+
{ key: 'flags', label: 'RISK', w: 13, prio: 5, render: (cv, x, y, w, r, bg) => {
|
|
68
|
+
let cx = x;
|
|
69
|
+
for (const f of r.flags.slice(0, 2)) {
|
|
70
|
+
if (cx + f.length + 2 > x + w)
|
|
71
|
+
break;
|
|
72
|
+
cx = badge(cv, cx, y, f, f === 'MINT' || f === 'FREEZE' ? t.down : t.warn, { on: bg }) + 1;
|
|
73
|
+
}
|
|
74
|
+
if (!r.flags.length)
|
|
75
|
+
cv.text(x, y, '✓ clean', { fg: mix(bg, t.up, 0.7), bg });
|
|
76
|
+
} },
|
|
77
|
+
];
|
|
78
|
+
table(c, box.x + 1, box.y, box.w - 2, box.h, { columns: cols, rows: pairs, selected: sel, scroll: 0, bg: box.bg });
|
|
79
|
+
if (sideW) {
|
|
80
|
+
c.rainZone(tableW, top, 1, bottom - top, 1);
|
|
81
|
+
pairSpotlight(c, c.w - sideW, top, sideW, bottom - top, pairs[sel], priceModeOf(state));
|
|
82
|
+
}
|
|
83
|
+
// task-1388: `g` opens the go-to prompt here too (keymap + the ? overlay list it); the footer is kept
|
|
84
|
+
// byte-identical to prototype-v2 (snapshot parity), so the hint lives in `?` and on the screener footer.
|
|
85
|
+
footer(c, [['j/k', 'move'], ['⏎', 'open'], ['p', 'pause feed'], ['f', 'filters'], ['w', 'watch'], ['a', 'ask claude'], ['q', 'quit']], state.theme.name, isCalm(state.vfx));
|
|
86
|
+
}
|
|
87
|
+
function pairSpotlight(c, x, y, w, h, tk, mode = 'price') {
|
|
88
|
+
const t = c.theme;
|
|
89
|
+
const box = c.box(x, y, w, h, { title: `$${tk.symbol}`, caption: `${fmtAge(tk.ageMin)} old`, focus: true });
|
|
90
|
+
const bg = box.bg;
|
|
91
|
+
let cy = box.y;
|
|
92
|
+
c.text(box.x + 1, cy, tk.name, { fg: t.muted, bg }, box.w - 2);
|
|
93
|
+
launchpadBadge(c, box.x + box.w - 8, cy, tk.launchpad, { on: bg });
|
|
94
|
+
cy += 1;
|
|
95
|
+
// task-1366: the headline follows the price mode (mcap, then the price beside it)
|
|
96
|
+
const head = priceCell(mode, tk.price, tk.mcap);
|
|
97
|
+
let cx = c.text(box.x + 1, cy, head.text, { fg: head.fallback ? t.warn : t.text, bg, b: true });
|
|
98
|
+
if (head.kind === 'mcap')
|
|
99
|
+
cx = c.text(cx + 1, cy, `(${fmtPrice(tk.price)})`, { fg: t.dim, bg });
|
|
100
|
+
c.text(cx + 1, cy, fmtPctDir(tk.chg.h1), { fg: tk.chg.h1 >= 0 ? t.up : t.down, bg });
|
|
101
|
+
if (head.fallback)
|
|
102
|
+
c.text(box.x + 1, cy + 1, MCAP_FALLBACK_NOTE, { fg: t.warn, bg, i: true }, box.w - 2);
|
|
103
|
+
cy += 2;
|
|
104
|
+
if (tk.bonding != null) {
|
|
105
|
+
c.text(box.x + 1, cy, 'BONDING CURVE', { fg: t.dim, bg });
|
|
106
|
+
c.textR(box.x + 1, cy, box.w - 2, `${Math.round(tk.bonding)}% → PumpSwap at $69K`, { fg: t.muted, bg });
|
|
107
|
+
cy += 1;
|
|
108
|
+
bar(c, box.x + 1, cy, box.w - 2, tk.bonding, { bg, color: tk.bonding > 80 ? t.warn : t.pump });
|
|
109
|
+
cy += 2;
|
|
110
|
+
}
|
|
111
|
+
sparkArea(c, box.x + 1, cy, box.w - 2, 4, tk.spark, { bg });
|
|
112
|
+
cy += 5;
|
|
113
|
+
cy += statGrid(c, box.x + 1, cy, box.w - 2, [
|
|
114
|
+
{ label: 'mcap', value: fmtUsd(tk.mcap, { digits: 1 }) },
|
|
115
|
+
{ label: 'liquidity', value: fmtUsd(tk.liq, { digits: 1 }) },
|
|
116
|
+
{ label: 'holders', value: fmtNum(tk.holders) },
|
|
117
|
+
{ label: 'vol since launch', value: fmtUsd(tk.vol24, { digits: 1 }) },
|
|
118
|
+
], { bg, cols: 2 });
|
|
119
|
+
cy += 1;
|
|
120
|
+
c.text(box.x + 1, cy, 'LAUNCH FORENSICS', { fg: t.dim, bg });
|
|
121
|
+
cy += 1;
|
|
122
|
+
const rows = [
|
|
123
|
+
['Dev wallet', `${tk.dev.toFixed(1)}% · ${tk.devHistory.launches} prior · ${tk.devHistory.rugged} rugs`, tk.devHistory.rugged ? 'bad' : tk.dev > 5 ? 'warn' : 'ok'],
|
|
124
|
+
['Bundled at launch', `${tk.bundled.toFixed(0)}% across ${Math.max(1, Math.round(tk.bundled / 4))} wallets`, tk.bundled > 15 ? 'bad' : tk.bundled > 8 ? 'warn' : 'ok'],
|
|
125
|
+
['Snipers block 0-2', `${tk.snipers} wallets`, tk.snipers > 10 ? 'bad' : tk.snipers > 4 ? 'warn' : 'ok'],
|
|
126
|
+
['Top 10 holders', `${tk.top10.toFixed(1)}%`, tk.top10 > 40 ? 'bad' : tk.top10 > 25 ? 'warn' : 'ok'],
|
|
127
|
+
['Mint / freeze', tk.mintAuth || tk.freezeAuth ? 'ENABLED' : 'revoked', tk.mintAuth || tk.freezeAuth ? 'bad' : 'ok'],
|
|
128
|
+
['Socials', `${[tk.socials.x && 'X', tk.socials.tg && 'TG', tk.socials.web && 'web'].filter(Boolean).join(' ') || 'none'}${tk.socials.x ? ` · ${fmtNum(tk.xFollowers)} followers` : ''}`, tk.socials.x ? 'ok' : 'warn'],
|
|
129
|
+
];
|
|
130
|
+
for (const [label, val, lvl] of rows) {
|
|
131
|
+
if (cy >= box.y + box.h - 6)
|
|
132
|
+
break;
|
|
133
|
+
const color = lvl === 'ok' ? t.up : lvl === 'warn' ? t.warn : t.down;
|
|
134
|
+
c.text(box.x + 1, cy, lvl === 'ok' ? '●' : lvl === 'warn' ? '◐' : '○', { fg: color, bg });
|
|
135
|
+
c.text(box.x + 3, cy, label, { fg: t.muted, bg });
|
|
136
|
+
cy += 1;
|
|
137
|
+
c.text(box.x + 3, cy, val, { fg: lvl === 'ok' ? t.text : color, bg }, box.w - 4);
|
|
138
|
+
cy += 1;
|
|
139
|
+
}
|
|
140
|
+
const ay = box.y + box.h - 4;
|
|
141
|
+
if (ay - 2 > cy)
|
|
142
|
+
c.rainZone(box.x + 1, cy, box.w - 2, ay - 2 - cy, 2);
|
|
143
|
+
c.hline(box.x, ay - 1, box.w, { fg: mix(t.border, t.ai, 0.4), bg, soft: true });
|
|
144
|
+
c.text(box.x + 1, ay, '✦ claude', { fg: t.ai, bg, b: true });
|
|
145
|
+
c.heading(box.x + 1, ay, 8);
|
|
146
|
+
const good = rows.filter((r) => r[2] === 'ok').length;
|
|
147
|
+
const line = good >= 5 ? 'clean launch, watch holder velocity for 10m' : good >= 3 ? 'mixed: bundled supply is the risk here' : 'skip: dev + bundle pattern matches prior rugs';
|
|
148
|
+
c.text(box.x + 1, ay + 1, line, { fg: t.ai, bg }, box.w - 2);
|
|
149
|
+
c.text(box.x + 1, ay + 2, `${good}/${rows.length} checks pass · a → full analysis`, { fg: t.dim, bg }, box.w - 2);
|
|
150
|
+
}
|