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,183 @@
|
|
|
1
|
+
import { skeletonRows } from "./rows.js";
|
|
2
|
+
import { applyFilter, parseFilter } from "./filter.js";
|
|
3
|
+
import { DEFAULT_SORT, SORT_KEYS, sortRows, sortSpec } from "./sort.js";
|
|
4
|
+
import { PRESETS, bakedFilter, cyclePreset, presetSpec, presetViews } from "./presets.js";
|
|
5
|
+
/**
|
|
6
|
+
* Classify the live feed's current condition from the rows it produced and the errors it reported.
|
|
7
|
+
* `ok` once any row is present; otherwise connecting (no error yet), offline (a network/DNS failure),
|
|
8
|
+
* or ratelimited (an HTTP 429). 429 wins over a network error because the host did answer.
|
|
9
|
+
*/
|
|
10
|
+
export function feedCondition(sc) {
|
|
11
|
+
if (sc.rows.length > 0)
|
|
12
|
+
return { kind: 'ok', message: '' };
|
|
13
|
+
const joined = Object.values(sc.errors ?? {}).join(' ').toLowerCase();
|
|
14
|
+
if (/\b429\b|rate.?limit|too many requests/.test(joined)) {
|
|
15
|
+
return { kind: 'ratelimited', message: 'rate-limited by the data feed — backing off and retrying (press r to retry now)' };
|
|
16
|
+
}
|
|
17
|
+
if (/enotfound|econnrefused|eai_again|getaddrinfo|fetch failed|network|timeout|etimedout|socket|dns/.test(joined)) {
|
|
18
|
+
return { kind: 'offline', message: "offline — can't reach the data feed; retrying (press r to retry, or run `dexterm doctor`)" };
|
|
19
|
+
}
|
|
20
|
+
return { kind: 'connecting', message: 'connecting to the live feed…' };
|
|
21
|
+
}
|
|
22
|
+
export function makeScreenerState(source, rows = [], opts = {}) {
|
|
23
|
+
return {
|
|
24
|
+
source, rows, query: '', filter: null, sort: { ...DEFAULT_SORT }, preset: 'trending',
|
|
25
|
+
presets: opts.presets ?? presetViews(), gates: opts.gates ?? null, mode: 'table',
|
|
26
|
+
edit: { text: '', error: null }, sortCursor: 0, notice: null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const SKELETON = skeletonRows();
|
|
30
|
+
/**
|
|
31
|
+
* True while a live/replay feed has produced nothing yet and nothing went wrong (task-1323): the table
|
|
32
|
+
* shows placeholder rows instead of an empty box. An error (offline / 429) keeps the explicit banner.
|
|
33
|
+
*/
|
|
34
|
+
export function showsSkeleton(s) {
|
|
35
|
+
return s.source !== 'fixtures' && s.rows.length === 0 && feedCondition(s).kind === 'connecting';
|
|
36
|
+
}
|
|
37
|
+
/** Rows as the table shows them: preset's baked filter, then the user's filter, then the sort. */
|
|
38
|
+
export function screenerView(s) {
|
|
39
|
+
if (showsSkeleton(s))
|
|
40
|
+
return SKELETON;
|
|
41
|
+
const spec = presetSpec(s.preset) ?? PRESETS[0];
|
|
42
|
+
const base = s.source === 'fixtures' || spec.baked ? applyFilter(s.rows, bakedFilter(spec)) : s.rows;
|
|
43
|
+
return sortRows(applyFilter(base, s.filter), s.sort.key, s.sort.dir);
|
|
44
|
+
}
|
|
45
|
+
/** Set the applied filter from text. Returns the parse error (and leaves the old filter) on bad input. */
|
|
46
|
+
export function setFilter(s, text) {
|
|
47
|
+
const trimmed = text.trim();
|
|
48
|
+
if (!trimmed) {
|
|
49
|
+
s.query = '';
|
|
50
|
+
s.filter = null;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const r = parseFilter(trimmed);
|
|
54
|
+
if (!r.ok)
|
|
55
|
+
return { error: r.error, at: r.at };
|
|
56
|
+
s.query = trimmed;
|
|
57
|
+
s.filter = r.filter;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
export function setSort(s, key, dir) {
|
|
61
|
+
const spec = sortSpec(key);
|
|
62
|
+
if (!spec)
|
|
63
|
+
return;
|
|
64
|
+
s.sort = { key: spec.key, dir: dir ?? spec.defaultDir };
|
|
65
|
+
}
|
|
66
|
+
/** Select a preset; disabled ones (needs key / pending card) are refused. Returns true when it changed. */
|
|
67
|
+
export function setPreset(s, id) {
|
|
68
|
+
const v = s.presets.find((p) => p.id === id);
|
|
69
|
+
if (!v || !v.available || s.preset === id)
|
|
70
|
+
return false;
|
|
71
|
+
s.preset = id;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
export function stepPreset(s, step) {
|
|
75
|
+
return setPreset(s, cyclePreset(s.presets, s.preset, step));
|
|
76
|
+
}
|
|
77
|
+
export function clampSelection(s, selected) {
|
|
78
|
+
const n = screenerView(s).length;
|
|
79
|
+
return n === 0 ? 0 : Math.max(0, Math.min(n - 1, selected));
|
|
80
|
+
}
|
|
81
|
+
// ---- modes --------------------------------------------------------------------------------
|
|
82
|
+
export function openFilterEditor(s) {
|
|
83
|
+
s.mode = 'filter';
|
|
84
|
+
s.edit = { text: s.query, error: null };
|
|
85
|
+
s.notice = null;
|
|
86
|
+
}
|
|
87
|
+
export function openSortMenu(s) {
|
|
88
|
+
s.mode = 'sort';
|
|
89
|
+
s.sortCursor = Math.max(0, SORT_KEYS.findIndex((k) => k.key === s.sort.key));
|
|
90
|
+
s.notice = null;
|
|
91
|
+
}
|
|
92
|
+
/** Keys while the filter editor is open. Enter applies (only when the text parses), esc cancels. */
|
|
93
|
+
export function filterEditorKey(s, k) {
|
|
94
|
+
const e = s.edit;
|
|
95
|
+
const revalidate = () => { const t = e.text.trim(); if (!t) {
|
|
96
|
+
e.error = null;
|
|
97
|
+
return;
|
|
98
|
+
} const r = parseFilter(t); e.error = r.ok ? null : { error: r.error, at: r.at }; };
|
|
99
|
+
if (k.name === 'esc') {
|
|
100
|
+
s.mode = 'table';
|
|
101
|
+
return 'closed';
|
|
102
|
+
}
|
|
103
|
+
if (k.name === 'enter') {
|
|
104
|
+
const err = setFilter(s, e.text);
|
|
105
|
+
if (err) {
|
|
106
|
+
e.error = err;
|
|
107
|
+
return 'handled';
|
|
108
|
+
}
|
|
109
|
+
s.mode = 'table';
|
|
110
|
+
return 'applied';
|
|
111
|
+
}
|
|
112
|
+
if (k.name === 'backspace') {
|
|
113
|
+
e.text = e.text.slice(0, -1);
|
|
114
|
+
revalidate();
|
|
115
|
+
return 'handled';
|
|
116
|
+
}
|
|
117
|
+
if (k.ctrl && k.ch === 'u') {
|
|
118
|
+
e.text = '';
|
|
119
|
+
e.error = null;
|
|
120
|
+
return 'handled';
|
|
121
|
+
}
|
|
122
|
+
if (k.ctrl && k.ch === 'w') {
|
|
123
|
+
e.text = e.text.replace(/\s*\S+\s*$/, '');
|
|
124
|
+
revalidate();
|
|
125
|
+
return 'handled';
|
|
126
|
+
}
|
|
127
|
+
if ((k.name === 'char' || k.name === 'space') && k.ch && !k.ctrl && !k.alt) {
|
|
128
|
+
if (e.text.length >= 200)
|
|
129
|
+
return 'handled';
|
|
130
|
+
e.text += k.ch;
|
|
131
|
+
revalidate();
|
|
132
|
+
return 'handled';
|
|
133
|
+
}
|
|
134
|
+
return 'ignored';
|
|
135
|
+
}
|
|
136
|
+
/** Keys while the sort menu is open: j/k move, enter picks (same key toggles direction), esc/s close. */
|
|
137
|
+
export function sortMenuKey(s, k) {
|
|
138
|
+
const n = SORT_KEYS.length;
|
|
139
|
+
if (k.name === 'esc' || (k.name === 'char' && (k.ch === 's' || k.ch === 'q'))) {
|
|
140
|
+
s.mode = 'table';
|
|
141
|
+
return 'closed';
|
|
142
|
+
}
|
|
143
|
+
if (k.name === 'down' || (k.name === 'char' && k.ch === 'j')) {
|
|
144
|
+
s.sortCursor = (s.sortCursor + 1) % n;
|
|
145
|
+
return 'handled';
|
|
146
|
+
}
|
|
147
|
+
if (k.name === 'up' || (k.name === 'char' && k.ch === 'k')) {
|
|
148
|
+
s.sortCursor = (s.sortCursor + n - 1) % n;
|
|
149
|
+
return 'handled';
|
|
150
|
+
}
|
|
151
|
+
if (k.name === 'home' || (k.name === 'char' && k.ch === 'g')) {
|
|
152
|
+
s.sortCursor = 0;
|
|
153
|
+
return 'handled';
|
|
154
|
+
}
|
|
155
|
+
if (k.name === 'end' || (k.name === 'char' && k.ch === 'G')) {
|
|
156
|
+
s.sortCursor = n - 1;
|
|
157
|
+
return 'handled';
|
|
158
|
+
}
|
|
159
|
+
if (k.name === 'enter' || k.name === 'space') {
|
|
160
|
+
const pick = SORT_KEYS[s.sortCursor];
|
|
161
|
+
if (pick.key === s.sort.key)
|
|
162
|
+
s.sort = { key: pick.key, dir: s.sort.dir === 'desc' ? 'asc' : 'desc' };
|
|
163
|
+
else
|
|
164
|
+
s.sort = { key: pick.key, dir: pick.defaultDir };
|
|
165
|
+
s.mode = 'table';
|
|
166
|
+
return 'applied';
|
|
167
|
+
}
|
|
168
|
+
return 'ignored';
|
|
169
|
+
}
|
|
170
|
+
/** Caption fragment for columns the registry says cannot be trusted right now, e.g. 'holders, org: degraded'. */
|
|
171
|
+
export function gateCaption(gates, columns) {
|
|
172
|
+
if (!gates)
|
|
173
|
+
return '';
|
|
174
|
+
const by = new Map();
|
|
175
|
+
for (const id of columns) {
|
|
176
|
+
const g = gates[id];
|
|
177
|
+
if (!g || g.available)
|
|
178
|
+
continue;
|
|
179
|
+
const label = g.state === 'needs-key' ? 'needs key' : g.state === 'disabled' ? 'off' : 'degraded';
|
|
180
|
+
(by.get(label) ?? by.set(label, []).get(label)).push(id);
|
|
181
|
+
}
|
|
182
|
+
return [...by.entries()].map(([label, ids]) => `${ids.join(', ')}: ${label}`).join(' · ');
|
|
183
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// dexterm — shared renderer for a live Claude analyst session (task-1302). Used by the full Analyst
|
|
2
|
+
// screen and the token ANALYST panel. Cyberspace styling: the verdict and the tool-call lines are
|
|
3
|
+
// coral, the answer decrypts in through code glyphs, and a cost/latency footer sits on the caption.
|
|
4
|
+
import { Canvas, badge, spinnerGlyph, decryptText, wrap, mix, fmtUsd, fmtPrice, fmtPctDir, } from '../../../tui/src/index.js';
|
|
5
|
+
const verdictColor = (c, v) => {
|
|
6
|
+
const t = c.theme;
|
|
7
|
+
if (v === 'WATCH')
|
|
8
|
+
return t.up;
|
|
9
|
+
if (v === 'AVOID')
|
|
10
|
+
return t.down;
|
|
11
|
+
if (v === 'SPECULATIVE' || v === 'AMBIGUOUS')
|
|
12
|
+
return t.warn;
|
|
13
|
+
return t.muted;
|
|
14
|
+
};
|
|
15
|
+
/** Draw the session inside a box at (x,y,w,h). Pure: reads only the session + opts. */
|
|
16
|
+
export function renderAnalystSession(c, x, y, w, h, s, opts) {
|
|
17
|
+
const t = c.theme;
|
|
18
|
+
const bc = mix(t.border, t.ai, 0.55);
|
|
19
|
+
// Narrow panels (token screen) drop the model so cost · latency · tools still fits beside the title.
|
|
20
|
+
const full = s.status === 'error' ? 'analyst unavailable' : s.footer;
|
|
21
|
+
const caption = (opts.title ?? '✦ ANALYST · claude').length + full.length + 10 <= w ? full : full.split(' · ').slice(0, 3).join(' · ');
|
|
22
|
+
const capFg = s.status === 'error' ? t.warn : t.dim;
|
|
23
|
+
const box = c.box(x, y, w, h, { title: opts.title ?? '✦ ANALYST · claude', titleFg: t.ai, caption, captionFg: capFg, border: bc });
|
|
24
|
+
const bg = box.bg;
|
|
25
|
+
const inner = box.w - 2;
|
|
26
|
+
const frame = opts.frame;
|
|
27
|
+
const inputY = box.y + box.h - 2;
|
|
28
|
+
let cy = box.y;
|
|
29
|
+
// context chips
|
|
30
|
+
if (opts.chips?.length) {
|
|
31
|
+
let cx = box.x + 1;
|
|
32
|
+
for (const chip of opts.chips) {
|
|
33
|
+
if (cx + chip.length + 3 > box.x + box.w - 1)
|
|
34
|
+
break;
|
|
35
|
+
cx = badge(c, cx, cy, chip, t.ai, { on: bg }) + 1;
|
|
36
|
+
}
|
|
37
|
+
cy += 2;
|
|
38
|
+
}
|
|
39
|
+
const line = (str, st, indent = 1) => {
|
|
40
|
+
for (const w2 of wrap(str, inner - indent)) {
|
|
41
|
+
if (cy >= inputY - 1)
|
|
42
|
+
return;
|
|
43
|
+
c.text(box.x + indent, cy, w2, st, inner - indent);
|
|
44
|
+
cy += 1;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const drawTail = () => drawInput(c, box, bc, inputY, opts, s);
|
|
48
|
+
// earlier exchanges on this warm session (compact marker; the current turn is drawn in full below)
|
|
49
|
+
if (s.history.length && cy < inputY - 1) {
|
|
50
|
+
c.text(box.x + 1, cy, `⟲ ${s.history.length} earlier ${s.history.length === 1 ? 'exchange' : 'exchanges'} · one warm claude session`, { fg: t.dim, bg, i: true }, inner - 1);
|
|
51
|
+
cy += 1;
|
|
52
|
+
const last = s.history[s.history.length - 1];
|
|
53
|
+
const prev = last.error ? `last: ${last.error}` : last.verdict ? `last: ${last.verdict}` : last.summary[0] ? `last: ${last.summary[0]}` : '';
|
|
54
|
+
if (prev && cy < inputY - 1) {
|
|
55
|
+
c.text(box.x + 3, cy, prev, { fg: t.dim, bg, i: true }, inner - 3);
|
|
56
|
+
cy += 1;
|
|
57
|
+
}
|
|
58
|
+
cy += 1;
|
|
59
|
+
}
|
|
60
|
+
// ---- the question
|
|
61
|
+
if (cy < inputY - 1) {
|
|
62
|
+
c.text(box.x + 1, cy, '›', { fg: t.accent, bg, b: true });
|
|
63
|
+
line(s.question, { fg: t.text, bg, b: true }, 3);
|
|
64
|
+
cy += 1;
|
|
65
|
+
}
|
|
66
|
+
// ---- interrupted (Esc during a run): soft note, input stays usable for the next question
|
|
67
|
+
if (s.status === 'interrupted') {
|
|
68
|
+
c.text(box.x + 1, cy, '○', { fg: t.warn, bg, b: true });
|
|
69
|
+
line(s.error?.message ?? 'interrupted', { fg: t.warn, bg, b: true }, 3);
|
|
70
|
+
if (s.error?.detail)
|
|
71
|
+
line(s.error.detail, { fg: t.dim, bg, i: true }, 3);
|
|
72
|
+
drawTail();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// ---- error (clean one-liner, app keeps working)
|
|
76
|
+
if (s.status === 'error' && s.error) {
|
|
77
|
+
c.text(box.x + 1, cy, '○', { fg: t.warn, bg, b: true });
|
|
78
|
+
line(s.error.message, { fg: t.warn, bg, b: true }, 3);
|
|
79
|
+
if (s.error.detail)
|
|
80
|
+
line(s.error.detail, { fg: t.dim, bg, i: true }, 3);
|
|
81
|
+
cy += 1;
|
|
82
|
+
line('The rest of dexterm keeps working; this panel is read-only and never trades.', { fg: t.dim, bg, i: true }, 1);
|
|
83
|
+
drawTail();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// ---- tool-call lines (coral), newest reasoning visible
|
|
87
|
+
for (const tl of s.toolLines) {
|
|
88
|
+
if (cy >= inputY - 1)
|
|
89
|
+
break;
|
|
90
|
+
c.text(box.x + 3, cy, '↳ ' + tl, { fg: t.ai, bg, i: true }, inner - 3);
|
|
91
|
+
cy += 1;
|
|
92
|
+
}
|
|
93
|
+
// ---- spinner while thinking / running
|
|
94
|
+
if (s.status === 'thinking' || s.status === 'running') {
|
|
95
|
+
if (cy < inputY - 1) {
|
|
96
|
+
c.text(box.x + 2, cy, spinnerGlyph(frame), { fg: t.ai, bg, b: true });
|
|
97
|
+
const status = s.status === 'thinking'
|
|
98
|
+
? ['Thinking', 'Loading skill', 'Connecting tools'][Math.floor(frame / 24) % 3]
|
|
99
|
+
: ['Querying tools', 'Reading chain', 'Weighing risk'][Math.floor(frame / 24) % 3];
|
|
100
|
+
c.text(box.x + 4, cy, `${status}…`, { fg: t.ai, bg, b: true });
|
|
101
|
+
const secs = ((Date.now() - s.startedAt) / 1000).toFixed(1);
|
|
102
|
+
c.text(box.x + 5 + status.length, cy, `(esc to interrupt · ${secs}s)`, { fg: t.dim, bg }, Math.max(0, inner - status.length - 6));
|
|
103
|
+
cy += 1;
|
|
104
|
+
}
|
|
105
|
+
// stream any natural-language text the model emitted before the structured answer
|
|
106
|
+
if (s.text && cy < inputY - 1) {
|
|
107
|
+
cy += 1;
|
|
108
|
+
line(s.text.slice(-400), { fg: t.muted, bg, i: true }, 2);
|
|
109
|
+
}
|
|
110
|
+
drawTail();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// ---- done: verdict (pinned) + scrollable summary (decrypts in), invalidation, flags, proposals
|
|
114
|
+
cy += 1;
|
|
115
|
+
// task-1311 (QA M12): the decrypt reveals fast (~600 chars/s) and any key reveals it in full, so a
|
|
116
|
+
// finished answer is never hidden; `revealAll` also skips it on demand / in calm mode.
|
|
117
|
+
const revealAll = opts.revealAll || opts.calm;
|
|
118
|
+
if (s.verdict && cy < inputY - 1) {
|
|
119
|
+
const vc = verdictColor(c, s.verdict);
|
|
120
|
+
c.fill(box.x + 1, cy, inner, 1, ' ', { bg: mix(bg, vc, 0.18) });
|
|
121
|
+
c.text(box.x + 2, cy, `● ${s.verdict}`, { fg: vc, bg: mix(bg, vc, 0.18), b: true });
|
|
122
|
+
const right = s.rugProbability !== null ? `rug ${(s.rugProbability * 100).toFixed(0)}% ` : s.confidence !== null ? `conf ${s.confidence.toFixed(2)} ` : '';
|
|
123
|
+
if (right)
|
|
124
|
+
c.textR(box.x + 1, cy, inner - 1, right, { fg: t.muted, bg: mix(bg, vc, 0.18) });
|
|
125
|
+
cy += 1;
|
|
126
|
+
}
|
|
127
|
+
// task-1313 trading view: the directional bias + setup quality, drawn under the risk verdict.
|
|
128
|
+
if (s.bias && cy < inputY - 1) {
|
|
129
|
+
const bc2 = s.bias === 'long' ? t.up : s.bias === 'short' || s.bias === 'avoid' ? t.down : t.warn;
|
|
130
|
+
c.text(box.x + 2, cy, `▸ bias ${s.bias.toUpperCase()}`, { fg: bc2, bg, b: true });
|
|
131
|
+
if (s.setupQuality)
|
|
132
|
+
c.text(box.x + 2 + 7 + s.bias.length + 1, cy, `· setup ${s.setupQuality}`, { fg: t.muted, bg }, inner - 10 - s.bias.length);
|
|
133
|
+
cy += 1;
|
|
134
|
+
}
|
|
135
|
+
if (s.injection && cy < inputY - 1) {
|
|
136
|
+
c.text(box.x + 2, cy, '⚠ prompt injection in token text — ignored', { fg: t.warn, bg, b: true }, inner - 2);
|
|
137
|
+
cy += 1;
|
|
138
|
+
}
|
|
139
|
+
// task-1313 part E: the structured filter the analyst applied for a plain-language request, so the
|
|
140
|
+
// user sees the numbers it chose and can correct them ("tighter", "only locked LP", …).
|
|
141
|
+
if (s.filterApplied && cy < inputY - 1) {
|
|
142
|
+
c.text(box.x + 2, cy, `applied filter: ${s.filterApplied.query}`, { fg: t.accent, bg, b: true }, inner - 2);
|
|
143
|
+
cy += 1;
|
|
144
|
+
if (s.filterApplied.notes) {
|
|
145
|
+
line(s.filterApplied.notes, { fg: t.dim, bg, i: true }, 4);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// task-1311 (QA M12): the answer body scrolls (j/k, PgUp/Dn) instead of truncating at 16 lines.
|
|
149
|
+
const sumLines = s.summary.flatMap((ln) => wrap(ln, inner - 2));
|
|
150
|
+
let capacity = Math.max(1, inputY - 1 - cy);
|
|
151
|
+
const overflow = sumLines.length > capacity;
|
|
152
|
+
if (overflow)
|
|
153
|
+
capacity -= 1; // reserve a row for the scroll marker
|
|
154
|
+
const maxScroll = Math.max(0, sumLines.length - capacity);
|
|
155
|
+
const scroll = Math.max(0, Math.min(opts.scroll ?? 0, maxScroll));
|
|
156
|
+
let revealLeft = revealAll ? 1e9 : s.doneAt ? Math.floor(((Date.now() - s.doneAt) / 1000) * 600) : 1e9;
|
|
157
|
+
for (let i = 0; i < scroll; i++)
|
|
158
|
+
revealLeft -= (sumLines[i]?.length ?? 0) + 1; // spend the budget on hidden lines first
|
|
159
|
+
for (const w2 of sumLines.slice(scroll, scroll + capacity)) {
|
|
160
|
+
if (cy >= inputY - 1)
|
|
161
|
+
break;
|
|
162
|
+
const n = Math.min(revealLeft, w2.length);
|
|
163
|
+
if (revealAll || n >= w2.length)
|
|
164
|
+
c.text(box.x + 2, cy, w2, { fg: t.text, bg }, inner - 2);
|
|
165
|
+
else
|
|
166
|
+
decryptText(c, box.x + 2, cy, w2, Math.max(0, n), frame, { fg: t.text, bg }, inner - 2, opts.code);
|
|
167
|
+
revealLeft -= w2.length + 1;
|
|
168
|
+
cy += 1;
|
|
169
|
+
}
|
|
170
|
+
if (overflow && cy < inputY - 1) {
|
|
171
|
+
const above = scroll, below = Math.max(0, sumLines.length - (scroll + capacity));
|
|
172
|
+
const marker = `${above ? `↑${above} ` : ''}${below ? `↓${below} more ` : ''}· j/k · PgUp/Dn`;
|
|
173
|
+
c.text(box.x + 2, cy, marker, { fg: t.dim, bg, i: true }, inner - 2);
|
|
174
|
+
cy += 1;
|
|
175
|
+
}
|
|
176
|
+
// task-1313 part F: a list-of-coins answer, rendered as a REAL table (symbol/price/change/liq/vol/
|
|
177
|
+
// mcap/age + the analyst's one-line reason), navigable with ↑↓ and ⏎-to-jump. The numbers come from
|
|
178
|
+
// live data (resolved by the caller from the app's data layer, never from the model's text).
|
|
179
|
+
if (opts.results?.length && cy < inputY - 1) {
|
|
180
|
+
cy = renderResultsTable(c, box, bg, cy, inputY, inner, opts.results, opts.resultsCursor ?? 0, opts.resultsActive ?? false);
|
|
181
|
+
}
|
|
182
|
+
if (s.candidates.length && cy < inputY - 1) {
|
|
183
|
+
cy += 1;
|
|
184
|
+
line('ambiguous — pick a mint:', { fg: t.warn, bg, b: true }, 2);
|
|
185
|
+
for (const cand of s.candidates.slice(0, 3)) {
|
|
186
|
+
if (cy >= inputY - 1)
|
|
187
|
+
break;
|
|
188
|
+
c.text(box.x + 2, cy, `· ${String(cand.mint).slice(0, 10)}… ${cand.launchpad ?? ''}`, { fg: t.muted, bg }, inner - 2);
|
|
189
|
+
cy += 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (s.keyLevels.length && cy < inputY - 1) {
|
|
193
|
+
cy += 1;
|
|
194
|
+
c.text(box.x + 2, cy, 'key levels:', { fg: t.dim, bg, i: true });
|
|
195
|
+
cy += 1;
|
|
196
|
+
for (const lv of s.keyLevels.slice(0, 6)) {
|
|
197
|
+
if (cy >= inputY - 1)
|
|
198
|
+
break;
|
|
199
|
+
const val = lv.value !== undefined && lv.value !== null ? ` ${lv.value}` : '';
|
|
200
|
+
const note = lv.note ? ` — ${lv.note}` : '';
|
|
201
|
+
c.text(box.x + 2, cy, `· ${lv.label}${val}${note}`, { fg: t.muted, bg }, inner - 2);
|
|
202
|
+
cy += 1;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (s.invalidation && cy < inputY - 1) {
|
|
206
|
+
cy += 1;
|
|
207
|
+
c.text(box.x + 2, cy, 'invalidates if:', { fg: t.dim, bg, i: true });
|
|
208
|
+
cy += 1;
|
|
209
|
+
line(s.invalidation, { fg: t.muted, bg }, 2);
|
|
210
|
+
}
|
|
211
|
+
if (s.sizeHint && cy < inputY - 1) {
|
|
212
|
+
cy += 1;
|
|
213
|
+
c.text(box.x + 2, cy, 'size:', { fg: t.dim, bg, i: true });
|
|
214
|
+
cy += 1;
|
|
215
|
+
line(s.sizeHint, { fg: t.muted, bg }, 2);
|
|
216
|
+
}
|
|
217
|
+
if (s.proposals.length && cy < inputY - 1) {
|
|
218
|
+
cy += 1;
|
|
219
|
+
for (const p of s.proposals) {
|
|
220
|
+
if (cy >= inputY - 1)
|
|
221
|
+
break;
|
|
222
|
+
c.text(box.x + 2, cy, `+ ${p.type}: ${p.text}`, { fg: t.accent, bg }, inner - 2);
|
|
223
|
+
cy += 1;
|
|
224
|
+
}
|
|
225
|
+
if (cy < inputY - 1) {
|
|
226
|
+
c.text(box.x + 2, cy, 'proposals only — confirm with a keypress', { fg: t.dim, bg, i: true }, inner - 2);
|
|
227
|
+
cy += 1;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
drawTail();
|
|
231
|
+
}
|
|
232
|
+
/** Compact age label from minutes: 45m, 3h, 2d, — when unknown (task-1313 part F table). */
|
|
233
|
+
function fmtAge(min) {
|
|
234
|
+
if (min === null || !Number.isFinite(min))
|
|
235
|
+
return '—';
|
|
236
|
+
if (min < 60)
|
|
237
|
+
return `${Math.round(min)}m`;
|
|
238
|
+
if (min < 1440)
|
|
239
|
+
return `${Math.round(min / 60)}h`;
|
|
240
|
+
return `${Math.round(min / 1440)}d`;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Draw a list-of-coins answer as a real table (task-1313 part F). Columns adapt to width: at ≥96 inner
|
|
244
|
+
* cols the full set (symbol/price/24h/liq/vol/mcap/age + reason); narrower drops columns right-to-left
|
|
245
|
+
* so it stays legible instead of wrapping into prose. The highlighted row (cursor) is tinted; when the
|
|
246
|
+
* table is active a `▸` marks it and the footer hint changes. Returns the new y.
|
|
247
|
+
*/
|
|
248
|
+
function renderResultsTable(c, box, bg, startY, inputY, inner, rows, cursor, active) {
|
|
249
|
+
const t = c.theme;
|
|
250
|
+
let cy = startY + 1;
|
|
251
|
+
if (cy >= inputY - 1)
|
|
252
|
+
return cy;
|
|
253
|
+
// Column plan: [symbol, price, 24h, liq, vol, mcap, age] then the reason fills the rest. Drop from
|
|
254
|
+
// the right as width shrinks so a narrow panel still shows symbol + price + change + a reason.
|
|
255
|
+
const full = [['SYMBOL', 10], ['PRICE', 9], ['24H', 8], ['LIQ', 8], ['VOL', 8], ['MCAP', 8], ['AGE', 5]];
|
|
256
|
+
let cols = full.slice();
|
|
257
|
+
// keep trimming trailing columns until the header + a minimal reason (12) fits
|
|
258
|
+
while (cols.length > 3 && cols.reduce((a, [, w]) => a + w + 1, 0) + 12 > inner - 2)
|
|
259
|
+
cols.pop();
|
|
260
|
+
const reasonX = cols.reduce((a, [, w]) => a + w + 1, 0) + box.x + 3;
|
|
261
|
+
const reasonW = Math.max(0, box.x + box.w - 1 - reasonX);
|
|
262
|
+
// header
|
|
263
|
+
c.text(box.x + 2, cy, `◆ ${rows.length} coin${rows.length === 1 ? '' : 's'}${active ? ' ↑↓ pick · ⏎ open · o ticket · esc back' : ' (↑↓ to browse)'}`, { fg: t.ai, bg, b: true }, inner - 2);
|
|
264
|
+
cy += 1;
|
|
265
|
+
if (cy >= inputY - 1)
|
|
266
|
+
return cy;
|
|
267
|
+
let hx = box.x + 3;
|
|
268
|
+
for (const [lab, w] of cols) {
|
|
269
|
+
c.textR(hx, cy, w, lab, { fg: t.dim, bg });
|
|
270
|
+
hx += w + 1;
|
|
271
|
+
}
|
|
272
|
+
if (reasonW >= 6)
|
|
273
|
+
c.text(reasonX, cy, 'WHY', { fg: t.dim, bg });
|
|
274
|
+
cy += 1;
|
|
275
|
+
const cap = Math.max(1, inputY - 1 - cy);
|
|
276
|
+
const startIdx = Math.max(0, Math.min(cursor - Math.floor(cap / 2), Math.max(0, rows.length - cap)));
|
|
277
|
+
for (let i = startIdx; i < rows.length && cy < inputY - 1; i++) {
|
|
278
|
+
const r = rows[i];
|
|
279
|
+
const on = active && i === cursor;
|
|
280
|
+
const rowBg = on ? mix(bg, t.accent, 0.3) : i % 2 ? mix(bg, t.panelAlt, 1) : bg;
|
|
281
|
+
c.fill(box.x + 1, cy, inner, 1, ' ', { bg: rowBg });
|
|
282
|
+
let rx = box.x + 1;
|
|
283
|
+
c.text(rx, cy, on ? '▸' : ' ', { fg: t.accent, bg: rowBg, b: true });
|
|
284
|
+
rx += 2;
|
|
285
|
+
for (const [lab, w] of cols) {
|
|
286
|
+
if (lab === 'SYMBOL')
|
|
287
|
+
c.text(rx, cy, r.symbol || r.mint.slice(0, 4), { fg: t.text, bg: rowBg, b: on }, w);
|
|
288
|
+
else if (lab === 'PRICE')
|
|
289
|
+
c.textR(rx, cy, w, r.price !== null ? fmtPrice(r.price) : '—', { fg: t.text, bg: rowBg });
|
|
290
|
+
else if (lab === '24H')
|
|
291
|
+
c.textR(rx, cy, w, r.chg24 !== null ? fmtPctDir(r.chg24) : '—', { fg: (r.chg24 ?? 0) >= 0 ? t.up : t.down, bg: rowBg });
|
|
292
|
+
else if (lab === 'LIQ')
|
|
293
|
+
c.textR(rx, cy, w, r.liq !== null ? fmtUsd(r.liq, { digits: 0 }) : '—', { fg: t.text, bg: rowBg });
|
|
294
|
+
else if (lab === 'VOL')
|
|
295
|
+
c.textR(rx, cy, w, r.vol24 !== null ? fmtUsd(r.vol24, { digits: 0 }) : '—', { fg: t.text, bg: rowBg });
|
|
296
|
+
else if (lab === 'MCAP')
|
|
297
|
+
c.textR(rx, cy, w, r.mcap !== null ? fmtUsd(r.mcap, { digits: 0 }) : '—', { fg: t.text, bg: rowBg });
|
|
298
|
+
else if (lab === 'AGE')
|
|
299
|
+
c.textR(rx, cy, w, fmtAge(r.ageMin), { fg: t.dim, bg: rowBg });
|
|
300
|
+
rx += w + 1;
|
|
301
|
+
}
|
|
302
|
+
if (reasonW >= 6 && r.reason)
|
|
303
|
+
c.text(reasonX, cy, r.reason, { fg: on ? t.text : t.muted, bg: rowBg }, reasonW);
|
|
304
|
+
cy += 1;
|
|
305
|
+
}
|
|
306
|
+
if (rows.length > startIdx + cap && cy < inputY - 1) {
|
|
307
|
+
c.text(box.x + 2, cy, `↓ ${rows.length - (startIdx + cap)} more`, { fg: t.dim, bg, i: true }, inner - 2);
|
|
308
|
+
cy += 1;
|
|
309
|
+
}
|
|
310
|
+
return cy;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* The chat input line (task-1303): the live buffer with a cursor when focused, a "queued" banner
|
|
314
|
+
* when a follow-up is waiting on an in-flight run, else the placeholder hint. Always usable.
|
|
315
|
+
*/
|
|
316
|
+
function drawInput(c, box, bc, inputY, opts, s) {
|
|
317
|
+
const t = c.theme;
|
|
318
|
+
const bg = box.bg;
|
|
319
|
+
c.hline(box.x, inputY - 1, box.w, { fg: bc, bg, soft: true });
|
|
320
|
+
c.put(box.x, inputY - 1, '├', { fg: bc, bg, soft: true });
|
|
321
|
+
c.put(box.x + box.w - 1, inputY - 1, '┤', { fg: bc, bg, soft: true });
|
|
322
|
+
const promptFg = s.busy ? t.dim : t.ai;
|
|
323
|
+
c.text(box.x + 1, inputY, '▌', { fg: promptFg, bg, b: true, soft: true });
|
|
324
|
+
const avail = box.w - 4;
|
|
325
|
+
const typed = opts.input ?? '';
|
|
326
|
+
if (typed) {
|
|
327
|
+
// Show the tail of what is typed so the caret stays visible, with a block cursor when focused.
|
|
328
|
+
const caret = opts.inputFocused ? '█' : '';
|
|
329
|
+
const shown = typed.length + caret.length > avail ? typed.slice(typed.length - (avail - caret.length)) : typed;
|
|
330
|
+
const x = c.text(box.x + 2, inputY, shown, { fg: t.text, bg }, avail);
|
|
331
|
+
if (caret)
|
|
332
|
+
c.text(x, inputY, caret, { fg: t.accent, bg, b: true });
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
const hint = s.queued
|
|
336
|
+
? `queued: ${s.queued} · will ask when the run finishes`
|
|
337
|
+
: s.busy
|
|
338
|
+
? 'streaming… type a follow-up (it queues) · esc interrupts'
|
|
339
|
+
: (opts.inputHint ?? 'ask a follow-up… ⏎ send · read-only · never trades');
|
|
340
|
+
const fg = s.queued ? t.warn : t.dim;
|
|
341
|
+
c.text(box.x + 2, inputY, (opts.inputFocused ? '█ ' : '') + hint, { fg, bg, i: true }, avail);
|
|
342
|
+
}
|
|
343
|
+
}
|