dexterm-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +379 -0
- package/dist/packages/agents/src/executor.js +177 -0
- package/dist/packages/agents/src/gate.js +176 -0
- package/dist/packages/agents/src/identity.js +179 -0
- package/dist/packages/agents/src/index.js +15 -0
- package/dist/packages/agents/src/proposals.js +153 -0
- package/dist/packages/agents/src/schema.js +87 -0
- package/dist/packages/agents/src/sdk.js +140 -0
- package/dist/packages/agents/src/tiers.js +114 -0
- package/dist/packages/agents/src/tools.js +69 -0
- package/dist/packages/agents/src/ws.js +158 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
- package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +67 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -0
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
- package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
- package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
- package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
- package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
- package/dist/packages/cli/bin/dexterm.mjs +11 -0
- package/dist/packages/cli/src/agent-cmd.js +303 -0
- package/dist/packages/cli/src/agent-roster.js +26 -0
- package/dist/packages/cli/src/analyst/backend.js +526 -0
- package/dist/packages/cli/src/analyst/results.js +38 -0
- package/dist/packages/cli/src/analyst/session.js +319 -0
- package/dist/packages/cli/src/audit.js +51 -0
- package/dist/packages/cli/src/backtest-cmd.js +149 -0
- package/dist/packages/cli/src/boot-state.js +62 -0
- package/dist/packages/cli/src/custom.js +261 -0
- package/dist/packages/cli/src/display.js +191 -0
- package/dist/packages/cli/src/doctor.js +361 -0
- package/dist/packages/cli/src/feed.js +634 -0
- package/dist/packages/cli/src/fixtures.js +275 -0
- package/dist/packages/cli/src/follow-cmd.js +221 -0
- package/dist/packages/cli/src/images-config.js +59 -0
- package/dist/packages/cli/src/index.js +31 -0
- package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
- package/dist/packages/cli/src/larp-record.js +21 -0
- package/dist/packages/cli/src/live/copy.js +80 -0
- package/dist/packages/cli/src/live/flow.js +233 -0
- package/dist/packages/cli/src/live/index.js +509 -0
- package/dist/packages/cli/src/live/intel.js +227 -0
- package/dist/packages/cli/src/live/newpairs.js +334 -0
- package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
- package/dist/packages/cli/src/live/screens/token-live.js +459 -0
- package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
- package/dist/packages/cli/src/live/token.js +585 -0
- package/dist/packages/cli/src/live/trade.js +929 -0
- package/dist/packages/cli/src/live/watchlist.js +178 -0
- package/dist/packages/cli/src/main.js +5583 -0
- package/dist/packages/cli/src/narrative-cmd.js +76 -0
- package/dist/packages/cli/src/pack-cmd.js +323 -0
- package/dist/packages/cli/src/plan-cmd.js +278 -0
- package/dist/packages/cli/src/popup.js +810 -0
- package/dist/packages/cli/src/rules-cmd.js +131 -0
- package/dist/packages/cli/src/rules.js +339 -0
- package/dist/packages/cli/src/scan/command.js +57 -0
- package/dist/packages/cli/src/scan/engine.js +419 -0
- package/dist/packages/cli/src/scan/observe.js +64 -0
- package/dist/packages/cli/src/scan/ui.js +98 -0
- package/dist/packages/cli/src/screener/filter.js +241 -0
- package/dist/packages/cli/src/screener/presets.js +80 -0
- package/dist/packages/cli/src/screener/rows.js +146 -0
- package/dist/packages/cli/src/screener/rug.js +50 -0
- package/dist/packages/cli/src/screener/sort.js +52 -0
- package/dist/packages/cli/src/screener/view.js +183 -0
- package/dist/packages/cli/src/screens/analyst-session.js +343 -0
- package/dist/packages/cli/src/screens/analyst.js +326 -0
- package/dist/packages/cli/src/screens/boot.js +74 -0
- package/dist/packages/cli/src/screens/bubbles.js +255 -0
- package/dist/packages/cli/src/screens/coin.js +33 -0
- package/dist/packages/cli/src/screens/follows.js +201 -0
- package/dist/packages/cli/src/screens/kol.js +90 -0
- package/dist/packages/cli/src/screens/narrative.js +90 -0
- package/dist/packages/cli/src/screens/newpairs.js +150 -0
- package/dist/packages/cli/src/screens/overlay.js +257 -0
- package/dist/packages/cli/src/screens/plans.js +126 -0
- package/dist/packages/cli/src/screens/positions.js +176 -0
- package/dist/packages/cli/src/screens/rules.js +170 -0
- package/dist/packages/cli/src/screens/screener.js +459 -0
- package/dist/packages/cli/src/screens/social-panel.js +107 -0
- package/dist/packages/cli/src/screens/ticket.js +342 -0
- package/dist/packages/cli/src/screens/token.js +312 -0
- package/dist/packages/cli/src/screens/trending.js +181 -0
- package/dist/packages/cli/src/screens/watchlist.js +104 -0
- package/dist/packages/cli/src/social/demo.js +96 -0
- package/dist/packages/cli/src/social/view.js +250 -0
- package/dist/packages/cli/src/state.js +463 -0
- package/dist/packages/cli/src/theme-cmd.js +126 -0
- package/dist/packages/cli/src/trade.js +319 -0
- package/dist/packages/cli/src/voice-hud.js +42 -0
- package/dist/packages/cli/src/wallet-cmd.js +440 -0
- package/dist/packages/cli/src/wallet-command.js +31 -0
- package/dist/packages/cli/src/widgets.js +208 -0
- package/dist/packages/cli/src/workspace-cmd.js +68 -0
- package/dist/packages/cli/src/x-cmd.js +154 -0
- package/dist/packages/custom/src/config.js +213 -0
- package/dist/packages/custom/src/index.js +10 -0
- package/dist/packages/custom/src/keymap.js +107 -0
- package/dist/packages/custom/src/layout.js +235 -0
- package/dist/packages/custom/src/packs.js +633 -0
- package/dist/packages/custom/src/rules.js +704 -0
- package/dist/packages/custom/src/schema.js +515 -0
- package/dist/packages/custom/src/themes.js +176 -0
- package/dist/packages/custom/src/widget-html.js +222 -0
- package/dist/packages/custom/src/workspace.js +141 -0
- package/dist/packages/data/src/analyst-actions.js +133 -0
- package/dist/packages/data/src/budget.js +88 -0
- package/dist/packages/data/src/cache.js +142 -0
- package/dist/packages/data/src/candles.js +168 -0
- package/dist/packages/data/src/clock.js +64 -0
- package/dist/packages/data/src/credits.js +137 -0
- package/dist/packages/data/src/detail.js +97 -0
- package/dist/packages/data/src/fast-lane.js +321 -0
- package/dist/packages/data/src/fixtures.js +135 -0
- package/dist/packages/data/src/health.js +245 -0
- package/dist/packages/data/src/http.js +277 -0
- package/dist/packages/data/src/images/cache.js +141 -0
- package/dist/packages/data/src/images/decode-worker.js +28 -0
- package/dist/packages/data/src/images/decode.js +81 -0
- package/dist/packages/data/src/images/fetch.js +162 -0
- package/dist/packages/data/src/images/gif.js +214 -0
- package/dist/packages/data/src/images/index.js +10 -0
- package/dist/packages/data/src/images/jpeg.js +544 -0
- package/dist/packages/data/src/images/png.js +310 -0
- package/dist/packages/data/src/images/service.js +334 -0
- package/dist/packages/data/src/images/types.js +27 -0
- package/dist/packages/data/src/index.js +208 -0
- package/dist/packages/data/src/intel/helius-history.js +59 -0
- package/dist/packages/data/src/intel/holder-class.js +125 -0
- package/dist/packages/data/src/intel/rules.js +421 -0
- package/dist/packages/data/src/intel/scan.js +170 -0
- package/dist/packages/data/src/larp/evidence.js +288 -0
- package/dist/packages/data/src/larp/links.js +137 -0
- package/dist/packages/data/src/larp/robots.js +90 -0
- package/dist/packages/data/src/larp/safe-fetch.js +389 -0
- package/dist/packages/data/src/limiter.js +341 -0
- package/dist/packages/data/src/model.js +12 -0
- package/dist/packages/data/src/paper.js +331 -0
- package/dist/packages/data/src/rpc-config.js +126 -0
- package/dist/packages/data/src/rpc-pool.js +166 -0
- package/dist/packages/data/src/social/chain.js +83 -0
- package/dist/packages/data/src/social/dedupe.js +205 -0
- package/dist/packages/data/src/social/feed.js +462 -0
- package/dist/packages/data/src/social/firewall.js +110 -0
- package/dist/packages/data/src/social/index.js +11 -0
- package/dist/packages/data/src/social/kol.js +269 -0
- package/dist/packages/data/src/social/model.js +11 -0
- package/dist/packages/data/src/social/rank.js +184 -0
- package/dist/packages/data/src/social/rss.js +113 -0
- package/dist/packages/data/src/social/summary.js +104 -0
- package/dist/packages/data/src/social/venues.js +186 -0
- package/dist/packages/data/src/sources/dexscreener.js +170 -0
- package/dist/packages/data/src/sources/geckoterminal.js +133 -0
- package/dist/packages/data/src/sources/goplus.js +161 -0
- package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
- package/dist/packages/data/src/sources/jupiter.js +198 -0
- package/dist/packages/data/src/sources/pda.js +113 -0
- package/dist/packages/data/src/sources/pump-curve.js +58 -0
- package/dist/packages/data/src/sources/pumpportal.js +247 -0
- package/dist/packages/data/src/sources/rugcheck.js +71 -0
- package/dist/packages/data/src/sources/solana-rpc.js +299 -0
- package/dist/packages/data/src/sources/solana-ws.js +287 -0
- package/dist/packages/data/src/store.js +289 -0
- package/dist/packages/data/src/trade/bridge.js +464 -0
- package/dist/packages/data/src/trade/caps.js +323 -0
- package/dist/packages/data/src/trade/copy/auto.js +385 -0
- package/dist/packages/data/src/trade/copy/follows.js +103 -0
- package/dist/packages/data/src/trade/copy/index.js +9 -0
- package/dist/packages/data/src/trade/copy/mirror.js +91 -0
- package/dist/packages/data/src/trade/copy/stats.js +120 -0
- package/dist/packages/data/src/trade/copy/tracker.js +158 -0
- package/dist/packages/data/src/trade/copy/trades.js +87 -0
- package/dist/packages/data/src/trade/copy/types.js +6 -0
- package/dist/packages/data/src/trade/executor.js +933 -0
- package/dist/packages/data/src/trade/kill-switch.js +47 -0
- package/dist/packages/data/src/trade/link-store.js +52 -0
- package/dist/packages/data/src/trade/plans/confirm.js +157 -0
- package/dist/packages/data/src/trade/plans/engine.js +784 -0
- package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
- package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
- package/dist/packages/data/src/trade/plans/executor.js +11 -0
- package/dist/packages/data/src/trade/plans/index.js +12 -0
- package/dist/packages/data/src/trade/plans/journal.js +77 -0
- package/dist/packages/data/src/trade/plans/mandate.js +129 -0
- package/dist/packages/data/src/trade/plans/plan.js +179 -0
- package/dist/packages/data/src/trade/plans/service.js +168 -0
- package/dist/packages/data/src/trade/plans/types.js +6 -0
- package/dist/packages/data/src/trade/rpc-exec.js +229 -0
- package/dist/packages/data/src/trade/signer/client.js +197 -0
- package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
- package/dist/packages/data/src/trade/signer/host.js +97 -0
- package/dist/packages/data/src/trade/signer/main.js +121 -0
- package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
- package/dist/packages/data/src/trade/signer/protocol.js +92 -0
- package/dist/packages/data/src/trade/signer/service.js +400 -0
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
- package/dist/packages/data/src/trade/sol-usd.js +86 -0
- package/dist/packages/data/src/trade/swap-build.js +108 -0
- package/dist/packages/data/src/trade/token-delta.js +201 -0
- package/dist/packages/data/src/trade/tx-decode.js +632 -0
- package/dist/packages/data/src/util.js +24 -0
- package/dist/packages/data/src/watchlist.js +176 -0
- package/dist/packages/data/src/x/api.js +256 -0
- package/dist/packages/data/src/x/budget.js +66 -0
- package/dist/packages/data/src/x/config.js +49 -0
- package/dist/packages/data/src/x/index.js +10 -0
- package/dist/packages/data/src/x/mock-server.js +217 -0
- package/dist/packages/data/src/x/oauth.js +242 -0
- package/dist/packages/data/src/x/pkce.js +21 -0
- package/dist/packages/data/src/x/source.js +285 -0
- package/dist/packages/data/src/x/token-store.js +134 -0
- package/dist/packages/data/src/x/transport.js +71 -0
- package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
- package/dist/packages/mcp/assets/cases.json +1194 -0
- package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
- package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
- package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
- package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
- package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
- package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
- package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
- package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
- package/dist/packages/mcp/src/dossier.js +78 -0
- package/dist/packages/mcp/src/firewall.js +21 -0
- package/dist/packages/mcp/src/index.js +22 -0
- package/dist/packages/mcp/src/server.js +849 -0
- package/dist/packages/mcp/src/user-content.js +434 -0
- package/dist/packages/packs/src/backtest.js +183 -0
- package/dist/packages/packs/src/digest.js +95 -0
- package/dist/packages/packs/src/firewall.js +57 -0
- package/dist/packages/packs/src/index.js +19 -0
- package/dist/packages/packs/src/larp-eval.js +100 -0
- package/dist/packages/packs/src/narrative.js +309 -0
- package/dist/packages/packs/src/reason.js +135 -0
- package/dist/packages/packs/src/rug-model.js +137 -0
- package/dist/packages/packs/src/schema.js +54 -0
- package/dist/packages/packs/src/token-dossier.js +174 -0
- package/dist/packages/packs/src/verdicts.js +17 -0
- package/dist/packages/packs/src/wallet-dossier.js +105 -0
- package/dist/packages/popup/src/config.js +71 -0
- package/dist/packages/popup/src/dock.js +271 -0
- package/dist/packages/popup/src/host.js +210 -0
- package/dist/packages/popup/src/index.js +12 -0
- package/dist/packages/popup/src/loopback.js +53 -0
- package/dist/packages/popup/src/open.js +183 -0
- package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
- package/dist/packages/popup/src/pages/bubbles.css +23 -0
- package/dist/packages/popup/src/pages/bubbles.html +36 -0
- package/dist/packages/popup/src/pages/bubbles.js +214 -0
- package/dist/packages/popup/src/pages/chart-lib.js +262 -0
- package/dist/packages/popup/src/pages/chart.css +50 -0
- package/dist/packages/popup/src/pages/chart.html +56 -0
- package/dist/packages/popup/src/pages/chart.js +554 -0
- package/dist/packages/popup/src/pages/common.css +34 -0
- package/dist/packages/popup/src/pages/common.js +187 -0
- package/dist/packages/popup/src/pages/feeds.html +20 -0
- package/dist/packages/popup/src/pages/feeds.js +77 -0
- package/dist/packages/popup/src/pages/narrative.html +20 -0
- package/dist/packages/popup/src/pages/narrative.js +61 -0
- package/dist/packages/popup/src/pages/plans.css +30 -0
- package/dist/packages/popup/src/pages/plans.html +26 -0
- package/dist/packages/popup/src/pages/plans.js +82 -0
- package/dist/packages/popup/src/pages/roster.css +41 -0
- package/dist/packages/popup/src/pages/roster.html +25 -0
- package/dist/packages/popup/src/pages/roster.js +75 -0
- package/dist/packages/popup/src/pages/token.html +20 -0
- package/dist/packages/popup/src/pages/token.js +112 -0
- package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
- package/dist/packages/popup/src/pages/widget.css +5 -0
- package/dist/packages/popup/src/pages/widget.html +21 -0
- package/dist/packages/popup/src/pages/widget.js +85 -0
- package/dist/packages/popup/src/protocol.js +31 -0
- package/dist/packages/popup/src/selftest.js +156 -0
- package/dist/packages/popup/src/server.js +582 -0
- package/dist/packages/popup/src/sinkproxy.js +167 -0
- package/dist/packages/popup/src/widget-html.js +231 -0
- package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
- package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
- package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
- package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
- package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
- package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
- package/dist/packages/recorder/src/cli.js +113 -0
- package/dist/packages/recorder/src/export.js +71 -0
- package/dist/packages/recorder/src/index.js +20 -0
- package/dist/packages/recorder/src/labels.js +151 -0
- package/dist/packages/recorder/src/open.js +29 -0
- package/dist/packages/recorder/src/recorder.js +553 -0
- package/dist/packages/recorder/src/schedule.js +43 -0
- package/dist/packages/recorder/src/schema.js +135 -0
- package/dist/packages/recorder/src/snapshot.js +191 -0
- package/dist/packages/recorder/src/sweep.js +52 -0
- package/dist/packages/recorder/src/verdicts.js +52 -0
- package/dist/packages/tui/src/bubbles.js +415 -0
- package/dist/packages/tui/src/canvas.js +370 -0
- package/dist/packages/tui/src/chrome.js +85 -0
- package/dist/packages/tui/src/color.js +133 -0
- package/dist/packages/tui/src/flush.js +62 -0
- package/dist/packages/tui/src/fmt.js +188 -0
- package/dist/packages/tui/src/fx-trade.js +278 -0
- package/dist/packages/tui/src/fx.js +527 -0
- package/dist/packages/tui/src/images/blocks.js +141 -0
- package/dist/packages/tui/src/images/coin.js +22 -0
- package/dist/packages/tui/src/images/detect.js +140 -0
- package/dist/packages/tui/src/images/index.js +7 -0
- package/dist/packages/tui/src/images/layer.js +146 -0
- package/dist/packages/tui/src/images/pixels.js +157 -0
- package/dist/packages/tui/src/images/protocols.js +87 -0
- package/dist/packages/tui/src/index.js +15 -0
- package/dist/packages/tui/src/keymap.js +355 -0
- package/dist/packages/tui/src/layout.js +11 -0
- package/dist/packages/tui/src/sound.js +177 -0
- package/dist/packages/tui/src/theme.js +182 -0
- package/dist/packages/tui/src/widgets.js +598 -0
- package/dist/packages/voice/src/controller.js +395 -0
- package/dist/packages/voice/src/doctor.js +63 -0
- package/dist/packages/voice/src/engines.js +44 -0
- package/dist/packages/voice/src/index.js +16 -0
- package/dist/packages/voice/src/router.js +502 -0
- package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
- package/dist/packages/voice/src/stt/apple.js +179 -0
- package/dist/packages/voice/src/stt/fake.js +34 -0
- package/dist/packages/voice/src/stt/whisper.js +102 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/say.js +96 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +42 -4
- package/README.md +0 -3
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
// dexterm T1 — `dexterm wallet <link|unlink|status|panic|resume>` (SPEC §6.5). A non-TUI one-shot that
|
|
2
|
+
// links a Phantom wallet through the loopback bridge (keys never enter dexterm; only the public key is
|
|
3
|
+
// kept), unlinks it, prints the current link + the active trade caps, or PANICS live trading (round 2:
|
|
4
|
+
// the cheapest safe signer panic path — a kill-switch file every dexterm process re-reads, plus a lock
|
|
5
|
+
// on the shared daily-cap ledger). `resume` needs a real TTY keypress; piped input cannot re-arm.
|
|
6
|
+
import { WalletBridge, WalletLinkStore, shortPubkey, defaultCaps, capsOnlyDownward, KillSwitch, CapLedger, defaultCapLedgerPath, defaultLiveLedgerPath, LiveLedger, HARD_MAX_PER_TRADE_USD, HARD_MAX_DAILY_USD, HARD_MAX_SOL_OUT, HARD_MAX_SLIPPAGE_BPS, parseSignerArgs, buildSignerService, lamportsToSol, FastWalletStore, defaultFastWalletPath, FAST_MAX_SOL_PER_FILL, createDataLayer, TradeRpc, DEVNET_RPC, } from '../../data/src/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Parse the trade-caps flags shared by the wallet command and (later) live trading. DEFECT 6: a USD
|
|
9
|
+
* flag above the hard ceiling (e.g. `--max-usd 1000000`, `--daily-usd 1e12`) is REJECTED rather than
|
|
10
|
+
* silently accepted, so a fat-fingered cap can never arm a huge money path. Throws on an over-ceiling
|
|
11
|
+
* value; the caller surfaces it as a usage error. (defaultCaps also clamps as a second line of defence.)
|
|
12
|
+
*/
|
|
13
|
+
export function parseCapsFlags(argv) {
|
|
14
|
+
let mainnet = false;
|
|
15
|
+
let perTradeUsd;
|
|
16
|
+
let dailyUsd;
|
|
17
|
+
let maxSolOut;
|
|
18
|
+
let maxSlippageBps;
|
|
19
|
+
const fmt = (n, unit) => (unit === '$' ? `$${n}` : `${n} ${unit}`);
|
|
20
|
+
const take = (flag, raw, ceiling, unit = '$') => {
|
|
21
|
+
const n = Number(raw);
|
|
22
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
23
|
+
return undefined; // ignore junk; defaults apply
|
|
24
|
+
if (n > ceiling)
|
|
25
|
+
throw new Error(`${flag} ${fmt(n, unit)} exceeds the hard ceiling of ${fmt(ceiling, unit)}`);
|
|
26
|
+
return n;
|
|
27
|
+
};
|
|
28
|
+
for (let i = 0; i < argv.length; i++) {
|
|
29
|
+
const k = argv[i];
|
|
30
|
+
if (k === '--mainnet')
|
|
31
|
+
mainnet = true;
|
|
32
|
+
else if (k === '--max-usd')
|
|
33
|
+
perTradeUsd = take('--max-usd', argv[++i], HARD_MAX_PER_TRADE_USD);
|
|
34
|
+
else if (k === '--daily-usd')
|
|
35
|
+
dailyUsd = take('--daily-usd', argv[++i], HARD_MAX_DAILY_USD);
|
|
36
|
+
else if (k === '--max-sol-out')
|
|
37
|
+
maxSolOut = take('--max-sol-out', argv[++i], HARD_MAX_SOL_OUT, 'SOL');
|
|
38
|
+
else if (k === '--max-slippage-bps')
|
|
39
|
+
maxSlippageBps = take('--max-slippage-bps', argv[++i], HARD_MAX_SLIPPAGE_BPS, 'bps');
|
|
40
|
+
}
|
|
41
|
+
// task-1328: without --mainnet the USD caps can only go DOWN from the defaults (what the signer applies).
|
|
42
|
+
return capsOnlyDownward(defaultCaps({ network: mainnet ? 'mainnet' : 'devnet', mainnetEnabled: mainnet, perTradeUsd, dailyUsd, maxSolOut, maxSlippageBps }));
|
|
43
|
+
}
|
|
44
|
+
function capsLine(caps) {
|
|
45
|
+
const net = caps.network === 'mainnet' ? 'MAINNET' : 'devnet';
|
|
46
|
+
return `mode: ${caps.network === 'mainnet' ? 'LIVE' : 'DRY-RUN'} · network: ${net} · per-trade $${caps.perTradeUsd} · daily $${caps.dailyUsd} · max impact ${caps.maxPriceImpactPct}% · max SOL out ${caps.maxSolOut} · max slippage ${caps.maxSlippageBps} bps`;
|
|
47
|
+
}
|
|
48
|
+
function panicLine(ks, ledger) {
|
|
49
|
+
const p = ks.engaged();
|
|
50
|
+
const l = ledger?.locked() ?? null;
|
|
51
|
+
if (!p && !l)
|
|
52
|
+
return null;
|
|
53
|
+
const parts = [];
|
|
54
|
+
if (p)
|
|
55
|
+
parts.push(`panic switch engaged${p.engagedAt ? ` at ${new Date(p.engagedAt).toISOString()}` : ''} (${p.reason})`);
|
|
56
|
+
if (l)
|
|
57
|
+
parts.push(`cap ledger locked (${l.reason})`);
|
|
58
|
+
return `⛔ LIVE TRADING DISABLED — ${parts.join('; ')}. Run \`dexterm wallet resume\` in a terminal to re-enable.`;
|
|
59
|
+
}
|
|
60
|
+
function printStatus(io, store, caps, ks, ledger) {
|
|
61
|
+
const w = store.get();
|
|
62
|
+
if (w)
|
|
63
|
+
io.stdout.write(`linked: ${shortPubkey(w.publicKey)} (${w.publicKey})\n`);
|
|
64
|
+
else
|
|
65
|
+
io.stdout.write('linked: (none) — run `dexterm wallet link`\n');
|
|
66
|
+
io.stdout.write(`${capsLine(caps)}\n`);
|
|
67
|
+
const p = panicLine(ks, ledger);
|
|
68
|
+
if (p)
|
|
69
|
+
io.stdout.write(`${p}\n`);
|
|
70
|
+
if (caps.network === 'mainnet')
|
|
71
|
+
io.stdout.write('⚠ --mainnet is on: real funds, within the caps above. Devnet is the default; drop --mainnet to stay safe.\n');
|
|
72
|
+
}
|
|
73
|
+
function liveLine(path) {
|
|
74
|
+
try {
|
|
75
|
+
const l = new LiveLedger({ path, readOnly: true });
|
|
76
|
+
try {
|
|
77
|
+
const fills = l.records(1000);
|
|
78
|
+
const open = l.positions().filter((p) => p.tokensRaw > 0n);
|
|
79
|
+
return `live ledger: ${fills.length} confirmed fill${fills.length === 1 ? '' : 's'} · ${open.length} open position${open.length === 1 ? '' : 's'}${open.length ? ` (${open.map((p) => `$${p.symbol}`).join(', ')})` : ''}`;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
l.close();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Wait for ONE real keypress on a TTY. Resolves with the key, or null when stdin is not a TTY (piped
|
|
91
|
+
* input, a subprocess, an agent) or no key arrives in time. Raw mode means a single keystroke — not a
|
|
92
|
+
* buffered line — and `isTTY` is the kernel's word, not the caller's: `echo y | dexterm wallet resume`
|
|
93
|
+
* is refused because a pipe is never a TTY.
|
|
94
|
+
*/
|
|
95
|
+
export async function ttyKeypress(stdin, timeoutMs) {
|
|
96
|
+
if (!stdin || stdin.isTTY !== true || typeof stdin.setRawMode !== 'function')
|
|
97
|
+
return null;
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
let done = false;
|
|
100
|
+
const finish = (v) => {
|
|
101
|
+
if (done)
|
|
102
|
+
return;
|
|
103
|
+
done = true;
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
try {
|
|
106
|
+
stdin.off?.('data', onData);
|
|
107
|
+
}
|
|
108
|
+
catch { /* ignore */ }
|
|
109
|
+
try {
|
|
110
|
+
stdin.setRawMode?.(false);
|
|
111
|
+
}
|
|
112
|
+
catch { /* ignore */ }
|
|
113
|
+
try {
|
|
114
|
+
stdin.pause?.();
|
|
115
|
+
}
|
|
116
|
+
catch { /* ignore */ }
|
|
117
|
+
resolve(v);
|
|
118
|
+
};
|
|
119
|
+
const onData = (chunk) => finish(chunk.toString('utf8').slice(0, 1));
|
|
120
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
121
|
+
try {
|
|
122
|
+
stdin.setRawMode?.(true);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
finish(null);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
stdin.once('data', onData);
|
|
129
|
+
stdin.resume?.();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Run the wallet subcommand. Returns a process exit code. */
|
|
133
|
+
export async function runWallet(argv, io, over = {}) {
|
|
134
|
+
const sub = argv[0] ?? 'status';
|
|
135
|
+
let caps;
|
|
136
|
+
try {
|
|
137
|
+
caps = parseCapsFlags(argv.slice(1));
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
io.stderr.write(`dexterm wallet: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
141
|
+
return 2;
|
|
142
|
+
}
|
|
143
|
+
const store = over.store ?? new WalletLinkStore();
|
|
144
|
+
const ks = over.killSwitch ?? new KillSwitch();
|
|
145
|
+
const ledgerPath = over.ledgerPath ?? defaultCapLedgerPath();
|
|
146
|
+
const openLedger = () => { try {
|
|
147
|
+
return new CapLedger({ path: ledgerPath });
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
} };
|
|
152
|
+
if (sub === 'status') {
|
|
153
|
+
const ledger = openLedger();
|
|
154
|
+
try {
|
|
155
|
+
printStatus(io, store, caps, ks, ledger);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
ledger?.close();
|
|
159
|
+
}
|
|
160
|
+
const ll = liveLine(over.liveLedgerPath ?? defaultLiveLedgerPath());
|
|
161
|
+
if (ll)
|
|
162
|
+
io.stdout.write(`${ll}\n`);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
// PROBE (task-1328): the devnet plumbing test from a shell — the signer service runs in THIS process
|
|
166
|
+
// (same code the TUI's signer child runs), builds the wrap-and-unwrap plan for the linked wallet,
|
|
167
|
+
// prints it, waits for a real TTY keypress, then opens Phantom, verifies, re-simulates, submits and
|
|
168
|
+
// confirms. Devnet only: the service refuses a non-devnet RPC and --mainnet is not accepted here.
|
|
169
|
+
if (sub === 'probe') {
|
|
170
|
+
if (argv.includes('--mainnet')) {
|
|
171
|
+
io.stderr.write('dexterm wallet probe: devnet only (drop --mainnet)\n');
|
|
172
|
+
return 2;
|
|
173
|
+
}
|
|
174
|
+
if (!store.get()) {
|
|
175
|
+
io.stderr.write('dexterm wallet probe: no wallet linked; run `dexterm wallet link` first\n');
|
|
176
|
+
return 2;
|
|
177
|
+
}
|
|
178
|
+
let service;
|
|
179
|
+
try {
|
|
180
|
+
service = over.signerService ?? buildSignerService(parseSignerArgs(argv.slice(1).filter((a) => a !== '--no-browser'), io.env), (l) => io.stderr.write(`${l}\n`));
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
io.stderr.write(`dexterm wallet probe: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
184
|
+
return 2;
|
|
185
|
+
}
|
|
186
|
+
const st = service.status();
|
|
187
|
+
if (st.network !== 'devnet') {
|
|
188
|
+
io.stderr.write(`dexterm wallet probe: the signer RPC is ${st.network}; the probe runs only on devnet\n`);
|
|
189
|
+
return 2;
|
|
190
|
+
}
|
|
191
|
+
if (st.panic || st.ledgerLocked) {
|
|
192
|
+
io.stderr.write(`${panicLine(ks, openLedger()) ?? 'panicked'}\n`);
|
|
193
|
+
return 2;
|
|
194
|
+
}
|
|
195
|
+
let plan;
|
|
196
|
+
try {
|
|
197
|
+
plan = await service.probe();
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
io.stderr.write(`dexterm wallet probe: plan failed: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
201
|
+
return 1;
|
|
202
|
+
}
|
|
203
|
+
io.stdout.write(`DEVNET PROBE for ${shortPubkey(plan.userPublicKey)} via ${st.rpcHost}\n`);
|
|
204
|
+
io.stdout.write(` wraps ${lamportsToSol(BigInt(plan.amountInRaw)).toFixed(4)} SOL into your own WSOL account and closes it back · fee ≈ ${lamportsToSol(BigInt(plan.feeLamports)).toFixed(6)} SOL\n`);
|
|
205
|
+
io.stdout.write(` will sign: ${plan.instructions.map((i) => `${i.program}${i.op ? ':' + i.op : ''}`).join(' · ')}\n`);
|
|
206
|
+
io.stdout.write(` simulation: ${plan.sim.ran ? (plan.sim.ok ? `ok (${plan.sim.solOutLamports ?? '?'} lamports leave the wallet)` : `FAILED ${plan.sim.error ?? ''}`) : 'not run'} · policy: ${plan.policy.ok ? 'ok' : `REFUSE ${plan.policy.reason}`} · caps: ${plan.precheck.ok ? 'ok' : `REFUSE ${plan.precheck.reason}`}\n`);
|
|
207
|
+
if (!plan.policy.ok || !plan.precheck.ok || !plan.sim.ok) {
|
|
208
|
+
io.stderr.write('dexterm wallet probe: the signer would refuse this plan; nothing to sign\n');
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
const stdin = io.stdin;
|
|
212
|
+
if (!stdin || stdin.isTTY !== true) {
|
|
213
|
+
io.stderr.write('dexterm wallet probe: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
|
|
214
|
+
return 2;
|
|
215
|
+
}
|
|
216
|
+
io.stdout.write('Press Y to sign this probe in Phantom (any other key aborts)… ');
|
|
217
|
+
const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 60_000);
|
|
218
|
+
io.stdout.write('\n');
|
|
219
|
+
if (key !== 'y' && key !== 'Y') {
|
|
220
|
+
io.stdout.write('aborted; nothing signed.\n');
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
const out = await service.confirm({ hash: plan.hash });
|
|
224
|
+
if (out.kind === 'filled') {
|
|
225
|
+
io.stdout.write(`✓ confirmed on devnet · signature ${out.signature}${out.lighthouseTail ? ` · Phantom appended ${out.lighthouseTail} Lighthouse instruction(s)` : ' · no Lighthouse tail'} · ${out.roundTripMs} ms\n`);
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
if (out.kind === 'refused') {
|
|
229
|
+
io.stdout.write(`✗ refused by the signer: ${out.reason} — ${out.message}\n`);
|
|
230
|
+
return 1;
|
|
231
|
+
}
|
|
232
|
+
if (out.kind === 'send-unknown') {
|
|
233
|
+
io.stdout.write(`? send outcome unknown: ${out.error} · signature ${out.signature} · do NOT resend\n`);
|
|
234
|
+
return 1;
|
|
235
|
+
}
|
|
236
|
+
io.stdout.write(`✗ send failed: ${out.error}${out.signature ? ` · signature ${out.signature}` : ''}\n`);
|
|
237
|
+
return 1;
|
|
238
|
+
}
|
|
239
|
+
// FAST WALLET (task-1363): the one key dexterm holds, devnet only, for standing-mandate fills at machine speed.
|
|
240
|
+
// create a new devnet key at ~/.dexterm/fast-wallet.json (mode 600) after a real TTY keypress; never overwrites
|
|
241
|
+
// status public key, devnet balance, the per-fill ceiling
|
|
242
|
+
// airdrop devnet faucet into the fast wallet (--sol N, default 1)
|
|
243
|
+
// remove delete the key (TTY keypress; refused while it still holds SOL unless --force)
|
|
244
|
+
// There is no export, no import, no mainnet variant, and the signer process only ever READS this file.
|
|
245
|
+
if (sub === 'fast') {
|
|
246
|
+
if (argv.includes('--mainnet')) {
|
|
247
|
+
io.stderr.write('dexterm wallet fast: devnet only (there is no mainnet fast wallet; drop --mainnet)\n');
|
|
248
|
+
return 2;
|
|
249
|
+
}
|
|
250
|
+
const action = argv[1] ?? 'status';
|
|
251
|
+
const fastStore = new FastWalletStore(over.fastWalletPath ?? defaultFastWalletPath(io.env));
|
|
252
|
+
const rpc = () => {
|
|
253
|
+
if (over.fastRpc)
|
|
254
|
+
return over.fastRpc;
|
|
255
|
+
const dl = createDataLayer({ dbPath: ':memory:', rpcUrl: DEVNET_RPC, rpcFallbackUrl: null });
|
|
256
|
+
const r = new TradeRpc(dl.http, { url: DEVNET_RPC });
|
|
257
|
+
return { balance: (pk) => r.balance(pk), airdrop: (pk, l) => r.airdrop(pk, l), confirm: (sig) => r.confirm(sig, { timeoutMs: 45_000 }) };
|
|
258
|
+
};
|
|
259
|
+
const balanceLine = async (pk) => {
|
|
260
|
+
try {
|
|
261
|
+
const l = await rpc().balance(pk);
|
|
262
|
+
return { lamports: l, text: `${lamportsToSol(l).toFixed(4)} SOL (devnet)` };
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
return { lamports: null, text: `balance unavailable (${e instanceof Error ? e.message : String(e)})` };
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
if (action === 'status') {
|
|
269
|
+
const rec = fastStore.get();
|
|
270
|
+
if (!rec) {
|
|
271
|
+
io.stdout.write(`fast wallet: (none) — create one with \`dexterm wallet fast create\` (devnet only)\n`);
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
const b = await balanceLine(rec.publicKey);
|
|
275
|
+
io.stdout.write(`fast wallet: ${shortPubkey(rec.publicKey)} (${rec.publicKey}) · devnet · created ${new Date(rec.createdAt).toISOString()}\n`);
|
|
276
|
+
io.stdout.write(` balance: ${b.text} · per-fill ceiling ${FAST_MAX_SOL_PER_FILL} SOL · the signer's USD per-trade/daily caps apply on every fill\n`);
|
|
277
|
+
io.stdout.write(` file: ${fastStore.path} (mode 600; never exported, never printed)\n`);
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
if (action === 'create') {
|
|
281
|
+
if (fastStore.exists()) {
|
|
282
|
+
io.stderr.write(`dexterm wallet fast create: a fast wallet already exists at ${fastStore.path} (remove it first, after draining)\n`);
|
|
283
|
+
return 2;
|
|
284
|
+
}
|
|
285
|
+
const stdin = io.stdin;
|
|
286
|
+
if (!stdin || stdin.isTTY !== true) {
|
|
287
|
+
io.stderr.write('dexterm wallet fast create: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
|
|
288
|
+
return 2;
|
|
289
|
+
}
|
|
290
|
+
io.stdout.write(`Create a DEVNET fast wallet at ${fastStore.path}?\n armed order plans inside a standing mandate fire from it at machine speed, within the signer's caps and a ${FAST_MAX_SOL_PER_FILL} SOL per-fill ceiling.\n devnet only — this key can never be used on mainnet. Press Y to create (any other key aborts)… `);
|
|
291
|
+
const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
|
|
292
|
+
io.stdout.write('\n');
|
|
293
|
+
if (key !== 'y' && key !== 'Y') {
|
|
294
|
+
io.stdout.write('aborted; nothing created.\n');
|
|
295
|
+
return 1;
|
|
296
|
+
}
|
|
297
|
+
const rec = fastStore.create({ network: 'devnet' });
|
|
298
|
+
io.stdout.write(`✓ fast wallet created: ${rec.publicKey} (devnet)\n fund it: dexterm wallet fast airdrop · then restart dexterm so the signer loads it\n`);
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
if (action === 'airdrop') {
|
|
302
|
+
const rec = fastStore.get();
|
|
303
|
+
if (!rec) {
|
|
304
|
+
io.stderr.write('dexterm wallet fast airdrop: no fast wallet; `dexterm wallet fast create` first\n');
|
|
305
|
+
return 2;
|
|
306
|
+
}
|
|
307
|
+
const solIdx = argv.indexOf('--sol');
|
|
308
|
+
const sol = solIdx >= 0 ? Number(argv[solIdx + 1]) : 1;
|
|
309
|
+
if (!Number.isFinite(sol) || sol <= 0 || sol > 5) {
|
|
310
|
+
io.stderr.write('dexterm wallet fast airdrop: --sol must be in (0, 5]\n');
|
|
311
|
+
return 2;
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const sig = await rpc().airdrop(rec.publicKey, BigInt(Math.round(sol * 1e9)));
|
|
315
|
+
io.stdout.write(`airdrop requested: ${sol} SOL (devnet) → ${shortPubkey(rec.publicKey)} · signature ${sig}\n`);
|
|
316
|
+
const c = rpc().confirm ? await rpc().confirm(sig).catch(() => null) : null;
|
|
317
|
+
if (c)
|
|
318
|
+
io.stdout.write(c.confirmed ? '✓ confirmed\n' : `? not confirmed yet (${c.err ?? 'pending'})\n`);
|
|
319
|
+
const b = await balanceLine(rec.publicKey);
|
|
320
|
+
io.stdout.write(` balance: ${b.text}\n`);
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
io.stderr.write(`dexterm wallet fast airdrop failed: ${e instanceof Error ? e.message : String(e)} — the public devnet faucet is often dry; try https://faucet.solana.com with the address above\n`);
|
|
325
|
+
return 1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (action === 'remove') {
|
|
329
|
+
const rec = fastStore.get();
|
|
330
|
+
if (!rec) {
|
|
331
|
+
io.stdout.write('no fast wallet to remove.\n');
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
334
|
+
const b = await balanceLine(rec.publicKey);
|
|
335
|
+
if (b.lamports !== null && b.lamports > 0n && !argv.includes('--force')) {
|
|
336
|
+
io.stderr.write(`dexterm wallet fast remove: the fast wallet still holds ${b.text}; move it out first, or pass --force to delete the key anyway (devnet SOL only)\n`);
|
|
337
|
+
return 2;
|
|
338
|
+
}
|
|
339
|
+
const stdin = io.stdin;
|
|
340
|
+
if (!stdin || stdin.isTTY !== true) {
|
|
341
|
+
io.stderr.write('dexterm wallet fast remove: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
|
|
342
|
+
return 2;
|
|
343
|
+
}
|
|
344
|
+
io.stdout.write(`Delete the fast wallet ${shortPubkey(rec.publicKey)} (${b.text})? Press Y to delete (any other key aborts)… `);
|
|
345
|
+
const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
|
|
346
|
+
io.stdout.write('\n');
|
|
347
|
+
if (key !== 'y' && key !== 'Y') {
|
|
348
|
+
io.stdout.write('aborted; the fast wallet is kept.\n');
|
|
349
|
+
return 1;
|
|
350
|
+
}
|
|
351
|
+
fastStore.remove();
|
|
352
|
+
io.stdout.write('✓ fast wallet removed. Restart dexterm so the signer stops loading it.\n');
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
io.stderr.write(`dexterm wallet fast: unknown action ${JSON.stringify(action)} (expected status | create | airdrop | remove)\n`);
|
|
356
|
+
return 2;
|
|
357
|
+
}
|
|
358
|
+
if (sub === 'unlink') {
|
|
359
|
+
const had = store.clear();
|
|
360
|
+
io.stdout.write(had ? 'unlinked.\n' : 'no wallet was linked.\n');
|
|
361
|
+
return 0;
|
|
362
|
+
}
|
|
363
|
+
// PANIC: instant, no confirmation, scriptable from anywhere (a second terminal, a hotkey, an agent).
|
|
364
|
+
// Engages the kill-switch file (every executor re-reads it on each plan/confirm) AND locks the shared
|
|
365
|
+
// daily-cap ledger so no reservation can be taken even if the file were removed by hand.
|
|
366
|
+
if (sub === 'panic') {
|
|
367
|
+
const reasonIdx = argv.indexOf('--reason');
|
|
368
|
+
const reason = reasonIdx >= 0 && argv[reasonIdx + 1] ? argv[reasonIdx + 1] : 'manual panic';
|
|
369
|
+
const rec = ks.engage(reason);
|
|
370
|
+
const ledger = openLedger();
|
|
371
|
+
let lockNote = '';
|
|
372
|
+
if (ledger) {
|
|
373
|
+
try {
|
|
374
|
+
ledger.lock(reason);
|
|
375
|
+
lockNote = ' · cap ledger locked';
|
|
376
|
+
}
|
|
377
|
+
finally {
|
|
378
|
+
ledger.close();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else
|
|
382
|
+
lockNote = ' · (cap ledger could not be opened; the kill-switch file still disables trading)';
|
|
383
|
+
io.stdout.write(`⛔ PANIC: live trading disabled (${rec.reason})${lockNote}\n`);
|
|
384
|
+
io.stdout.write(`kill switch: ${ks.path}\nRe-enable only from a real terminal: dexterm wallet resume\n`);
|
|
385
|
+
return 0;
|
|
386
|
+
}
|
|
387
|
+
// RESUME: the only way back. Requires a physical keypress on a TTY — not piped/scripted input — so a
|
|
388
|
+
// model, a script or a compromised popup cannot silently re-arm live trading.
|
|
389
|
+
if (sub === 'resume') {
|
|
390
|
+
const stdin = io.stdin;
|
|
391
|
+
if (!stdin || stdin.isTTY !== true) {
|
|
392
|
+
io.stderr.write('dexterm wallet resume: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
|
|
393
|
+
return 2;
|
|
394
|
+
}
|
|
395
|
+
const p = ks.engaged();
|
|
396
|
+
const ledger = openLedger();
|
|
397
|
+
try {
|
|
398
|
+
const l = ledger?.locked() ?? null;
|
|
399
|
+
if (!p && !l) {
|
|
400
|
+
io.stdout.write('live trading is not panicked; nothing to resume.\n');
|
|
401
|
+
return 0;
|
|
402
|
+
}
|
|
403
|
+
io.stdout.write(`${panicLine(ks, ledger) ?? ''}\nPress Y to re-enable live trading (any other key aborts)… `);
|
|
404
|
+
const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
|
|
405
|
+
io.stdout.write('\n');
|
|
406
|
+
if (key === null) {
|
|
407
|
+
io.stderr.write('dexterm wallet resume: no keypress; still panicked.\n');
|
|
408
|
+
return 2;
|
|
409
|
+
}
|
|
410
|
+
if (key !== 'y' && key !== 'Y') {
|
|
411
|
+
io.stdout.write('aborted; still panicked.\n');
|
|
412
|
+
return 1;
|
|
413
|
+
}
|
|
414
|
+
ks.release();
|
|
415
|
+
ledger?.unlock();
|
|
416
|
+
io.stdout.write('✓ live trading re-enabled (caps still apply).\n');
|
|
417
|
+
return 0;
|
|
418
|
+
}
|
|
419
|
+
finally {
|
|
420
|
+
ledger?.close();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (sub === 'link') {
|
|
424
|
+
const bridge = over.bridge ?? new WalletBridge(over.open ? { open: over.open } : {});
|
|
425
|
+
io.stdout.write('Opening your browser to link a Solana wallet (sign-a-message only; no transaction)…\n');
|
|
426
|
+
try {
|
|
427
|
+
const res = await bridge.link();
|
|
428
|
+
const rec = store.set(res.publicKey, { network: caps.network });
|
|
429
|
+
io.stdout.write(`linked: ${shortPubkey(rec.publicKey)} (${rec.publicKey})\n`);
|
|
430
|
+
io.stdout.write(`${capsLine(caps)}\n`);
|
|
431
|
+
return 0;
|
|
432
|
+
}
|
|
433
|
+
catch (e) {
|
|
434
|
+
io.stderr.write(`dexterm wallet link failed: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
435
|
+
return 1;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
io.stderr.write(`dexterm wallet: unknown subcommand ${JSON.stringify(sub)} (expected link | unlink | status | panic | resume | probe | fast)\n`);
|
|
439
|
+
return 2;
|
|
440
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a typed command: `/wallet` · `/wallet status` · `/wallet link` · `/wallet unlink` ·
|
|
3
|
+
* `/wallet panic` · `/wallet probe`. The leading slash is optional (the screener's `/` line ate it).
|
|
4
|
+
* Anything else — including a real filter — returns null. `resume` is deliberately NOT a command here:
|
|
5
|
+
* re-enabling live trading needs a real TTY keypress in `dexterm wallet resume`.
|
|
6
|
+
*/
|
|
7
|
+
export function parseWalletCommand(text) {
|
|
8
|
+
const m = /^\s*\/?wallet(?:\s+(\S+))?\s*$/i.exec(String(text ?? ''));
|
|
9
|
+
if (!m)
|
|
10
|
+
return null;
|
|
11
|
+
const arg = (m[1] ?? '').toLowerCase();
|
|
12
|
+
if (arg === '' || arg === 'status')
|
|
13
|
+
return 'status';
|
|
14
|
+
if (arg === 'link')
|
|
15
|
+
return 'link';
|
|
16
|
+
if (arg === 'unlink')
|
|
17
|
+
return 'unlink';
|
|
18
|
+
if (arg === 'panic')
|
|
19
|
+
return 'panic';
|
|
20
|
+
if (arg === 'probe')
|
|
21
|
+
return 'probe';
|
|
22
|
+
if (arg === 'resume')
|
|
23
|
+
return { error: 'resume needs a real terminal keypress: run `dexterm wallet resume` in a shell' };
|
|
24
|
+
return { error: `unknown /wallet command "${arg.slice(0, 16)}" (status · link · unlink · panic · probe)` };
|
|
25
|
+
}
|
|
26
|
+
export const WALLET_HELP = ` /wallet [status] linked wallet (pubkey only), the signer's network + caps, panic state
|
|
27
|
+
/wallet link open the browser to link a Phantom wallet (sign-a-message proof; no transaction)
|
|
28
|
+
/wallet unlink forget the linked public key
|
|
29
|
+
/wallet panic disable live signing at once (kill-switch file + cap-ledger lock); resume needs \`dexterm wallet resume\`
|
|
30
|
+
/wallet probe devnet plumbing test: a wrap-and-unwrap transaction you sign in Phantom (fees only)
|
|
31
|
+
dexterm wallet fast … the capped DEVNET fast wallet armed order plans fire from: create (TTY keypress) · status · airdrop · remove`;
|