dexterm-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +379 -0
- package/dist/packages/agents/src/executor.js +177 -0
- package/dist/packages/agents/src/gate.js +176 -0
- package/dist/packages/agents/src/identity.js +179 -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 +114 -0
- package/dist/packages/agents/src/tools.js +69 -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 +67 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -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 +303 -0
- package/dist/packages/cli/src/agent-roster.js +26 -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 +149 -0
- package/dist/packages/cli/src/boot-state.js +62 -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 +361 -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 +509 -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 +5583 -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 +339 -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/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/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/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 +515 -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/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 +208 -0
- package/dist/packages/data/src/intel/helius-history.js +59 -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/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 +170 -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 +247 -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 +849 -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 +19 -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/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 +15 -0
- package/dist/packages/tui/src/keymap.js +355 -0
- package/dist/packages/tui/src/layout.js +11 -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 +395 -0
- package/dist/packages/voice/src/doctor.js +63 -0
- package/dist/packages/voice/src/engines.js +44 -0
- package/dist/packages/voice/src/index.js +16 -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 +102 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/say.js +96 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +42 -4
- package/README.md +0 -3
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// dexterm — the per-token SOCIAL panel + AI SUMMARY box (task-1330; INTEGRATIONS-DESIGN §5.2, mockups
|
|
2
|
+
// social-panel-{176,120,90}.txt). Drawn by both token renderers (fixture + live) in place of the ON-CHAIN /
|
|
3
|
+
// HOLDERS band when `state.tokenSocial` is on. Every post text here has been through the ingest firewall; the
|
|
4
|
+
// table clips to the cell width, so a post can never overflow its row or carry an escape sequence.
|
|
5
|
+
//
|
|
6
|
+
// Columns by breakpoint (design §5.2): 176 age/src/author/tier/cred/vel/post/♥/⟳/dup/flag · 120 drops
|
|
7
|
+
// tier/vel/⟳/dup · 90 age/author/cred/post/flag.
|
|
8
|
+
import { Canvas, table, badge, breakpoint, mix, wrap, fmtAgeSec } from '../../../tui/src/index.js';
|
|
9
|
+
import { fmtAgeShort, buildSummaryWindow } from '../../../data/src/index.js';
|
|
10
|
+
import { socialOf, panelPosts, hiddenCounts, fmtVelocity, velocityMarks, TIER_LABEL, platformLabel, flagLabel, postText, authorRate, sourcesLine, xBadge } from "../social/view.js";
|
|
11
|
+
export const SUMMARY_BOX_H = 6;
|
|
12
|
+
/** Draw the SOCIAL table + AI SUMMARY into [x, x+w) × [y, y+h). Returns the rows drawn. */
|
|
13
|
+
export function renderSocialPanel(c, x, y, w, h, state, opts) {
|
|
14
|
+
const t = c.theme;
|
|
15
|
+
const bp = breakpoint(c.w);
|
|
16
|
+
const feed = socialOf(state);
|
|
17
|
+
const { mint, symbol, now } = opts;
|
|
18
|
+
const sumH = h >= 14 ? SUMMARY_BOX_H : 0;
|
|
19
|
+
const listH = h - sumH;
|
|
20
|
+
if (!feed) {
|
|
21
|
+
const box = c.box(x, y, w, listH, { title: `SOCIAL · $${symbol}`, caption: 'no social feed in this mode' });
|
|
22
|
+
c.textC(box.x, box.y + 1, box.w, 'social layer needs live or fixture mode', { fg: t.muted, bg: box.bg, i: true });
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
const row = feed.trendRows().find((r) => r.mint === mint) ?? null;
|
|
26
|
+
const posts = panelPosts(state, mint);
|
|
27
|
+
const hidden = hiddenCounts(feed, mint);
|
|
28
|
+
const filter = state.socialFilter ?? 'all';
|
|
29
|
+
const pph = row ? `${row.postsPerHour.toLocaleString('en-US')} posts/1h` : `${posts.length} posts`;
|
|
30
|
+
const vel = row && row.velocity !== null ? ` (${row.velocity >= 1 ? '▲' : '▼'}${fmtVelocity(row.velocity, row.velocityBasis)} vs 24h mean)` : row ? ' (new)' : '';
|
|
31
|
+
const kols = row ? ` · ${row.kolCount} KOL${row.kolCount === 1 ? '' : 's'}` : '';
|
|
32
|
+
const hid = hidden.spam ? ` · ${hidden.spam} hidden as spam` : '';
|
|
33
|
+
const inj = hidden.injection ? ` · ${hidden.injection} withheld (firewall)` : '';
|
|
34
|
+
const only = state.socialAuthor ? ` · @${state.socialAuthor} only` : '';
|
|
35
|
+
const title = `SOCIAL · $${symbol} · ${pph}${vel}${kols}${hid}${inj}${only} · ${xBadge(feed)}`;
|
|
36
|
+
const cap = `${filter === 'all' ? '' : `filter ${filter} · `}${state.socialShowSpam ? 'x=hide spam' : 'x=show hidden'} · f=filter · K=KOL lists · a=summary`;
|
|
37
|
+
const box = c.box(x, y, w, listH, { title, caption: cap });
|
|
38
|
+
const bg = box.bg;
|
|
39
|
+
const age = (p) => fmtAgeSec(Math.max(0, Math.round((now - p.t) / 1000)));
|
|
40
|
+
const credOf = (p) => feed.credibilityOfPost(p);
|
|
41
|
+
const credStyle = (n) => ({ fg: n >= 70 ? t.up : n >= 40 ? t.text : n >= 20 ? t.muted : t.down, bg });
|
|
42
|
+
const authorFg = (p) => (p.author.tier === 'kol-a' || p.author.tier === 'kol-b' ? t.accent : p.author.tier === 'src' ? t.muted : p.author.tier === 'spam' ? t.down : p.author.tier === 'own' ? t.ai : t.text);
|
|
43
|
+
const colAge = { key: 'age', label: 'age', w: 4, prio: 0, render: (cv, cx, cy, cw, p, rb) => { cv.text(cx, cy, age(p), { fg: t.dim, bg: rb }, cw); } };
|
|
44
|
+
const colSrc = { key: 'src', label: 'src', w: 5, prio: 1, render: (cv, cx, cy, cw, p, rb) => { cv.text(cx, cy, platformLabel(p.platform), { fg: p.platform === 'chain' ? t.up : t.muted, bg: rb }, cw); } };
|
|
45
|
+
const colAuthor = { key: 'author', label: 'author', w: bp === 'compact' ? 13 : 16, prio: 0, render: (cv, cx, cy, cw, p, rb) => { cv.text(cx, cy, p.author.handle, { fg: authorFg(p), bg: rb, b: p.author.tier === 'kol-a' }, cw); } };
|
|
46
|
+
const colTier = { key: 'tier', label: 'tier', w: 5, prio: 2, render: (cv, cx, cy, cw, p, rb) => { cv.text(cx, cy, TIER_LABEL[p.author.tier], { fg: p.author.tier === 'spam' ? t.down : p.author.tier.startsWith('kol') ? t.accent : t.muted, bg: rb }, cw); } };
|
|
47
|
+
const colCred = { key: 'cred', label: bp === 'compact' ? 'cr' : 'cred', w: bp === 'compact' ? 3 : 4, align: 'right', prio: 0, render: (cv, cx, cy, cw, p, rb) => { const n = credOf(p); cv.textR(cx, cy, cw, String(n), { ...credStyle(n), bg: rb }); } };
|
|
48
|
+
const colVel = { key: 'vel', label: 'vel', w: 3, prio: 3, render: (cv, cx, cy, cw, p, rb) => { const n = authorRate(feed, mint, p.author.handle, now); cv.text(cx, cy, n >= 7 ? '▲▲▲' : n >= 4 ? '▲▲' : n >= 2 ? '▲' : '·', { fg: n >= 4 ? t.warn : t.muted, bg: rb }, cw); } };
|
|
49
|
+
const colPost = { key: 'post', label: bp === 'compact' ? 'post' : 'post (firewalled, truncated)', w: 24, prio: 0, flex: true, render: (cv, cx, cy, cw, p, rb) => {
|
|
50
|
+
const txt = postText(p);
|
|
51
|
+
const fg = p.collapsed ? t.dim : p.flags.includes('injection') ? t.down : p.platform === 'chain' ? t.up : p.platform === 'news' ? t.muted : t.text;
|
|
52
|
+
cv.text(cx, cy, txt, { fg, bg: rb, i: !!p.collapsed }, cw);
|
|
53
|
+
} };
|
|
54
|
+
const colLikes = { key: 'likes', label: '♥', w: 4, align: 'right', prio: 1, render: (cv, cx, cy, cw, p, rb) => { cv.textR(cx, cy, cw, p.platform === 'x' || p.platform === 'telegram' || p.platform === 'discord' ? String(p.metrics.likes) : '', { fg: t.muted, bg: rb }); } };
|
|
55
|
+
const colReposts = { key: 'reposts', label: '⟳', w: 4, align: 'right', prio: 3, render: (cv, cx, cy, cw, p, rb) => { cv.textR(cx, cy, cw, p.platform === 'x' ? String(p.metrics.reposts) : '', { fg: t.muted, bg: rb }); } };
|
|
56
|
+
const colDup = { key: 'dup', label: 'dup', w: 3, align: 'right', prio: 2, render: (cv, cx, cy, cw, p, rb) => { cv.textR(cx, cy, cw, p.dupCount > 1 ? String(p.dupCount) : '', { fg: t.muted, bg: rb }); } };
|
|
57
|
+
const colFlag = { key: 'flag', label: bp === 'compact' ? 'fl' : 'flag', w: 5, prio: 0, render: (cv, cx, cy, cw, p, rb) => { const f = flagLabel(p); if (f)
|
|
58
|
+
badge(cv, cx, cy, f.slice(0, cw), f === 'INJ' || f === 'SPAM' || f === 'SQUAT' ? t.down : t.warn, { on: rb }); } };
|
|
59
|
+
const cols = bp === 'compact'
|
|
60
|
+
? [colAge, colAuthor, colCred, colPost, colFlag]
|
|
61
|
+
: bp === 'standard'
|
|
62
|
+
? [colAge, colSrc, colAuthor, colCred, colPost, colLikes, colFlag]
|
|
63
|
+
: [colAge, colSrc, colAuthor, colTier, colCred, colVel, colPost, colLikes, colReposts, colDup, colFlag];
|
|
64
|
+
if (posts.length === 0) {
|
|
65
|
+
const xs = feed.status();
|
|
66
|
+
const why = xs.xState === 'logged-out' ? 'x-stream ○ logged out · dexterm x login (your own X app) · keyless sources: news + on-chain rows'
|
|
67
|
+
: xs.xState === 'ok' ? 'x-stream ● logged in · nothing matched yet (search runs every 60 s for the open token)'
|
|
68
|
+
: `${xBadge(feed)} · ${xs.xDetail} · keyless sources: news + on-chain rows`;
|
|
69
|
+
c.textC(box.x, box.y + 1, box.w, `no posts yet · ${why}`, { fg: xs.xState === 'ok' || xs.xState === 'logged-out' ? t.muted : t.warn, bg, i: true });
|
|
70
|
+
c.text(box.x + 1, box.y, 'age author cred post', { fg: t.dim, bg });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
table(c, box.x + 1, box.y, box.w - 2, box.h, { columns: cols, rows: posts, selected: -1, bg, zebra: false });
|
|
74
|
+
}
|
|
75
|
+
if (sumH)
|
|
76
|
+
renderSummaryBox(c, x, y + listH, w, sumH, state, feed, opts, row?.mint ?? mint, posts.length);
|
|
77
|
+
return posts.length;
|
|
78
|
+
}
|
|
79
|
+
function renderSummaryBox(c, x, y, w, h, state, feed, opts, mint, shown) {
|
|
80
|
+
const t = c.theme;
|
|
81
|
+
const border = mix(t.border, t.ai, 0.35);
|
|
82
|
+
const box = c.box(x, y, w, h, { title: '✦ AI SUMMARY', titleFg: t.ai, caption: 'model output, not facts', captionFg: t.dim, border });
|
|
83
|
+
const bg = box.bg;
|
|
84
|
+
const label = `✦ analyst · model output, not facts · sources: ${sourcesLine(feed)}`;
|
|
85
|
+
const win = buildSummaryWindow({ mint, symbol: opts.symbol, posts: feed.postsFor(mint, { includeInjection: true, includeSpam: true, includeHidden: true }), row: null, news: feed.newsFor(mint), ages: feed.ages(), now: opts.now, max: 40 });
|
|
86
|
+
const cached = state.socialSummaries?.get(mint) ?? null;
|
|
87
|
+
const lines = [];
|
|
88
|
+
if (cached) {
|
|
89
|
+
lines.push(`${label} · ${fmtAgeShort(opts.now - cached.at)} ago`);
|
|
90
|
+
lines.push(...cached.text.split('\n'));
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
lines.push(label);
|
|
94
|
+
const withheld = `window: ${win.blocks.length} firewalled block${win.blocks.length === 1 ? '' : 's'} (≤ 40 newest) · withheld ${win.withheld.injection} instruction-like, ${win.withheld.spam} spam, ${win.withheld.squat} squat, ${win.withheld.hidden} near-dupes · ${shown} posts in 24 h`;
|
|
95
|
+
lines.push(withheld);
|
|
96
|
+
lines.push(state.live ? 'press a to ask the analyst for a ≤4-line read of this window (one claude turn, rate-limited 1 per 2 min); the reply lands in the analyst panel.' : 'fixtures: the analyst summary runs only in live mode (the window above is what it would receive — never the raw posts).');
|
|
97
|
+
}
|
|
98
|
+
let cy = box.y;
|
|
99
|
+
for (const ln of lines) {
|
|
100
|
+
for (const part of wrap(ln, box.w - 2)) {
|
|
101
|
+
if (cy >= box.y + box.h)
|
|
102
|
+
return;
|
|
103
|
+
c.text(box.x + 1, cy, part, { fg: cy === box.y ? t.ai : t.muted, bg, i: cy !== box.y }, box.w - 2);
|
|
104
|
+
cy += 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// dexterm — trade ticket screen (task-1310 T0 PAPER; task-1328 T2 LIVE). PAPER: a simulated buy/sell
|
|
2
|
+
// priced from a live keyless Jupiter quote, every frame labelled PAPER. LIVE: the same ticket in LIVE
|
|
3
|
+
// mode — ⏎ asks the SIGNER PROCESS to quote → build → decode → simulate and shows the PLAN (what will be
|
|
4
|
+
// signed, in/out, impact as a percent, fee, route, the signer's caps verdict); Y is the keypress; the
|
|
5
|
+
// signer then opens Phantom. The banner's network comes from the signer's own status (classified from
|
|
6
|
+
// the RPC it talks to) — never from a flag the UI holds, so the label cannot disagree with the
|
|
7
|
+
// connection. Pure renderer: the state machine + bindings live elsewhere; nothing here signs.
|
|
8
|
+
import { Canvas, badge, header, footer, breakpoint, mix, isCalm, fmtPrice, fmtPriceBare, fmtAmount, fmtPct, } from '../../../tui/src/index.js';
|
|
9
|
+
import { lamportsToSol, rawToTokens } from '../../../data/src/index.js';
|
|
10
|
+
import { headerModel } from "../state.js";
|
|
11
|
+
import { slippageBps, simFailureHint } from "../trade.js";
|
|
12
|
+
function fmtSol(sol, digits = 4) {
|
|
13
|
+
const a = Math.abs(sol);
|
|
14
|
+
return `${a.toFixed(digits)} SOL`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* task-1368 item e: a token quantity for the ticket. `fmtAmount` keeps small amounts (0.0144 AVGOx, never "0");
|
|
18
|
+
* when the token's decimals are still unknown the number would be a guess, so say that and show the raw units.
|
|
19
|
+
*/
|
|
20
|
+
function tokenAmount(raw, decimals, symbol, decimalsKnown) {
|
|
21
|
+
if (!decimalsKnown)
|
|
22
|
+
return `${symbol}: decimals unknown yet (raw ${raw.toString()})`;
|
|
23
|
+
return `${fmtAmount(rawToTokens(raw, decimals))} ${symbol}`;
|
|
24
|
+
}
|
|
25
|
+
/** Word-wrap `text` into at most `maxLines` lines of `width` columns; the last line is cut with an ellipsis. */
|
|
26
|
+
function wrapLines(text, width, maxLines) {
|
|
27
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
28
|
+
const lines = [];
|
|
29
|
+
let cur = '';
|
|
30
|
+
for (const w of words) {
|
|
31
|
+
const next = cur ? `${cur} ${w}` : w;
|
|
32
|
+
if (next.length <= width) {
|
|
33
|
+
cur = next;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (cur)
|
|
37
|
+
lines.push(cur);
|
|
38
|
+
cur = w.length > width ? `${w.slice(0, Math.max(1, width - 1))}…` : w;
|
|
39
|
+
if (lines.length === maxLines)
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (cur && lines.length < maxLines)
|
|
43
|
+
lines.push(cur);
|
|
44
|
+
if (lines.length > maxLines)
|
|
45
|
+
lines.length = maxLines;
|
|
46
|
+
const joined = lines.join(' ');
|
|
47
|
+
if (joined.length < text.length && lines.length === maxLines) {
|
|
48
|
+
const last = lines[maxLines - 1];
|
|
49
|
+
lines[maxLines - 1] = last.length >= width - 1 ? `${last.slice(0, Math.max(1, width - 1))}…` : `${last}…`;
|
|
50
|
+
}
|
|
51
|
+
return lines;
|
|
52
|
+
}
|
|
53
|
+
export function renderTicket(c, state) {
|
|
54
|
+
const t = c.theme;
|
|
55
|
+
header(c, headerModel(state));
|
|
56
|
+
const bp = breakpoint(c.w);
|
|
57
|
+
// Live binding is the source of truth; the seeded demo view backs fixture/one-shot frames.
|
|
58
|
+
const view = (state.live?.ticket ? state.live.ticket.view() : state.ticket) ?? null;
|
|
59
|
+
const live = view?.live ?? null;
|
|
60
|
+
const isLive = !!live;
|
|
61
|
+
// Always-on banner strip (never hidden, any state). PAPER = warn tint; LIVE = the signer's network.
|
|
62
|
+
const bannerY = 2;
|
|
63
|
+
const net = live?.signer?.network ?? null; // the SIGNER's classified network, or unknown
|
|
64
|
+
const mainnet = net === 'mainnet';
|
|
65
|
+
const bannerColor = !isLive ? t.warn : mainnet ? t.down : t.up;
|
|
66
|
+
c.fill(0, bannerY, c.w, 1, ' ', { bg: mix(t.bg, bannerColor, mainnet ? 0.3 : 0.16) });
|
|
67
|
+
const banner = !isLive
|
|
68
|
+
? ' ◈ PAPER TRADING — simulated · no real orders · no keys · no signing '
|
|
69
|
+
: net === null
|
|
70
|
+
? ' ◉ LIVE — signer not reachable · nothing can be signed '
|
|
71
|
+
: mainnet
|
|
72
|
+
? ` ⚠ LIVE · MAINNET — REAL FUNDS · caps $${live?.signer?.caps.perTradeUsd ?? '?'}/trade $${live?.signer?.caps.dailyUsd ?? '?'}/day · Phantom signs · dexterm holds no key `
|
|
73
|
+
: ` ◉ LIVE · DEVNET — real signing via Phantom · devnet funds only · dexterm holds no key `;
|
|
74
|
+
c.text(2, bannerY, banner, { fg: bannerColor, bg: mix(t.bg, bannerColor, mainnet ? 0.3 : 0.16), b: true });
|
|
75
|
+
const linked = state.live?.linkedWallet;
|
|
76
|
+
if (linked)
|
|
77
|
+
c.textR(2, bannerY, c.w - 3, `linked: ${linked.publicKey.slice(0, 4)}…${linked.publicKey.slice(-4)} `, { fg: t.muted, bg: mix(t.bg, bannerColor, mainnet ? 0.3 : 0.16) });
|
|
78
|
+
c.noRain(0, bannerY, c.w, 1);
|
|
79
|
+
if (live && (live.signer?.panic || live.signer?.ledgerLocked)) {
|
|
80
|
+
c.fill(0, bannerY + 1, c.w, 1, ' ', { bg: mix(t.bg, t.down, 0.3) });
|
|
81
|
+
c.text(2, bannerY + 1, ' ⛔ PANIC — live signing is disabled · re-enable only from a shell: dexterm wallet resume ', { fg: t.down, bg: mix(t.bg, t.down, 0.3), b: true });
|
|
82
|
+
c.noRain(0, bannerY + 1, c.w, 1);
|
|
83
|
+
}
|
|
84
|
+
if (!view) {
|
|
85
|
+
c.text(2, 4, 'Open a trade ticket from a token screen: B buy · S sell (L on the ticket flips PAPER ⇄ LIVE)', { fg: t.muted, bg: t.bg });
|
|
86
|
+
footer(c, [['esc', 'back'], ['P', 'positions'], ['q', 'quit']], t.name, isCalm(state.vfx));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const ts = view.ticket;
|
|
90
|
+
const buy = ts.side === 'buy';
|
|
91
|
+
const sideColor = buy ? t.up : t.down;
|
|
92
|
+
const top = 4;
|
|
93
|
+
const w = Math.min(bp === 'compact' ? c.w - 2 : 78, c.w - 2);
|
|
94
|
+
const x0 = 1;
|
|
95
|
+
const h = c.h - top - 1;
|
|
96
|
+
const title = isLive ? `◉ LIVE ${live?.probe ? 'DEVNET PROBE' : `${ts.side.toUpperCase()} TICKET`}` : `◈ PAPER ${ts.side.toUpperCase()} TICKET`;
|
|
97
|
+
const caption = isLive ? `signer process · jupiter quote · phantom signs${net ? ` · ${net}` : ''}` : 'jupiter keyless quote · paper fill';
|
|
98
|
+
// task-1340: the ticket is PLAIN by construction — no heading (no chromatic split on its title) and every cell of
|
|
99
|
+
// its rect is shielded below, so no effect pass (rain, mosh, shake, scanlines, trade moments) can write a glyph, a
|
|
100
|
+
// colour or a background into it in any phase. The banner rows above it are shielded too.
|
|
101
|
+
const box = c.box(x0, top, w, h, { title, titleFg: isLive && mainnet ? t.down : sideColor, caption, captionFg: t.dim, border: mix(t.border, isLive && mainnet ? t.down : sideColor, 0.5), plain: true });
|
|
102
|
+
c.shieldRect(x0, top, w, h);
|
|
103
|
+
c.shieldRect(0, bannerY, c.w, 2);
|
|
104
|
+
const bg = box.bg;
|
|
105
|
+
const ix = box.x + 2;
|
|
106
|
+
const iw = box.w - 4;
|
|
107
|
+
let y = box.y + 1;
|
|
108
|
+
// identity + side toggle
|
|
109
|
+
let x = c.text(ix, y, `$${ts.symbol}`, { fg: t.borderFocus, bg, b: true });
|
|
110
|
+
x = c.text(x + 1, y, `${ts.mint.slice(0, 4)}…${ts.mint.slice(-4)}`, { fg: t.dim, bg });
|
|
111
|
+
x = badge(c, x + 2, y, buy ? '▲ BUY' : '▼ SELL', sideColor, { on: bg, bold: true }) + 1;
|
|
112
|
+
x = badge(c, x + 1, y, isLive ? 'LIVE' : 'PAPER', isLive ? (mainnet ? t.down : t.up) : t.warn, { on: bg, bold: true }) + 1;
|
|
113
|
+
c.textR(ix, y, iw, ts.phase === 'editing' ? '← → flip side · L paper⇄live' : '', { fg: t.dim, bg });
|
|
114
|
+
y += 2;
|
|
115
|
+
// size field
|
|
116
|
+
if (!live?.probe) {
|
|
117
|
+
const sizeUnit = buy ? (ts.sizeUnit === 'sol' ? 'SOL' : '% of balance') : '% of position';
|
|
118
|
+
field(c, ix, y, iw, bg, 'Size', `${ts.sizeInput || '—'} ${sizeUnit}`, ts.field === 'size' && ts.phase === 'editing', t);
|
|
119
|
+
y += 1;
|
|
120
|
+
field(c, ix, y, iw, bg, 'Slippage', `${slippageBps(ts)} bps`, ts.field === 'slippage' && ts.phase === 'editing', t);
|
|
121
|
+
y += 2;
|
|
122
|
+
}
|
|
123
|
+
const showPlan = isLive && live && live.plan && (ts.phase === 'plan' || ts.phase === 'signing' || ts.phase === 'filled' || ts.phase === 'refused' || ts.phase === 'error');
|
|
124
|
+
if (showPlan && live?.plan) {
|
|
125
|
+
y = renderPlan(c, ix, y, iw, bg, live, ts.decimals, ts.symbol, buy, state.now.getTime());
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// quote block (paper warm quote; LIVE editing shows the same keyless quote as a preview)
|
|
129
|
+
c.text(ix, y, isLive ? 'PREVIEW QUOTE (keyless; the signer re-quotes on ⏎)' : 'QUOTE', { fg: t.dim, bg });
|
|
130
|
+
c.textR(ix, y, iw, view.loading ? '· fetching…' : view.quote ? `· ${Math.max(0, Math.round((view.quoteAgeMs ?? 0) / 1000))}s ago` : view.error ? '· unavailable' : '', { fg: view.error ? t.down : t.dim, bg });
|
|
131
|
+
c.hline(box.x + 1, y + 1, box.w - 2, { fg: t.border, bg, soft: true });
|
|
132
|
+
y += 2;
|
|
133
|
+
const q = view.quote;
|
|
134
|
+
const preview = view.preview;
|
|
135
|
+
const decimalsKnown = view.decimalsKnown !== false;
|
|
136
|
+
if (q && preview) {
|
|
137
|
+
const tokenAbs = buy ? preview.tokenDelta : -preview.tokenDelta;
|
|
138
|
+
const spend = buy ? `${fmtSol(lamportsToSol(-preview.solDelta))}` : tokenAmount(tokenAbs, ts.decimals, ts.symbol, decimalsKnown);
|
|
139
|
+
const recv = buy ? tokenAmount(tokenAbs, ts.decimals, ts.symbol, decimalsKnown) : `${fmtSol(lamportsToSol(preview.solDelta))}`;
|
|
140
|
+
kv(c, ix, y++, iw, bg, 'You pay', spend, t, buy ? t.down : t.text);
|
|
141
|
+
kv(c, ix, y++, iw, bg, 'You receive', recv, t, buy && !decimalsKnown ? t.warn : t.up);
|
|
142
|
+
kv(c, ix, y++, iw, bg, 'Price', decimalsKnown ? `${fmtPriceBare(preview.priceSol)} SOL${preview.priceUsd !== null ? ` · ${fmtPrice(preview.priceUsd)}` : ''}` : 'per-token price needs the decimals', t, decimalsKnown ? t.text : t.warn);
|
|
143
|
+
const impact = preview.priceImpactPct;
|
|
144
|
+
kv(c, ix, y++, iw, bg, 'Price impact', impact === null ? '—' : fmtPct(impact, { sign: false }), t, impact !== null && impact > 5 ? t.down : impact !== null && impact > 1 ? t.warn : t.muted);
|
|
145
|
+
kv(c, ix, y++, iw, bg, 'Min received', buy ? tokenAmount(q.otherAmountThreshold, ts.decimals, ts.symbol, decimalsKnown) : fmtSol(lamportsToSol(q.otherAmountThreshold)), t, t.muted);
|
|
146
|
+
kv(c, ix, y++, iw, bg, isLive ? 'Est. fee' : 'Sim fee', fmtSol(lamportsToSol(preview.feeLamports), 6), t, t.muted);
|
|
147
|
+
kv(c, ix, y++, iw, bg, 'Route', q.route.length ? q.route.join(' → ') : 'direct', t, t.accent);
|
|
148
|
+
}
|
|
149
|
+
else if (view.error) {
|
|
150
|
+
c.text(ix, y++, `⚠ ${view.error}`, { fg: t.down, bg }, iw);
|
|
151
|
+
}
|
|
152
|
+
else if (live?.probe) {
|
|
153
|
+
c.text(ix, y++, 'wrap 0.001 SOL into your own WSOL account, sync, memo, close it back — fees only', { fg: t.dim, bg }, iw);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
c.text(ix, y++, view.loading ? 'fetching a live quote…' : 'enter a size to see a quote', { fg: t.dim, bg }, iw);
|
|
157
|
+
}
|
|
158
|
+
// task-1368 items b + c: BEFORE ⏎ on a LIVE ticket — the size in USD against the signer's caps (red when the
|
|
159
|
+
// signer would refuse it, with the size that fits), and the devnet-cannot-fill-mainnet-tokens line.
|
|
160
|
+
if (isLive && live && !live.probe && ts.phase === 'editing') {
|
|
161
|
+
const pf = live.preflight ?? null;
|
|
162
|
+
if (pf) {
|
|
163
|
+
const color = pf.over ? t.down : pf.sizeUsd === null && pf.sizeSol > 0 ? t.warn : t.muted;
|
|
164
|
+
for (const line of wrapLines(`${pf.over ? '✗ ' : ''}${pf.line}`, iw, 2))
|
|
165
|
+
c.text(ix, y++, line, { fg: color, bg, b: pf.over }, iw);
|
|
166
|
+
}
|
|
167
|
+
if (live.devnetWarning)
|
|
168
|
+
for (const line of wrapLines(`⚠ ${live.devnetWarning}`, iw, 2))
|
|
169
|
+
c.text(ix, y++, line, { fg: t.warn, bg, b: true }, iw);
|
|
170
|
+
}
|
|
171
|
+
y += 1;
|
|
172
|
+
}
|
|
173
|
+
// wallet line
|
|
174
|
+
c.hline(box.x + 1, y, box.w - 2, { fg: t.border, bg, soft: true });
|
|
175
|
+
y += 1;
|
|
176
|
+
if (isLive) {
|
|
177
|
+
kv(c, ix, y++, iw, bg, 'Signer', live?.signer ? `${live.signer.network.toUpperCase()} · ${live.signer.rpcHost} · pid ${live.signer.pid}` : (live?.error ?? 'not reachable'), t, live?.signer ? t.text : t.down);
|
|
178
|
+
if (live?.signer)
|
|
179
|
+
kv(c, ix, y++, iw, bg, 'Caps', `$${live.signer.caps.perTradeUsd}/trade · $${live.signer.caps.dailyUsd}/day · spent $${live.signer.spentTodayUsd.toFixed(2)} · impact ≤ ${live.signer.caps.maxPriceImpactPct}% · ≤ ${live.signer.caps.maxSolOut} SOL out`, t, t.muted);
|
|
180
|
+
kv(c, ix, y++, iw, bg, 'Wallet', live?.linked ? `${live.linked} (Phantom signs; dexterm holds no key)` : 'none linked', t, live?.linked ? t.text : t.down);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
kv(c, ix, y++, iw, bg, 'Paper balance', fmtSol(view.balanceSol), t, t.text);
|
|
184
|
+
}
|
|
185
|
+
if (!buy || view.positionTokens > 0)
|
|
186
|
+
kv(c, ix, y++, iw, bg, `Position`, `${fmtAmount(view.positionTokens)} ${ts.symbol}${isLive ? ' (live ledger)' : ''}`, t, t.muted);
|
|
187
|
+
y += 1;
|
|
188
|
+
// gate / receipt region
|
|
189
|
+
const gy = box.y + box.h - 3;
|
|
190
|
+
c.hline(box.x, gy - 1, box.w, { fg: mix(t.border, sideColor, 0.5), bg, soft: true });
|
|
191
|
+
const tint = mix(bg, sideColor, 0.18);
|
|
192
|
+
if (ts.phase === 'confirm') {
|
|
193
|
+
c.fill(box.x + 1, gy, box.w - 2, 2, ' ', { bg: tint });
|
|
194
|
+
if (ts.typebackRequired) {
|
|
195
|
+
c.text(ix, gy, `⚠ large order — type the size (${ts.sizeInput}) then press Y:`, { fg: t.warn, bg: tint, b: true }, iw);
|
|
196
|
+
c.text(ix, gy + 1, `▌ ${ts.typeback || ''}`, { fg: t.text, bg: tint, b: true });
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
c.text(ix, gy, `CONFIRM PAPER ${ts.side.toUpperCase()} — press Y (esc cancels)`, { fg: sideColor, bg: tint, b: true }, iw);
|
|
200
|
+
}
|
|
201
|
+
if (ts.message)
|
|
202
|
+
c.text(ix, gy + 1, ts.message, { fg: t.warn, bg: tint }, iw);
|
|
203
|
+
}
|
|
204
|
+
else if (ts.phase === 'planning' && live?.retryAt) {
|
|
205
|
+
// task-1368: rate limited by Jupiter — the plan is NOT dropped; countdown + r / esc.
|
|
206
|
+
const left = Math.max(0, Math.ceil((live.retryAt - state.now.getTime()) / 1000));
|
|
207
|
+
const what = (live.retryMessage ?? 'Jupiter is rate limiting this IP').replace(/; retry in \d+s$/, '');
|
|
208
|
+
c.fill(box.x + 1, gy, box.w - 2, 2, ' ', { bg: mix(bg, t.warn, 0.18) });
|
|
209
|
+
c.text(ix, gy, `⚠ ${what}, retrying in ${left}s`, { fg: t.warn, bg: mix(bg, t.warn, 0.18), b: true }, iw);
|
|
210
|
+
c.text(ix, gy + 1, 'r = retry now · esc = cancel · the plan is kept (nothing was built or signed)', { fg: t.muted, bg: mix(bg, t.warn, 0.18) }, iw);
|
|
211
|
+
}
|
|
212
|
+
else if (ts.phase === 'planning') {
|
|
213
|
+
c.text(ix, gy, 'signer: quote → build → decode → policy → simulate… (esc cancels)', { fg: t.ai, bg, b: true }, iw);
|
|
214
|
+
}
|
|
215
|
+
else if (ts.phase === 'plan' && live?.plan) {
|
|
216
|
+
const refused = !live.plan.policy.ok || !live.plan.precheck.ok;
|
|
217
|
+
c.fill(box.x + 1, gy, box.w - 2, 2, ' ', { bg: tint });
|
|
218
|
+
if (refused) {
|
|
219
|
+
const v = !live.plan.policy.ok ? live.plan.policy : live.plan.precheck;
|
|
220
|
+
c.text(ix, gy, `✗ the signer WILL REFUSE this plan: ${v.reason ?? ''} — ${v.message ?? ''}`, { fg: t.down, bg: tint, b: true }, iw);
|
|
221
|
+
c.text(ix, gy + 1, 'esc back to editing (Y asks anyway and gets the signer\'s refusal)', { fg: t.muted, bg: tint }, iw);
|
|
222
|
+
}
|
|
223
|
+
else if (ts.typebackRequired) {
|
|
224
|
+
c.text(ix, gy, `⚠ large order — type the size (${ts.sizeInput}) then press Y to sign in Phantom:`, { fg: t.warn, bg: tint, b: true }, iw);
|
|
225
|
+
c.text(ix, gy + 1, `▌ ${ts.typeback || ''}`, { fg: t.text, bg: tint, b: true });
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
c.text(ix, gy, `${mainnet ? '⚠ REAL FUNDS · ' : ''}Y = sign this exact plan in Phantom (esc cancels · any other key cancels)`, { fg: mainnet ? t.down : sideColor, bg: tint, b: true }, iw);
|
|
229
|
+
c.text(ix, gy + 1, `the signer re-checks caps, verifies the signed bytes and re-simulates before it submits`, { fg: t.muted, bg: tint }, iw);
|
|
230
|
+
}
|
|
231
|
+
if (ts.message)
|
|
232
|
+
c.text(ix, gy + 1, ts.message, { fg: t.warn, bg: tint }, iw);
|
|
233
|
+
}
|
|
234
|
+
else if (ts.phase === 'signing') {
|
|
235
|
+
c.text(ix, gy, 'approve in Phantom (browser) — the signer verifies, re-simulates and submits…', { fg: t.ai, bg, b: true }, iw);
|
|
236
|
+
c.text(ix, gy + 1, 'nothing is sent until the signed bytes match the plan', { fg: t.muted, bg }, iw);
|
|
237
|
+
}
|
|
238
|
+
else if (ts.phase === 'filling') {
|
|
239
|
+
const line = view.rateLimited
|
|
240
|
+
? '⚠ rate limited by Jupiter — retrying… (esc cancels)'
|
|
241
|
+
: 'booking paper fill from a fresh quote… (esc cancels)';
|
|
242
|
+
c.text(ix, gy, line, { fg: view.rateLimited ? t.warn : t.ai, bg, b: true }, iw);
|
|
243
|
+
}
|
|
244
|
+
else if (ts.phase === 'filled' && isLive && live?.outcome?.kind === 'filled') {
|
|
245
|
+
const o = live.outcome;
|
|
246
|
+
const got = live.probe ? `wrap-and-unwrap confirmed · fee ${fmtSol(lamportsToSol(BigInt(o.fill.feeLamports)), 6)}` : `${fmtAmount(rawToTokens(o.fill.side === 'buy' ? BigInt(o.fill.tokenDelta) : -BigInt(o.fill.tokenDelta), o.fill.decimals))} ${o.fill.symbol} @ ${fmtPriceBare(o.fill.priceSol)} SOL`;
|
|
247
|
+
c.text(ix, gy, `✓ LIVE ${o.network.toUpperCase()} ${live.probe ? 'PROBE' : o.fill.side.toUpperCase()} confirmed — ${got}${o.lighthouseTail ? ` · +${o.lighthouseTail} Lighthouse` : ''}`, { fg: t.up, bg, b: true }, iw);
|
|
248
|
+
c.text(ix, gy + 1, `sig ${o.signature.slice(0, 12)}…${o.signature.slice(-6)} · esc=back · P=positions`, { fg: t.muted, bg }, iw);
|
|
249
|
+
}
|
|
250
|
+
else if (ts.phase === 'filled' && view.receipt) {
|
|
251
|
+
const r = view.receipt;
|
|
252
|
+
const got = r.side === 'buy' ? `${fmtAmount(rawToTokens(r.tokenDelta, r.decimals))} ${r.symbol}` : fmtSol(lamportsToSol(r.solDelta));
|
|
253
|
+
c.text(ix, gy, `✓ PAPER ${r.side.toUpperCase()} filled — ${got} @ ${fmtPriceBare(r.priceSol)} SOL`, { fg: t.up, bg, b: true }, iw);
|
|
254
|
+
const extra = r.side === 'sell' ? `realized ${r.realizedLamports >= 0n ? '+' : ''}${fmtSol(lamportsToSol(r.realizedLamports))}` : 'cost booked to position';
|
|
255
|
+
c.text(ix, gy + 1, `${extra} · esc=back to token · P=positions`, { fg: t.muted, bg }, iw);
|
|
256
|
+
}
|
|
257
|
+
else if (ts.phase === 'refused') {
|
|
258
|
+
c.text(ix, gy, `✗ REFUSED by the signer — ${ts.message ?? 'refused'}`, { fg: t.down, bg, b: true }, iw);
|
|
259
|
+
c.text(ix, gy + 1, 'nothing was signed or sent · esc=back · any key = new ticket', { fg: t.muted, bg }, iw);
|
|
260
|
+
}
|
|
261
|
+
else if (ts.phase === 'error') {
|
|
262
|
+
const unknown = live?.outcome?.kind === 'send-unknown';
|
|
263
|
+
c.text(ix, gy, `⚠ ${ts.message ?? 'order failed'}`, { fg: t.down, bg, b: true }, iw);
|
|
264
|
+
c.text(ix, gy + 1, unknown && live?.outcome?.kind === 'send-unknown' ? `sig ${live.outcome.signature.slice(0, 12)}… · DO NOT resend · check the explorer · esc=back` : 'esc=back to token · P=positions', { fg: unknown ? t.warn : t.muted, bg }, iw);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
c.text(ix, gy, isLive ? `⏎ build the plan in the signer → Y signs in Phantom · gate ${ts.gateOn ? 'ON' : 'off'}` : `⏎ review → Y confirm · gate ${ts.gateOn ? 'ON' : 'off'} (keypress required)`, { fg: t.muted, bg }, iw);
|
|
268
|
+
if (ts.message)
|
|
269
|
+
c.text(ix, gy + 1, ts.message, { fg: t.warn, bg }, iw);
|
|
270
|
+
}
|
|
271
|
+
const keys = [['⏎', isLive ? 'plan' : 'review'], ['Y', isLive ? 'sign' : 'confirm'], ['L', isLive ? 'paper' : 'live'], ['tab', 'field'], ['←→', 'side'], ['u', 'unit']];
|
|
272
|
+
if (isLive && buy && ts.phase === 'editing' && !live?.probe)
|
|
273
|
+
keys.push(['f', 'fit cap']);
|
|
274
|
+
if (ts.phase === 'planning' && live?.retryAt)
|
|
275
|
+
keys.push(['r', 'retry now']);
|
|
276
|
+
keys.push(['esc', 'cancel'], ['P', 'positions']);
|
|
277
|
+
footer(c, keys, t.name, isCalm(state.vfx));
|
|
278
|
+
}
|
|
279
|
+
/** The PLAN block: everything the signer will sign, from the signer's own view. */
|
|
280
|
+
function renderPlan(c, ix, y, iw, bg, live, decimals, symbol, buy, now) {
|
|
281
|
+
const t = c.theme;
|
|
282
|
+
const p = live.plan;
|
|
283
|
+
const left = Math.max(0, Math.round((p.expiresAt - now) / 1000));
|
|
284
|
+
c.text(ix, y, `PLAN · ${p.network.toUpperCase()} · ${p.kind === 'probe' ? 'devnet probe' : 'jupiter swap'}`, { fg: p.network === 'mainnet' ? t.down : t.up, bg, b: true });
|
|
285
|
+
c.textR(ix, y, iw, `expires in ${left}s · ${p.hash.slice(0, 8)}`, { fg: left < 15 ? t.warn : t.dim, bg });
|
|
286
|
+
c.hline(ix - 1, y + 1, iw + 2, { fg: t.border, bg, soft: true });
|
|
287
|
+
y += 2;
|
|
288
|
+
const inRaw = BigInt(p.amountInRaw), outRaw = BigInt(p.amountOutRaw), minRaw = BigInt(p.minOutRaw);
|
|
289
|
+
if (p.kind === 'probe') {
|
|
290
|
+
kv(c, ix, y++, iw, bg, 'Wraps', `${fmtSol(lamportsToSol(inRaw))} → your own WSOL account → back to you`, t, t.text);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// The plan's decimals are the ones the order was built with (the signer echoes them back).
|
|
294
|
+
const pay = buy ? fmtSol(lamportsToSol(inRaw)) : `${fmtAmount(rawToTokens(inRaw, p.decimals ?? decimals))} ${symbol}`;
|
|
295
|
+
const recv = buy ? `${fmtAmount(rawToTokens(outRaw, p.decimals ?? decimals))} ${symbol}` : fmtSol(lamportsToSol(outRaw));
|
|
296
|
+
const min = buy ? `${fmtAmount(rawToTokens(minRaw, p.decimals ?? decimals))} ${symbol}` : fmtSol(lamportsToSol(minRaw));
|
|
297
|
+
kv(c, ix, y++, iw, bg, 'You pay', pay, t, buy ? t.down : t.text);
|
|
298
|
+
kv(c, ix, y++, iw, bg, 'You receive', recv, t, t.up);
|
|
299
|
+
kv(c, ix, y++, iw, bg, 'Min received', `${min} (${p.slippageBps} bps slippage)`, t, t.muted);
|
|
300
|
+
const impact = p.priceImpactPct;
|
|
301
|
+
kv(c, ix, y++, iw, bg, 'Price impact', impact === null ? 'unknown' : fmtPct(impact, { sign: false }), t, impact !== null && impact > 3 ? t.down : impact !== null && impact > 1 ? t.warn : t.muted);
|
|
302
|
+
kv(c, ix, y++, iw, bg, 'Route', p.route.length ? p.route.join(' → ') : 'direct', t, t.accent);
|
|
303
|
+
}
|
|
304
|
+
kv(c, ix, y++, iw, bg, 'Fee', `${fmtSol(lamportsToSol(BigInt(p.feeLamports)), 6)}${p.prioritizationFeeLamports ? ` (incl. ${p.prioritizationFeeLamports} lamports priority)` : ''}`, t, t.muted);
|
|
305
|
+
kv(c, ix, y++, iw, bg, 'Notional', p.notionalUsd !== null ? `$${p.notionalUsd.toFixed(2)} (SOL $${p.solUsd.price?.toFixed(2) ?? '?'} via ${p.solUsd.sources?.join('+') ?? '?'})` : (p.network === 'mainnet' ? `unpriced — ${p.solUsd.reason ?? ''}` : 'devnet (no USD value)'), t, p.notionalUsd === null && p.network === 'mainnet' ? t.down : t.muted);
|
|
306
|
+
if (!p.sim.ran || p.sim.ok) {
|
|
307
|
+
kv(c, ix, y++, iw, bg, 'Simulation', !p.sim.ran ? 'not run' : `ok · ${p.sim.solOutLamports !== null ? `${fmtSol(lamportsToSol(BigInt(p.sim.solOutLamports)), 6)} leaves the wallet` : 'wallet delta unknown'}`, t, p.sim.ok ? t.up : t.down);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// task-1368 item f: the FULL RPC error, wrapped (it used to be cut to `invalid transaction: Tr…`), then the hint.
|
|
311
|
+
kv(c, ix, y++, iw, bg, 'Simulation', 'FAILED', t, t.down);
|
|
312
|
+
for (const line of wrapLines(p.sim.error ?? 'unknown error', iw - 2, 3))
|
|
313
|
+
c.text(ix + 2, y++, line, { fg: t.down, bg }, iw - 2);
|
|
314
|
+
const hint = simFailureHint(p, live.signer ? { perTradeUsd: live.signer.caps.perTradeUsd, dailyUsd: live.signer.caps.dailyUsd } : undefined);
|
|
315
|
+
if (hint)
|
|
316
|
+
for (const line of wrapLines(`↳ ${hint}`, iw - 2, 4))
|
|
317
|
+
c.text(ix + 2, y++, line, { fg: t.warn, bg }, iw - 2);
|
|
318
|
+
}
|
|
319
|
+
const ins = p.instructions.map((i) => `${i.program}${i.op ? `:${i.op}` : ''}`).join(' · ');
|
|
320
|
+
kv(c, ix, y++, iw, bg, 'Will sign', ins || '—', t, t.text);
|
|
321
|
+
kv(c, ix, y++, iw, bg, 'Policy', p.policy.ok ? 'swap shape ok · route bound to this plan · no fee account' : `REFUSE ${p.policy.reason}: ${p.policy.message}`, t, p.policy.ok ? t.up : t.down);
|
|
322
|
+
kv(c, ix, y++, iw, bg, 'Caps', p.precheck.ok ? 'within caps' : `REFUSE ${p.precheck.reason}: ${p.precheck.message}`, t, p.precheck.ok ? t.up : t.down);
|
|
323
|
+
return y + 1;
|
|
324
|
+
}
|
|
325
|
+
function field(c, x, y, w, bg, label, value, active, t) {
|
|
326
|
+
const lblW = 10;
|
|
327
|
+
c.text(x, y, label, { fg: t.dim, bg });
|
|
328
|
+
const vx = x + lblW;
|
|
329
|
+
if (active) {
|
|
330
|
+
c.fill(vx - 1, y, w - lblW + 1, 1, ' ', { bg: mix(t.bg, t.accent, 0.16) });
|
|
331
|
+
c.text(vx, y, value, { fg: t.text, bg: mix(t.bg, t.accent, 0.16), b: true });
|
|
332
|
+
c.put(vx - 1, y, '▌', { fg: t.accent, bg });
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
c.text(vx, y, value, { fg: t.text, bg });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function kv(c, x, y, w, bg, label, value, t, valColor) {
|
|
339
|
+
const v = value.length > w - 14 ? value.slice(0, Math.max(1, w - 15)) + '…' : value;
|
|
340
|
+
c.text(x, y, label, { fg: t.muted, bg }, Math.max(0, w - v.length - 1));
|
|
341
|
+
c.textR(x, y, w, v, { fg: valColor, bg });
|
|
342
|
+
}
|