dexterm-cli 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +394 -0
- package/dist/packages/agents/src/executor.js +202 -0
- package/dist/packages/agents/src/gate.js +179 -0
- package/dist/packages/agents/src/identity.js +218 -0
- package/dist/packages/agents/src/index.js +15 -0
- package/dist/packages/agents/src/proposals.js +153 -0
- package/dist/packages/agents/src/schema.js +87 -0
- package/dist/packages/agents/src/sdk.js +140 -0
- package/dist/packages/agents/src/tiers.js +119 -0
- package/dist/packages/agents/src/tools.js +72 -0
- package/dist/packages/agents/src/ws.js +158 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
- package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +75 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -0
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
- package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
- package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
- package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
- package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
- package/dist/packages/cli/bin/dexterm.mjs +11 -0
- package/dist/packages/cli/src/agent-cmd.js +336 -0
- package/dist/packages/cli/src/agent-roster.js +26 -0
- package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
- package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
- package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
- package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
- package/dist/packages/cli/src/alerts/creds.js +83 -0
- package/dist/packages/cli/src/alerts/format.js +41 -0
- package/dist/packages/cli/src/alerts/index.js +10 -0
- package/dist/packages/cli/src/alerts/notifier.js +53 -0
- package/dist/packages/cli/src/alerts/route.js +19 -0
- package/dist/packages/cli/src/alerts/types.js +1 -0
- package/dist/packages/cli/src/alerts-cmd.js +212 -0
- package/dist/packages/cli/src/analyst/backend.js +526 -0
- package/dist/packages/cli/src/analyst/results.js +38 -0
- package/dist/packages/cli/src/analyst/session.js +319 -0
- package/dist/packages/cli/src/audit.js +51 -0
- package/dist/packages/cli/src/backtest-cmd.js +208 -0
- package/dist/packages/cli/src/boot-state.js +62 -0
- package/dist/packages/cli/src/coin-target.js +2 -0
- package/dist/packages/cli/src/custom.js +261 -0
- package/dist/packages/cli/src/display.js +191 -0
- package/dist/packages/cli/src/doctor.js +375 -0
- package/dist/packages/cli/src/export.js +235 -0
- package/dist/packages/cli/src/feed.js +634 -0
- package/dist/packages/cli/src/fixtures.js +275 -0
- package/dist/packages/cli/src/follow-cmd.js +221 -0
- package/dist/packages/cli/src/images-config.js +59 -0
- package/dist/packages/cli/src/index.js +31 -0
- package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
- package/dist/packages/cli/src/larp-record.js +21 -0
- package/dist/packages/cli/src/live/copy.js +80 -0
- package/dist/packages/cli/src/live/flow.js +233 -0
- package/dist/packages/cli/src/live/index.js +511 -0
- package/dist/packages/cli/src/live/intel.js +227 -0
- package/dist/packages/cli/src/live/newpairs.js +334 -0
- package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
- package/dist/packages/cli/src/live/screens/token-live.js +459 -0
- package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
- package/dist/packages/cli/src/live/token.js +585 -0
- package/dist/packages/cli/src/live/trade.js +929 -0
- package/dist/packages/cli/src/live/watchlist.js +178 -0
- package/dist/packages/cli/src/main.js +5785 -0
- package/dist/packages/cli/src/mcp-cmd.js +326 -0
- package/dist/packages/cli/src/narrative-cmd.js +76 -0
- package/dist/packages/cli/src/pack-cmd.js +323 -0
- package/dist/packages/cli/src/plan-cmd.js +278 -0
- package/dist/packages/cli/src/popup.js +810 -0
- package/dist/packages/cli/src/rules-cmd.js +131 -0
- package/dist/packages/cli/src/rules.js +358 -0
- package/dist/packages/cli/src/scan/command.js +57 -0
- package/dist/packages/cli/src/scan/engine.js +419 -0
- package/dist/packages/cli/src/scan/observe.js +64 -0
- package/dist/packages/cli/src/scan/ui.js +98 -0
- package/dist/packages/cli/src/screener/filter.js +241 -0
- package/dist/packages/cli/src/screener/presets.js +80 -0
- package/dist/packages/cli/src/screener/rows.js +146 -0
- package/dist/packages/cli/src/screener/rug.js +50 -0
- package/dist/packages/cli/src/screener/sort.js +52 -0
- package/dist/packages/cli/src/screener/view.js +183 -0
- package/dist/packages/cli/src/screens/analyst-session.js +343 -0
- package/dist/packages/cli/src/screens/analyst.js +326 -0
- package/dist/packages/cli/src/screens/boot.js +74 -0
- package/dist/packages/cli/src/screens/bubbles.js +255 -0
- package/dist/packages/cli/src/screens/coin.js +33 -0
- package/dist/packages/cli/src/screens/follows.js +201 -0
- package/dist/packages/cli/src/screens/kol.js +90 -0
- package/dist/packages/cli/src/screens/narrative.js +90 -0
- package/dist/packages/cli/src/screens/newpairs.js +150 -0
- package/dist/packages/cli/src/screens/overlay.js +257 -0
- package/dist/packages/cli/src/screens/plans.js +126 -0
- package/dist/packages/cli/src/screens/positions.js +176 -0
- package/dist/packages/cli/src/screens/rules.js +170 -0
- package/dist/packages/cli/src/screens/screener.js +459 -0
- package/dist/packages/cli/src/screens/social-panel.js +107 -0
- package/dist/packages/cli/src/screens/ticket.js +342 -0
- package/dist/packages/cli/src/screens/token.js +312 -0
- package/dist/packages/cli/src/screens/trending.js +181 -0
- package/dist/packages/cli/src/screens/watchlist.js +104 -0
- package/dist/packages/cli/src/skill-eval-build.js +109 -0
- package/dist/packages/cli/src/social/demo.js +96 -0
- package/dist/packages/cli/src/social/view.js +250 -0
- package/dist/packages/cli/src/state.js +463 -0
- package/dist/packages/cli/src/theme-cmd.js +126 -0
- package/dist/packages/cli/src/trade.js +319 -0
- package/dist/packages/cli/src/update.js +294 -0
- package/dist/packages/cli/src/version.js +3 -0
- package/dist/packages/cli/src/voice-cmd.js +119 -0
- package/dist/packages/cli/src/voice-hud.js +42 -0
- package/dist/packages/cli/src/wallet-cmd.js +440 -0
- package/dist/packages/cli/src/wallet-command.js +31 -0
- package/dist/packages/cli/src/watch-cmd.js +190 -0
- package/dist/packages/cli/src/widgets.js +208 -0
- package/dist/packages/cli/src/workspace-cmd.js +68 -0
- package/dist/packages/cli/src/x-cmd.js +154 -0
- package/dist/packages/custom/src/config.js +213 -0
- package/dist/packages/custom/src/index.js +10 -0
- package/dist/packages/custom/src/keymap.js +107 -0
- package/dist/packages/custom/src/layout.js +235 -0
- package/dist/packages/custom/src/packs.js +633 -0
- package/dist/packages/custom/src/rules.js +704 -0
- package/dist/packages/custom/src/schema.js +538 -0
- package/dist/packages/custom/src/themes.js +176 -0
- package/dist/packages/custom/src/widget-html.js +222 -0
- package/dist/packages/custom/src/workspace.js +141 -0
- package/dist/packages/data/src/analyst-actions.js +133 -0
- package/dist/packages/data/src/budget.js +88 -0
- package/dist/packages/data/src/cache.js +142 -0
- package/dist/packages/data/src/candles.js +168 -0
- package/dist/packages/data/src/clock.js +64 -0
- package/dist/packages/data/src/coin-target.js +82 -0
- package/dist/packages/data/src/credits.js +137 -0
- package/dist/packages/data/src/detail.js +97 -0
- package/dist/packages/data/src/fast-lane.js +321 -0
- package/dist/packages/data/src/fixtures.js +135 -0
- package/dist/packages/data/src/health.js +245 -0
- package/dist/packages/data/src/http.js +277 -0
- package/dist/packages/data/src/images/cache.js +141 -0
- package/dist/packages/data/src/images/decode-worker.js +28 -0
- package/dist/packages/data/src/images/decode.js +81 -0
- package/dist/packages/data/src/images/fetch.js +162 -0
- package/dist/packages/data/src/images/gif.js +214 -0
- package/dist/packages/data/src/images/index.js +10 -0
- package/dist/packages/data/src/images/jpeg.js +544 -0
- package/dist/packages/data/src/images/png.js +310 -0
- package/dist/packages/data/src/images/service.js +334 -0
- package/dist/packages/data/src/images/types.js +27 -0
- package/dist/packages/data/src/index.js +211 -0
- package/dist/packages/data/src/intel/helius-history.js +70 -0
- package/dist/packages/data/src/intel/holder-class.js +125 -0
- package/dist/packages/data/src/intel/rules.js +421 -0
- package/dist/packages/data/src/intel/scan.js +170 -0
- package/dist/packages/data/src/larp/evidence.js +288 -0
- package/dist/packages/data/src/larp/link-baseline.js +196 -0
- package/dist/packages/data/src/larp/links.js +137 -0
- package/dist/packages/data/src/larp/robots.js +90 -0
- package/dist/packages/data/src/larp/safe-fetch.js +389 -0
- package/dist/packages/data/src/limiter.js +341 -0
- package/dist/packages/data/src/model.js +12 -0
- package/dist/packages/data/src/paper.js +331 -0
- package/dist/packages/data/src/rpc-config.js +126 -0
- package/dist/packages/data/src/rpc-pool.js +166 -0
- package/dist/packages/data/src/social/chain.js +83 -0
- package/dist/packages/data/src/social/dedupe.js +205 -0
- package/dist/packages/data/src/social/feed.js +462 -0
- package/dist/packages/data/src/social/firewall.js +110 -0
- package/dist/packages/data/src/social/index.js +11 -0
- package/dist/packages/data/src/social/kol.js +269 -0
- package/dist/packages/data/src/social/model.js +11 -0
- package/dist/packages/data/src/social/rank.js +184 -0
- package/dist/packages/data/src/social/rss.js +113 -0
- package/dist/packages/data/src/social/summary.js +104 -0
- package/dist/packages/data/src/social/venues.js +186 -0
- package/dist/packages/data/src/sources/dexscreener.js +184 -0
- package/dist/packages/data/src/sources/geckoterminal.js +133 -0
- package/dist/packages/data/src/sources/goplus.js +161 -0
- package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
- package/dist/packages/data/src/sources/jupiter.js +198 -0
- package/dist/packages/data/src/sources/pda.js +113 -0
- package/dist/packages/data/src/sources/pump-curve.js +58 -0
- package/dist/packages/data/src/sources/pumpportal.js +252 -0
- package/dist/packages/data/src/sources/rugcheck.js +71 -0
- package/dist/packages/data/src/sources/solana-rpc.js +299 -0
- package/dist/packages/data/src/sources/solana-ws.js +287 -0
- package/dist/packages/data/src/store.js +289 -0
- package/dist/packages/data/src/trade/bridge.js +464 -0
- package/dist/packages/data/src/trade/caps.js +323 -0
- package/dist/packages/data/src/trade/copy/auto.js +385 -0
- package/dist/packages/data/src/trade/copy/follows.js +103 -0
- package/dist/packages/data/src/trade/copy/index.js +9 -0
- package/dist/packages/data/src/trade/copy/mirror.js +91 -0
- package/dist/packages/data/src/trade/copy/stats.js +120 -0
- package/dist/packages/data/src/trade/copy/tracker.js +158 -0
- package/dist/packages/data/src/trade/copy/trades.js +87 -0
- package/dist/packages/data/src/trade/copy/types.js +6 -0
- package/dist/packages/data/src/trade/executor.js +933 -0
- package/dist/packages/data/src/trade/kill-switch.js +47 -0
- package/dist/packages/data/src/trade/link-store.js +52 -0
- package/dist/packages/data/src/trade/plans/confirm.js +157 -0
- package/dist/packages/data/src/trade/plans/engine.js +784 -0
- package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
- package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
- package/dist/packages/data/src/trade/plans/executor.js +11 -0
- package/dist/packages/data/src/trade/plans/index.js +12 -0
- package/dist/packages/data/src/trade/plans/journal.js +77 -0
- package/dist/packages/data/src/trade/plans/mandate.js +129 -0
- package/dist/packages/data/src/trade/plans/plan.js +179 -0
- package/dist/packages/data/src/trade/plans/service.js +168 -0
- package/dist/packages/data/src/trade/plans/types.js +6 -0
- package/dist/packages/data/src/trade/rpc-exec.js +229 -0
- package/dist/packages/data/src/trade/signer/client.js +197 -0
- package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
- package/dist/packages/data/src/trade/signer/host.js +97 -0
- package/dist/packages/data/src/trade/signer/main.js +121 -0
- package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
- package/dist/packages/data/src/trade/signer/protocol.js +92 -0
- package/dist/packages/data/src/trade/signer/service.js +400 -0
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
- package/dist/packages/data/src/trade/sol-usd.js +86 -0
- package/dist/packages/data/src/trade/swap-build.js +108 -0
- package/dist/packages/data/src/trade/token-delta.js +201 -0
- package/dist/packages/data/src/trade/tx-decode.js +632 -0
- package/dist/packages/data/src/util.js +24 -0
- package/dist/packages/data/src/watchlist.js +176 -0
- package/dist/packages/data/src/x/api.js +256 -0
- package/dist/packages/data/src/x/budget.js +66 -0
- package/dist/packages/data/src/x/config.js +49 -0
- package/dist/packages/data/src/x/index.js +10 -0
- package/dist/packages/data/src/x/mock-server.js +217 -0
- package/dist/packages/data/src/x/oauth.js +242 -0
- package/dist/packages/data/src/x/pkce.js +21 -0
- package/dist/packages/data/src/x/source.js +285 -0
- package/dist/packages/data/src/x/token-store.js +134 -0
- package/dist/packages/data/src/x/transport.js +71 -0
- package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
- package/dist/packages/mcp/assets/cases.json +1194 -0
- package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
- package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
- package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
- package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
- package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
- package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
- package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
- package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
- package/dist/packages/mcp/src/dossier.js +78 -0
- package/dist/packages/mcp/src/firewall.js +21 -0
- package/dist/packages/mcp/src/index.js +22 -0
- package/dist/packages/mcp/src/server.js +936 -0
- package/dist/packages/mcp/src/user-content.js +434 -0
- package/dist/packages/packs/src/backtest.js +183 -0
- package/dist/packages/packs/src/digest.js +95 -0
- package/dist/packages/packs/src/firewall.js +57 -0
- package/dist/packages/packs/src/index.js +20 -0
- package/dist/packages/packs/src/larp-eval.js +100 -0
- package/dist/packages/packs/src/narrative.js +309 -0
- package/dist/packages/packs/src/reason.js +135 -0
- package/dist/packages/packs/src/rug-model.js +137 -0
- package/dist/packages/packs/src/schema.js +54 -0
- package/dist/packages/packs/src/skill-backtest.js +300 -0
- package/dist/packages/packs/src/token-dossier.js +174 -0
- package/dist/packages/packs/src/verdicts.js +17 -0
- package/dist/packages/packs/src/wallet-dossier.js +105 -0
- package/dist/packages/popup/src/config.js +71 -0
- package/dist/packages/popup/src/dock.js +271 -0
- package/dist/packages/popup/src/host.js +210 -0
- package/dist/packages/popup/src/index.js +12 -0
- package/dist/packages/popup/src/loopback.js +53 -0
- package/dist/packages/popup/src/open.js +183 -0
- package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
- package/dist/packages/popup/src/pages/bubbles.css +23 -0
- package/dist/packages/popup/src/pages/bubbles.html +36 -0
- package/dist/packages/popup/src/pages/bubbles.js +214 -0
- package/dist/packages/popup/src/pages/chart-lib.js +262 -0
- package/dist/packages/popup/src/pages/chart.css +50 -0
- package/dist/packages/popup/src/pages/chart.html +56 -0
- package/dist/packages/popup/src/pages/chart.js +554 -0
- package/dist/packages/popup/src/pages/common.css +34 -0
- package/dist/packages/popup/src/pages/common.js +187 -0
- package/dist/packages/popup/src/pages/feeds.html +20 -0
- package/dist/packages/popup/src/pages/feeds.js +77 -0
- package/dist/packages/popup/src/pages/narrative.html +20 -0
- package/dist/packages/popup/src/pages/narrative.js +61 -0
- package/dist/packages/popup/src/pages/plans.css +30 -0
- package/dist/packages/popup/src/pages/plans.html +26 -0
- package/dist/packages/popup/src/pages/plans.js +82 -0
- package/dist/packages/popup/src/pages/roster.css +41 -0
- package/dist/packages/popup/src/pages/roster.html +25 -0
- package/dist/packages/popup/src/pages/roster.js +75 -0
- package/dist/packages/popup/src/pages/token.html +20 -0
- package/dist/packages/popup/src/pages/token.js +112 -0
- package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
- package/dist/packages/popup/src/pages/widget.css +5 -0
- package/dist/packages/popup/src/pages/widget.html +21 -0
- package/dist/packages/popup/src/pages/widget.js +85 -0
- package/dist/packages/popup/src/protocol.js +31 -0
- package/dist/packages/popup/src/selftest.js +156 -0
- package/dist/packages/popup/src/server.js +582 -0
- package/dist/packages/popup/src/sinkproxy.js +167 -0
- package/dist/packages/popup/src/widget-html.js +231 -0
- package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
- package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
- package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
- package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
- package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
- package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
- package/dist/packages/recorder/src/cli.js +113 -0
- package/dist/packages/recorder/src/export.js +71 -0
- package/dist/packages/recorder/src/index.js +20 -0
- package/dist/packages/recorder/src/labels.js +151 -0
- package/dist/packages/recorder/src/open.js +29 -0
- package/dist/packages/recorder/src/recorder.js +553 -0
- package/dist/packages/recorder/src/schedule.js +43 -0
- package/dist/packages/recorder/src/schema.js +135 -0
- package/dist/packages/recorder/src/snapshot.js +191 -0
- package/dist/packages/recorder/src/sweep.js +52 -0
- package/dist/packages/recorder/src/verdicts.js +52 -0
- package/dist/packages/tui/src/bubbles.js +415 -0
- package/dist/packages/tui/src/canvas.js +370 -0
- package/dist/packages/tui/src/chrome.js +85 -0
- package/dist/packages/tui/src/color.js +133 -0
- package/dist/packages/tui/src/flush.js +62 -0
- package/dist/packages/tui/src/fmt.js +188 -0
- package/dist/packages/tui/src/fx-trade.js +278 -0
- package/dist/packages/tui/src/fx.js +527 -0
- package/dist/packages/tui/src/images/blocks.js +141 -0
- package/dist/packages/tui/src/images/coin.js +22 -0
- package/dist/packages/tui/src/images/detect.js +140 -0
- package/dist/packages/tui/src/images/index.js +7 -0
- package/dist/packages/tui/src/images/layer.js +146 -0
- package/dist/packages/tui/src/images/pixels.js +157 -0
- package/dist/packages/tui/src/images/protocols.js +87 -0
- package/dist/packages/tui/src/index.js +18 -0
- package/dist/packages/tui/src/keymap.js +357 -0
- package/dist/packages/tui/src/layout.js +11 -0
- package/dist/packages/tui/src/raster-font.js +195 -0
- package/dist/packages/tui/src/raster.js +213 -0
- package/dist/packages/tui/src/sound.js +177 -0
- package/dist/packages/tui/src/theme.js +182 -0
- package/dist/packages/tui/src/widgets.js +598 -0
- package/dist/packages/voice/src/controller.js +407 -0
- package/dist/packages/voice/src/doctor.js +131 -0
- package/dist/packages/voice/src/download.js +184 -0
- package/dist/packages/voice/src/engines.js +84 -0
- package/dist/packages/voice/src/index.js +20 -0
- package/dist/packages/voice/src/router.js +502 -0
- package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
- package/dist/packages/voice/src/stt/apple.js +179 -0
- package/dist/packages/voice/src/stt/fake.js +34 -0
- package/dist/packages/voice/src/stt/whisper.js +112 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/espeak.js +131 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/piper.js +206 -0
- package/dist/packages/voice/src/tts/say.js +132 -0
- package/dist/packages/voice/src/tts/voices.js +206 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +38 -4
- package/README.md +0 -3
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// dexterm T1 — signer-side trade caps (SPEC §6.0 invariant 3, §6.2 CL-04). The policy the UI, the
|
|
2
|
+
// model, the voice router and a compromised popup CANNOT bypass: every real trade is re-checked here
|
|
3
|
+
// against hard limits before anything is signed or submitted, and refused out of policy.
|
|
4
|
+
//
|
|
5
|
+
// Hard rules (task-1310 scope §3):
|
|
6
|
+
// * DEVNET is the default. MAINNET requires `mainnetEnabled` (the `--mainnet` flag) AND a known USD
|
|
7
|
+
// notional AND it must pass a per-trade USD cap (default $5) and a daily USD cap (default $20).
|
|
8
|
+
// * A plan whose simulation failed, whose price impact exceeds 3%, or whose signed/submitted intent
|
|
9
|
+
// differs from the shown plan (plan-hash mismatch) is refused.
|
|
10
|
+
// * Round 2 (SPEC §6.2): the decoded transaction must pass the program allow-list / forbidden-
|
|
11
|
+
// instruction scan, the simulated SOL OUT of the wallet must not exceed the plan (+ a fee
|
|
12
|
+
// allowance) nor the absolute SOL-out cap, slippage has a ceiling, and a mainnet notional must be
|
|
13
|
+
// strictly positive and known.
|
|
14
|
+
// * Caps live here, not in the UI. This module holds no keys and performs no I/O beyond the daily
|
|
15
|
+
// ledger; it only decides ALLOW / REFUSE.
|
|
16
|
+
//
|
|
17
|
+
// This file imports no wallet, signing or crypto-key library. `node:crypto` is used only for a SHA-256
|
|
18
|
+
// plan hash (binding), never for key material.
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
21
|
+
import { mkdirSync } from 'node:fs';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { systemClock } from "../clock.js";
|
|
25
|
+
export const DEFAULT_PER_TRADE_USD = 5;
|
|
26
|
+
export const DEFAULT_DAILY_USD = 20;
|
|
27
|
+
export const MAX_PRICE_IMPACT_PCT = 3;
|
|
28
|
+
/** Absolute SOL that may leave the wallet in one transaction (any network). */
|
|
29
|
+
export const DEFAULT_MAX_SOL_OUT = 1;
|
|
30
|
+
export const HARD_MAX_SOL_OUT = 5;
|
|
31
|
+
/** Slippage ceiling (SPEC §6.2 "slippage exceeds the configured maximum"). */
|
|
32
|
+
export const DEFAULT_MAX_SLIPPAGE_BPS = 500;
|
|
33
|
+
export const HARD_MAX_SLIPPAGE_BPS = 1000;
|
|
34
|
+
/**
|
|
35
|
+
* How much MORE SOL than the planned SOL leg a transaction may consume: network fee + priority fee +
|
|
36
|
+
* up to two ATA rents (2 × 0.00203928 SOL). A buy of 0.5 SOL may simulate as 0.5 + fees; 0.51 may not.
|
|
37
|
+
*/
|
|
38
|
+
export const SOL_OVERHEAD_ALLOWANCE_LAMPORTS = 10000000n; // 0.01 SOL
|
|
39
|
+
// DEFECT 6: hard ceilings the caps can NEVER exceed, however they are configured. `--max-usd 1000000`
|
|
40
|
+
// or `--daily-usd 1e12` must not be accepted as-is. defaultCaps() clamps any override down to these,
|
|
41
|
+
// the CLI flag parser rejects an over-ceiling value outright, and (round 2) checkCaps clamps AGAIN at
|
|
42
|
+
// the decision point so a caps object literal or a mutated object cannot raise them either.
|
|
43
|
+
export const HARD_MAX_PER_TRADE_USD = 500;
|
|
44
|
+
export const HARD_MAX_DAILY_USD = 2000;
|
|
45
|
+
function clampCap(v, dflt, hardMax) {
|
|
46
|
+
const n = typeof v === 'number' ? v : Number(v);
|
|
47
|
+
if (!(n > 0) || !Number.isFinite(n))
|
|
48
|
+
return dflt;
|
|
49
|
+
return Math.min(n, hardMax);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Safe defaults: devnet, mainnet disabled, tiny caps. Override only from explicit flags. The result is
|
|
53
|
+
* FROZEN (round 2): mutating a caps object after construction throws instead of raising a cap.
|
|
54
|
+
*/
|
|
55
|
+
export function defaultCaps(over = {}) {
|
|
56
|
+
const caps = {
|
|
57
|
+
network: over.network === 'mainnet' ? 'mainnet' : 'devnet',
|
|
58
|
+
mainnetEnabled: over.mainnetEnabled === true,
|
|
59
|
+
// Defensive clamps: a caller can only make caps SMALLER than the defaults by override, never silently
|
|
60
|
+
// larger through a bad value. Non-finite / negative values fall back to the safe default; an
|
|
61
|
+
// over-ceiling value is clamped DOWN to the hard ceiling (DEFECT 6).
|
|
62
|
+
perTradeUsd: clampCap(over.perTradeUsd, DEFAULT_PER_TRADE_USD, HARD_MAX_PER_TRADE_USD),
|
|
63
|
+
dailyUsd: clampCap(over.dailyUsd, DEFAULT_DAILY_USD, HARD_MAX_DAILY_USD),
|
|
64
|
+
maxPriceImpactPct: clampCap(over.maxPriceImpactPct, MAX_PRICE_IMPACT_PCT, MAX_PRICE_IMPACT_PCT),
|
|
65
|
+
maxSolOut: clampCap(over.maxSolOut, DEFAULT_MAX_SOL_OUT, HARD_MAX_SOL_OUT),
|
|
66
|
+
maxSlippageBps: clampCap(over.maxSlippageBps, DEFAULT_MAX_SLIPPAGE_BPS, HARD_MAX_SLIPPAGE_BPS),
|
|
67
|
+
};
|
|
68
|
+
// mainnet is only ever on when explicitly enabled AND the network is mainnet.
|
|
69
|
+
if (caps.network === 'mainnet' && !caps.mainnetEnabled)
|
|
70
|
+
caps.network = 'devnet';
|
|
71
|
+
return Object.freeze(caps);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* task-1328: without `--mainnet` the USD caps are configurable only DOWNWARD — a value above the
|
|
75
|
+
* defaults ($5 / $20) is clamped back to the default. Raising them needs the explicit mainnet flag
|
|
76
|
+
* (and the hard ceilings still apply). The signer applies this to the caps it was started with.
|
|
77
|
+
*/
|
|
78
|
+
export function capsOnlyDownward(caps) {
|
|
79
|
+
if (caps.mainnetEnabled)
|
|
80
|
+
return caps;
|
|
81
|
+
return defaultCaps({
|
|
82
|
+
...caps,
|
|
83
|
+
perTradeUsd: Math.min(caps.perTradeUsd, DEFAULT_PER_TRADE_USD),
|
|
84
|
+
dailyUsd: Math.min(caps.dailyUsd, DEFAULT_DAILY_USD),
|
|
85
|
+
// task-1337 (verify-1328 minor): the SOL-out and slippage ceilings are downward-only too without
|
|
86
|
+
// --mainnet; `--max-slippage-bps 900` used to be honoured on a devnet-labelled signer.
|
|
87
|
+
maxSolOut: Math.min(caps.maxSolOut, DEFAULT_MAX_SOL_OUT),
|
|
88
|
+
maxSlippageBps: Math.min(caps.maxSlippageBps, DEFAULT_MAX_SLIPPAGE_BPS),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The caps as the decision point actually applies them: every numeric cap clamped to its hard ceiling
|
|
93
|
+
* (or its default when missing/invalid), whatever object the caller handed in. A `TradeCaps` literal
|
|
94
|
+
* `{perTradeUsd: 1e9}` is therefore worth exactly HARD_MAX_PER_TRADE_USD here (re-verify MEDIUM #6).
|
|
95
|
+
*/
|
|
96
|
+
export function effectiveCaps(caps) {
|
|
97
|
+
const c = caps ?? {};
|
|
98
|
+
return defaultCaps({
|
|
99
|
+
network: c.network, mainnetEnabled: c.mainnetEnabled,
|
|
100
|
+
perTradeUsd: c.perTradeUsd, dailyUsd: c.dailyUsd, maxPriceImpactPct: c.maxPriceImpactPct,
|
|
101
|
+
maxSolOut: c.maxSolOut, maxSlippageBps: c.maxSlippageBps,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const ok = { ok: true };
|
|
105
|
+
function refuse(reason, message) {
|
|
106
|
+
return { ok: false, reason, message };
|
|
107
|
+
}
|
|
108
|
+
function lamportsToSolText(l) { return (Number(l) / 1e9).toFixed(4); }
|
|
109
|
+
export function checkCaps(plan, rawCaps, spentTodayUsd, opts = {}) {
|
|
110
|
+
const caps = effectiveCaps(rawCaps);
|
|
111
|
+
// 1. Network gate — mainnet is the dangerous one.
|
|
112
|
+
if (plan.network === 'mainnet') {
|
|
113
|
+
if (!caps.mainnetEnabled || caps.network !== 'mainnet') {
|
|
114
|
+
return refuse('mainnet-not-enabled', 'mainnet trading requires the --mainnet flag (and an explicit opt-in)');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (plan.network !== caps.network) {
|
|
118
|
+
return refuse('network-mismatch', `plan network ${plan.network} does not match configured ${caps.network}`);
|
|
119
|
+
}
|
|
120
|
+
// 2. Static transaction policy (SPEC §6.2): quote binding, program allow-list, forbidden
|
|
121
|
+
// instructions, signer. Checked BEFORE the simulation gate because a plan that failed policy was
|
|
122
|
+
// (deliberately) never simulated. A plan with no verdict at all is refused: only the executor's
|
|
123
|
+
// decoder produces one, so its absence means the bytes were never inspected.
|
|
124
|
+
const pol = plan.txPolicy;
|
|
125
|
+
if (!pol || typeof pol !== 'object' || pol.ok !== true) {
|
|
126
|
+
if (pol && pol.ok === false)
|
|
127
|
+
return refuse(pol.reason, pol.message);
|
|
128
|
+
return refuse('program-not-allowed', 'the transaction was not inspected');
|
|
129
|
+
}
|
|
130
|
+
// 3. Simulation must have run and passed (no confirm on a red sim).
|
|
131
|
+
if (!plan.simulated)
|
|
132
|
+
return refuse('not-simulated', 'the transaction was not simulated');
|
|
133
|
+
if (!plan.simOk)
|
|
134
|
+
return refuse('simulation-failed', `simulation failed: ${plan.simError ?? 'unknown error'}`);
|
|
135
|
+
// 4. Price impact ceiling (any network). Unknown impact is refused on mainnet (re-verify LOW #10).
|
|
136
|
+
if (plan.priceImpactPct === null || !Number.isFinite(plan.priceImpactPct)) {
|
|
137
|
+
if (plan.network === 'mainnet')
|
|
138
|
+
return refuse('price-impact', 'the quote carried no price impact; refusing on mainnet');
|
|
139
|
+
}
|
|
140
|
+
else if (plan.priceImpactPct > caps.maxPriceImpactPct) {
|
|
141
|
+
return refuse('price-impact', `price impact ${plan.priceImpactPct.toFixed(2)}% exceeds the ${caps.maxPriceImpactPct}% ceiling`);
|
|
142
|
+
}
|
|
143
|
+
// 5. Slippage ceiling (any network).
|
|
144
|
+
if (!Number.isFinite(plan.slippageBps) || plan.slippageBps < 0 || plan.slippageBps > caps.maxSlippageBps) {
|
|
145
|
+
return refuse('slippage', `slippage ${plan.slippageBps} bps exceeds the ${caps.maxSlippageBps} bps ceiling`);
|
|
146
|
+
}
|
|
147
|
+
// 6. SOL out of the wallet, from the SIMULATED balance delta (any network): must be known, must not
|
|
148
|
+
// exceed the planned SOL leg plus the fee allowance, and must not exceed the absolute cap.
|
|
149
|
+
if (typeof plan.solOutLamports !== 'bigint') {
|
|
150
|
+
return refuse('sol-delta-unknown', 'the simulation did not report the wallet balance change');
|
|
151
|
+
}
|
|
152
|
+
if (typeof plan.solLegLamports !== 'bigint' || plan.solLegLamports < 0n) {
|
|
153
|
+
return refuse('sol-out-cap', 'the plan carries no SOL leg');
|
|
154
|
+
}
|
|
155
|
+
if (plan.solOutLamports > plan.solLegLamports + SOL_OVERHEAD_ALLOWANCE_LAMPORTS) {
|
|
156
|
+
return refuse('sol-out-cap', `the transaction moves ${lamportsToSolText(plan.solOutLamports)} SOL out but the plan is ${lamportsToSolText(plan.solLegLamports)} SOL`);
|
|
157
|
+
}
|
|
158
|
+
const maxOutLamports = BigInt(Math.round(caps.maxSolOut * 1e9));
|
|
159
|
+
if (plan.solOutLamports > maxOutLamports) {
|
|
160
|
+
return refuse('sol-out-cap', `${lamportsToSolText(plan.solOutLamports)} SOL out exceeds the ${caps.maxSolOut} SOL per-transaction cap`);
|
|
161
|
+
}
|
|
162
|
+
// 7. USD caps — enforced on mainnet (devnet funds have no value), and on devnet when the caller asks
|
|
163
|
+
// (task-1363 fast-wallet lane). Mainnet with no known USD value, or a zero/negative one, is refused
|
|
164
|
+
// rather than waved through (round 2: `<= 0`, was `< 0`).
|
|
165
|
+
if (plan.network === 'mainnet' || opts.enforceUsd === true) {
|
|
166
|
+
if (plan.notionalUsd === null || !Number.isFinite(plan.notionalUsd) || plan.notionalUsd <= 0) {
|
|
167
|
+
return refuse('no-usd-value', `cannot price this trade in USD (or it prices at $0); refusing${plan.network === 'mainnet' ? ' on mainnet' : ' (USD caps are enforced on this lane)'}`);
|
|
168
|
+
}
|
|
169
|
+
if (plan.notionalUsd > caps.perTradeUsd) {
|
|
170
|
+
return refuse('per-trade-cap', `trade $${plan.notionalUsd.toFixed(2)} exceeds the per-trade cap $${caps.perTradeUsd.toFixed(2)}`);
|
|
171
|
+
}
|
|
172
|
+
const spent = Number.isFinite(spentTodayUsd) && spentTodayUsd > 0 ? spentTodayUsd : 0;
|
|
173
|
+
if (spent + plan.notionalUsd > caps.dailyUsd) {
|
|
174
|
+
return refuse('daily-cap', `trade $${plan.notionalUsd.toFixed(2)} would exceed today's remaining budget ($${Math.max(0, caps.dailyUsd - spent).toFixed(2)} of $${caps.dailyUsd.toFixed(2)})`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return ok;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A plan hash binds a confirm/signature to the exact plan the user saw (SPEC §6.5 plan-hash binding).
|
|
181
|
+
* If any material field changes between display and submit, the hash changes and the confirm is void.
|
|
182
|
+
* Amounts are included so an inflated size or a swapped mint invalidates the confirm.
|
|
183
|
+
*/
|
|
184
|
+
export function planHash(plan) {
|
|
185
|
+
const canonical = [
|
|
186
|
+
plan.network, plan.mint, plan.side,
|
|
187
|
+
plan.amountInRaw.toString(), plan.amountOutRaw.toString(),
|
|
188
|
+
String(plan.slippageBps), plan.route.join('>'),
|
|
189
|
+
typeof plan.solLegLamports === 'bigint' ? plan.solLegLamports.toString() : 'none',
|
|
190
|
+
typeof plan.solOutLamports === 'bigint' ? plan.solOutLamports.toString() : 'none',
|
|
191
|
+
].join('|');
|
|
192
|
+
return createHash('sha256').update(canonical).digest('hex');
|
|
193
|
+
}
|
|
194
|
+
/** True when two plans are the same trade (same hash). Used to refuse a mutated plan at submit time. */
|
|
195
|
+
export function samePlan(a, b) {
|
|
196
|
+
return planHash(a) === planHash(b);
|
|
197
|
+
}
|
|
198
|
+
function utcDay(now) {
|
|
199
|
+
return new Date(now).toISOString().slice(0, 10); // YYYY-MM-DD (UTC)
|
|
200
|
+
}
|
|
201
|
+
/** Where the CLI keeps the daily-cap ledger (shared by every dexterm process on this machine). */
|
|
202
|
+
export function defaultCapLedgerPath() {
|
|
203
|
+
return join(homedir(), '.dexterm', 'trade-caps.sqlite');
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Persistent daily-spend ledger for the USD daily cap. Backed by node:sqlite so the cap survives a
|
|
207
|
+
* restart (a compromised UI cannot "forget" today's spend by relaunching). An in-memory DB is used
|
|
208
|
+
* when no path is given (tests). Only realized mainnet USD is recorded.
|
|
209
|
+
*
|
|
210
|
+
* Round 2: `tryReserve` checks AND charges inside one `BEGIN IMMEDIATE` transaction, so two dexterm
|
|
211
|
+
* PROCESSES sharing the file cannot both pass the daily check (re-verify MEDIUM #7); a `busy_timeout`
|
|
212
|
+
* makes a concurrent open wait instead of throwing SQLITE_BUSY. `lock()` is the panic path: a locked
|
|
213
|
+
* ledger reserves nothing until `unlock()`.
|
|
214
|
+
*/
|
|
215
|
+
export class CapLedger {
|
|
216
|
+
db;
|
|
217
|
+
clock;
|
|
218
|
+
constructor(opts = {}) {
|
|
219
|
+
const path = opts.path ?? ':memory:';
|
|
220
|
+
if (path !== ':memory:')
|
|
221
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
222
|
+
this.db = new DatabaseSync(path);
|
|
223
|
+
this.clock = opts.clock ?? systemClock;
|
|
224
|
+
this.db.exec(`
|
|
225
|
+
PRAGMA busy_timeout = 5000;
|
|
226
|
+
PRAGMA journal_mode = WAL;
|
|
227
|
+
CREATE TABLE IF NOT EXISTS trade_day_spend (
|
|
228
|
+
day TEXT PRIMARY KEY,
|
|
229
|
+
usd REAL NOT NULL DEFAULT 0,
|
|
230
|
+
trades INTEGER NOT NULL DEFAULT 0
|
|
231
|
+
) WITHOUT ROWID;
|
|
232
|
+
CREATE TABLE IF NOT EXISTS trade_lock (
|
|
233
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
234
|
+
locked_at INTEGER NOT NULL,
|
|
235
|
+
reason TEXT NOT NULL
|
|
236
|
+
);
|
|
237
|
+
`);
|
|
238
|
+
}
|
|
239
|
+
/** USD already spent on mainnet today (UTC). */
|
|
240
|
+
spentToday(now = this.clock.now()) {
|
|
241
|
+
const row = this.db.prepare('SELECT usd FROM trade_day_spend WHERE day = ?').get(utcDay(now));
|
|
242
|
+
return row && Number.isFinite(row.usd) ? Number(row.usd) : 0;
|
|
243
|
+
}
|
|
244
|
+
/** Record a realized mainnet trade's USD against today's budget. Devnet trades record nothing. */
|
|
245
|
+
record(usd, now = this.clock.now()) {
|
|
246
|
+
if (!(usd > 0))
|
|
247
|
+
return;
|
|
248
|
+
const day = utcDay(now);
|
|
249
|
+
this.db.prepare(`INSERT INTO trade_day_spend (day, usd, trades) VALUES (?, ?, 1)
|
|
250
|
+
ON CONFLICT(day) DO UPDATE SET usd = usd + excluded.usd, trades = trades + 1`).run(day, usd);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Atomically check `spent + usd <= dailyCap` and reserve `usd` for today. The SELECT and the INSERT
|
|
254
|
+
* run inside BEGIN IMMEDIATE, which takes the write lock first, so concurrent processes serialize
|
|
255
|
+
* here. Returns the reservation's DAY so a refund credits the same day even across midnight.
|
|
256
|
+
*/
|
|
257
|
+
tryReserve(usd, dailyCap, now = this.clock.now()) {
|
|
258
|
+
const day = utcDay(now);
|
|
259
|
+
if (!(usd > 0))
|
|
260
|
+
return { ok: true, day, usd: 0 };
|
|
261
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
262
|
+
try {
|
|
263
|
+
if (this.lockedRow()) {
|
|
264
|
+
this.db.exec('ROLLBACK');
|
|
265
|
+
return { ok: false, reason: 'panicked', spent: this.spentToday(now) };
|
|
266
|
+
}
|
|
267
|
+
const row = this.db.prepare('SELECT usd FROM trade_day_spend WHERE day = ?').get(day);
|
|
268
|
+
const spent = row && Number.isFinite(row.usd) && Number(row.usd) > 0 ? Number(row.usd) : 0;
|
|
269
|
+
if (spent + usd > dailyCap) {
|
|
270
|
+
this.db.exec('ROLLBACK');
|
|
271
|
+
return { ok: false, reason: 'daily-cap', spent };
|
|
272
|
+
}
|
|
273
|
+
this.db.prepare(`INSERT INTO trade_day_spend (day, usd, trades) VALUES (?, ?, 1)
|
|
274
|
+
ON CONFLICT(day) DO UPDATE SET usd = usd + excluded.usd, trades = trades + 1`).run(day, usd);
|
|
275
|
+
this.db.exec('COMMIT');
|
|
276
|
+
return { ok: true, day, usd };
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
try {
|
|
280
|
+
this.db.exec('ROLLBACK');
|
|
281
|
+
}
|
|
282
|
+
catch { /* not in a transaction */ }
|
|
283
|
+
throw e;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Undo a reservation (DEFECT 5): the executor charges a trade's USD BEFORE it signs/submits so a
|
|
288
|
+
* burst of concurrent trades cannot each pass the daily check and collectively blow the cap. If the
|
|
289
|
+
* send definitely never left (sign failed, tx-byte mismatch, a pre-broadcast JSON-RPC rejection) the
|
|
290
|
+
* reservation is refunded here, to the DAY it was reserved on (re-verify LOW #9). A send that was
|
|
291
|
+
* broadcast, or MAY have been (transport error), is NOT refunded — the funds may have moved.
|
|
292
|
+
*/
|
|
293
|
+
refund(usd, dayOrNow = this.clock.now()) {
|
|
294
|
+
if (!(usd > 0))
|
|
295
|
+
return;
|
|
296
|
+
const day = typeof dayOrNow === 'string' ? dayOrNow : utcDay(dayOrNow);
|
|
297
|
+
// Floor at 0; one trade's refund can never drive the day negative.
|
|
298
|
+
this.db.prepare(`UPDATE trade_day_spend SET usd = MAX(0, usd - ?), trades = MAX(0, trades - 1) WHERE day = ?`).run(usd, day);
|
|
299
|
+
}
|
|
300
|
+
lockedRow() {
|
|
301
|
+
const row = this.db.prepare('SELECT locked_at, reason FROM trade_lock WHERE id = 1').get();
|
|
302
|
+
return row ?? null;
|
|
303
|
+
}
|
|
304
|
+
/** The panic lock, if engaged. Re-read every call so a lock from another process is seen at once. */
|
|
305
|
+
locked() {
|
|
306
|
+
const row = this.lockedRow();
|
|
307
|
+
return row ? { lockedAt: Number(row.locked_at), reason: String(row.reason) } : null;
|
|
308
|
+
}
|
|
309
|
+
/** PANIC: refuse every reservation until unlock(). Idempotent. */
|
|
310
|
+
lock(reason = 'panic', now = this.clock.now()) {
|
|
311
|
+
this.db.prepare('INSERT OR IGNORE INTO trade_lock (id, locked_at, reason) VALUES (1, ?, ?)').run(now, reason.slice(0, 200));
|
|
312
|
+
}
|
|
313
|
+
unlock() {
|
|
314
|
+
const r = this.db.prepare('DELETE FROM trade_lock WHERE id = 1').run();
|
|
315
|
+
return Number(r.changes) > 0;
|
|
316
|
+
}
|
|
317
|
+
close() {
|
|
318
|
+
try {
|
|
319
|
+
this.db.close();
|
|
320
|
+
}
|
|
321
|
+
catch { /* already closed */ }
|
|
322
|
+
}
|
|
323
|
+
}
|