dexterm-cli 0.1.6 → 0.1.7
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/dist/packages/agents/src/schema.js +22 -1
- package/dist/packages/agents/src/story.js +11 -2
- package/dist/packages/agents/src/tiers.js +27 -2
- package/dist/packages/agents/src/tools.js +30 -2
- package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
- package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
- package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
- package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
- package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
- package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
- package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
- package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
- package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
- package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
- package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
- package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
- package/dist/packages/autotrade/src/account.js +74 -0
- package/dist/packages/autotrade/src/backtest.js +357 -0
- package/dist/packages/autotrade/src/basis-text.js +32 -0
- package/dist/packages/autotrade/src/chat/approvals.js +484 -0
- package/dist/packages/autotrade/src/chat/audit.js +140 -0
- package/dist/packages/autotrade/src/chat/config.js +93 -0
- package/dist/packages/autotrade/src/chat/connector.js +24 -0
- package/dist/packages/autotrade/src/chat/control.js +103 -0
- package/dist/packages/autotrade/src/chat/fake.js +100 -0
- package/dist/packages/autotrade/src/chat/format.js +71 -0
- package/dist/packages/autotrade/src/chat/hub.js +187 -0
- package/dist/packages/autotrade/src/chat/index.js +18 -0
- package/dist/packages/autotrade/src/chat/notify.js +292 -0
- package/dist/packages/autotrade/src/chat/pairing.js +46 -0
- package/dist/packages/autotrade/src/chat/redact.js +48 -0
- package/dist/packages/autotrade/src/chat/secrets.js +99 -0
- package/dist/packages/autotrade/src/chat/service.js +248 -0
- package/dist/packages/autotrade/src/chat/telegram.js +290 -0
- package/dist/packages/autotrade/src/desk-executor.js +188 -0
- package/dist/packages/autotrade/src/engine.js +387 -0
- package/dist/packages/autotrade/src/execution-port.js +97 -0
- package/dist/packages/autotrade/src/fleet-config.js +169 -0
- package/dist/packages/autotrade/src/fleet-control.js +276 -0
- package/dist/packages/autotrade/src/fleet.js +639 -0
- package/dist/packages/autotrade/src/index.js +53 -0
- package/dist/packages/autotrade/src/ledger.js +108 -0
- package/dist/packages/autotrade/src/live-source.js +182 -0
- package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
- package/dist/packages/autotrade/src/monitor.js +190 -0
- package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
- package/dist/packages/autotrade/src/paper-accounts.js +72 -0
- package/dist/packages/autotrade/src/replay.js +198 -0
- package/dist/packages/autotrade/src/runner-config.js +99 -0
- package/dist/packages/autotrade/src/runner.js +872 -0
- package/dist/packages/autotrade/src/safety.js +243 -0
- package/dist/packages/autotrade/src/session.js +269 -0
- package/dist/packages/autotrade/src/sessions.js +503 -0
- package/dist/packages/autotrade/src/store.js +250 -0
- package/dist/packages/autotrade/src/strategy.js +256 -0
- package/dist/packages/autotrade/src/trade-ledger.js +241 -0
- package/dist/packages/autotrade/src/yolo.js +140 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
- package/dist/packages/cli/src/ai-cmd.js +412 -0
- package/dist/packages/cli/src/analyst/backend.js +82 -15
- package/dist/packages/cli/src/analyst/codex.js +14 -9
- package/dist/packages/cli/src/analyst/ollama.js +263 -0
- package/dist/packages/cli/src/analyst/session.js +5 -2
- package/dist/packages/cli/src/app-help-gen.js +44 -8
- package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
- package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
- package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
- package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
- package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
- package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
- package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
- package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
- package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
- package/dist/packages/cli/src/autotrade-cmd.js +168 -0
- package/dist/packages/cli/src/display.js +156 -17
- package/dist/packages/cli/src/doctor.js +62 -3
- package/dist/packages/cli/src/export.js +10 -2
- package/dist/packages/cli/src/index.js +1 -1
- package/dist/packages/cli/src/ledger-cmd.js +288 -0
- package/dist/packages/cli/src/live/chart-fills.js +46 -0
- package/dist/packages/cli/src/live/index.js +23 -89
- package/dist/packages/cli/src/live/screens/token-live.js +9 -6
- package/dist/packages/cli/src/live/trade.js +12 -457
- package/dist/packages/cli/src/live/watchlist.js +1 -1
- package/dist/packages/cli/src/main.js +147 -190
- package/dist/packages/cli/src/mcp-cmd.js +7 -1
- package/dist/packages/cli/src/plan-cmd.js +1 -1
- package/dist/packages/cli/src/pnl-app.js +31 -21
- package/dist/packages/cli/src/pnl-card.js +158 -21
- package/dist/packages/cli/src/popup.js +22 -6
- package/dist/packages/cli/src/record-cmd.js +141 -20
- package/dist/packages/cli/src/screens/agent-hud.js +34 -2
- package/dist/packages/cli/src/screens/analyst-session.js +7 -4
- package/dist/packages/cli/src/screens/analyst.js +3 -2
- package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
- package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
- package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
- package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
- package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
- package/dist/packages/cli/src/screens/brain.js +37 -11
- package/dist/packages/cli/src/screens/overlay.js +5 -2
- package/dist/packages/cli/src/screens/plans.js +5 -17
- package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
- package/dist/packages/cli/src/screens/positions.js +6 -28
- package/dist/packages/cli/src/screens/ticket.js +38 -205
- package/dist/packages/cli/src/screens/token.js +3 -3
- package/dist/packages/cli/src/state.js +25 -19
- package/dist/packages/cli/src/telegram-cmd.js +451 -0
- package/dist/packages/cli/src/trade.js +3 -128
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/cli/src/wallet-cmd.js +39 -340
- package/dist/packages/cli/src/wallet-command.js +8 -11
- package/dist/packages/cli/src/widgets.js +0 -12
- package/dist/packages/custom/src/config.js +20 -2
- package/dist/packages/custom/src/schema.js +35 -10
- package/dist/packages/custom/src/workspace.js +3 -3
- package/dist/packages/data/src/basis.js +191 -0
- package/dist/packages/data/src/detail.js +53 -2
- package/dist/packages/data/src/index.js +0 -14
- package/dist/packages/data/src/store.js +14 -5
- package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
- package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
- package/dist/packages/data/src/trade/bridge.js +4 -102
- package/dist/packages/data/src/trade/ledger/export.js +101 -0
- package/dist/packages/data/src/trade/ledger/file.js +135 -0
- package/dist/packages/data/src/trade/ledger/fold.js +191 -0
- package/dist/packages/data/src/trade/ledger/index.js +9 -0
- package/dist/packages/data/src/trade/ledger/schema.js +83 -0
- package/dist/packages/data/src/trade/ledger/sources.js +307 -0
- package/dist/packages/data/src/trade/ledger/stats.js +123 -0
- package/dist/packages/data/src/trade/ledger/tags.js +116 -0
- package/dist/packages/data/src/trade/ledger/view.js +51 -0
- package/dist/packages/data/src/trade/plans/index.js +0 -1
- package/dist/packages/data/src/trade/plans/service.js +3 -27
- package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
- package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
- package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
- package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
- package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
- package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
- package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
- package/dist/packages/mcp/src/app-help.js +1 -1
- package/dist/packages/mcp/src/autotrade-tools.js +944 -0
- package/dist/packages/mcp/src/backtest-requests.js +71 -0
- package/dist/packages/mcp/src/engine/config.js +159 -0
- package/dist/packages/mcp/src/engine/index.js +10 -0
- package/dist/packages/mcp/src/engine/loop.js +101 -0
- package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
- package/dist/packages/mcp/src/engine/ollama.js +258 -0
- package/dist/packages/mcp/src/engine/probe.js +110 -0
- package/dist/packages/mcp/src/engine/reason.js +35 -0
- package/dist/packages/mcp/src/engine/restrictions.js +111 -0
- package/dist/packages/mcp/src/index.js +7 -1
- package/dist/packages/mcp/src/ledger-tools.js +346 -0
- package/dist/packages/mcp/src/paper-agent.js +2 -0
- package/dist/packages/mcp/src/server.js +127 -19
- package/dist/packages/packs/src/backtest.js +3 -1
- package/dist/packages/popup/src/pages/chart-lib.js +134 -21
- package/dist/packages/popup/src/pages/chart.css +9 -0
- package/dist/packages/popup/src/pages/chart.html +3 -1
- package/dist/packages/popup/src/pages/chart.js +104 -7
- package/dist/packages/popup/src/protocol.js +2 -0
- package/dist/packages/popup/src/server.js +4 -0
- package/dist/packages/recorder/src/index.js +2 -0
- package/dist/packages/recorder/src/recorder.js +63 -6
- package/dist/packages/recorder/src/retention.js +100 -0
- package/dist/packages/recorder/src/schema.js +34 -1
- package/dist/packages/recorder/src/ticks.js +237 -0
- package/dist/packages/tui/src/index.js +1 -0
- package/dist/packages/tui/src/keymap.js +21 -1
- package/dist/packages/tui/src/markers.js +219 -0
- package/dist/packages/tui/src/widgets.js +149 -2
- package/package.json +2 -1
- package/dist/packages/data/src/trade/caps.js +0 -323
- package/dist/packages/data/src/trade/executor.js +0 -933
- package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
- package/dist/packages/data/src/trade/rpc-exec.js +0 -229
- package/dist/packages/data/src/trade/signer/client.js +0 -197
- package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
- package/dist/packages/data/src/trade/signer/host.js +0 -97
- package/dist/packages/data/src/trade/signer/main.js +0 -121
- package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
- package/dist/packages/data/src/trade/signer/protocol.js +0 -92
- package/dist/packages/data/src/trade/signer/service.js +0 -400
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
- package/dist/packages/data/src/trade/sol-usd.js +0 -86
- package/dist/packages/data/src/trade/swap-build.js +0 -108
- package/dist/packages/data/src/trade/token-delta.js +0 -201
- package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
// `dexterm telegram …` (task-1527, AUTOTRADE-SPEC §8.3): your OWN Telegram bot for the autotrade runner.
|
|
2
|
+
//
|
|
3
|
+
// setup read the bot token with a hidden prompt (or from a pipe), check it with getMe (prints the bot's
|
|
4
|
+
// username only), store it in the OS secret store (macOS keychain, Linux libsecret) or a 0600 file
|
|
5
|
+
// under ~/.dexterm/secrets/, then pair. The token never goes in a config file, a log, argv or env.
|
|
6
|
+
// pair a 6-digit code on THIS screen, valid 5 minutes; send `/pair <code>` to the bot from your own account
|
|
7
|
+
// in a private chat. Refused while a webhook or a 409 conflict shows someone else polling the bot (they
|
|
8
|
+
// could read the code); the account that sent it (@username + id) is shown here and needs a local y/N.
|
|
9
|
+
// Only that account in that chat is allowed; the bot answers nobody else.
|
|
10
|
+
// status what is set up (never the token), the paired chats, approvals, notify settings, the audit chain.
|
|
11
|
+
// approvals on|off (local; chat can only turn them on). notify <event> on|off. set summary-at|quiet|expiry.
|
|
12
|
+
// test send one test message to the paired chats (the manual smoke test; nothing in CI talks to Telegram).
|
|
13
|
+
// audit [--verify] the chat audit chain (autotrade/chat/<day>.jsonl).
|
|
14
|
+
// revoke remove the token from the store and forget every paired chat.
|
|
15
|
+
// The runner starts the bot by itself when it is set up and paired (`dexterm autotrade run … --paper`; --no-telegram
|
|
16
|
+
// keeps it off). Nothing here is hosted: the bot long-polls from this machine.
|
|
17
|
+
import { dextermHome } from '../../custom/src/index.js';
|
|
18
|
+
import { ChatAudit, PairingWindow, TelegramConnector, TIERS_ALL, chooseSecretStore, connectorsPath, defaultTelegram, loadConnectors, looksLikeBotToken, redact, saveConnectors, secretStore, verifyChatAudit, ChatHub, } from '../../autotrade/src/index.js';
|
|
19
|
+
export const TELEGRAM_USAGE = `dexterm telegram: your own Telegram bot for the autotrade runner (paper first). Nothing is hosted.
|
|
20
|
+
|
|
21
|
+
dexterm telegram setup [--tiers notify,control,approve] [--no-pair]
|
|
22
|
+
store your bot token (hidden prompt or a pipe; never an argument), check it, then pair your account
|
|
23
|
+
dexterm telegram pair [--tiers notify,control,approve]
|
|
24
|
+
show a one-time code here; send /pair <code> to your bot from your own private chat (5 minutes)
|
|
25
|
+
dexterm telegram status [--json]
|
|
26
|
+
dexterm telegram approvals on|off
|
|
27
|
+
on: every entry waits for your tap (chat can turn this on, never off)
|
|
28
|
+
dexterm telegram notify candidates digest|each|off · notify entries|closes|halts|spend|daily on|off
|
|
29
|
+
dexterm telegram set summary-at HH:MM|off · set quiet HH:MM-HH:MM|off · set expiry 60..120
|
|
30
|
+
dexterm telegram test send one test message to your paired chat (manual check)
|
|
31
|
+
dexterm telegram audit [--verify]
|
|
32
|
+
dexterm telegram revoke remove the token and forget every paired chat
|
|
33
|
+
|
|
34
|
+
Telegram is not end-to-end encrypted: the bot never sends keys, seed phrases, wallet addresses or balances,
|
|
35
|
+
and it never asks for them. The bot is your OWN bot for alerts and control of PAPER runs: dexterm holds no key and places
|
|
36
|
+
no live order (live trading happens in the Telegram trading bot of your choice). Experimental; not financial advice.
|
|
37
|
+
`;
|
|
38
|
+
const flag = (argv, name) => { const i = argv.indexOf(name); return i >= 0 ? argv[i + 1] : undefined; };
|
|
39
|
+
function parseTiers(v) {
|
|
40
|
+
if (!v)
|
|
41
|
+
return [...TIERS_ALL];
|
|
42
|
+
const xs = v.split(',').map((s) => s.trim()).filter(Boolean);
|
|
43
|
+
if (!xs.length || xs.some((x) => !TIERS_ALL.includes(x)))
|
|
44
|
+
return null;
|
|
45
|
+
return [...new Set(xs)];
|
|
46
|
+
}
|
|
47
|
+
/** Hidden input on a real terminal (nothing echoed), or the first line of a pipe. Never an argument or env var. */
|
|
48
|
+
export async function readHidden(io, prompt) {
|
|
49
|
+
const stdin = io.stdin;
|
|
50
|
+
if (!stdin)
|
|
51
|
+
return null;
|
|
52
|
+
if (stdin.isTTY && typeof stdin.setRawMode === 'function') {
|
|
53
|
+
io.stderr.write(prompt);
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
let buf = '';
|
|
56
|
+
const done = (v) => { try {
|
|
57
|
+
stdin.setRawMode(false);
|
|
58
|
+
}
|
|
59
|
+
catch { /* ignore */ } stdin.off('data', onData); stdin.pause(); io.stderr.write('\n'); resolve(v); };
|
|
60
|
+
const onData = (chunk) => {
|
|
61
|
+
for (const ch of chunk.toString('utf8')) {
|
|
62
|
+
if (ch === '\r' || ch === '\n') {
|
|
63
|
+
done(buf.trim() || null);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (ch === '\u0003' || ch === '\u001b') {
|
|
67
|
+
done(null);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (ch === '\u007f' || ch === '\b') {
|
|
71
|
+
buf = buf.slice(0, -1);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (ch >= ' ')
|
|
75
|
+
buf += ch;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
stdin.setRawMode(true);
|
|
79
|
+
stdin.on('data', onData);
|
|
80
|
+
stdin.resume();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return pipeLine(stdin);
|
|
84
|
+
}
|
|
85
|
+
/** A pipe is read once to its end; its later lines answer later questions (`printf '%s\ny\n' "$TOKEN" | … setup`). */
|
|
86
|
+
const pipeRest = new WeakMap();
|
|
87
|
+
async function pipeLine(stdin) {
|
|
88
|
+
let lines = pipeRest.get(stdin);
|
|
89
|
+
if (!lines) {
|
|
90
|
+
lines = new Promise((resolve) => {
|
|
91
|
+
let buf = '';
|
|
92
|
+
stdin.setEncoding('utf8');
|
|
93
|
+
stdin.on('data', (c) => { buf += c; });
|
|
94
|
+
stdin.on('end', () => resolve(buf.split('\n')));
|
|
95
|
+
stdin.on('error', () => resolve([]));
|
|
96
|
+
});
|
|
97
|
+
pipeRest.set(stdin, lines);
|
|
98
|
+
}
|
|
99
|
+
const l = (await lines).shift();
|
|
100
|
+
return l === undefined ? null : l.trim() || null;
|
|
101
|
+
}
|
|
102
|
+
/** One visible line from this terminal (or the next line of a pipe); null when there is no input. */
|
|
103
|
+
async function readLine(io, prompt) {
|
|
104
|
+
const stdin = io.stdin;
|
|
105
|
+
if (!stdin)
|
|
106
|
+
return null;
|
|
107
|
+
io.stderr.write(prompt);
|
|
108
|
+
if (!stdin.isTTY) {
|
|
109
|
+
const l = await pipeLine(stdin);
|
|
110
|
+
io.stderr.write('\n');
|
|
111
|
+
return l;
|
|
112
|
+
}
|
|
113
|
+
const { createInterface } = await import('node:readline');
|
|
114
|
+
const rl = createInterface({ input: stdin, terminal: false });
|
|
115
|
+
try {
|
|
116
|
+
return await new Promise((resolve) => { rl.once('line', (l) => resolve(l.trim())); rl.once('close', () => resolve(null)); });
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
rl.close();
|
|
120
|
+
stdin.pause();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** The account as Telegram reports it, safe to print: a handle is [A-Za-z0-9_] only; a display name loses controls. */
|
|
124
|
+
export function describeSender(senderId, sender) {
|
|
125
|
+
const handle = sender?.username && /^[A-Za-z0-9_]{1,32}$/.test(sender.username) ? `@${sender.username}` : '(no @username)';
|
|
126
|
+
// eslint-disable-next-line no-control-regex
|
|
127
|
+
const name = (sender?.name ?? '').replace(/[\u0000-\u001F\u007F-\u009F---]/g, '').trim().slice(0, 64);
|
|
128
|
+
return `${handle} (id ${senderId}${name ? `, name ${JSON.stringify(name)}` : ''})`;
|
|
129
|
+
}
|
|
130
|
+
const CONFLICT_HELP = 'Someone else may hold your bot token (and may have read the code). Revoke it in @BotFather (/revoke), then run `dexterm telegram setup` with the new token.';
|
|
131
|
+
function load(home, io) {
|
|
132
|
+
const r = loadConnectors(home);
|
|
133
|
+
if (!r.ok) {
|
|
134
|
+
io.stderr.write(`dexterm telegram: ${r.error}\n`);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
return r.config;
|
|
138
|
+
}
|
|
139
|
+
function storeOf(home, cfg, io, deps) {
|
|
140
|
+
if (deps.store)
|
|
141
|
+
return deps.store;
|
|
142
|
+
return cfg.telegram.secretStore ? secretStore(cfg.telegram.secretStore, home) : chooseSecretStore(home, io.env);
|
|
143
|
+
}
|
|
144
|
+
function connectorFor(deps, token, io) {
|
|
145
|
+
return deps.connector ? deps.connector(token) : new TelegramConnector({ token, onLog: (l) => io.stderr.write(`telegram: ${l}\n`) });
|
|
146
|
+
}
|
|
147
|
+
async function pairFlow(home, io, deps, tiers) {
|
|
148
|
+
const cfg = load(home, io);
|
|
149
|
+
if (!cfg)
|
|
150
|
+
return 1;
|
|
151
|
+
const store = storeOf(home, cfg, io, deps);
|
|
152
|
+
const token = store.get();
|
|
153
|
+
if (!token) {
|
|
154
|
+
io.stderr.write('dexterm telegram: no bot token yet: run `dexterm telegram setup`\n');
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
const now = deps.now ?? Date.now;
|
|
158
|
+
const conn = connectorFor(deps, () => token, io);
|
|
159
|
+
const audit = new ChatAudit(home, { knownToken: () => token });
|
|
160
|
+
// 1. Nobody else may be reading this bot: a webhook (or an unanswered check) refuses before a code exists.
|
|
161
|
+
const hook = conn.checkWebhook ? await conn.checkWebhook() : 'clear';
|
|
162
|
+
if (hook !== 'clear' || conn.health().conflict) {
|
|
163
|
+
audit.add(now(), 'pairing_refused', { why: hook === 'clear' ? 'conflict' : `webhook-${hook}` });
|
|
164
|
+
io.stderr.write(hook === 'unknown'
|
|
165
|
+
? 'dexterm telegram: could not check the bot with Telegram (getWebhookInfo); nothing was paired. Try again.\n'
|
|
166
|
+
: `dexterm telegram: NOT pairing: ${conn.health().conflict ?? 'a webhook is set on this bot'}. ${CONFLICT_HELP}\n`);
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
const win = new PairingWindow({ now });
|
|
170
|
+
const hub = new ChatHub({ connector: conn, config: () => cfg.telegram, audit, badge: 'PAPER', now });
|
|
171
|
+
const ac = new AbortController();
|
|
172
|
+
let paired = null;
|
|
173
|
+
hub.openPairing(win, (r, c) => { if (r.ok) {
|
|
174
|
+
paired = { senderId: r.senderId, chatId: r.chatId, sender: c.sender };
|
|
175
|
+
ac.abort();
|
|
176
|
+
}
|
|
177
|
+
else if (r.why === 'locked' || r.why === 'conflict')
|
|
178
|
+
ac.abort(); });
|
|
179
|
+
io.stdout.write(`Pairing code: ${win.code.slice(0, 3)} ${win.code.slice(3)}\n`);
|
|
180
|
+
io.stdout.write(`Open a PRIVATE chat with ${cfg.telegram.bot ? `@${cfg.telegram.bot.username}` : 'your bot'} from your own account and send:\n /pair ${win.code}\n`);
|
|
181
|
+
io.stdout.write('The code is valid for 5 minutes and works once. Waiting… (Ctrl-C to stop)\n');
|
|
182
|
+
// The pairing window's own timer holds the event loop until it ends (cleared in the finally below), so the wait
|
|
183
|
+
// cannot be dropped as "nothing pending" when the connector has no open request at that moment (task-1574).
|
|
184
|
+
const limit = setTimeout(() => ac.abort(), deps.pairTimeoutMs ?? Math.max(0, win.expiresAt - now()));
|
|
185
|
+
// 2. A 409 (another getUpdates poll) at any point in the window ends it: that poller may have read the code.
|
|
186
|
+
const watch = setInterval(() => { if (conn.health().conflict)
|
|
187
|
+
ac.abort(); }, 100);
|
|
188
|
+
watch.unref?.();
|
|
189
|
+
const onSig = () => ac.abort();
|
|
190
|
+
process.once('SIGINT', onSig);
|
|
191
|
+
try {
|
|
192
|
+
await conn.start(ac.signal);
|
|
193
|
+
await hub.idle();
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
clearTimeout(limit);
|
|
197
|
+
clearInterval(watch);
|
|
198
|
+
process.removeListener('SIGINT', onSig);
|
|
199
|
+
}
|
|
200
|
+
// Confirm the update offset, so the runner's first poll does not see the /pair message again.
|
|
201
|
+
try {
|
|
202
|
+
await conn.confirmOffset?.();
|
|
203
|
+
}
|
|
204
|
+
catch { /* best effort */ }
|
|
205
|
+
const conflict = conn.health().conflict;
|
|
206
|
+
if (conflict) {
|
|
207
|
+
audit.add(now(), 'pairing_refused', { why: 'conflict', codeAccepted: paired !== null });
|
|
208
|
+
io.stderr.write(`dexterm telegram: NOT paired: ${conflict}. ${CONFLICT_HELP}\n`);
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
if (!paired) {
|
|
212
|
+
io.stderr.write('dexterm telegram: not paired (the code expired, was wrong too often, or you stopped). Run `dexterm telegram pair` again.\n');
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
const p = paired;
|
|
216
|
+
// 3. The code is a shared secret that travelled through Telegram: the person at this computer says whose account it is.
|
|
217
|
+
const who = describeSender(p.senderId, p.sender);
|
|
218
|
+
io.stdout.write(`The code came from ${who}, in its private chat with the bot.\n`);
|
|
219
|
+
const question = `Is ${who} YOUR Telegram account? It gets ${tiers.join(', ')}. [y/N] `;
|
|
220
|
+
const yes = deps.confirm ? await deps.confirm(question) : /^(y|yes)$/i.test((await readLine(io, question)) ?? '');
|
|
221
|
+
if (!yes) {
|
|
222
|
+
audit.add(now(), 'pairing_refused', { why: 'not-confirmed-locally', senderId: p.senderId });
|
|
223
|
+
io.stderr.write(`dexterm telegram: not paired. If that account is not yours, someone else has your bot token. ${CONFLICT_HELP}\n`);
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
// A conflict or a webhook that showed up while the question was open counts too.
|
|
227
|
+
const late = conn.health().conflict ?? (conn.checkWebhook && (await conn.checkWebhook()) !== 'clear' ? (conn.health().conflict ?? 'the bot could not be checked again') : null);
|
|
228
|
+
if (late) {
|
|
229
|
+
audit.add(now(), 'pairing_refused', { why: 'conflict-after-confirm' });
|
|
230
|
+
io.stderr.write(`dexterm telegram: NOT paired: ${late}. ${CONFLICT_HELP}\n`);
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
const next = load(home, io) ?? cfg;
|
|
234
|
+
next.telegram.allow = [...next.telegram.allow.filter((a) => !(a.senderId === p.senderId && a.chatId === p.chatId)), { senderId: p.senderId, chatId: p.chatId, tiers, pairedAt: now() }];
|
|
235
|
+
next.telegram.enabled = true;
|
|
236
|
+
saveConnectors(home, next);
|
|
237
|
+
audit.add(now(), 'paired', { senderId: p.senderId, chatId: p.chatId, tiers });
|
|
238
|
+
try {
|
|
239
|
+
const ref = await conn.send({ chatId: p.chatId }, { kind: 'text', badge: 'PAPER', lines: ['Paired with dexterm on your computer. /help lists the commands. I never ask for keys or seed phrases.'] });
|
|
240
|
+
audit.add(now(), 'out', { template: 'paired', chatId: p.chatId, messageId: ref.messageId });
|
|
241
|
+
}
|
|
242
|
+
catch { /* the pairing is saved; the chat note is a courtesy */ }
|
|
243
|
+
io.stdout.write(`✓ paired: ${who} in its private chat, tiers ${tiers.join(', ')}. The bot answers nobody else.\n`);
|
|
244
|
+
io.stdout.write('The runner starts the bot by itself: dexterm autotrade run <strategy> --paper. Turn on Telegram two-step verification for your account.\n');
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
export async function telegramCommand(argv, io, deps = {}) {
|
|
248
|
+
const sub = argv[0];
|
|
249
|
+
const home = dextermHome(io.env);
|
|
250
|
+
try {
|
|
251
|
+
switch (sub) {
|
|
252
|
+
case undefined:
|
|
253
|
+
case 'help':
|
|
254
|
+
case '--help':
|
|
255
|
+
case '-h':
|
|
256
|
+
io.stdout.write(TELEGRAM_USAGE);
|
|
257
|
+
return 0;
|
|
258
|
+
case 'setup': {
|
|
259
|
+
const tiers = parseTiers(flag(argv, '--tiers'));
|
|
260
|
+
if (!tiers) {
|
|
261
|
+
io.stderr.write('dexterm telegram: --tiers takes notify,control,approve\n');
|
|
262
|
+
return 2;
|
|
263
|
+
}
|
|
264
|
+
if (argv.slice(1).some((a) => looksLikeBotToken(a))) {
|
|
265
|
+
io.stderr.write('dexterm telegram: never pass the token as an argument (it would land in your shell history); run setup and paste it at the prompt\n');
|
|
266
|
+
return 2;
|
|
267
|
+
}
|
|
268
|
+
const cfg = load(home, io);
|
|
269
|
+
if (!cfg)
|
|
270
|
+
return 1;
|
|
271
|
+
io.stdout.write('1. In Telegram, open @BotFather, send /newbot, and copy the token it gives you.\n2. Paste it below (nothing is shown while you type).\n');
|
|
272
|
+
const token = await (deps.readToken ? deps.readToken() : readHidden(io, 'bot token: '));
|
|
273
|
+
if (!token) {
|
|
274
|
+
io.stderr.write('dexterm telegram: no token given\n');
|
|
275
|
+
return 2;
|
|
276
|
+
}
|
|
277
|
+
if (!looksLikeBotToken(token)) {
|
|
278
|
+
io.stderr.write('dexterm telegram: that does not look like a bot token (digits, a colon, then about 35 letters, digits, - or _)\n');
|
|
279
|
+
return 2;
|
|
280
|
+
}
|
|
281
|
+
const conn = connectorFor(deps, () => token, io);
|
|
282
|
+
let me;
|
|
283
|
+
try {
|
|
284
|
+
if (!conn.getMe)
|
|
285
|
+
throw new Error('this connector cannot check a token');
|
|
286
|
+
me = await conn.getMe();
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
io.stderr.write(`dexterm telegram: the token did not work: ${redact(e.message, token)}\n`);
|
|
290
|
+
return 1;
|
|
291
|
+
}
|
|
292
|
+
const store = deps.store ?? chooseSecretStore(home, io.env);
|
|
293
|
+
store.set(token);
|
|
294
|
+
cfg.telegram = { ...cfg.telegram, bot: { id: me.id, username: me.username }, secretStore: store.kind === 'memory' ? 'file' : store.kind, enabled: cfg.telegram.allow.length > 0 };
|
|
295
|
+
saveConnectors(home, cfg);
|
|
296
|
+
io.stdout.write(`✓ bot @${me.username} checked; token stored in ${store.kind === 'keychain' ? 'the macOS keychain' : store.kind === 'libsecret' ? 'the system keyring (libsecret)' : store.kind === 'file' ? 'a 0600 file under your dexterm home (secrets/)' : 'memory'}. Config: ${connectorsPath(home)} (no secrets in it).\n`);
|
|
297
|
+
if (argv.includes('--no-pair')) {
|
|
298
|
+
io.stdout.write('Pair later: dexterm telegram pair\n');
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
return pairFlow(home, io, deps, tiers);
|
|
302
|
+
}
|
|
303
|
+
case 'pair': {
|
|
304
|
+
const tiers = parseTiers(flag(argv, '--tiers'));
|
|
305
|
+
if (!tiers) {
|
|
306
|
+
io.stderr.write('dexterm telegram: --tiers takes notify,control,approve\n');
|
|
307
|
+
return 2;
|
|
308
|
+
}
|
|
309
|
+
return pairFlow(home, io, deps, tiers);
|
|
310
|
+
}
|
|
311
|
+
case 'status': {
|
|
312
|
+
const cfg = load(home, io);
|
|
313
|
+
if (!cfg)
|
|
314
|
+
return 1;
|
|
315
|
+
const t = cfg.telegram;
|
|
316
|
+
const store = t.secretStore ? (deps.store ?? secretStore(t.secretStore, home)) : null;
|
|
317
|
+
const hasToken = store ? store.get() !== null : false;
|
|
318
|
+
const chain = verifyChatAudit(home);
|
|
319
|
+
const view = {
|
|
320
|
+
enabled: t.enabled, bot: t.bot ? `@${t.bot.username}` : null, tokenStored: hasToken, secretStore: t.secretStore, tiers: t.tiers,
|
|
321
|
+
paired: t.allow.map((a) => ({ senderId: a.senderId, chatId: a.chatId, tiers: a.tiers, pairedAt: a.pairedAt ? new Date(a.pairedAt).toISOString() : null })),
|
|
322
|
+
approvals: t.approvals, approvalExpirySec: t.approvalExpirySec, repriceTolPct: t.repriceTolPct, notify: t.notify, dailySummaryAt: t.dailySummaryAt, quietHours: t.quietHours,
|
|
323
|
+
audit: { days: chain.length, broken: chain.filter((c) => !c.check.ok).map((c) => c.day) },
|
|
324
|
+
};
|
|
325
|
+
if (argv.includes('--json')) {
|
|
326
|
+
io.stdout.write(`${JSON.stringify(view, null, 2)}\n`);
|
|
327
|
+
return 0;
|
|
328
|
+
}
|
|
329
|
+
io.stdout.write(`telegram · ${t.enabled && t.allow.length && hasToken ? 'ready (the runner starts it)' : 'not ready'}\n`);
|
|
330
|
+
io.stdout.write(` bot ${view.bot ?? '(not set up)'} · token ${hasToken ? `stored (${t.secretStore})` : 'missing'}\n`);
|
|
331
|
+
io.stdout.write(` paired ${t.allow.length ? t.allow.map((a) => `id ${a.senderId} [${a.tiers.join(',')}]`).join('; ') : 'nobody (dexterm telegram pair)'}\n`);
|
|
332
|
+
io.stdout.write(` approvals ${t.approvals} · expiry ${t.approvalExpirySec} s · re-quote tolerance ${t.repriceTolPct}%\n`);
|
|
333
|
+
io.stdout.write(` notify candidates ${t.notify.candidates} · entries ${t.notify.entries ? 'on' : 'off'} · closes ${t.notify.closes ? 'on' : 'off'} · halts ${t.notify.halts ? 'on' : 'off'} · spend ${t.notify.spend ? 'on' : 'off'} · daily ${t.notify.daily ? `at ${t.dailySummaryAt ?? 'off'}` : 'off'}${t.quietHours ? ` · quiet ${t.quietHours}` : ''}\n`);
|
|
334
|
+
io.stdout.write(` audit ${chain.length} day file${chain.length === 1 ? '' : 's'}${view.audit.broken.length ? ` · BROKEN: ${view.audit.broken.join(', ')}` : ''}\n`);
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
case 'approvals': {
|
|
338
|
+
const v = argv[1];
|
|
339
|
+
if (v !== 'on' && v !== 'off') {
|
|
340
|
+
io.stderr.write('dexterm telegram: approvals on|off\n');
|
|
341
|
+
return 2;
|
|
342
|
+
}
|
|
343
|
+
const cfg = load(home, io);
|
|
344
|
+
if (!cfg)
|
|
345
|
+
return 1;
|
|
346
|
+
cfg.telegram.approvals = v;
|
|
347
|
+
saveConnectors(home, cfg);
|
|
348
|
+
io.stdout.write(v === 'on' ? 'approvals on: every new entry waits for a tap in your paired chat (no answer = skip)\n' : 'approvals off: entries follow the strategy (`entry.decide: "agent+approve"` still asks every time)\n');
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
351
|
+
case 'notify': {
|
|
352
|
+
const ev = argv[1];
|
|
353
|
+
const v = argv[2];
|
|
354
|
+
const cfg = load(home, io);
|
|
355
|
+
if (!cfg)
|
|
356
|
+
return 1;
|
|
357
|
+
if (ev === 'candidates' && (v === 'digest' || v === 'each' || v === 'off'))
|
|
358
|
+
cfg.telegram.notify.candidates = v;
|
|
359
|
+
else if (['entries', 'closes', 'halts', 'spend', 'daily'].includes(ev ?? '') && (v === 'on' || v === 'off'))
|
|
360
|
+
cfg.telegram.notify[ev] = v === 'on';
|
|
361
|
+
else {
|
|
362
|
+
io.stderr.write('dexterm telegram: notify candidates digest|each|off, or notify entries|closes|halts|spend|daily on|off\n');
|
|
363
|
+
return 2;
|
|
364
|
+
}
|
|
365
|
+
saveConnectors(home, cfg);
|
|
366
|
+
io.stdout.write(`notify ${ev} ${v}\n`);
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
case 'set': {
|
|
370
|
+
const k = argv[1];
|
|
371
|
+
const v = argv[2];
|
|
372
|
+
const cfg = load(home, io);
|
|
373
|
+
if (!cfg || !v) {
|
|
374
|
+
if (cfg)
|
|
375
|
+
io.stderr.write('dexterm telegram: set summary-at HH:MM|off · set quiet HH:MM-HH:MM|off · set expiry 60..120\n');
|
|
376
|
+
return cfg ? 2 : 1;
|
|
377
|
+
}
|
|
378
|
+
if (k === 'summary-at' && (v === 'off' || /^([01]\d|2[0-3]):[0-5]\d$/.test(v)))
|
|
379
|
+
cfg.telegram.dailySummaryAt = v === 'off' ? null : v;
|
|
380
|
+
else if (k === 'quiet' && (v === 'off' || /^([01]\d|2[0-3]):[0-5]\d-([01]\d|2[0-3]):[0-5]\d$/.test(v)))
|
|
381
|
+
cfg.telegram.quietHours = v === 'off' ? null : v;
|
|
382
|
+
else if (k === 'expiry' && Number(v) >= 60 && Number(v) <= 120)
|
|
383
|
+
cfg.telegram.approvalExpirySec = Math.round(Number(v));
|
|
384
|
+
else {
|
|
385
|
+
io.stderr.write('dexterm telegram: set summary-at HH:MM|off · set quiet HH:MM-HH:MM|off · set expiry 60..120\n');
|
|
386
|
+
return 2;
|
|
387
|
+
}
|
|
388
|
+
saveConnectors(home, cfg);
|
|
389
|
+
io.stdout.write(`set ${k} ${v}\n`);
|
|
390
|
+
return 0;
|
|
391
|
+
}
|
|
392
|
+
case 'test': {
|
|
393
|
+
const cfg = load(home, io);
|
|
394
|
+
if (!cfg)
|
|
395
|
+
return 1;
|
|
396
|
+
const store = storeOf(home, cfg, io, deps);
|
|
397
|
+
const token = store.get();
|
|
398
|
+
if (!token || !cfg.telegram.allow.length) {
|
|
399
|
+
io.stderr.write('dexterm telegram: set up and pair first (dexterm telegram setup)\n');
|
|
400
|
+
return 1;
|
|
401
|
+
}
|
|
402
|
+
const conn = connectorFor(deps, () => token, io);
|
|
403
|
+
const audit = new ChatAudit(home, { knownToken: () => token });
|
|
404
|
+
let bad = 0;
|
|
405
|
+
for (const chatId of [...new Set(cfg.telegram.allow.map((a) => a.chatId))]) {
|
|
406
|
+
try {
|
|
407
|
+
const ref = await conn.send({ chatId }, { kind: 'text', badge: 'PAPER', lines: ['TEST · dexterm can reach this chat.', 'Commands: /status /pause /kill /help. Trades are approved only with buttons.'] });
|
|
408
|
+
audit.add((deps.now ?? Date.now)(), 'out', { template: 'test', chatId, messageId: ref.messageId });
|
|
409
|
+
io.stdout.write(`✓ sent to chat ${chatId}\n`);
|
|
410
|
+
}
|
|
411
|
+
catch (e) {
|
|
412
|
+
bad++;
|
|
413
|
+
io.stderr.write(`✗ chat ${chatId}: ${redact(e.message, token)}\n`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return bad ? 1 : 0;
|
|
417
|
+
}
|
|
418
|
+
case 'audit': {
|
|
419
|
+
const chain = verifyChatAudit(home);
|
|
420
|
+
if (!chain.length) {
|
|
421
|
+
io.stdout.write('no chat audit yet (autotrade/chat/ under your dexterm home)\n');
|
|
422
|
+
return 0;
|
|
423
|
+
}
|
|
424
|
+
const verify = argv.includes('--verify');
|
|
425
|
+
for (const c of chain)
|
|
426
|
+
io.stdout.write(verify ? (c.check.ok ? `✓ ${c.day} ${c.check.events} events head ${c.check.head}\n` : `✗ ${c.day} broken at event ${c.check.at}: ${c.check.error}\n`) : ` ${c.day} ${c.check.events} events\n`);
|
|
427
|
+
const broken = chain.filter((c) => !c.check.ok).length;
|
|
428
|
+
if (verify)
|
|
429
|
+
io.stdout.write(broken ? `${broken} broken day file${broken === 1 ? '' : 's'}\n` : 'every chat audit chain verifies\n');
|
|
430
|
+
return verify && broken ? 1 : 0;
|
|
431
|
+
}
|
|
432
|
+
case 'revoke': {
|
|
433
|
+
const cfg = load(home, io);
|
|
434
|
+
if (!cfg)
|
|
435
|
+
return 1;
|
|
436
|
+
storeOf(home, cfg, io, deps).remove();
|
|
437
|
+
cfg.telegram = { ...defaultTelegram(), notify: cfg.telegram.notify, dailySummaryAt: cfg.telegram.dailySummaryAt, quietHours: cfg.telegram.quietHours };
|
|
438
|
+
saveConnectors(home, cfg);
|
|
439
|
+
io.stdout.write('✓ token removed and every paired chat forgotten. Also revoke the token in @BotFather (/revoke) if it may have leaked.\n');
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
default:
|
|
443
|
+
io.stderr.write(`dexterm telegram: unknown subcommand ${JSON.stringify(sub)}\n\n${TELEGRAM_USAGE}`);
|
|
444
|
+
return 2;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch (e) {
|
|
448
|
+
io.stderr.write(`dexterm telegram: ${redact(e.message)}\n`);
|
|
449
|
+
return 1;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
@@ -104,55 +104,16 @@ function editField(ts, key) {
|
|
|
104
104
|
*/
|
|
105
105
|
export function ticketKey(ts, key, ctx) {
|
|
106
106
|
ts.message = null;
|
|
107
|
-
if (ts.phase === 'filling'
|
|
107
|
+
if (ts.phase === 'filling')
|
|
108
108
|
return 'none'; // in-flight: ignore input
|
|
109
|
-
if (ts.phase === '
|
|
110
|
-
|
|
111
|
-
if (ts.phase === 'filled' || ts.phase === 'error' || ts.phase === 'refused') {
|
|
112
|
-
// a receipt is showing: esc closes, any other key starts a fresh ticket for the same mint (same mode)
|
|
109
|
+
if (ts.phase === 'filled' || ts.phase === 'error') {
|
|
110
|
+
// a receipt is showing: esc closes, any other key starts a fresh ticket for the same mint
|
|
113
111
|
if (key.name === 'esc')
|
|
114
112
|
return 'close';
|
|
115
113
|
const side = ts.side;
|
|
116
114
|
Object.assign(ts, openTicket({ mint: ts.mint, symbol: ts.symbol, decimals: ts.decimals, side, gateOn: ts.gateOn, slippageBps: slippageBps(ts), mode: ts.mode }));
|
|
117
115
|
return 'changed';
|
|
118
116
|
}
|
|
119
|
-
// ---- LIVE plan screen (task-1328): the keypress gate for a REAL trade. Y / ⏎ confirms (with the
|
|
120
|
-
// typed-back size above the threshold, exactly like paper); esc returns to editing; ANY other key
|
|
121
|
-
// cancels — a stray key can never confirm, and never navigates mid-plan.
|
|
122
|
-
if (ts.phase === 'plan') {
|
|
123
|
-
if (key.name === 'esc') {
|
|
124
|
-
ts.phase = 'editing';
|
|
125
|
-
ts.typeback = '';
|
|
126
|
-
return 'drop-plan';
|
|
127
|
-
}
|
|
128
|
-
if (ts.typebackRequired && key.name === 'char' && key.ch && DIGIT.test(key.ch)) {
|
|
129
|
-
if (key.ch === '.' && ts.typeback.includes('.'))
|
|
130
|
-
return 'none';
|
|
131
|
-
ts.typeback = (ts.typeback + key.ch).slice(0, 12);
|
|
132
|
-
return 'none';
|
|
133
|
-
}
|
|
134
|
-
if (ts.typebackRequired && key.name === 'backspace') {
|
|
135
|
-
ts.typeback = ts.typeback.slice(0, -1);
|
|
136
|
-
return 'none';
|
|
137
|
-
}
|
|
138
|
-
const confirmKey = (key.name === 'char' && (key.ch === 'y' || key.ch === 'Y')) || key.name === 'enter';
|
|
139
|
-
if (confirmKey) {
|
|
140
|
-
if (ts.typebackRequired) {
|
|
141
|
-
const want = parseNum(ts.sizeInput);
|
|
142
|
-
const got = parseNum(ts.typeback);
|
|
143
|
-
if (!(Number.isFinite(got) && Math.abs(got - want) < 1e-9)) {
|
|
144
|
-
ts.message = `type the size (${ts.sizeInput}) to confirm this LIVE ${ts.side}`;
|
|
145
|
-
return 'none';
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
ts.phase = 'signing';
|
|
149
|
-
return 'confirm-live';
|
|
150
|
-
}
|
|
151
|
-
ts.phase = 'editing';
|
|
152
|
-
ts.typeback = '';
|
|
153
|
-
ts.message = 'plan cancelled';
|
|
154
|
-
return 'drop-plan';
|
|
155
|
-
}
|
|
156
117
|
if (ts.phase === 'confirm') {
|
|
157
118
|
if (key.name === 'esc') {
|
|
158
119
|
ts.phase = 'editing';
|
|
@@ -191,8 +152,6 @@ export function ticketKey(ts, key, ctx) {
|
|
|
191
152
|
// ---- editing
|
|
192
153
|
if (key.name === 'esc')
|
|
193
154
|
return 'close';
|
|
194
|
-
if (key.name === 'char' && (key.ch === 'L' || key.ch === 'l') && !key.ctrl && !key.alt)
|
|
195
|
-
return 'toggle-live';
|
|
196
155
|
if (key.name === 'tab') {
|
|
197
156
|
ts.field = ts.field === 'size' ? 'slippage' : 'size';
|
|
198
157
|
return 'none';
|
|
@@ -217,97 +176,13 @@ export function ticketKey(ts, key, ctx) {
|
|
|
217
176
|
}
|
|
218
177
|
ts.typebackRequired = ts.gateOn && ctx.notionalSol > ctx.typebackThresholdSol;
|
|
219
178
|
ts.typeback = '';
|
|
220
|
-
if (ts.mode === 'live') {
|
|
221
|
-
ts.phase = 'planning';
|
|
222
|
-
return 'plan';
|
|
223
|
-
}
|
|
224
179
|
ts.phase = 'confirm';
|
|
225
180
|
return 'none';
|
|
226
181
|
}
|
|
227
|
-
// task-1368: f on a LIVE buy = size the order to the signer's cap (the binding knows the caps + SOL/USD; the
|
|
228
|
-
// reducer only names the effect). Inert on PAPER (no caps) and on a sell (sized in % of the position).
|
|
229
|
-
if (key.name === 'char' && (key.ch === 'f' || key.ch === 'F') && !key.ctrl && !key.alt) {
|
|
230
|
-
return ts.mode === 'live' && ts.side === 'buy' ? 'fit-cap' : 'none';
|
|
231
|
-
}
|
|
232
182
|
if (editField(ts, key))
|
|
233
183
|
return 'changed';
|
|
234
184
|
return 'none';
|
|
235
185
|
}
|
|
236
|
-
// --------------------------------------------------------------------------- task-1368 preflight + hints (pure)
|
|
237
|
-
/**
|
|
238
|
-
* Fees + token-account rent the signer's simulated SOL-out may add on top of the size of a BUY (a tx fee, a priority
|
|
239
|
-
* fee and one associated-token-account rent of 0.00203928 SOL when the wallet has never held the token). The
|
|
240
|
-
* signer prices the LARGER of the planned SOL leg and the simulated SOL out, so a size that is exactly the cap in
|
|
241
|
-
* USD is refused once rent lands on top; `fitSizeSol` leaves this much room.
|
|
242
|
-
*/
|
|
243
|
-
export const CAP_FIT_OVERHEAD_SOL = 0.0025;
|
|
244
|
-
const usd = (n) => `$${n.toFixed(2).replace(/\.00$/, '')}`;
|
|
245
|
-
/**
|
|
246
|
-
* Size vs the signer's caps BEFORE ⏎ (task-1368 item b): the USD value of the size at the signer's own SOL/USD, the
|
|
247
|
-
* per-trade cap and what is left of the daily cap, a red verdict when the size is over, and the size that fits.
|
|
248
|
-
* Pure: the signer still decides at precheck/confirm — this only stops the user from building a plan that will be
|
|
249
|
-
* refused for a reason they could not see.
|
|
250
|
-
*/
|
|
251
|
-
export function capPreflight(i) {
|
|
252
|
-
const sizeSol = Number.isFinite(i.sizeSol) && i.sizeSol > 0 ? i.sizeSol : 0;
|
|
253
|
-
const price = i.solUsd !== null && Number.isFinite(i.solUsd) && i.solUsd > 0 ? i.solUsd : null;
|
|
254
|
-
const perTradeUsd = Math.max(0, i.perTradeUsd);
|
|
255
|
-
const dailyRemainingUsd = Math.max(0, i.dailyUsd - Math.max(0, i.spentTodayUsd));
|
|
256
|
-
const capUsd = Math.min(perTradeUsd, dailyRemainingUsd);
|
|
257
|
-
const sizeUsd = price !== null && sizeSol > 0 ? sizeSol * price : null;
|
|
258
|
-
const over = sizeUsd !== null && sizeUsd > capUsd + 1e-9;
|
|
259
|
-
const binding = !over ? null : dailyRemainingUsd < perTradeUsd ? 'daily' : 'per-trade';
|
|
260
|
-
const overhead = Math.max(0, i.overheadSol ?? CAP_FIT_OVERHEAD_SOL);
|
|
261
|
-
let fitSizeSol = null;
|
|
262
|
-
if (price !== null && capUsd > 0) {
|
|
263
|
-
const fit = Math.floor((capUsd / price - overhead) * 1000) / 1000;
|
|
264
|
-
fitSizeSol = fit > 0 ? fit : null;
|
|
265
|
-
}
|
|
266
|
-
const capText = `cap ${usd(perTradeUsd)}/trade · ${usd(dailyRemainingUsd)} left of ${usd(Math.max(0, i.dailyUsd))} today`;
|
|
267
|
-
let line;
|
|
268
|
-
if (sizeSol <= 0)
|
|
269
|
-
line = `${capText} · enter a size`;
|
|
270
|
-
else if (price === null)
|
|
271
|
-
line = `${sizeSol} SOL = $? (the signer has no SOL/USD yet; it will refuse an unpriced trade on mainnet) · ${capText}`;
|
|
272
|
-
else if (over) {
|
|
273
|
-
const which = binding === 'daily' ? `${usd(dailyRemainingUsd)} left today` : `${usd(perTradeUsd)} cap`;
|
|
274
|
-
line = `${sizeSol} SOL = ${usd(sizeUsd)} > ${which}: ${fitSizeSol !== null ? `use ≤ ${fitSizeSol} SOL (f fits it; fees + token-account rent counted)` : 'nothing fits — the cap is spent for today'}`;
|
|
275
|
-
}
|
|
276
|
-
else
|
|
277
|
-
line = `${sizeSol} SOL ≈ ${usd(sizeUsd)} at SOL ${usd(price)} · ${capText}`;
|
|
278
|
-
return { sizeSol, sizeUsd, perTradeUsd, dailyRemainingUsd, capUsd, over, fitSizeSol, binding, line };
|
|
279
|
-
}
|
|
280
|
-
const WSOL = 'So11111111111111111111111111111111111111112';
|
|
281
|
-
/**
|
|
282
|
-
* task-1368 item c: a DEVNET signer + a Jupiter swap = a transaction built on MAINNET state (there is no Jupiter
|
|
283
|
-
* devnet): it can never simulate or fill on devnet, so say so BEFORE ⏎ and name the two ways out. Null for the
|
|
284
|
-
* probe (built for devnet on purpose), for wrapped SOL itself, and on mainnet.
|
|
285
|
-
*/
|
|
286
|
-
export function devnetMintWarning(a) {
|
|
287
|
-
if (a.network !== 'devnet' || a.probe || a.mint === WSOL)
|
|
288
|
-
return null;
|
|
289
|
-
const caps = `caps ${usd(a.perTradeUsd ?? 5)}/${usd(a.dailyUsd ?? 20)}`;
|
|
290
|
-
return `DEVNET cannot fill mainnet tokens: paper trade (L) or restart with --mainnet (real funds, ${caps})`;
|
|
291
|
-
}
|
|
292
|
-
const MAINNET_STATE_ERR = /invalid transaction|blockhash|account.*not found|not found|could not find|address (table|lookup)|AccountNotFound|ProgramAccountNotFound|unknown program|InvalidAccountData/i;
|
|
293
|
-
/**
|
|
294
|
-
* task-1368 item f: a plain-English hint under a failed simulation when the failure pattern is the devnet-vs-mainnet
|
|
295
|
-
* mismatch (a mainnet route simulated on a devnet node: accounts, lookup tables or blockhash not found). Null when
|
|
296
|
-
* the simulation passed, was not run, or failed for another reason (the full error is still shown).
|
|
297
|
-
*/
|
|
298
|
-
export function simFailureHint(p, caps) {
|
|
299
|
-
if (p.kind !== 'swap' || !p.sim.ran || p.sim.ok)
|
|
300
|
-
return null;
|
|
301
|
-
const err = p.sim.error ?? '';
|
|
302
|
-
const mainnetState = MAINNET_STATE_ERR.test(err);
|
|
303
|
-
if (p.network === 'devnet' && (mainnetState || p.mintOnNetwork === false)) {
|
|
304
|
-
const mint = p.mintOnNetwork === false ? ' (the mint does not exist on devnet)' : '';
|
|
305
|
-
return `devnet-vs-mainnet wall${mint}: a Jupiter swap is built on MAINNET state (accounts, lookup tables, blockhash) that a devnet node does not have. Paper trade (L) or restart with --mainnet (real funds, caps ${usd(caps?.perTradeUsd ?? 5)}/${usd(caps?.dailyUsd ?? 20)}).`;
|
|
306
|
-
}
|
|
307
|
-
if (mainnetState)
|
|
308
|
-
return 'the node could not find the route\'s accounts or blockhash: a stale plan or the wrong RPC network — rebuild with ⏎, or check --trade-rpc';
|
|
309
|
-
return null;
|
|
310
|
-
}
|
|
311
186
|
/** SOL notional of a buy order sized in SOL/%, used to gate the typed-back step without a quote. */
|
|
312
187
|
export function buyNotionalSol(ts, balanceLamports) {
|
|
313
188
|
const n = parseNum(ts.sizeInput);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// The one place the app's version lives (task-1397). --version, the help banner and `dexterm update` read it;
|
|
2
2
|
// cli.test.ts keeps it equal to packages/cli/package.json (release.yml refuses a tag that differs).
|
|
3
|
-
export const VERSION = '0.1.
|
|
3
|
+
export const VERSION = '0.1.7';
|