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,161 @@
|
|
|
1
|
+
// GoPlus Solana token-security adapter (task-1298; inventory 2026-10-06 §1d). Keyless, read-only.
|
|
2
|
+
// GET https://api.gopluslabs.io/api/v1/solana/token_security?contract_addresses={mint}
|
|
3
|
+
// -> { code: 1, message: "ok", result: { [mint]: {
|
|
4
|
+
// mintable | freezable | closable | balance_mutable_authority | metadata_mutable |
|
|
5
|
+
// transfer_fee_upgradable | transfer_hook_upgradable | default_account_state_upgradable:
|
|
6
|
+
// { status: "0"|"1", authority: [{ address, malicious_address }] | ... },
|
|
7
|
+
// non_transferable: "0"|"1", default_account_state: "0"|"1", transfer_hook: [...], transfer_fee: {...},
|
|
8
|
+
// holder_count, total_supply, holders: [{ account, token_account, balance, percent, is_locked, tag }],
|
|
9
|
+
// lp_holders: [...], dex: [{ dex_name, id, tvl, lp_amount, price, type, ... }],
|
|
10
|
+
// creators: [{ address, malicious_address }], metadata: { name, symbol, uri, description }, trusted_token } } }
|
|
11
|
+
// Docs: https://docs.gopluslabs.io/reference/solanatokensecurityusingget (fetched for task-1285).
|
|
12
|
+
// A second opinion next to RugCheck: authorities, Token-2022 hooks/fees, LP holders. `code !== 1`
|
|
13
|
+
// is a failure. All free-text fields (names, tags, descriptions) are untrusted (CL-06).
|
|
14
|
+
import { numOrNull } from "../util.js";
|
|
15
|
+
export const GOPLUS_BASE = 'https://api.gopluslabs.io/api/v1';
|
|
16
|
+
const META = { source: 'goplus', bucket: 'goplus' };
|
|
17
|
+
function flag(v) {
|
|
18
|
+
if (v === undefined || v === null || v === '')
|
|
19
|
+
return null;
|
|
20
|
+
const s = String(v);
|
|
21
|
+
if (s === '1' || s === 'true')
|
|
22
|
+
return true;
|
|
23
|
+
if (s === '0' || s === 'false')
|
|
24
|
+
return false;
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function authorityFlag(a) {
|
|
28
|
+
if (!a)
|
|
29
|
+
return null;
|
|
30
|
+
const st = flag(a.status);
|
|
31
|
+
if (st !== null)
|
|
32
|
+
return st;
|
|
33
|
+
return Array.isArray(a.authority) ? a.authority.length > 0 : null;
|
|
34
|
+
}
|
|
35
|
+
/** GoPlus percentages are fractions on EVM ("0.0123"); accept both fraction and percent forms. */
|
|
36
|
+
export function toPercent(v, total = null, balance = null) {
|
|
37
|
+
const p = numOrNull(v);
|
|
38
|
+
if (p !== null)
|
|
39
|
+
return p <= 1 ? p * 100 : p;
|
|
40
|
+
const b = numOrNull(balance);
|
|
41
|
+
if (b !== null && total && total > 0)
|
|
42
|
+
return (b / total) * 100;
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
function holder(h, total) {
|
|
46
|
+
const locked = flag(h.is_locked);
|
|
47
|
+
return {
|
|
48
|
+
address: h.account ?? h.token_account ?? '',
|
|
49
|
+
tokenAccount: h.token_account ?? null,
|
|
50
|
+
pct: Math.round(toPercent(h.percent, total, h.balance) * 1000) / 1000,
|
|
51
|
+
amount: numOrNull(h.balance) ?? 0,
|
|
52
|
+
isLocked: locked,
|
|
53
|
+
tag: typeof h.tag === 'string' && h.tag !== '' ? h.tag : null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const str = (v) => (typeof v === 'string' && v !== '' ? v : null);
|
|
57
|
+
export function normalizeGoPlus(mint, raw, fetchedAt) {
|
|
58
|
+
const total = numOrNull(raw.total_supply);
|
|
59
|
+
const malicious = new Set();
|
|
60
|
+
const addrs = (a) => {
|
|
61
|
+
for (const x of (a?.authority ?? [])) {
|
|
62
|
+
if (typeof x === 'object' && x && x.address && flag(x.malicious_address) === true)
|
|
63
|
+
malicious.add(x.address);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
for (const a of [raw.mintable, raw.freezable, raw.closable, raw.balance_mutable_authority, raw.metadata_mutable, raw.transfer_fee_upgradable, raw.transfer_hook_upgradable, raw.default_account_state_upgradable])
|
|
67
|
+
addrs(a);
|
|
68
|
+
const creators = [];
|
|
69
|
+
for (const c of raw.creators ?? []) {
|
|
70
|
+
if (c?.address) {
|
|
71
|
+
creators.push(c.address);
|
|
72
|
+
if (flag(c.malicious_address) === true)
|
|
73
|
+
malicious.add(c.address);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const sec = {
|
|
77
|
+
mint,
|
|
78
|
+
mintable: authorityFlag(raw.mintable),
|
|
79
|
+
freezable: authorityFlag(raw.freezable),
|
|
80
|
+
closable: authorityFlag(raw.closable),
|
|
81
|
+
balanceMutable: authorityFlag(raw.balance_mutable_authority),
|
|
82
|
+
metadataMutable: authorityFlag(raw.metadata_mutable),
|
|
83
|
+
transferFeeUpgradable: authorityFlag(raw.transfer_fee_upgradable),
|
|
84
|
+
transferHookUpgradable: authorityFlag(raw.transfer_hook_upgradable),
|
|
85
|
+
defaultAccountStateUpgradable: authorityFlag(raw.default_account_state_upgradable),
|
|
86
|
+
nonTransferable: flag(raw.non_transferable),
|
|
87
|
+
transferHook: raw.transfer_hook === undefined ? null : Array.isArray(raw.transfer_hook) && raw.transfer_hook.length > 0,
|
|
88
|
+
transferFee: raw.transfer_fee === undefined ? null : !!raw.transfer_fee && Object.keys(raw.transfer_fee).length > 0,
|
|
89
|
+
defaultAccountState: raw.default_account_state === undefined || raw.default_account_state === null ? null : String(raw.default_account_state),
|
|
90
|
+
holderCount: numOrNull(raw.holder_count),
|
|
91
|
+
totalSupply: total,
|
|
92
|
+
holders: (raw.holders ?? []).filter((h) => h && (h.account || h.token_account)).map((h) => holder(h, total)).sort((a, b) => b.pct - a.pct),
|
|
93
|
+
lpHolders: (raw.lp_holders ?? []).filter((h) => h && (h.account || h.token_account)).map((h) => holder(h, total)).sort((a, b) => b.pct - a.pct),
|
|
94
|
+
dex: (raw.dex ?? []).filter((d) => d && (d.dex_name || d.id)).map((d) => ({ name: str(d.dex_name) ?? 'unknown', pool: str(d.id), tvlUsd: numOrNull(d.tvl), lpAmount: numOrNull(d.lp_amount), type: str(d.type) })),
|
|
95
|
+
creators,
|
|
96
|
+
maliciousAddresses: [...malicious],
|
|
97
|
+
trustedToken: flag(raw.trusted_token),
|
|
98
|
+
flags: [],
|
|
99
|
+
source: 'goplus',
|
|
100
|
+
metadataUri: typeof raw.metadata?.uri === 'string' ? raw.metadata.uri : null,
|
|
101
|
+
fetchedAt,
|
|
102
|
+
};
|
|
103
|
+
const flags = [];
|
|
104
|
+
if (sec.mintable)
|
|
105
|
+
flags.push('goplus:mintable');
|
|
106
|
+
if (sec.freezable)
|
|
107
|
+
flags.push('goplus:freezable');
|
|
108
|
+
if (sec.closable)
|
|
109
|
+
flags.push('goplus:closable');
|
|
110
|
+
if (sec.balanceMutable)
|
|
111
|
+
flags.push('goplus:balance-mutable');
|
|
112
|
+
if (sec.nonTransferable)
|
|
113
|
+
flags.push('goplus:non-transferable');
|
|
114
|
+
if (sec.transferHook)
|
|
115
|
+
flags.push('goplus:transfer-hook');
|
|
116
|
+
if (sec.transferFee)
|
|
117
|
+
flags.push('goplus:transfer-fee');
|
|
118
|
+
if (sec.defaultAccountState === '2')
|
|
119
|
+
flags.push('goplus:default-frozen');
|
|
120
|
+
if (sec.maliciousAddresses.length)
|
|
121
|
+
flags.push('goplus:malicious-address');
|
|
122
|
+
sec.flags = flags;
|
|
123
|
+
return sec;
|
|
124
|
+
}
|
|
125
|
+
/** The Audit fields GoPlus can speak to (authorities, top-10 by token account, risk names). */
|
|
126
|
+
export function auditPatchFromGoPlus(s) {
|
|
127
|
+
const top10 = s.holders.slice(0, 10).reduce((sum, h) => sum + h.pct, 0);
|
|
128
|
+
return {
|
|
129
|
+
mintAuthority: s.mintable,
|
|
130
|
+
freezeAuthority: s.freezable,
|
|
131
|
+
top10Pct: s.holders.length ? Math.round(top10 * 1000) / 1000 : null,
|
|
132
|
+
rugRisks: s.flags,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** `code !== 1` is an API-level failure even with HTTP 200; code 4029 is GoPlus's rate limit. */
|
|
136
|
+
export const goplusBodyCheck = (data) => {
|
|
137
|
+
const r = data;
|
|
138
|
+
if (!r || typeof r !== 'object')
|
|
139
|
+
return { error: 'goplus: empty body' };
|
|
140
|
+
if (r.code === 1)
|
|
141
|
+
return null;
|
|
142
|
+
const msg = `goplus code ${r.code ?? '?'}: ${r.message ?? 'unknown'}`;
|
|
143
|
+
return r.code === 4029 || /rate|limit|too many/i.test(r.message ?? '') ? { status: 429, error: msg } : { error: msg };
|
|
144
|
+
};
|
|
145
|
+
export class GoPlus {
|
|
146
|
+
http;
|
|
147
|
+
constructor(http) {
|
|
148
|
+
this.http = http;
|
|
149
|
+
}
|
|
150
|
+
static url(mint) {
|
|
151
|
+
return `${GOPLUS_BASE}/solana/token_security?contract_addresses=${encodeURIComponent(mint)}`;
|
|
152
|
+
}
|
|
153
|
+
async tokenSecurity(mint) {
|
|
154
|
+
const r = await this.http.getJson(GoPlus.url(mint), META, goplusBodyCheck);
|
|
155
|
+
const result = r.data.result ?? {};
|
|
156
|
+
const raw = result[mint] ?? Object.values(result).find((v) => v) ?? null;
|
|
157
|
+
if (!raw)
|
|
158
|
+
throw new Error(`goplus: no result for ${mint}`);
|
|
159
|
+
return normalizeGoPlus(mint, raw, r.fetchedAt);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Jupiter Swap *Quote* API (SPEC §3.1 S11, §6.1). KEYLESS, READ-ONLY, quote only.
|
|
2
|
+
//
|
|
3
|
+
// This adapter fetches a swap quote — route, in/out amounts, price impact, fees, slippage —
|
|
4
|
+
// and NOTHING else. There is deliberately no /swap build, no /order?taker, no /execute and no
|
|
5
|
+
// transaction assembly in this file, so the paper-trading lane (task-1310 T0) can price a fill
|
|
6
|
+
// from a live route while being provably unable to reach a signable transaction (SPEC §6.0).
|
|
7
|
+
//
|
|
8
|
+
// GET {host}/swap/v1/quote?inputMint=&outputMint=&amount=&slippageBps=&swapMode=
|
|
9
|
+
//
|
|
10
|
+
// Host order mirrors sources/jupiter.ts: the documented `api.jup.ag` first; on a keyless refusal
|
|
11
|
+
// (401/403) switch, for the session, to `lite-api.jup.ag` (still keyless; SPEC §9.1). Shares the
|
|
12
|
+
// keyless `jupiter` rate-limit bucket (see SOURCE_BUCKET['jupiter-swap']='jupiter') and so is paced
|
|
13
|
+
// and budget-metered exactly like the Tokens/Price calls (free-forever, per-IP quota).
|
|
14
|
+
import { HttpError, SourceUnavailableError } from "../http.js";
|
|
15
|
+
import { systemClock } from "../clock.js";
|
|
16
|
+
import { numOrNull } from "../util.js";
|
|
17
|
+
export const JUP_SWAP_HOSTS = ['https://api.jup.ag', 'https://lite-api.jup.ag'];
|
|
18
|
+
const SWAP = { source: 'jupiter-swap', bucket: 'jupiter' };
|
|
19
|
+
// ------------------------------------------------------------------------------------ retry policy (task-1368)
|
|
20
|
+
// A live ticket died on `build-failed: HTTP 429 for https://api.jup.ag/swap/v1/swap`: the old adapters
|
|
21
|
+
// switched host only on a keyless refusal (401/403), never retried, and a single per-IP 429 on the build killed
|
|
22
|
+
// the plan while the shared `jupiter` bucket paused for 30 s (the preview "fetching… forever"). Both hosts answer
|
|
23
|
+
// 200 a moment later, so a 429 (or a 5xx / a dropped connection) is TRANSIENT and is handled here, once, for
|
|
24
|
+
// the quote adapter AND the build adapter: honour Retry-After, retry with a jittered exponential backoff, try the
|
|
25
|
+
// OTHER host (api ⇄ lite-api) for the next attempt, and give up only when the attempt count or the total time
|
|
26
|
+
// budget is spent — with a typed error that says exactly when to try again. Nothing here changes what the
|
|
27
|
+
// signer checks: it only decides when a keyless HTTP call is re-issued.
|
|
28
|
+
/** Retry budget for a keyless Jupiter call: at most this many attempts within this many ms. */
|
|
29
|
+
export const JUP_RETRY_MAX_ATTEMPTS = 3;
|
|
30
|
+
export const JUP_RETRY_MAX_TOTAL_MS = 10_000;
|
|
31
|
+
/** First backoff (doubles per attempt), multiplied by a jitter factor in [0.5, 1.5). */
|
|
32
|
+
export const JUP_RETRY_BASE_MS = 1_000;
|
|
33
|
+
/**
|
|
34
|
+
* A keyless Jupiter call was rate limited (429) and the retry budget is spent — or the data layer's own health
|
|
35
|
+
* gate is cooling the source down after repeated 429s (SPEC §3.3: two 429s inside a minute → 30 s, doubling).
|
|
36
|
+
* `retryAfterMs` is the honest wait: the largest of the server's Retry-After / reset, the next backoff, the
|
|
37
|
+
* shared bucket's remaining pause and the health cool-down, so a UI that shows "retrying in Ns" and retries then
|
|
38
|
+
* will usually get through. `status` is 429 when the last answer was a 429; 0 when only the cool-down spoke.
|
|
39
|
+
*/
|
|
40
|
+
export class JupiterRateLimitedError extends Error {
|
|
41
|
+
status;
|
|
42
|
+
host;
|
|
43
|
+
retryAfterMs;
|
|
44
|
+
attempts;
|
|
45
|
+
constructor(status, host, retryAfterMs, attempts) {
|
|
46
|
+
const s = Math.max(1, Math.ceil(retryAfterMs / 1000));
|
|
47
|
+
super(status === 429
|
|
48
|
+
? `Jupiter is rate limiting this IP (429 from ${hostnameOf(host)}); retry in ${s}s`
|
|
49
|
+
: `Jupiter calls are paused after repeated rate limits (dexterm cool-down); retry in ${s}s`);
|
|
50
|
+
this.name = 'JupiterRateLimitedError';
|
|
51
|
+
this.status = status;
|
|
52
|
+
this.host = host;
|
|
53
|
+
this.retryAfterMs = retryAfterMs;
|
|
54
|
+
this.attempts = attempts;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function hostnameOf(host) {
|
|
58
|
+
try {
|
|
59
|
+
return new URL(host).hostname;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return host;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** 429, any 5xx, a dropped connection or a transport timeout: worth another attempt. A caller abort never is. */
|
|
66
|
+
export function isTransientJupiterError(err) {
|
|
67
|
+
if (err instanceof HttpError)
|
|
68
|
+
return err.status === 429 || (err.status >= 500 && err.status <= 599);
|
|
69
|
+
if (err instanceof Error) {
|
|
70
|
+
if (err.name === 'AbortError')
|
|
71
|
+
return false;
|
|
72
|
+
if (err.name === 'TimeoutError')
|
|
73
|
+
return true;
|
|
74
|
+
// undici's `fetch failed` (DNS, reset, refused) is a TypeError.
|
|
75
|
+
return err instanceof TypeError;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Host rotation + the retry policy above, shared by the quote and build adapters (one instance per adapter).
|
|
81
|
+
* `run` calls `fn(host)`; on a keyless refusal (401/403) it fails over to the next host for the session (as
|
|
82
|
+
* before); on a transient error it alternates to the other host and retries after `max(Retry-After, backoff)`
|
|
83
|
+
* while the budget allows; otherwise it rethrows. The caller's `signal` cuts a backoff sleep short.
|
|
84
|
+
*/
|
|
85
|
+
export class JupiterHosts {
|
|
86
|
+
hosts;
|
|
87
|
+
index = 0;
|
|
88
|
+
clock;
|
|
89
|
+
maxAttempts;
|
|
90
|
+
maxTotalMs;
|
|
91
|
+
baseMs;
|
|
92
|
+
random;
|
|
93
|
+
bucketPausedMs;
|
|
94
|
+
sourceUnavailableMs;
|
|
95
|
+
constructor(hosts = JUP_SWAP_HOSTS, opts = {}) {
|
|
96
|
+
if (hosts.length === 0)
|
|
97
|
+
throw new Error('JupiterHosts needs at least one host');
|
|
98
|
+
this.hosts = hosts;
|
|
99
|
+
this.clock = opts.clock ?? systemClock;
|
|
100
|
+
this.maxAttempts = Math.max(1, opts.maxAttempts ?? JUP_RETRY_MAX_ATTEMPTS);
|
|
101
|
+
this.maxTotalMs = Math.max(0, opts.maxTotalMs ?? JUP_RETRY_MAX_TOTAL_MS);
|
|
102
|
+
this.baseMs = Math.max(0, opts.baseBackoffMs ?? JUP_RETRY_BASE_MS);
|
|
103
|
+
this.random = opts.random ?? Math.random;
|
|
104
|
+
this.bucketPausedMs = opts.bucketPausedMs ?? (() => 0);
|
|
105
|
+
this.sourceUnavailableMs = opts.sourceUnavailableMs ?? (() => 0);
|
|
106
|
+
}
|
|
107
|
+
get host() { return this.hosts[this.index]; }
|
|
108
|
+
async run(fn, signal) {
|
|
109
|
+
const started = this.clock.now();
|
|
110
|
+
let attempt = 0;
|
|
111
|
+
let lastStatus = 0;
|
|
112
|
+
for (;;) {
|
|
113
|
+
attempt++;
|
|
114
|
+
const host = this.host;
|
|
115
|
+
try {
|
|
116
|
+
return await fn(host, attempt);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (signal?.aborted)
|
|
120
|
+
throw err;
|
|
121
|
+
// The health gate refused to send (the source is DEGRADED — after two 429s inside a minute, or three
|
|
122
|
+
// failures in a row): report it as the rate-limit wait it is, so the ticket counts down instead of
|
|
123
|
+
// failing with "source jupiter-swap is degraded". Status 429 when this run saw one, else the cool-down form.
|
|
124
|
+
if (err instanceof SourceUnavailableError) {
|
|
125
|
+
const wait = Math.max(this.sourceUnavailableMs(), this.bucketPausedMs(), 1_000);
|
|
126
|
+
throw new JupiterRateLimitedError(lastStatus === 429 ? 429 : 0, host, wait, attempt);
|
|
127
|
+
}
|
|
128
|
+
// Keyless refusal: switch host for the session (sticky), no wait, not counted as a retry.
|
|
129
|
+
if (err instanceof HttpError && (err.status === 401 || err.status === 403)) {
|
|
130
|
+
if (this.index < this.hosts.length - 1) {
|
|
131
|
+
this.index++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
if (!isTransientJupiterError(err))
|
|
137
|
+
throw err;
|
|
138
|
+
// Transient: the NEXT attempt goes to the other host (round robin; it stays there afterwards).
|
|
139
|
+
if (this.hosts.length > 1)
|
|
140
|
+
this.index = (this.index + 1) % this.hosts.length;
|
|
141
|
+
const status = err instanceof HttpError ? err.status : 0;
|
|
142
|
+
lastStatus = status;
|
|
143
|
+
const retryAfter = err instanceof HttpError && err.retryAfterMs !== undefined ? err.retryAfterMs : 0;
|
|
144
|
+
const backoff = Math.round(this.baseMs * 2 ** (attempt - 1) * (0.5 + this.random()));
|
|
145
|
+
const wait = Math.max(retryAfter, backoff);
|
|
146
|
+
const elapsed = this.clock.now() - started;
|
|
147
|
+
if (attempt >= this.maxAttempts || elapsed + wait > this.maxTotalMs) {
|
|
148
|
+
if (status === 429)
|
|
149
|
+
throw new JupiterRateLimitedError(status, host, Math.max(wait, this.bucketPausedMs()), attempt);
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
await this.clock.sleep(wait, signal);
|
|
153
|
+
if (signal?.aborted)
|
|
154
|
+
throw abortError();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function abortError() {
|
|
160
|
+
const e = new Error('aborted by caller');
|
|
161
|
+
e.name = 'AbortError';
|
|
162
|
+
return e;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Parse a raw base-unit amount. STRICT (task-1310 round 2): only a non-negative decimal integer string
|
|
166
|
+
* is accepted. `"1.5e11"`, `"abc"`, `"-1"`, `""`, a number or a missing field THROW instead of mapping
|
|
167
|
+
* to 0n — a zero amount silently voided the USD caps downstream.
|
|
168
|
+
*/
|
|
169
|
+
function toBig(s, field = 'amount') {
|
|
170
|
+
if (typeof s !== 'string' || !/^[0-9]{1,30}$/.test(s)) {
|
|
171
|
+
throw new Error(`malformed quote: ${field} is not an integer string (${JSON.stringify(s).slice(0, 40)})`);
|
|
172
|
+
}
|
|
173
|
+
return BigInt(s);
|
|
174
|
+
}
|
|
175
|
+
export function normalizeSwapQuote(raw, fetchedAt) {
|
|
176
|
+
return {
|
|
177
|
+
inputMint: raw.inputMint,
|
|
178
|
+
outputMint: raw.outputMint,
|
|
179
|
+
inAmount: toBig(raw.inAmount, 'inAmount'),
|
|
180
|
+
outAmount: toBig(raw.outAmount, 'outAmount'),
|
|
181
|
+
otherAmountThreshold: toBig(raw.otherAmountThreshold, 'otherAmountThreshold'),
|
|
182
|
+
mode: raw.swapMode === 'ExactOut' ? 'ExactOut' : 'ExactIn',
|
|
183
|
+
slippageBps: numOrNull(raw.slippageBps) ?? 0,
|
|
184
|
+
// DEFECT 1: Jupiter's priceImpactPct is a 0..1 fraction; convert to a percent exactly once.
|
|
185
|
+
priceImpactPct: (() => { const f = numOrNull(raw.priceImpactPct); return f === null ? null : f * 100; })(),
|
|
186
|
+
route: (raw.routePlan ?? []).map((r) => r.swapInfo?.label ?? '').filter((s) => s.length > 0),
|
|
187
|
+
platformFeeBps: numOrNull(raw.platformFee?.feeBps),
|
|
188
|
+
platformFeeAmount: raw.platformFee?.amount !== undefined ? toBig(raw.platformFee.amount, 'platformFee.amount') : null,
|
|
189
|
+
usdValue: numOrNull(raw.swapUsdValue),
|
|
190
|
+
contextSlot: numOrNull(raw.contextSlot),
|
|
191
|
+
source: 'jupiter-swap',
|
|
192
|
+
fetchedAt,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export class JupiterSwap {
|
|
196
|
+
http;
|
|
197
|
+
hosts;
|
|
198
|
+
constructor(http, hosts = JUP_SWAP_HOSTS, retry = {}) {
|
|
199
|
+
this.http = http;
|
|
200
|
+
this.hosts = new JupiterHosts(hosts, { clock: http.clock, bucketPausedMs: () => http.pausedFor(SWAP.bucket), sourceUnavailableMs: () => http.unavailableFor(SWAP.source), ...retry });
|
|
201
|
+
}
|
|
202
|
+
get host() {
|
|
203
|
+
return this.hosts.host;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* A single keyless quote. Throws HttpError (redacted) on transport/HTTP/validation failure.
|
|
207
|
+
* `signal` bounds the wait (task-1310): a confirm-time quote passes an 8 s abort so a 429 pause
|
|
208
|
+
* on the shared keyless bucket can never hang the booking; the abort rejects with an AbortError.
|
|
209
|
+
*/
|
|
210
|
+
async quote(req, signal) {
|
|
211
|
+
if (!(req.amount > 0n))
|
|
212
|
+
throw new Error('quote amount must be a positive integer of base units');
|
|
213
|
+
if (req.inputMint === req.outputMint)
|
|
214
|
+
throw new Error('quote needs two different mints');
|
|
215
|
+
const params = new URLSearchParams({
|
|
216
|
+
inputMint: req.inputMint,
|
|
217
|
+
outputMint: req.outputMint,
|
|
218
|
+
amount: req.amount.toString(),
|
|
219
|
+
slippageBps: String(Math.max(0, Math.round(req.slippageBps))),
|
|
220
|
+
swapMode: req.mode ?? 'ExactIn',
|
|
221
|
+
});
|
|
222
|
+
const r = await this.get(`/swap/v1/quote?${params.toString()}`, signal);
|
|
223
|
+
const d = r.data;
|
|
224
|
+
if (!d || typeof d !== 'object' || d.inAmount === undefined || d.outAmount === undefined) {
|
|
225
|
+
throw new HttpError(r.status, `${this.host}/swap/v1/quote`, 'empty or malformed quote');
|
|
226
|
+
}
|
|
227
|
+
return normalizeSwapQuote(d, r.fetchedAt);
|
|
228
|
+
}
|
|
229
|
+
/** task-1368: 401/403 → sticky failover; 429/5xx/transport → alternate host + bounded retry (JupiterHosts). */
|
|
230
|
+
get(path, signal) {
|
|
231
|
+
return this.hosts.run((host) => this.http.getJson(`${host}${path}`, SWAP, (data) => {
|
|
232
|
+
const e = data?.error;
|
|
233
|
+
return typeof e === 'string' && e.length > 0 ? { error: e } : null;
|
|
234
|
+
}, signal), signal);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Jupiter Tokens API v2 + Price API v3 adapter (SPEC §3.1 S2/S3). Keyless, read-only.
|
|
2
|
+
// Docs: solana-launchpad/docs/jupiter/tokens-api.md, tokens-api-list.md;
|
|
3
|
+
// rate limits: developers.jup.ag/docs/portal/rate-limits (keyless 0.5 rps, one
|
|
4
|
+
// bucket shared by Tokens and Price).
|
|
5
|
+
// GET /tokens/v2/search?query=<csv ≤100 mints | symbol | name>
|
|
6
|
+
// GET /tokens/v2/recent
|
|
7
|
+
// GET /tokens/v2/{toptrending|toporganicscore|toptraded}/{5m|1h|6h|24h}?limit=
|
|
8
|
+
// GET /price/v3?ids=<csv ≤50>
|
|
9
|
+
// Host: the documented `api.jup.ag` first; if it refuses keyless access
|
|
10
|
+
// (401/403) we switch, for the session, to `lite-api.jup.ag`, which still
|
|
11
|
+
// answers keyless (SPEC §9.1).
|
|
12
|
+
import { HttpError } from "../http.js";
|
|
13
|
+
import { chunk, isoToMs, num, numOrNull } from "../util.js";
|
|
14
|
+
export const JUPITER_HOSTS = ['https://api.jup.ag', 'https://lite-api.jup.ag'];
|
|
15
|
+
export const JUP_SEARCH_BATCH = 100;
|
|
16
|
+
export const JUP_PRICE_BATCH = 50;
|
|
17
|
+
const TOKENS = { source: 'jupiter-tokens', bucket: 'jupiter' };
|
|
18
|
+
const PRICE = { source: 'jupiter-price', bucket: 'jupiter' };
|
|
19
|
+
const STAT_KEYS = {
|
|
20
|
+
m5: 'stats5m',
|
|
21
|
+
h1: 'stats1h',
|
|
22
|
+
h6: 'stats6h',
|
|
23
|
+
h24: 'stats24h',
|
|
24
|
+
};
|
|
25
|
+
export function launchpadFromJupiter(lp, mint) {
|
|
26
|
+
const s = (lp ?? '').toLowerCase();
|
|
27
|
+
if (s.includes('pump'))
|
|
28
|
+
return 'pump.fun';
|
|
29
|
+
if (s.includes('bonk'))
|
|
30
|
+
return 'bonk';
|
|
31
|
+
if (s.includes('dbc') || s.includes('dynamic-bonding'))
|
|
32
|
+
return 'dbc';
|
|
33
|
+
if (s.includes('launchlab') || s.includes('raydium'))
|
|
34
|
+
return 'raydium';
|
|
35
|
+
if (s.includes('meteora'))
|
|
36
|
+
return 'meteora';
|
|
37
|
+
if (!s && mint.endsWith('pump'))
|
|
38
|
+
return 'pump.fun';
|
|
39
|
+
return 'unknown';
|
|
40
|
+
}
|
|
41
|
+
function socials(raw) {
|
|
42
|
+
return { x: raw.twitter ?? null, tg: raw.telegram ?? null, web: raw.website ?? null };
|
|
43
|
+
}
|
|
44
|
+
function windowStats(s) {
|
|
45
|
+
if (!s)
|
|
46
|
+
return null;
|
|
47
|
+
return {
|
|
48
|
+
numTraders: numOrNull(s.numTraders),
|
|
49
|
+
numNetBuyers: numOrNull(s.numNetBuyers),
|
|
50
|
+
buyVol: numOrNull(s.buyOrganicVolume),
|
|
51
|
+
sellVol: numOrNull(s.sellOrganicVolume),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** true = authority enabled. Jupiter sends `audit.*Disabled`, or omits it and sends the authority address. */
|
|
55
|
+
function authority(disabled, address) {
|
|
56
|
+
if (disabled !== undefined)
|
|
57
|
+
return !disabled;
|
|
58
|
+
if (typeof address === 'string' && address.length > 0)
|
|
59
|
+
return true;
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
export function normalizeJupToken(raw, fetchedAt) {
|
|
63
|
+
const createdAt = isoToMs(raw.firstPool?.createdAt) ?? isoToMs(raw.createdAt);
|
|
64
|
+
const token = {
|
|
65
|
+
source: 'jupiter-tokens',
|
|
66
|
+
fetchedAt,
|
|
67
|
+
mint: raw.id,
|
|
68
|
+
symbol: raw.symbol,
|
|
69
|
+
name: raw.name,
|
|
70
|
+
decimals: numOrNull(raw.decimals),
|
|
71
|
+
launchpad: launchpadFromJupiter(raw.launchpad, raw.id),
|
|
72
|
+
createdAt,
|
|
73
|
+
image: raw.icon ?? null,
|
|
74
|
+
socials: socials(raw),
|
|
75
|
+
verified: raw.isVerified === true,
|
|
76
|
+
// audit.isSus is only present when flagged (docs): presence is the signal.
|
|
77
|
+
sus: raw.audit?.isSus !== undefined ? raw.audit.isSus !== false : false,
|
|
78
|
+
};
|
|
79
|
+
const perWindow = {};
|
|
80
|
+
const txns = {};
|
|
81
|
+
const volume = {};
|
|
82
|
+
const priceChange = {};
|
|
83
|
+
for (const w of Object.keys(STAT_KEYS)) {
|
|
84
|
+
const s = raw[STAT_KEYS[w]];
|
|
85
|
+
perWindow[w] = windowStats(s);
|
|
86
|
+
txns[w] = { buys: num(s?.numBuys), sells: num(s?.numSells) };
|
|
87
|
+
volume[w] = s && (s.buyVolume !== undefined || s.sellVolume !== undefined) ? num(s.buyVolume) + num(s.sellVolume) : null;
|
|
88
|
+
priceChange[w] = numOrNull(s?.priceChange);
|
|
89
|
+
}
|
|
90
|
+
const stats = {
|
|
91
|
+
source: 'jupiter-tokens',
|
|
92
|
+
fetchedAt,
|
|
93
|
+
mint: raw.id,
|
|
94
|
+
holderCount: numOrNull(raw.holderCount),
|
|
95
|
+
holders24hDelta: numOrNull(raw.stats24h?.holderChange),
|
|
96
|
+
organicScore: numOrNull(raw.organicScore),
|
|
97
|
+
perWindow,
|
|
98
|
+
};
|
|
99
|
+
const audit = {
|
|
100
|
+
source: 'jupiter-tokens',
|
|
101
|
+
fetchedAt,
|
|
102
|
+
mint: raw.id,
|
|
103
|
+
mintAuthority: authority(raw.audit?.mintAuthorityDisabled, raw.mintAuthority),
|
|
104
|
+
freezeAuthority: authority(raw.audit?.freezeAuthorityDisabled, raw.freezeAuthority),
|
|
105
|
+
// Jupiter's topHoldersPercentage (top-10 share as Jupiter computes it; SPEC §3.6 LP/CEX exclusion is not applied here).
|
|
106
|
+
top10Pct: numOrNull(raw.audit?.topHoldersPercentage),
|
|
107
|
+
devPct: numOrNull(raw.audit?.devBalancePercentage),
|
|
108
|
+
devMints: numOrNull(raw.audit?.devMints),
|
|
109
|
+
lpLockedPct: null,
|
|
110
|
+
rugRisks: [],
|
|
111
|
+
rugScore: null,
|
|
112
|
+
};
|
|
113
|
+
const pair = {
|
|
114
|
+
source: 'jupiter-tokens',
|
|
115
|
+
fetchedAt,
|
|
116
|
+
pairAddress: raw.firstPool?.id ?? '',
|
|
117
|
+
dex: 'unknown',
|
|
118
|
+
baseMint: raw.id,
|
|
119
|
+
quoteMint: '',
|
|
120
|
+
priceUsd: num(raw.usdPrice),
|
|
121
|
+
priceNative: 0,
|
|
122
|
+
liquidityUsd: numOrNull(raw.liquidity),
|
|
123
|
+
fdv: numOrNull(raw.fdv),
|
|
124
|
+
mcap: numOrNull(raw.mcap),
|
|
125
|
+
txns,
|
|
126
|
+
volume,
|
|
127
|
+
priceChange,
|
|
128
|
+
createdAt,
|
|
129
|
+
};
|
|
130
|
+
return { token, stats, audit, pair };
|
|
131
|
+
}
|
|
132
|
+
export class Jupiter {
|
|
133
|
+
http;
|
|
134
|
+
hosts;
|
|
135
|
+
hostIndex = 0;
|
|
136
|
+
constructor(http, hosts = JUPITER_HOSTS) {
|
|
137
|
+
this.http = http;
|
|
138
|
+
this.hosts = hosts;
|
|
139
|
+
}
|
|
140
|
+
get host() {
|
|
141
|
+
return this.hosts[this.hostIndex];
|
|
142
|
+
}
|
|
143
|
+
/** Search by mint list (batched ≤100), symbol or name. */
|
|
144
|
+
async search(query) {
|
|
145
|
+
const queries = Array.isArray(query) ? chunk([...new Set(query)], JUP_SEARCH_BATCH).map((b) => b.join(',')) : [query];
|
|
146
|
+
const out = [];
|
|
147
|
+
for (const q of queries) {
|
|
148
|
+
const r = await this.get(`/tokens/v2/search?query=${encodeURIComponent(q)}`, TOKENS);
|
|
149
|
+
for (const t of Array.isArray(r.data) ? r.data : [])
|
|
150
|
+
out.push(normalizeJupToken(t, r.fetchedAt));
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
/** Newest tokens by first-pool creation time (default 30). */
|
|
155
|
+
async recent() {
|
|
156
|
+
return this.list('/tokens/v2/recent');
|
|
157
|
+
}
|
|
158
|
+
async category(category, interval, limit) {
|
|
159
|
+
return this.list(`/tokens/v2/${category}/${interval}${limit ? `?limit=${limit}` : ''}`);
|
|
160
|
+
}
|
|
161
|
+
/** USD prices for up to N mints, batched 50 per call. Unknown mints are absent. */
|
|
162
|
+
async price(mints) {
|
|
163
|
+
const out = new Map();
|
|
164
|
+
for (const batch of chunk([...new Set(mints)], JUP_PRICE_BATCH)) {
|
|
165
|
+
const r = await this.get(`/price/v3?ids=${batch.join(',')}`, PRICE);
|
|
166
|
+
for (const [mint, p] of Object.entries(r.data ?? {})) {
|
|
167
|
+
if (!p || numOrNull(p.usdPrice) === null)
|
|
168
|
+
continue;
|
|
169
|
+
out.set(mint, {
|
|
170
|
+
mint,
|
|
171
|
+
usdPrice: num(p.usdPrice),
|
|
172
|
+
priceChange24h: numOrNull(p.priceChange24h),
|
|
173
|
+
liquidity: numOrNull(p.liquidity),
|
|
174
|
+
source: 'jupiter-price',
|
|
175
|
+
fetchedAt: r.fetchedAt,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
async list(path) {
|
|
182
|
+
const r = await this.get(path, TOKENS);
|
|
183
|
+
return (Array.isArray(r.data) ? r.data : []).map((t) => normalizeJupToken(t, r.fetchedAt));
|
|
184
|
+
}
|
|
185
|
+
async get(path, meta) {
|
|
186
|
+
for (;;) {
|
|
187
|
+
try {
|
|
188
|
+
return await this.http.getJson(`${this.host}${path}`, meta);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
const refused = err instanceof HttpError && (err.status === 401 || err.status === 403);
|
|
192
|
+
if (!refused || this.hostIndex >= this.hosts.length - 1)
|
|
193
|
+
throw err;
|
|
194
|
+
this.hostIndex++; // sticky for the session
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|