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,261 @@
|
|
|
1
|
+
// dexterm — the customization layer's app adapter (task-1358 Tier 1: workspaces). Pure helpers over
|
|
2
|
+
// @dexterm/custom that the key handler, the interactive loop and the screens call: load config + workspaces
|
|
3
|
+
// into `state.custom`, turn a workspace into a plan the loop applies (theme, intensity, screener preset /
|
|
4
|
+
// filter / sort, token panel toggles, start screen, HUD popups), decide which popups to open, and hand the
|
|
5
|
+
// renderers the solved layout numbers for the active workspace.
|
|
6
|
+
//
|
|
7
|
+
// A workspace switch is a UI action and nothing more: it can navigate, re-theme and open READ-ONLY popups
|
|
8
|
+
// (the same popups the keys open). It never opens a ticket, never confirms, never arms, never signs.
|
|
9
|
+
import { THEMES, THEME_NAMES, isThemeName, DEFAULT_KEYMAP, Keymap } from '../../tui/src/index.js';
|
|
10
|
+
import { loadConfig, listWorkspaces, writeWorkspace, captureWorkspace, layoutNumbers, dextermHome, DEFAULT_WORKSPACE, MORNING_HUD, listUserThemes, resolveTheme, applyOverrides, applyKeymapConfig, loadPacks, } from '../../custom/src/index.js';
|
|
11
|
+
import { parseSortArg } from "./screener/sort.js";
|
|
12
|
+
import { presetSpec } from "./screener/presets.js";
|
|
13
|
+
export const CHORD_TTL_MS = 1500;
|
|
14
|
+
export const MAX_WORKSPACE_NAME = 40;
|
|
15
|
+
/** Load config.json + the workspaces directory into `state.custom`. Never throws (problems land in configErrors). */
|
|
16
|
+
export function initCustom(env = process.env, now = Date.now()) {
|
|
17
|
+
const home = dextermHome(env);
|
|
18
|
+
const loaded = loadConfig(home);
|
|
19
|
+
const keymapResult = applyKeymapConfig(DEFAULT_KEYMAP.bindings, loaded.config.keymap);
|
|
20
|
+
const ui = {
|
|
21
|
+
home, config: loaded.config, configErrors: loaded.errors, configNotes: loaded.notes,
|
|
22
|
+
workspaces: listWorkspaces(home), active: DEFAULT_WORKSPACE,
|
|
23
|
+
chord: null, chordAt: now, nameInput: null, pendingPopups: null, notice: null, listCursor: 0,
|
|
24
|
+
themes: listUserThemes(home, loaded.config), themeErrors: [],
|
|
25
|
+
keymap: keymapResult.errors.length ? DEFAULT_KEYMAP : new Keymap(keymapResult.bindings),
|
|
26
|
+
keymapResult,
|
|
27
|
+
packs: null, packRules: [],
|
|
28
|
+
};
|
|
29
|
+
// task-1358 Tier 4: installed packs contribute namespaced workspaces (`<pack>/<name>`), themes (`<pack>-<name>`)
|
|
30
|
+
// and rules. Widgets stay registered DISABLED until `dexterm pack widget … --enable`; a pack keymap is never
|
|
31
|
+
// merged here (only `dexterm pack keymap <name> --merge` does, through the same validation as config.keymap).
|
|
32
|
+
if (loaded.config.packs?.length) {
|
|
33
|
+
const packs = loadPacks(home, loaded.config);
|
|
34
|
+
ui.packs = packs;
|
|
35
|
+
for (const w of packs.workspaces)
|
|
36
|
+
if (!ui.workspaces.some((x) => x.name === w.name))
|
|
37
|
+
ui.workspaces.push({ name: w.name, path: w.path, workspace: w.workspace, builtin: false, pack: w.pack });
|
|
38
|
+
for (const t of packs.themes)
|
|
39
|
+
if (!ui.themes.some((x) => x.name === t.name))
|
|
40
|
+
ui.themes.push({ name: t.name, path: t.path, doc: t.doc, pack: t.pack });
|
|
41
|
+
ui.packRules = packs.rules;
|
|
42
|
+
for (const e of packs.errors)
|
|
43
|
+
ui.configErrors.push(e);
|
|
44
|
+
for (const n of packs.notes)
|
|
45
|
+
ui.configNotes.push(n);
|
|
46
|
+
}
|
|
47
|
+
for (const t of ui.themes)
|
|
48
|
+
if (t.error)
|
|
49
|
+
ui.themeErrors.push(`theme ${t.name}: ${t.error}`);
|
|
50
|
+
for (const e of keymapResult.errors)
|
|
51
|
+
ui.configErrors.push(`keymap ${e.path}: ${e.msg} (the default keys are in use)`);
|
|
52
|
+
const configured = loaded.config.layout?.workspace;
|
|
53
|
+
if (configured && ui.workspaces.some((w) => w.name === configured && !w.error))
|
|
54
|
+
ui.active = configured;
|
|
55
|
+
return ui;
|
|
56
|
+
}
|
|
57
|
+
// ------------------------------------------------------------------ themes
|
|
58
|
+
/** Built-in names first (the `t` cycle order), then the user themes that load. */
|
|
59
|
+
export function themeNames(ui) {
|
|
60
|
+
return [...THEME_NAMES, ...(ui?.themes ?? []).filter((t) => !t.error).map((t) => t.name)];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a theme by name: a built-in or a user theme merged over its base, with `config.theme.overrides`
|
|
64
|
+
* applied on top. A theme that fails the contrast / hue guard is null (the caller keeps the current one) and the
|
|
65
|
+
* reason lands in `ui.themeErrors` once.
|
|
66
|
+
*/
|
|
67
|
+
export function themeByName(ui, name) {
|
|
68
|
+
const overrides = ui?.config.theme?.overrides;
|
|
69
|
+
const note = (msg) => { if (ui && !ui.themeErrors.includes(msg))
|
|
70
|
+
ui.themeErrors.push(msg); };
|
|
71
|
+
let base = null;
|
|
72
|
+
if (isThemeName(name))
|
|
73
|
+
base = THEMES[name];
|
|
74
|
+
else {
|
|
75
|
+
const t = ui?.themes.find((x) => x.name === name && !x.error);
|
|
76
|
+
if (!t)
|
|
77
|
+
return null;
|
|
78
|
+
const r = resolveTheme(t.doc, THEMES);
|
|
79
|
+
if (!r.theme) {
|
|
80
|
+
note(`theme ${name}: ${r.errors[0].path} ${r.errors[0].msg}`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
base = r.theme;
|
|
84
|
+
}
|
|
85
|
+
const o = applyOverrides(base, overrides);
|
|
86
|
+
if (o.errors.length)
|
|
87
|
+
note(`theme overrides: ${o.errors[0].path} ${o.errors[0].msg} (ignored)`);
|
|
88
|
+
return o.theme;
|
|
89
|
+
}
|
|
90
|
+
/** The next theme in the cycle after `current` (built-ins, then user themes); skips themes that fail to resolve. */
|
|
91
|
+
export function cycleTheme(ui, current) {
|
|
92
|
+
const names = themeNames(ui);
|
|
93
|
+
let i = names.indexOf(current.name);
|
|
94
|
+
for (let n = 0; n < names.length; n++) {
|
|
95
|
+
i = (i + 1) % names.length;
|
|
96
|
+
const t = themeByName(ui, names[i]);
|
|
97
|
+
if (t)
|
|
98
|
+
return t;
|
|
99
|
+
}
|
|
100
|
+
return current;
|
|
101
|
+
}
|
|
102
|
+
export function findWorkspace(ui, name) {
|
|
103
|
+
return ui.workspaces.find((w) => w.name === name && !w.error)?.workspace ?? null;
|
|
104
|
+
}
|
|
105
|
+
export function activeWorkspace(ui) {
|
|
106
|
+
return ui ? findWorkspace(ui, ui.active) : null;
|
|
107
|
+
}
|
|
108
|
+
/** Workspace names in list order (built-ins first), bad files excluded; the 1-9 slots of the overlay and `ctrl+w <digit>`. */
|
|
109
|
+
export function workspaceSlots(ui) { return ui.workspaces.filter((w) => !w.error).map((w) => w.name); }
|
|
110
|
+
export function nextWorkspaceName(ui, step) {
|
|
111
|
+
const names = workspaceSlots(ui);
|
|
112
|
+
if (!names.length)
|
|
113
|
+
return DEFAULT_WORKSPACE;
|
|
114
|
+
const i = Math.max(0, names.indexOf(ui.active));
|
|
115
|
+
return names[(i + step + names.length) % names.length];
|
|
116
|
+
}
|
|
117
|
+
const START_ACTION = {
|
|
118
|
+
screener: 'screen.screener', newpairs: 'screen.newpairs', watchlist: 'screen.watchlist', analyst: 'screen.analyst',
|
|
119
|
+
positions: 'screen.positions', narrative: 'screen.narrative', plans: 'screen.plans', trending: 'screen.trending',
|
|
120
|
+
};
|
|
121
|
+
export function planWorkspace(ws, opts = {}) {
|
|
122
|
+
const warnings = [];
|
|
123
|
+
const themeKnown = opts.themeKnown ?? ((n) => isThemeName(n));
|
|
124
|
+
let theme = null;
|
|
125
|
+
if (ws.theme) {
|
|
126
|
+
if (themeKnown(ws.theme))
|
|
127
|
+
theme = ws.theme;
|
|
128
|
+
else
|
|
129
|
+
warnings.push(`theme "${ws.theme}" is not installed (kept the current one)`);
|
|
130
|
+
}
|
|
131
|
+
let preset = null;
|
|
132
|
+
if (ws.screener?.preset) {
|
|
133
|
+
if (presetSpec(ws.screener.preset))
|
|
134
|
+
preset = presetSpec(ws.screener.preset).id;
|
|
135
|
+
else
|
|
136
|
+
warnings.push(`unknown preset "${ws.screener.preset}"`);
|
|
137
|
+
}
|
|
138
|
+
let sort = null;
|
|
139
|
+
if (ws.screener?.sort) {
|
|
140
|
+
try {
|
|
141
|
+
sort = parseSortArg(ws.screener.sort);
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
warnings.push(`bad sort "${ws.screener.sort}": ${e.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const start = ws.start ?? null;
|
|
148
|
+
const startAction = start && start !== 'token' ? START_ACTION[start] ?? null : null;
|
|
149
|
+
return {
|
|
150
|
+
name: ws.name, theme, intensity: ws.intensity ?? null, priceMode: ws.priceMode ?? null,
|
|
151
|
+
screener: { preset, filter: ws.screener?.filter ?? null, sort },
|
|
152
|
+
token: { ...(ws.token ?? {}) },
|
|
153
|
+
start: startAction, startScreen: start,
|
|
154
|
+
popups: [...(ws.popups ?? [])], warnings,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/** Apply the theme part of a plan (the loop and tests both use it): built-in or user theme. */
|
|
158
|
+
export function themeFor(name, ui) { return themeByName(ui, name); }
|
|
159
|
+
/**
|
|
160
|
+
* Which of a workspace's popups the loop should open now. Chart/bubbles/token need an open token (the
|
|
161
|
+
* workspace says `mint: selected`); the roster window has no workspace binding yet and is reported, not
|
|
162
|
+
* guessed. Already-open popups are left alone (never toggled closed). A `widget` popup opens only when the
|
|
163
|
+
* user enabled that pack widget (`dexterm pack widget <pack> <w> --enable`): a workspace can never consent for them.
|
|
164
|
+
*/
|
|
165
|
+
export function planPopupOpens(specs, st) {
|
|
166
|
+
const out = { open: [], widgets: [], skipped: [] };
|
|
167
|
+
const seen = new Set();
|
|
168
|
+
for (const s of specs) {
|
|
169
|
+
const id = s.kind === 'widget' ? `widget:${s.widget ?? ''}` : s.kind;
|
|
170
|
+
if (seen.has(id))
|
|
171
|
+
continue;
|
|
172
|
+
seen.add(id);
|
|
173
|
+
if (s.kind === 'widget') {
|
|
174
|
+
const ref = s.widget ?? '';
|
|
175
|
+
if (!ref)
|
|
176
|
+
out.skipped.push({ kind: 'widget', why: 'no widget named' });
|
|
177
|
+
else if (!st.widgetEnabled)
|
|
178
|
+
out.skipped.push({ kind: 'widget', why: `${ref}: widgets need live or replay mode` });
|
|
179
|
+
else if (!st.widgetEnabled(ref))
|
|
180
|
+
out.skipped.push({ kind: 'widget', why: `${ref} is not enabled (dexterm pack widget ${ref.split('/').slice(0, 2).join(' ')} --enable)` });
|
|
181
|
+
else if (!st.widgetOpen?.(ref))
|
|
182
|
+
out.widgets.push(ref);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
switch (s.kind) {
|
|
186
|
+
case 'chart':
|
|
187
|
+
if (!st.hasToken)
|
|
188
|
+
out.skipped.push({ kind: 'chart', why: 'no token open' });
|
|
189
|
+
else if (!st.chartOpen)
|
|
190
|
+
out.open.push('chart');
|
|
191
|
+
break;
|
|
192
|
+
case 'bubbles':
|
|
193
|
+
if (!st.hasToken)
|
|
194
|
+
out.skipped.push({ kind: 'bubbles', why: 'no token open' });
|
|
195
|
+
else if (!st.bubblesOpen)
|
|
196
|
+
out.open.push('bubbles');
|
|
197
|
+
break;
|
|
198
|
+
case 'token':
|
|
199
|
+
if (!st.hasToken)
|
|
200
|
+
out.skipped.push({ kind: 'token', why: 'no token open' });
|
|
201
|
+
else if (!st.tokenOpen)
|
|
202
|
+
out.open.push('token');
|
|
203
|
+
break;
|
|
204
|
+
case 'narrative':
|
|
205
|
+
if (!st.narrativeOpen)
|
|
206
|
+
out.open.push('narrative');
|
|
207
|
+
break;
|
|
208
|
+
case 'plans':
|
|
209
|
+
if (!st.plansOpen)
|
|
210
|
+
out.open.push('plans');
|
|
211
|
+
break;
|
|
212
|
+
case 'feeds':
|
|
213
|
+
if (!st.feedsOpen)
|
|
214
|
+
out.open.push('feeds');
|
|
215
|
+
break;
|
|
216
|
+
default: out.skipped.push({ kind: s.kind, why: 'not wired to workspaces yet' });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
/** Validate a typed name and persist the current layout as a workspace file; returns the new file entry. */
|
|
222
|
+
export function saveWorkspace(ui, input) {
|
|
223
|
+
const name = input.name.trim();
|
|
224
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(name))
|
|
225
|
+
throw new Error('name: lowercase letters, digits and dashes, starting with a letter (max 40)');
|
|
226
|
+
if (name === MORNING_HUD || name === DEFAULT_WORKSPACE)
|
|
227
|
+
throw new Error(`"${name}" is a built-in workspace; pick another name`);
|
|
228
|
+
const ws = captureWorkspace(name, { ...input, screens: activeWorkspace(ui)?.screens });
|
|
229
|
+
const path = writeWorkspace(ui.home, ws);
|
|
230
|
+
const entry = { name, path, workspace: ws, builtin: false };
|
|
231
|
+
const i = ui.workspaces.findIndex((w) => w.name === name);
|
|
232
|
+
if (i >= 0)
|
|
233
|
+
ui.workspaces[i] = entry;
|
|
234
|
+
else
|
|
235
|
+
ui.workspaces.push(entry);
|
|
236
|
+
return entry;
|
|
237
|
+
}
|
|
238
|
+
// ------------------------------------------------------------------ layout numbers for the renderers
|
|
239
|
+
/** The solved layout numbers for the active workspace at this frame size (cached per workspace × size). */
|
|
240
|
+
export function layoutFor(ui, w, h) {
|
|
241
|
+
if (!ui)
|
|
242
|
+
return layoutNumbers(null, w, h);
|
|
243
|
+
const key = `${ui.active}|${w}x${h}`;
|
|
244
|
+
if (ui.layoutCache?.key === key)
|
|
245
|
+
return ui.layoutCache.numbers;
|
|
246
|
+
const numbers = layoutNumbers(activeWorkspace(ui), w, h);
|
|
247
|
+
ui.layoutCache = { key, numbers };
|
|
248
|
+
return numbers;
|
|
249
|
+
}
|
|
250
|
+
/** Footer/band badge text for the workspace state, or null when there is nothing to show (default, idle). */
|
|
251
|
+
export function workspaceBadge(ui, now = Date.now()) {
|
|
252
|
+
if (!ui)
|
|
253
|
+
return null;
|
|
254
|
+
if (ui.nameInput !== null)
|
|
255
|
+
return { text: ` save workspace as: ${ui.nameInput}▏ (⏎ save · esc cancel) `, kind: 'input' };
|
|
256
|
+
if (ui.chord && now - ui.chordAt <= CHORD_TTL_MS)
|
|
257
|
+
return { text: ' ^W w list · n/N next/prev · s save · m morning HUD · o popups · d default · 1-9 pick ', kind: 'chord' };
|
|
258
|
+
if (ui.active !== DEFAULT_WORKSPACE)
|
|
259
|
+
return { text: ` ⌗ ${ui.active} `, kind: 'name' };
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// dexterm — the price display mode (task-1366): show USD price or MARKET CAP on every screen and both charts.
|
|
2
|
+
//
|
|
3
|
+
// A DISPLAY mode only. Market cap = price × supply, where the supply is what the data layer already reports
|
|
4
|
+
// (the row's mcap / price at the latest tick: the "supply ratio"). Historical candles are scaled by the CURRENT
|
|
5
|
+
// ratio, so every mcap axis says `mcap (current supply)`: a burn or a mint since that candle is not reflected,
|
|
6
|
+
// and nobody should read it as exact historical market cap. When a token has no usable supply (mcap or price
|
|
7
|
+
// missing / zero), the screen falls back to price and SAYS so; it never invents a number.
|
|
8
|
+
//
|
|
9
|
+
// No network calls, nothing here is read by the signer, the caps, tickets, plans or the confirm gate.
|
|
10
|
+
import { fmtMcap, fmtMcapBare, fmtPrice } from '../../tui/src/index.js';
|
|
11
|
+
import { applyAndJournal, configPath, dextermHome, loadConfig, DEFAULT_PRICE_MODE, PRICE_MODES } from '../../custom/src/index.js';
|
|
12
|
+
export { PRICE_MODES, DEFAULT_PRICE_MODE };
|
|
13
|
+
/** Axis / caption label for an mcap chart: the history is scaled by today's supply, and says so. */
|
|
14
|
+
export const MCAP_AXIS_LABEL = 'mcap (current supply)';
|
|
15
|
+
/** Shown wherever mcap mode is on but a token's supply is unknown, so the number on screen is a price. */
|
|
16
|
+
export const MCAP_FALLBACK_NOTE = 'supply unknown · showing price';
|
|
17
|
+
/** Short per-cell marker for a price shown in an MCAP column (fallback). */
|
|
18
|
+
export const FALLBACK_MARK = 'ᵖ';
|
|
19
|
+
export function isPriceMode(v) { return PRICE_MODES.includes(v); }
|
|
20
|
+
/**
|
|
21
|
+
* Supply ratio = mcap / price at the latest tick (i.e. the circulating supply the source used). Null when either
|
|
22
|
+
* side is missing, non-finite or ≤ 0: the caller must fall back to price and say so.
|
|
23
|
+
*/
|
|
24
|
+
export function supplyRatio(price, mcap) {
|
|
25
|
+
if (price == null || mcap == null || !Number.isFinite(price) || !Number.isFinite(mcap) || price <= 0 || mcap <= 0)
|
|
26
|
+
return null;
|
|
27
|
+
const r = mcap / price;
|
|
28
|
+
return Number.isFinite(r) && r > 0 ? r : null;
|
|
29
|
+
}
|
|
30
|
+
/** A price expressed as market cap with the given supply ratio (null ratio or price → null). */
|
|
31
|
+
export function mcapAt(price, ratio) {
|
|
32
|
+
if (price == null || ratio == null || !Number.isFinite(price))
|
|
33
|
+
return null;
|
|
34
|
+
return price * ratio;
|
|
35
|
+
}
|
|
36
|
+
/** Format a token's price slot: market cap in mcap mode when known, otherwise the price (flagged as a fallback). */
|
|
37
|
+
export function priceCell(mode, price, mcap) {
|
|
38
|
+
if (mode === 'mcap') {
|
|
39
|
+
if (mcap != null && Number.isFinite(mcap) && mcap > 0)
|
|
40
|
+
return { text: fmtMcap(mcap), kind: 'mcap', fallback: false };
|
|
41
|
+
return { text: price == null ? '—' : fmtPrice(price), kind: 'price', fallback: price != null };
|
|
42
|
+
}
|
|
43
|
+
return { text: fmtPrice(price), kind: 'price', fallback: false };
|
|
44
|
+
}
|
|
45
|
+
/** "mcap $1.2M ($0.0₄123)": both numbers, for headers, tickets and tooltips. Price only when the supply is unknown. */
|
|
46
|
+
export function mcapWithPrice(price, mcap) {
|
|
47
|
+
if (mcap != null && Number.isFinite(mcap) && mcap > 0)
|
|
48
|
+
return price != null ? `mcap ${fmtMcap(mcap)} (${fmtPrice(price)})` : `mcap ${fmtMcap(mcap)}`;
|
|
49
|
+
return price != null ? `${fmtPrice(price)} · ${MCAP_FALLBACK_NOTE}` : '—';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* mcap mode for a table: the MCAP column takes the PRICE column's place AND priority (it is the headline number now)
|
|
53
|
+
* and PRICE moves to MCAP's old slot, so both stay on screen and every sort key keeps working. Price mode: unchanged.
|
|
54
|
+
*/
|
|
55
|
+
export function promoteMcap(cols, mode) {
|
|
56
|
+
if (mode !== 'mcap')
|
|
57
|
+
return cols;
|
|
58
|
+
const i = cols.findIndex((c) => c.key === 'price');
|
|
59
|
+
const j = cols.findIndex((c) => c.key === 'mcap');
|
|
60
|
+
if (i < 0 || j < 0)
|
|
61
|
+
return cols;
|
|
62
|
+
const out = cols.slice();
|
|
63
|
+
out[i] = { ...cols[j], prio: cols[i].prio };
|
|
64
|
+
out[j] = { ...cols[i], prio: cols[j].prio };
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
/** How many rows show a price in the MCAP column because their supply is unknown (for the panel caption). */
|
|
68
|
+
export function fallbackCount(rows, get) {
|
|
69
|
+
let n = 0;
|
|
70
|
+
for (const r of rows) {
|
|
71
|
+
const v = get(r);
|
|
72
|
+
if (priceCell('mcap', v.price, v.mcap).fallback)
|
|
73
|
+
n++;
|
|
74
|
+
}
|
|
75
|
+
return n;
|
|
76
|
+
}
|
|
77
|
+
/** Caption segment for a table in mcap mode with fallback rows (null when there are none). */
|
|
78
|
+
export function fallbackCaption(n) {
|
|
79
|
+
return n > 0 ? `${FALLBACK_MARK} ${n} price${n > 1 ? 's' : ''} (supply unknown)` : null;
|
|
80
|
+
}
|
|
81
|
+
/** The chart scale for a token: mcap mode with a known supply scales by the CURRENT supply ratio, else prices (+ a note). */
|
|
82
|
+
export function chartMode(mode, price, mcap) {
|
|
83
|
+
if (mode !== 'mcap')
|
|
84
|
+
return { scale: null, axisFmt: undefined, kind: 'price', label: null };
|
|
85
|
+
const ratio = supplyRatio(price, mcap);
|
|
86
|
+
if (ratio === null)
|
|
87
|
+
return { scale: null, axisFmt: undefined, kind: 'price', label: MCAP_FALLBACK_NOTE };
|
|
88
|
+
return { scale: ratio, axisFmt: fmtMcapBare, kind: 'mcap', label: MCAP_AXIS_LABEL };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* One plan level with BOTH numbers, in the order the mode prefers: mcap mode `mc ≤ $300K ($0.0003)`, price mode
|
|
92
|
+
* `≤ $0.0003 (mc $300K)`. The price of a market-cap trigger is mc / the plan's OWN supply snapshot (supplyAtProposal,
|
|
93
|
+
* the supply its triggers were read back against), so the preview never re-derives what the plan means.
|
|
94
|
+
*/
|
|
95
|
+
export function triggerLevel(t, supply, mode) {
|
|
96
|
+
if (t.kind === 'immediate')
|
|
97
|
+
return 'now';
|
|
98
|
+
const below = t.kind === 'mc-below' || t.kind === 'price-below';
|
|
99
|
+
const op = below ? '≤' : '≥';
|
|
100
|
+
const okSupply = Number.isFinite(supply) && supply > 0;
|
|
101
|
+
const mc = t.kind.startsWith('mc-') ? t.value : okSupply ? t.value * supply : null;
|
|
102
|
+
const px = t.kind.startsWith('price-') ? t.value : okSupply ? t.value / supply : null;
|
|
103
|
+
if (mode === 'mcap')
|
|
104
|
+
return `mc ${op} ${mc === null ? '—' : fmtMcap(mc)}${px === null ? '' : ` (${fmtPrice(px)})`}`;
|
|
105
|
+
return `${op} ${px === null ? '—' : fmtPrice(px)}${mc === null ? '' : ` (mc ${fmtMcap(mc)})`}`;
|
|
106
|
+
}
|
|
107
|
+
/** The order-plan preview line: entry · each take-profit · stop, each with market cap AND price. Display only. */
|
|
108
|
+
export function planLevels(plan, mode) {
|
|
109
|
+
const s = plan.supplyAtProposal;
|
|
110
|
+
const parts = [`entry ${triggerLevel(plan.entry, s, mode)}`];
|
|
111
|
+
for (const e of plan.exits)
|
|
112
|
+
parts.push(`TP ${Math.round(e.fraction * 100)}% ${triggerLevel(e.at, s, mode)}`);
|
|
113
|
+
if (plan.stop)
|
|
114
|
+
parts.push(`stop ${triggerLevel(plan.stop, s, mode)}`);
|
|
115
|
+
return parts.join(' · ');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A held position's AVG / PRICE cells: with a supply (mcap mode, supply known) they read market cap — `AVG MC` is the
|
|
119
|
+
* average entry price × the CURRENT supply — else the existing price text (marked ᵖ when mcap mode had no supply).
|
|
120
|
+
* PnL and value cells are not touched: they stay USD / SOL.
|
|
121
|
+
*/
|
|
122
|
+
export function positionPriceCells(mode, supply, avgUsd, priceUsd, avgText, priceText) {
|
|
123
|
+
if (mode === 'mcap' && supply !== null && supply > 0) {
|
|
124
|
+
return {
|
|
125
|
+
avg: { label: 'AVG MC', value: avgUsd !== null ? fmtMcap(avgUsd * supply) : avgText === '—' ? '—' : avgText + FALLBACK_MARK },
|
|
126
|
+
price: { label: 'MCAP', value: priceUsd !== null ? fmtMcap(priceUsd * supply) : priceText === '—' ? '—' : priceText + FALLBACK_MARK },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const mark = mode === 'mcap' ? FALLBACK_MARK : '';
|
|
130
|
+
return { avg: { label: 'AVG ENTRY', value: avgText === '—' ? avgText : avgText + mark }, price: { label: 'PRICE', value: priceText === '—' ? priceText : priceText + mark } };
|
|
131
|
+
}
|
|
132
|
+
/** The mode in effect: the state's (set from config, a workspace or `$`), default price. */
|
|
133
|
+
export function priceModeOf(state) {
|
|
134
|
+
return state?.priceMode === 'mcap' ? 'mcap' : 'price';
|
|
135
|
+
}
|
|
136
|
+
/** The mode a run starts in: config.display.priceMode, else price. */
|
|
137
|
+
export function configPriceMode(cfg) {
|
|
138
|
+
const m = cfg?.display?.priceMode;
|
|
139
|
+
return isPriceMode(m) ? m : DEFAULT_PRICE_MODE;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* `$`: flip the mode and persist it to config.display.priceMode through the journal (actor user, so `undo` works).
|
|
143
|
+
* Returns the notice for the toast. Persistence failure keeps the new mode for this run and says why.
|
|
144
|
+
*/
|
|
145
|
+
export function togglePriceMode(state) {
|
|
146
|
+
const next = priceModeOf(state) === 'mcap' ? 'price' : 'mcap';
|
|
147
|
+
state.priceMode = next;
|
|
148
|
+
const what = next === 'mcap' ? `showing MARKET CAP · charts: ${MCAP_AXIS_LABEL}` : 'showing USD PRICE';
|
|
149
|
+
const ui = state.custom;
|
|
150
|
+
if (!ui)
|
|
151
|
+
return `${what} (not saved: no config on this run)`;
|
|
152
|
+
try {
|
|
153
|
+
const op = ui.config.display ? { op: 'add', path: '/display/priceMode', value: next } : { op: 'add', path: '/display', value: { priceMode: next } };
|
|
154
|
+
const { config } = applyAndJournal(ui.home, [op], { actor: 'user', why: `$ price mode ${next}`, now: state.now.getTime() });
|
|
155
|
+
ui.config = config;
|
|
156
|
+
return `${what} · saved ($ to switch back)`;
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
return `${what} (not saved: ${e.message})`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export const DISPLAY_USAGE = 'usage: dexterm display [price|mcap] show or set display.priceMode in ~/.dexterm/config.json ($ in the app toggles it)';
|
|
163
|
+
/** `dexterm display [price|mcap]`: print the saved price display mode, or set it (journaled, actor user). No network. */
|
|
164
|
+
export function displayCommand(argv, io) {
|
|
165
|
+
const home = dextermHome(io.env);
|
|
166
|
+
const [arg] = argv;
|
|
167
|
+
const loaded = loadConfig(home);
|
|
168
|
+
if (arg === undefined) {
|
|
169
|
+
if (!loaded.ok) {
|
|
170
|
+
io.stderr.write(`dexterm display: ${loaded.errors[0]}\n`);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
const m = configPriceMode(loaded.config);
|
|
174
|
+
io.stdout.write(`display.priceMode = ${m}${m === 'mcap' ? ` (tables show MCAP; charts: ${MCAP_AXIS_LABEL}; tokens without a supply fall back to price)` : ' (USD price per token)'}\n`);
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
if (!isPriceMode(arg)) {
|
|
178
|
+
io.stderr.write(`dexterm display: unknown mode ${JSON.stringify(arg.slice(0, 20))}\n${DISPLAY_USAGE}\n`);
|
|
179
|
+
return 2;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const op = loaded.config.display ? { op: 'add', path: '/display/priceMode', value: arg } : { op: 'add', path: '/display', value: { priceMode: arg } };
|
|
183
|
+
applyAndJournal(home, [op], { actor: 'user', why: `dexterm display ${arg}` });
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
io.stderr.write(`dexterm display: ${e.message}\n`);
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
io.stdout.write(`display.priceMode = ${arg} (saved to ${configPath(home)})\n`);
|
|
190
|
+
return 0;
|
|
191
|
+
}
|