dexterm-cli 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/LICENSE +661 -0
  2. package/dist/packages/agents/src/audit.js +122 -0
  3. package/dist/packages/agents/src/bus.js +379 -0
  4. package/dist/packages/agents/src/executor.js +177 -0
  5. package/dist/packages/agents/src/gate.js +176 -0
  6. package/dist/packages/agents/src/identity.js +179 -0
  7. package/dist/packages/agents/src/index.js +15 -0
  8. package/dist/packages/agents/src/proposals.js +153 -0
  9. package/dist/packages/agents/src/schema.js +87 -0
  10. package/dist/packages/agents/src/sdk.js +140 -0
  11. package/dist/packages/agents/src/tiers.js +114 -0
  12. package/dist/packages/agents/src/tools.js +69 -0
  13. package/dist/packages/agents/src/ws.js +158 -0
  14. package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
  15. package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
  16. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
  17. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +67 -0
  18. package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -0
  19. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
  20. package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
  21. package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
  22. package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
  23. package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
  24. package/dist/packages/cli/bin/dexterm.mjs +11 -0
  25. package/dist/packages/cli/src/agent-cmd.js +303 -0
  26. package/dist/packages/cli/src/agent-roster.js +26 -0
  27. package/dist/packages/cli/src/analyst/backend.js +526 -0
  28. package/dist/packages/cli/src/analyst/results.js +38 -0
  29. package/dist/packages/cli/src/analyst/session.js +319 -0
  30. package/dist/packages/cli/src/audit.js +51 -0
  31. package/dist/packages/cli/src/backtest-cmd.js +149 -0
  32. package/dist/packages/cli/src/boot-state.js +62 -0
  33. package/dist/packages/cli/src/custom.js +261 -0
  34. package/dist/packages/cli/src/display.js +191 -0
  35. package/dist/packages/cli/src/doctor.js +361 -0
  36. package/dist/packages/cli/src/feed.js +634 -0
  37. package/dist/packages/cli/src/fixtures.js +275 -0
  38. package/dist/packages/cli/src/follow-cmd.js +221 -0
  39. package/dist/packages/cli/src/images-config.js +59 -0
  40. package/dist/packages/cli/src/index.js +31 -0
  41. package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
  42. package/dist/packages/cli/src/larp-record.js +21 -0
  43. package/dist/packages/cli/src/live/copy.js +80 -0
  44. package/dist/packages/cli/src/live/flow.js +233 -0
  45. package/dist/packages/cli/src/live/index.js +509 -0
  46. package/dist/packages/cli/src/live/intel.js +227 -0
  47. package/dist/packages/cli/src/live/newpairs.js +334 -0
  48. package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
  49. package/dist/packages/cli/src/live/screens/token-live.js +459 -0
  50. package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
  51. package/dist/packages/cli/src/live/token.js +585 -0
  52. package/dist/packages/cli/src/live/trade.js +929 -0
  53. package/dist/packages/cli/src/live/watchlist.js +178 -0
  54. package/dist/packages/cli/src/main.js +5583 -0
  55. package/dist/packages/cli/src/narrative-cmd.js +76 -0
  56. package/dist/packages/cli/src/pack-cmd.js +323 -0
  57. package/dist/packages/cli/src/plan-cmd.js +278 -0
  58. package/dist/packages/cli/src/popup.js +810 -0
  59. package/dist/packages/cli/src/rules-cmd.js +131 -0
  60. package/dist/packages/cli/src/rules.js +339 -0
  61. package/dist/packages/cli/src/scan/command.js +57 -0
  62. package/dist/packages/cli/src/scan/engine.js +419 -0
  63. package/dist/packages/cli/src/scan/observe.js +64 -0
  64. package/dist/packages/cli/src/scan/ui.js +98 -0
  65. package/dist/packages/cli/src/screener/filter.js +241 -0
  66. package/dist/packages/cli/src/screener/presets.js +80 -0
  67. package/dist/packages/cli/src/screener/rows.js +146 -0
  68. package/dist/packages/cli/src/screener/rug.js +50 -0
  69. package/dist/packages/cli/src/screener/sort.js +52 -0
  70. package/dist/packages/cli/src/screener/view.js +183 -0
  71. package/dist/packages/cli/src/screens/analyst-session.js +343 -0
  72. package/dist/packages/cli/src/screens/analyst.js +326 -0
  73. package/dist/packages/cli/src/screens/boot.js +74 -0
  74. package/dist/packages/cli/src/screens/bubbles.js +255 -0
  75. package/dist/packages/cli/src/screens/coin.js +33 -0
  76. package/dist/packages/cli/src/screens/follows.js +201 -0
  77. package/dist/packages/cli/src/screens/kol.js +90 -0
  78. package/dist/packages/cli/src/screens/narrative.js +90 -0
  79. package/dist/packages/cli/src/screens/newpairs.js +150 -0
  80. package/dist/packages/cli/src/screens/overlay.js +257 -0
  81. package/dist/packages/cli/src/screens/plans.js +126 -0
  82. package/dist/packages/cli/src/screens/positions.js +176 -0
  83. package/dist/packages/cli/src/screens/rules.js +170 -0
  84. package/dist/packages/cli/src/screens/screener.js +459 -0
  85. package/dist/packages/cli/src/screens/social-panel.js +107 -0
  86. package/dist/packages/cli/src/screens/ticket.js +342 -0
  87. package/dist/packages/cli/src/screens/token.js +312 -0
  88. package/dist/packages/cli/src/screens/trending.js +181 -0
  89. package/dist/packages/cli/src/screens/watchlist.js +104 -0
  90. package/dist/packages/cli/src/social/demo.js +96 -0
  91. package/dist/packages/cli/src/social/view.js +250 -0
  92. package/dist/packages/cli/src/state.js +463 -0
  93. package/dist/packages/cli/src/theme-cmd.js +126 -0
  94. package/dist/packages/cli/src/trade.js +319 -0
  95. package/dist/packages/cli/src/voice-hud.js +42 -0
  96. package/dist/packages/cli/src/wallet-cmd.js +440 -0
  97. package/dist/packages/cli/src/wallet-command.js +31 -0
  98. package/dist/packages/cli/src/widgets.js +208 -0
  99. package/dist/packages/cli/src/workspace-cmd.js +68 -0
  100. package/dist/packages/cli/src/x-cmd.js +154 -0
  101. package/dist/packages/custom/src/config.js +213 -0
  102. package/dist/packages/custom/src/index.js +10 -0
  103. package/dist/packages/custom/src/keymap.js +107 -0
  104. package/dist/packages/custom/src/layout.js +235 -0
  105. package/dist/packages/custom/src/packs.js +633 -0
  106. package/dist/packages/custom/src/rules.js +704 -0
  107. package/dist/packages/custom/src/schema.js +515 -0
  108. package/dist/packages/custom/src/themes.js +176 -0
  109. package/dist/packages/custom/src/widget-html.js +222 -0
  110. package/dist/packages/custom/src/workspace.js +141 -0
  111. package/dist/packages/data/src/analyst-actions.js +133 -0
  112. package/dist/packages/data/src/budget.js +88 -0
  113. package/dist/packages/data/src/cache.js +142 -0
  114. package/dist/packages/data/src/candles.js +168 -0
  115. package/dist/packages/data/src/clock.js +64 -0
  116. package/dist/packages/data/src/credits.js +137 -0
  117. package/dist/packages/data/src/detail.js +97 -0
  118. package/dist/packages/data/src/fast-lane.js +321 -0
  119. package/dist/packages/data/src/fixtures.js +135 -0
  120. package/dist/packages/data/src/health.js +245 -0
  121. package/dist/packages/data/src/http.js +277 -0
  122. package/dist/packages/data/src/images/cache.js +141 -0
  123. package/dist/packages/data/src/images/decode-worker.js +28 -0
  124. package/dist/packages/data/src/images/decode.js +81 -0
  125. package/dist/packages/data/src/images/fetch.js +162 -0
  126. package/dist/packages/data/src/images/gif.js +214 -0
  127. package/dist/packages/data/src/images/index.js +10 -0
  128. package/dist/packages/data/src/images/jpeg.js +544 -0
  129. package/dist/packages/data/src/images/png.js +310 -0
  130. package/dist/packages/data/src/images/service.js +334 -0
  131. package/dist/packages/data/src/images/types.js +27 -0
  132. package/dist/packages/data/src/index.js +208 -0
  133. package/dist/packages/data/src/intel/helius-history.js +59 -0
  134. package/dist/packages/data/src/intel/holder-class.js +125 -0
  135. package/dist/packages/data/src/intel/rules.js +421 -0
  136. package/dist/packages/data/src/intel/scan.js +170 -0
  137. package/dist/packages/data/src/larp/evidence.js +288 -0
  138. package/dist/packages/data/src/larp/links.js +137 -0
  139. package/dist/packages/data/src/larp/robots.js +90 -0
  140. package/dist/packages/data/src/larp/safe-fetch.js +389 -0
  141. package/dist/packages/data/src/limiter.js +341 -0
  142. package/dist/packages/data/src/model.js +12 -0
  143. package/dist/packages/data/src/paper.js +331 -0
  144. package/dist/packages/data/src/rpc-config.js +126 -0
  145. package/dist/packages/data/src/rpc-pool.js +166 -0
  146. package/dist/packages/data/src/social/chain.js +83 -0
  147. package/dist/packages/data/src/social/dedupe.js +205 -0
  148. package/dist/packages/data/src/social/feed.js +462 -0
  149. package/dist/packages/data/src/social/firewall.js +110 -0
  150. package/dist/packages/data/src/social/index.js +11 -0
  151. package/dist/packages/data/src/social/kol.js +269 -0
  152. package/dist/packages/data/src/social/model.js +11 -0
  153. package/dist/packages/data/src/social/rank.js +184 -0
  154. package/dist/packages/data/src/social/rss.js +113 -0
  155. package/dist/packages/data/src/social/summary.js +104 -0
  156. package/dist/packages/data/src/social/venues.js +186 -0
  157. package/dist/packages/data/src/sources/dexscreener.js +170 -0
  158. package/dist/packages/data/src/sources/geckoterminal.js +133 -0
  159. package/dist/packages/data/src/sources/goplus.js +161 -0
  160. package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
  161. package/dist/packages/data/src/sources/jupiter.js +198 -0
  162. package/dist/packages/data/src/sources/pda.js +113 -0
  163. package/dist/packages/data/src/sources/pump-curve.js +58 -0
  164. package/dist/packages/data/src/sources/pumpportal.js +247 -0
  165. package/dist/packages/data/src/sources/rugcheck.js +71 -0
  166. package/dist/packages/data/src/sources/solana-rpc.js +299 -0
  167. package/dist/packages/data/src/sources/solana-ws.js +287 -0
  168. package/dist/packages/data/src/store.js +289 -0
  169. package/dist/packages/data/src/trade/bridge.js +464 -0
  170. package/dist/packages/data/src/trade/caps.js +323 -0
  171. package/dist/packages/data/src/trade/copy/auto.js +385 -0
  172. package/dist/packages/data/src/trade/copy/follows.js +103 -0
  173. package/dist/packages/data/src/trade/copy/index.js +9 -0
  174. package/dist/packages/data/src/trade/copy/mirror.js +91 -0
  175. package/dist/packages/data/src/trade/copy/stats.js +120 -0
  176. package/dist/packages/data/src/trade/copy/tracker.js +158 -0
  177. package/dist/packages/data/src/trade/copy/trades.js +87 -0
  178. package/dist/packages/data/src/trade/copy/types.js +6 -0
  179. package/dist/packages/data/src/trade/executor.js +933 -0
  180. package/dist/packages/data/src/trade/kill-switch.js +47 -0
  181. package/dist/packages/data/src/trade/link-store.js +52 -0
  182. package/dist/packages/data/src/trade/plans/confirm.js +157 -0
  183. package/dist/packages/data/src/trade/plans/engine.js +784 -0
  184. package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
  185. package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
  186. package/dist/packages/data/src/trade/plans/executor.js +11 -0
  187. package/dist/packages/data/src/trade/plans/index.js +12 -0
  188. package/dist/packages/data/src/trade/plans/journal.js +77 -0
  189. package/dist/packages/data/src/trade/plans/mandate.js +129 -0
  190. package/dist/packages/data/src/trade/plans/plan.js +179 -0
  191. package/dist/packages/data/src/trade/plans/service.js +168 -0
  192. package/dist/packages/data/src/trade/plans/types.js +6 -0
  193. package/dist/packages/data/src/trade/rpc-exec.js +229 -0
  194. package/dist/packages/data/src/trade/signer/client.js +197 -0
  195. package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
  196. package/dist/packages/data/src/trade/signer/host.js +97 -0
  197. package/dist/packages/data/src/trade/signer/main.js +121 -0
  198. package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
  199. package/dist/packages/data/src/trade/signer/protocol.js +92 -0
  200. package/dist/packages/data/src/trade/signer/service.js +400 -0
  201. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
  202. package/dist/packages/data/src/trade/sol-usd.js +86 -0
  203. package/dist/packages/data/src/trade/swap-build.js +108 -0
  204. package/dist/packages/data/src/trade/token-delta.js +201 -0
  205. package/dist/packages/data/src/trade/tx-decode.js +632 -0
  206. package/dist/packages/data/src/util.js +24 -0
  207. package/dist/packages/data/src/watchlist.js +176 -0
  208. package/dist/packages/data/src/x/api.js +256 -0
  209. package/dist/packages/data/src/x/budget.js +66 -0
  210. package/dist/packages/data/src/x/config.js +49 -0
  211. package/dist/packages/data/src/x/index.js +10 -0
  212. package/dist/packages/data/src/x/mock-server.js +217 -0
  213. package/dist/packages/data/src/x/oauth.js +242 -0
  214. package/dist/packages/data/src/x/pkce.js +21 -0
  215. package/dist/packages/data/src/x/source.js +285 -0
  216. package/dist/packages/data/src/x/token-store.js +134 -0
  217. package/dist/packages/data/src/x/transport.js +71 -0
  218. package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
  219. package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
  220. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
  221. package/dist/packages/mcp/assets/cases.json +1194 -0
  222. package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
  223. package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
  224. package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
  225. package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
  226. package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
  227. package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
  228. package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
  229. package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
  230. package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
  231. package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
  232. package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
  233. package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
  234. package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
  235. package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
  236. package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
  237. package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
  238. package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
  239. package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
  240. package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
  241. package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
  242. package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
  243. package/dist/packages/mcp/src/dossier.js +78 -0
  244. package/dist/packages/mcp/src/firewall.js +21 -0
  245. package/dist/packages/mcp/src/index.js +22 -0
  246. package/dist/packages/mcp/src/server.js +849 -0
  247. package/dist/packages/mcp/src/user-content.js +434 -0
  248. package/dist/packages/packs/src/backtest.js +183 -0
  249. package/dist/packages/packs/src/digest.js +95 -0
  250. package/dist/packages/packs/src/firewall.js +57 -0
  251. package/dist/packages/packs/src/index.js +19 -0
  252. package/dist/packages/packs/src/larp-eval.js +100 -0
  253. package/dist/packages/packs/src/narrative.js +309 -0
  254. package/dist/packages/packs/src/reason.js +135 -0
  255. package/dist/packages/packs/src/rug-model.js +137 -0
  256. package/dist/packages/packs/src/schema.js +54 -0
  257. package/dist/packages/packs/src/token-dossier.js +174 -0
  258. package/dist/packages/packs/src/verdicts.js +17 -0
  259. package/dist/packages/packs/src/wallet-dossier.js +105 -0
  260. package/dist/packages/popup/src/config.js +71 -0
  261. package/dist/packages/popup/src/dock.js +271 -0
  262. package/dist/packages/popup/src/host.js +210 -0
  263. package/dist/packages/popup/src/index.js +12 -0
  264. package/dist/packages/popup/src/loopback.js +53 -0
  265. package/dist/packages/popup/src/open.js +183 -0
  266. package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
  267. package/dist/packages/popup/src/pages/bubbles.css +23 -0
  268. package/dist/packages/popup/src/pages/bubbles.html +36 -0
  269. package/dist/packages/popup/src/pages/bubbles.js +214 -0
  270. package/dist/packages/popup/src/pages/chart-lib.js +262 -0
  271. package/dist/packages/popup/src/pages/chart.css +50 -0
  272. package/dist/packages/popup/src/pages/chart.html +56 -0
  273. package/dist/packages/popup/src/pages/chart.js +554 -0
  274. package/dist/packages/popup/src/pages/common.css +34 -0
  275. package/dist/packages/popup/src/pages/common.js +187 -0
  276. package/dist/packages/popup/src/pages/feeds.html +20 -0
  277. package/dist/packages/popup/src/pages/feeds.js +77 -0
  278. package/dist/packages/popup/src/pages/narrative.html +20 -0
  279. package/dist/packages/popup/src/pages/narrative.js +61 -0
  280. package/dist/packages/popup/src/pages/plans.css +30 -0
  281. package/dist/packages/popup/src/pages/plans.html +26 -0
  282. package/dist/packages/popup/src/pages/plans.js +82 -0
  283. package/dist/packages/popup/src/pages/roster.css +41 -0
  284. package/dist/packages/popup/src/pages/roster.html +25 -0
  285. package/dist/packages/popup/src/pages/roster.js +75 -0
  286. package/dist/packages/popup/src/pages/token.html +20 -0
  287. package/dist/packages/popup/src/pages/token.js +112 -0
  288. package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
  289. package/dist/packages/popup/src/pages/widget.css +5 -0
  290. package/dist/packages/popup/src/pages/widget.html +21 -0
  291. package/dist/packages/popup/src/pages/widget.js +85 -0
  292. package/dist/packages/popup/src/protocol.js +31 -0
  293. package/dist/packages/popup/src/selftest.js +156 -0
  294. package/dist/packages/popup/src/server.js +582 -0
  295. package/dist/packages/popup/src/sinkproxy.js +167 -0
  296. package/dist/packages/popup/src/widget-html.js +231 -0
  297. package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
  298. package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
  299. package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
  300. package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
  301. package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
  302. package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
  303. package/dist/packages/recorder/src/cli.js +113 -0
  304. package/dist/packages/recorder/src/export.js +71 -0
  305. package/dist/packages/recorder/src/index.js +20 -0
  306. package/dist/packages/recorder/src/labels.js +151 -0
  307. package/dist/packages/recorder/src/open.js +29 -0
  308. package/dist/packages/recorder/src/recorder.js +553 -0
  309. package/dist/packages/recorder/src/schedule.js +43 -0
  310. package/dist/packages/recorder/src/schema.js +135 -0
  311. package/dist/packages/recorder/src/snapshot.js +191 -0
  312. package/dist/packages/recorder/src/sweep.js +52 -0
  313. package/dist/packages/recorder/src/verdicts.js +52 -0
  314. package/dist/packages/tui/src/bubbles.js +415 -0
  315. package/dist/packages/tui/src/canvas.js +370 -0
  316. package/dist/packages/tui/src/chrome.js +85 -0
  317. package/dist/packages/tui/src/color.js +133 -0
  318. package/dist/packages/tui/src/flush.js +62 -0
  319. package/dist/packages/tui/src/fmt.js +188 -0
  320. package/dist/packages/tui/src/fx-trade.js +278 -0
  321. package/dist/packages/tui/src/fx.js +527 -0
  322. package/dist/packages/tui/src/images/blocks.js +141 -0
  323. package/dist/packages/tui/src/images/coin.js +22 -0
  324. package/dist/packages/tui/src/images/detect.js +140 -0
  325. package/dist/packages/tui/src/images/index.js +7 -0
  326. package/dist/packages/tui/src/images/layer.js +146 -0
  327. package/dist/packages/tui/src/images/pixels.js +157 -0
  328. package/dist/packages/tui/src/images/protocols.js +87 -0
  329. package/dist/packages/tui/src/index.js +15 -0
  330. package/dist/packages/tui/src/keymap.js +355 -0
  331. package/dist/packages/tui/src/layout.js +11 -0
  332. package/dist/packages/tui/src/sound.js +177 -0
  333. package/dist/packages/tui/src/theme.js +182 -0
  334. package/dist/packages/tui/src/widgets.js +598 -0
  335. package/dist/packages/voice/src/controller.js +395 -0
  336. package/dist/packages/voice/src/doctor.js +63 -0
  337. package/dist/packages/voice/src/engines.js +44 -0
  338. package/dist/packages/voice/src/index.js +16 -0
  339. package/dist/packages/voice/src/router.js +502 -0
  340. package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
  341. package/dist/packages/voice/src/stt/apple.js +179 -0
  342. package/dist/packages/voice/src/stt/fake.js +34 -0
  343. package/dist/packages/voice/src/stt/whisper.js +102 -0
  344. package/dist/packages/voice/src/summary.js +44 -0
  345. package/dist/packages/voice/src/tts/fake.js +30 -0
  346. package/dist/packages/voice/src/tts/say.js +96 -0
  347. package/dist/packages/voice/src/types.js +3 -0
  348. package/package.json +42 -4
  349. package/README.md +0 -3
@@ -0,0 +1,197 @@
1
+ // dexterm T2 — the signer CLIENT (task-1328): the UI's only handle on the signer process. It spawns the
2
+ // signer as a child with piped stdio, hands it the session secret as the first stdin line, and sends
3
+ // authenticated requests with per-call timeouts. Every failure is a REFUSAL (fail closed): a dead or
4
+ // silent signer never becomes a "maybe it signed". The client never respawns a signer that died — LIVE
5
+ // trading stays off for the rest of the run, and the user restarts dexterm.
6
+ //
7
+ // The client forwards exactly the argv the user started dexterm with (caps flags, --mainnet, --rpc).
8
+ // It cannot change caps afterwards: there is no such message.
9
+ import { spawn } from 'node:child_process';
10
+ import { createInterface } from 'node:readline';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { decodeFrame, encodeFrame, encodeHello, isEvent, isResponse, newSecret } from "./protocol.js";
13
+ export class SignerUnavailableError extends Error {
14
+ /** The signer's refusal/error reason when it sent one (`rate-limited`, `build-failed`, a CapRefusal, …). */
15
+ reason;
16
+ /** task-1368: for `rate-limited`, how long to wait before asking the signer again. */
17
+ retryAfterMs;
18
+ constructor(message, detail = {}) {
19
+ super(message);
20
+ this.name = 'SignerUnavailableError';
21
+ this.reason = detail.reason ?? null;
22
+ this.retryAfterMs = typeof detail.retryAfterMs === 'number' && Number.isFinite(detail.retryAfterMs) ? detail.retryAfterMs : null;
23
+ }
24
+ }
25
+ export const DEFAULT_TIMEOUTS = { status: 8_000, plan: 60_000, probe: 30_000, precheck: 8_000, confirm: 240_000, drop: 8_000, panic: 8_000, fast: 12_000, fill: 30_000, quote: 20_000 };
26
+ export function defaultSignerEntry() {
27
+ return fileURLToPath(new URL('./main.js', import.meta.url));
28
+ }
29
+ export class SignerClient {
30
+ child = null;
31
+ secret = newSecret();
32
+ outSeq = 0;
33
+ inSeq = 0;
34
+ nextId = 1;
35
+ pending = new Map();
36
+ ready = null;
37
+ dead = null;
38
+ opts;
39
+ argv;
40
+ pid = null;
41
+ constructor(opts = {}) {
42
+ this.opts = opts;
43
+ this.argv = [...(opts.argv ?? [])];
44
+ }
45
+ /**
46
+ * Available for LIVE work: not dead. A client that has not started yet IS alive — the signer is
47
+ * spawned lazily on the first request. task-1337: the old `child !== null && …` made every real run
48
+ * refuse `L` with "the signer process died" before the signer had ever been started (the ticket
49
+ * checks `alive` before its first call); only the fake signer in the tests, alive from construction,
50
+ * ever reached LIVE. `dead` is the fail-closed marker: set once on exit/error/timeout/close, never reset.
51
+ */
52
+ get alive() { return this.dead === null; }
53
+ /** Spawn + handshake (idempotent). Rejects when the signer cannot start; never retries. */
54
+ start() {
55
+ if (this.dead)
56
+ return Promise.reject(new SignerUnavailableError(this.dead));
57
+ if (this.ready)
58
+ return this.ready;
59
+ this.ready = new Promise((resolve, reject) => {
60
+ const entry = this.opts.entry ?? defaultSignerEntry();
61
+ let child;
62
+ try {
63
+ child = spawn(process.execPath, [entry, ...this.argv], { stdio: ['pipe', 'pipe', 'pipe'], env: this.opts.env ?? process.env });
64
+ }
65
+ catch (e) {
66
+ this.dead = `spawn failed: ${e instanceof Error ? e.message : String(e)}`;
67
+ reject(new SignerUnavailableError(this.dead));
68
+ return;
69
+ }
70
+ this.child = child;
71
+ this.pid = child.pid ?? null;
72
+ let settled = false;
73
+ const readyTimer = setTimeout(() => { if (!settled) {
74
+ settled = true;
75
+ this.kill('signer did not become ready');
76
+ reject(new SignerUnavailableError('signer did not become ready'));
77
+ } }, this.opts.readyTimeoutMs ?? 15_000);
78
+ readyTimer.unref?.();
79
+ child.on('error', (e) => { this.markDead(`signer error: ${e.message}`); if (!settled) {
80
+ settled = true;
81
+ clearTimeout(readyTimer);
82
+ reject(new SignerUnavailableError(this.dead ?? 'signer error'));
83
+ } });
84
+ child.on('exit', (code, signal) => { this.markDead(`signer exited (${code ?? signal ?? '?'})`); if (!settled) {
85
+ settled = true;
86
+ clearTimeout(readyTimer);
87
+ reject(new SignerUnavailableError(this.dead ?? 'signer exited'));
88
+ } });
89
+ if (child.stderr)
90
+ createInterface({ input: child.stderr }).on('line', (l) => this.opts.log?.(l));
91
+ if (child.stdout) {
92
+ createInterface({ input: child.stdout, crlfDelay: Infinity }).on('line', (line) => {
93
+ const d = decodeFrame(this.secret, line, this.inSeq);
94
+ if (!d.ok) {
95
+ this.opts.log?.(`signer client: dropped frame (${d.why})`);
96
+ return;
97
+ }
98
+ this.inSeq = d.seq + 1;
99
+ this.onMessage(d.message, () => { if (!settled) {
100
+ settled = true;
101
+ clearTimeout(readyTimer);
102
+ resolve();
103
+ } });
104
+ });
105
+ }
106
+ child.stdin?.on('error', () => { });
107
+ child.stdin?.write(encodeHello(this.secret));
108
+ });
109
+ return this.ready;
110
+ }
111
+ onMessage(m, onReady) {
112
+ if (isEvent(m)) {
113
+ if (m.event === 'ready')
114
+ onReady();
115
+ return;
116
+ }
117
+ if (!isResponse(m))
118
+ return;
119
+ const p = this.pending.get(m.id);
120
+ if (!p)
121
+ return;
122
+ this.pending.delete(m.id);
123
+ clearTimeout(p.timer);
124
+ if (m.ok)
125
+ p.resolve(m.result);
126
+ else
127
+ p.reject(new SignerUnavailableError(m.error, { reason: m.reason, retryAfterMs: m.retryAfterMs }));
128
+ }
129
+ markDead(why) {
130
+ if (this.dead)
131
+ return;
132
+ this.dead = why;
133
+ for (const [id, p] of this.pending) {
134
+ clearTimeout(p.timer);
135
+ this.pending.delete(id);
136
+ p.reject(new SignerUnavailableError(why));
137
+ }
138
+ }
139
+ kill(why) {
140
+ this.markDead(why);
141
+ try {
142
+ this.child?.kill('SIGTERM');
143
+ }
144
+ catch { /* ignore */ }
145
+ }
146
+ async request(method, params, timeoutMs = DEFAULT_TIMEOUTS[method] ?? 8_000) {
147
+ if (this.dead)
148
+ throw new SignerUnavailableError(this.dead);
149
+ await this.start();
150
+ if (this.dead || !this.child?.stdin)
151
+ throw new SignerUnavailableError(this.dead ?? 'signer has no stdin');
152
+ const id = this.nextId++;
153
+ return new Promise((resolve, reject) => {
154
+ const timer = setTimeout(() => { this.pending.delete(id); reject(new SignerUnavailableError(`signer ${method} timed out after ${Math.round(timeoutMs / 1000)} s`)); }, timeoutMs);
155
+ timer.unref?.();
156
+ this.pending.set(id, { resolve: resolve, reject, timer });
157
+ try {
158
+ this.child.stdin.write(encodeFrame(this.secret, this.outSeq++, { id, method, params }));
159
+ }
160
+ catch (e) {
161
+ this.pending.delete(id);
162
+ clearTimeout(timer);
163
+ reject(new SignerUnavailableError(`write failed: ${e instanceof Error ? e.message : String(e)}`));
164
+ }
165
+ });
166
+ }
167
+ status() { return this.request('status', {}); }
168
+ plan(params) { return this.request('plan', { ...params }); }
169
+ probe() { return this.request('probe', {}); }
170
+ precheck(hash) { return this.request('precheck', { hash }); }
171
+ confirm(hash) { return this.request('confirm', { hash }); }
172
+ async drop(hash) { await this.request('drop', { hash }); }
173
+ async panic(reason) { await this.request('panic', { reason }); }
174
+ fast() { return this.request('fast', {}); }
175
+ fill(params) { return this.request('fill', { ...params }); }
176
+ quote(params) { return this.request('quote', { ...params }); }
177
+ close() {
178
+ this.markDead('closed');
179
+ try {
180
+ this.child?.stdin?.end();
181
+ }
182
+ catch { /* ignore */ }
183
+ const c = this.child;
184
+ if (c) {
185
+ const t = setTimeout(() => { try {
186
+ c.kill('SIGKILL');
187
+ }
188
+ catch { /* ignore */ } }, 2_000);
189
+ t.unref?.();
190
+ c.on('exit', () => clearTimeout(t));
191
+ try {
192
+ c.kill('SIGTERM');
193
+ }
194
+ catch { /* ignore */ }
195
+ }
196
+ }
197
+ }
@@ -0,0 +1,154 @@
1
+ // task-1363 — the capped FAST WALLET (SPEC §6.13.2 `wallet: 'fast'`): the one key dexterm ever holds, and only
2
+ // on DEVNET. Standing-mandate fills must run at machine speed, which a Phantom round-trip per fill cannot do,
3
+ // so an armed mandate fires from this separate hot key instead of the user's main wallet. The main wallet is
4
+ // never named by a mandate (the schema says `fast`), and this signer refuses to sign for any other public key.
5
+ //
6
+ // Boundaries, by construction:
7
+ // * devnet only — the record carries `network: 'devnet'`; create/load refuse anything else, and the signer
8
+ // process only loads it when ITS RPC endpoint classifies as devnet (signer/main.ts). There is no mainnet path.
9
+ // * created only by `dexterm wallet fast create` on a real TTY after a keypress; there is NO channel method
10
+ // to create, export, import or replace it (the signer protocol has `fast` = status and `fill` = build).
11
+ // * hard per-fill ceiling: a single fill may move at most FAST_MAX_SOL_PER_FILL SOL out of it (lamports
12
+ // bound at build and again on the signed bytes), on top of the signer's USD caps which this lane enforces
13
+ // even on devnet (task-1363: "per-trade and daily caps enforced signer-side").
14
+ // * the seed lives in ~/.dexterm/fast-wallet.json at mode 600 and never leaves this module: `get()` returns
15
+ // the public record only; `load()` returns a signer object with a sign method, not the key bytes.
16
+ //
17
+ // Ed25519 via node:crypto only (no wallet library). The signed wire tx is `shortvec(1) + sig + message`, the
18
+ // same shape the executor decodes, compares to the approved message, Ed25519-verifies and re-simulates.
19
+ import { createPrivateKey, createPublicKey, randomBytes, sign as edSign } from 'node:crypto';
20
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
21
+ import { homedir } from 'node:os';
22
+ import { dirname, join } from 'node:path';
23
+ import { base58 } from "../../sources/solana-rpc.js";
24
+ import { decodeTransaction } from "../tx-decode.js";
25
+ export const FAST_WALLET_NETWORK = 'devnet';
26
+ /** Hard ceiling on the SOL a single mandate fill may move out of the fast wallet (devnet-sized). */
27
+ export const FAST_MAX_SOL_PER_FILL = 0.05;
28
+ export const FAST_MAX_SOL_PER_FILL_LAMPORTS = 50000000n;
29
+ export const FAST_WALLET_PROVIDER = 'fast-wallet';
30
+ const B58 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
31
+ /** PKCS#8 DER prefix for an Ed25519 private key (RFC 8410): the 32-byte seed follows. */
32
+ const PKCS8_ED25519_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');
33
+ export function defaultFastWalletPath(env = process.env) {
34
+ const home = env.DEXTERM_HOME && env.DEXTERM_HOME.length ? env.DEXTERM_HOME : join(homedir(), '.dexterm');
35
+ return join(home, 'fast-wallet.json');
36
+ }
37
+ function privateKeyFromSeed(seed) {
38
+ if (seed.length !== 32)
39
+ throw new Error('fast wallet: seed must be 32 bytes');
40
+ return createPrivateKey({ key: Buffer.concat([PKCS8_ED25519_PREFIX, seed]), format: 'der', type: 'pkcs8' });
41
+ }
42
+ function publicKeyOf(priv) {
43
+ const der = createPublicKey(priv).export({ format: 'der', type: 'spki' });
44
+ return base58(der.subarray(der.length - 32));
45
+ }
46
+ function shortvec(n) {
47
+ const out = [];
48
+ do {
49
+ let b = n & 0x7f;
50
+ n >>= 7;
51
+ if (n)
52
+ b |= 0x80;
53
+ out.push(b);
54
+ } while (n);
55
+ return Buffer.from(out);
56
+ }
57
+ /** The thing that signs: holds the key object, exposes only `publicKey` and `signTransaction`. */
58
+ export class FastWalletSigner {
59
+ publicKey;
60
+ provider = FAST_WALLET_PROVIDER;
61
+ key;
62
+ constructor(seed) {
63
+ this.key = privateKeyFromSeed(seed);
64
+ this.publicKey = publicKeyOf(this.key);
65
+ }
66
+ /**
67
+ * Sign ONE transaction for the fast wallet itself (same call shape as the Phantom bridge, so the executor's
68
+ * pipeline is unchanged). Refuses: a caller expecting any other key, a transaction whose fee payer / sole
69
+ * required signer is not this wallet, more than one required signature, or undecodable bytes.
70
+ */
71
+ async signTransaction(args) {
72
+ const t0 = Date.now();
73
+ if (args.expectPublicKey !== undefined && args.expectPublicKey !== this.publicKey)
74
+ throw new Error('fast wallet: refusing to sign for a different public key');
75
+ const tx = decodeTransaction(Buffer.from(args.unsignedTxBase64, 'base64')); // throws on malformed bytes
76
+ if (tx.numRequiredSignatures !== 1)
77
+ throw new Error(`fast wallet: refusing a transaction with ${tx.numRequiredSignatures} required signatures`);
78
+ if (tx.staticKeys[0] !== this.publicKey)
79
+ throw new Error('fast wallet: the transaction fee payer is not the fast wallet');
80
+ const sig = edSign(null, tx.message, this.key);
81
+ const wire = Buffer.concat([shortvec(1), sig, tx.message]).toString('base64');
82
+ return { signedTxBase64: wire, provider: FAST_WALLET_PROVIDER, roundTripMs: Date.now() - t0 };
83
+ }
84
+ }
85
+ /**
86
+ * The on-disk record. `create` writes a fresh key (never overwrites), `get` reads the PUBLIC fields only,
87
+ * `load` returns a signer (or null when there is none / the file is unreadable or not devnet), `remove`
88
+ * deletes the file. Nothing here talks to the network.
89
+ */
90
+ export class FastWalletStore {
91
+ path;
92
+ constructor(path = defaultFastWalletPath()) { this.path = path; }
93
+ exists() { return existsSync(this.path); }
94
+ read() {
95
+ try {
96
+ if (!existsSync(this.path))
97
+ return null;
98
+ const raw = JSON.parse(readFileSync(this.path, 'utf8'));
99
+ if (raw.v !== 1 || raw.network !== FAST_WALLET_NETWORK)
100
+ return null;
101
+ if (typeof raw.publicKey !== 'string' || !B58.test(raw.publicKey) || typeof raw.seed !== 'string')
102
+ return null;
103
+ return { v: 1, network: FAST_WALLET_NETWORK, publicKey: raw.publicKey, seed: raw.seed, createdAt: Number(raw.createdAt) || 0 };
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ /** Public fields only (no seed). Null when absent/malformed. */
110
+ get() {
111
+ const r = this.read();
112
+ return r ? { publicKey: r.publicKey, network: r.network, createdAt: r.createdAt } : null;
113
+ }
114
+ /** A signer over the stored seed, or null. The seed is checked against the stored public key. */
115
+ load() {
116
+ const r = this.read();
117
+ if (!r)
118
+ return null;
119
+ try {
120
+ const signer = new FastWalletSigner(Buffer.from(r.seed, 'base64'));
121
+ return signer.publicKey === r.publicKey ? signer : null;
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ /**
128
+ * Create a NEW devnet fast wallet. Refuses to overwrite an existing one (remove it first, after draining)
129
+ * and refuses any network but devnet. The caller (the CLI) gates this behind a real TTY keypress.
130
+ */
131
+ create(opts) {
132
+ if (opts.network !== FAST_WALLET_NETWORK)
133
+ throw new Error('fast wallet: devnet only — there is no mainnet fast wallet');
134
+ if (this.exists())
135
+ throw new Error(`fast wallet: ${this.path} already exists; \`dexterm wallet fast remove\` first (after draining it)`);
136
+ const seed = randomBytes(32);
137
+ const signer = new FastWalletSigner(seed);
138
+ const rec = { v: 1, network: FAST_WALLET_NETWORK, publicKey: signer.publicKey, seed: seed.toString('base64'), createdAt: opts.now ?? Date.now() };
139
+ mkdirSync(dirname(this.path), { recursive: true });
140
+ writeFileSync(this.path, JSON.stringify(rec, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
141
+ try {
142
+ chmodSync(this.path, 0o600);
143
+ }
144
+ catch { /* best effort on platforms without chmod */ }
145
+ return { publicKey: rec.publicKey, network: rec.network, createdAt: rec.createdAt };
146
+ }
147
+ /** Delete the record. Returns true when one existed. */
148
+ remove() {
149
+ if (!existsSync(this.path))
150
+ return false;
151
+ rmSync(this.path, { force: true });
152
+ return true;
153
+ }
154
+ }
@@ -0,0 +1,97 @@
1
+ // dexterm T2 — the signer HOST loop (task-1328): runs inside the signer process, reads authenticated
2
+ // frames from `input` (the child's stdin), dispatches to the SignerService, and writes authenticated
3
+ // responses to `output` (the child's stdout). Diagnostics go to `log` (stderr) and never carry bytes,
4
+ // keys or the secret. Fails closed: a bad hello, too many bad frames, or a closed input ends the loop
5
+ // (and the production entry exits the process).
6
+ import { createInterface } from 'node:readline';
7
+ import { decodeFrame, encodeFrame, isRequest, parseHello, MAX_BAD_FRAMES, MAX_FRAME_BYTES, SIGNER_METHODS } from "./protocol.js";
8
+ /** Serve until the input ends. Resolves with the reason the loop stopped. */
9
+ export function serveSigner(opts) {
10
+ const log = opts.log ?? (() => { });
11
+ return new Promise((resolve) => {
12
+ let secret = null;
13
+ let inSeq = 0;
14
+ let outSeq = 0;
15
+ let bad = 0;
16
+ let ended = false;
17
+ const rl = createInterface({ input: opts.input, crlfDelay: Infinity });
18
+ const end = (why) => {
19
+ if (ended)
20
+ return;
21
+ ended = true;
22
+ try {
23
+ rl.close();
24
+ }
25
+ catch { /* ignore */ }
26
+ opts.onEnd?.(why);
27
+ resolve(why);
28
+ };
29
+ const send = (m) => {
30
+ if (!secret)
31
+ return;
32
+ try {
33
+ opts.output.write(encodeFrame(secret, outSeq++, m));
34
+ }
35
+ catch (e) {
36
+ log(`signer: write failed: ${e instanceof Error ? e.message : String(e)}`);
37
+ end('output closed');
38
+ }
39
+ };
40
+ const strike = (why) => {
41
+ bad++;
42
+ log(`signer: dropped frame (${why}) ${bad}/${MAX_BAD_FRAMES}`);
43
+ if (bad >= MAX_BAD_FRAMES)
44
+ end('too many bad frames');
45
+ };
46
+ rl.on('line', (line) => {
47
+ if (ended)
48
+ return;
49
+ if (line.length > MAX_FRAME_BYTES) {
50
+ strike('too large');
51
+ return;
52
+ }
53
+ if (!secret) {
54
+ const h = parseHello(line);
55
+ if (!h) {
56
+ end('bad hello');
57
+ return;
58
+ }
59
+ secret = h.secret;
60
+ send({ event: 'ready', detail: { pid: process.pid } });
61
+ return;
62
+ }
63
+ const d = decodeFrame(secret, line, inSeq);
64
+ if (!d.ok) {
65
+ strike(d.why);
66
+ return;
67
+ }
68
+ inSeq = d.seq + 1;
69
+ const m = d.message;
70
+ if (!isRequest(m)) {
71
+ strike('not a request');
72
+ return;
73
+ }
74
+ const id = m.id;
75
+ if (!SIGNER_METHODS.has(m.method)) {
76
+ send({ id, ok: false, error: `unknown method ${JSON.stringify(String(m.method).slice(0, 32))}` });
77
+ return;
78
+ }
79
+ void opts.service.call(m.method, m.params).then((result) => send({ id, ok: true, result }), (e) => {
80
+ if (!(e instanceof Error)) {
81
+ send({ id, ok: false, error: String(e) });
82
+ return;
83
+ }
84
+ const reason = e.reason;
85
+ const retryAfterMs = e.retryAfterMs;
86
+ send({
87
+ id, ok: false,
88
+ error: `${typeof reason === 'string' ? `${reason}: ` : ''}${e.message}`,
89
+ ...(typeof reason === 'string' ? { reason } : {}),
90
+ ...(typeof retryAfterMs === 'number' && Number.isFinite(retryAfterMs) ? { retryAfterMs } : {}),
91
+ });
92
+ });
93
+ });
94
+ rl.on('close', () => end('input closed'));
95
+ opts.input.on('error', () => end('input error'));
96
+ });
97
+ }
@@ -0,0 +1,121 @@
1
+ // dexterm T2 — the signer PROCESS entry (task-1328). `node main.ts [caps flags]`. Wires the real
2
+ // dependencies — its OWN HTTP client for Jupiter + RPC, its OWN SOL/USD feed, the wallet bridge (opens
3
+ // the system browser for Phantom), the kill-switch file, the shared daily-cap ledger and the live fill
4
+ // ledger — and serves the authenticated stdio channel until the parent closes it.
5
+ //
6
+ // Caps come from THIS process's argv (what the user typed when starting dexterm), clamped to the hard
7
+ // ceilings and, without --mainnet, only ever DOWN from the defaults. Nothing on the channel can change
8
+ // them. The RPC defaults to DEVNET; `--mainnet` switches to the user's configured mainnet RPC
9
+ // (DEXTERM_RPC_URL / ~/.dexterm/helius.key / public). `--rpc URL` is honoured but the NETWORK the
10
+ // signer reports and caps by is always classified from the URL it will actually talk to.
11
+ import { createDataLayer } from "../../index.js";
12
+ import { JupiterSwapBuild } from "../swap-build.js";
13
+ import { TradeRpc, DEVNET_RPC, rpcNetwork } from "../rpc-exec.js";
14
+ import { WalletBridge } from "../bridge.js";
15
+ import { defaultCaps, capsOnlyDownward, CapLedger, defaultCapLedgerPath, HARD_MAX_PER_TRADE_USD, HARD_MAX_DAILY_USD, HARD_MAX_SOL_OUT, HARD_MAX_SLIPPAGE_BPS } from "../caps.js";
16
+ import { LiveExecutor, LiveLedger, defaultLiveLedgerPath } from "../executor.js";
17
+ import { KillSwitch, defaultPanicPath } from "../kill-switch.js";
18
+ import { WalletLinkStore, defaultLinkPath } from "../link-store.js";
19
+ import { resolveRpcUrl } from "../../rpc-config.js";
20
+ import { DevnetProbeBuilder } from "./probe-builder.js";
21
+ import { SolUsdFeed } from "./sol-usd-feed.js";
22
+ import { FastWalletStore, defaultFastWalletPath } from "./fast-wallet.js";
23
+ import { SignerService } from "./service.js";
24
+ import { serveSigner } from "./host.js";
25
+ /** Parse the signer's argv. Over-ceiling caps are an error (never silently accepted). */
26
+ export function parseSignerArgs(argv, env = process.env) {
27
+ let mainnet = false;
28
+ let perTradeUsd, dailyUsd, maxSolOut, maxSlippageBps;
29
+ let rpcUrl;
30
+ let ledgerPath = defaultCapLedgerPath(), liveLedgerPath = defaultLiveLedgerPath(), linkPath = defaultLinkPath(), panicPath = defaultPanicPath();
31
+ let fastWalletPath = defaultFastWalletPath(env);
32
+ let noBrowser = false;
33
+ const take = (flag, raw, ceiling) => {
34
+ const n = Number(raw);
35
+ if (!Number.isFinite(n) || n <= 0)
36
+ return undefined;
37
+ if (n > ceiling)
38
+ throw new Error(`${flag} ${n} exceeds the hard ceiling of ${ceiling}`);
39
+ return n;
40
+ };
41
+ for (let i = 0; i < argv.length; i++) {
42
+ const k = argv[i];
43
+ if (k === '--mainnet')
44
+ mainnet = true;
45
+ else if (k === '--max-usd')
46
+ perTradeUsd = take(k, argv[++i], HARD_MAX_PER_TRADE_USD);
47
+ else if (k === '--daily-usd')
48
+ dailyUsd = take(k, argv[++i], HARD_MAX_DAILY_USD);
49
+ else if (k === '--max-sol-out')
50
+ maxSolOut = take(k, argv[++i], HARD_MAX_SOL_OUT);
51
+ else if (k === '--max-slippage-bps')
52
+ maxSlippageBps = take(k, argv[++i], HARD_MAX_SLIPPAGE_BPS);
53
+ else if (k === '--rpc')
54
+ rpcUrl = argv[++i];
55
+ else if (k === '--ledger')
56
+ ledgerPath = argv[++i];
57
+ else if (k === '--live-ledger')
58
+ liveLedgerPath = argv[++i];
59
+ else if (k === '--link')
60
+ linkPath = argv[++i];
61
+ else if (k === '--panic-file')
62
+ panicPath = argv[++i];
63
+ else if (k === '--fast-wallet')
64
+ fastWalletPath = argv[++i];
65
+ else if (k === '--no-browser')
66
+ noBrowser = true;
67
+ }
68
+ const caps = capsOnlyDownward(defaultCaps({ network: mainnet ? 'mainnet' : 'devnet', mainnetEnabled: mainnet, perTradeUsd, dailyUsd, maxSolOut, maxSlippageBps }));
69
+ // The RPC: explicit --rpc, else devnet; with --mainnet the user's configured mainnet endpoint.
70
+ const url = rpcUrl ?? (mainnet ? resolveRpcUrl({ env }).url : DEVNET_RPC);
71
+ // Never let a devnet-labelled signer talk to mainnet by accident: without --mainnet a non-devnet URL is refused.
72
+ if (!mainnet && rpcNetwork(url) !== 'devnet')
73
+ throw new Error('without --mainnet the signer RPC must be a devnet endpoint');
74
+ return { caps, rpcUrl: url, ledgerPath, liveLedgerPath, linkPath, panicPath, fastWalletPath, noBrowser };
75
+ }
76
+ /**
77
+ * task-1368: the signer's own `jupiter` bucket carries only its quote/build calls (a handful per ticket), so after a
78
+ * 429 it pauses from this base (doubling per consecutive 429, capped as before) instead of the data layer's 30 s —
79
+ * which was tuned for the terminal's enrichment flood and made every retry inside the 10 s budget impossible.
80
+ * Retry-After from the server still wins when it is longer.
81
+ */
82
+ export const SIGNER_RATE_LIMIT_PAUSE_MS = 3_000;
83
+ export function buildSignerService(a, log) {
84
+ const dl = createDataLayer({ dbPath: ':memory:', rpcUrl: a.rpcUrl, rpcFallbackUrl: null, rateLimitPauseMs: SIGNER_RATE_LIMIT_PAUSE_MS });
85
+ const rpc = new TradeRpc(dl.http, { url: a.rpcUrl, clock: dl.clock });
86
+ const build = new JupiterSwapBuild(dl.http);
87
+ const bridge = new WalletBridge(a.noBrowser ? { open: (url) => { log(`signer: open this page to approve: ${url}`); } } : {});
88
+ const ledger = new CapLedger({ path: a.ledgerPath, clock: dl.clock });
89
+ const liveLedger = new LiveLedger({ path: a.liveLedgerPath, clock: dl.clock });
90
+ const killSwitch = new KillSwitch(a.panicPath);
91
+ const linkStore = new WalletLinkStore(a.linkPath);
92
+ const feed = new SolUsdFeed({ clock: dl.clock });
93
+ // task-1363: the capped fast wallet is loaded ONLY against a devnet RPC (by classification of the URL the
94
+ // signer will actually talk to). On mainnet there is no fast signer, so `fill` and fast-lane confirms refuse.
95
+ const fastStore = new FastWalletStore(a.fastWalletPath);
96
+ const fastSigner = rpc.network === 'devnet' ? fastStore.load() ?? undefined : undefined;
97
+ if (fastSigner)
98
+ log(`signer: fast wallet loaded (devnet) ${fastSigner.publicKey}`);
99
+ const deps = { build, rpc, bridge, caps: a.caps, ledger, liveLedger, clock: dl.clock, solUsd: () => feed.samples(), killSwitch, probe: new DevnetProbeBuilder(rpc), fastSigner };
100
+ const executor = new LiveExecutor(deps);
101
+ return new SignerService({ executor, deps, linkStore, liveLedger, solUsdFeed: feed, clock: dl.clock, fastStore, log });
102
+ }
103
+ export async function main(argv) {
104
+ const log = (l) => process.stderr.write(`${l}\n`);
105
+ let args;
106
+ try {
107
+ args = parseSignerArgs(argv);
108
+ }
109
+ catch (e) {
110
+ log(`signer: ${e instanceof Error ? e.message : String(e)}`);
111
+ return 2;
112
+ }
113
+ const service = buildSignerService(args, log);
114
+ const why = await serveSigner({ service, input: process.stdin, output: process.stdout, log });
115
+ log(`signer: stopped (${why})`);
116
+ return why === 'input closed' ? 0 : 1;
117
+ }
118
+ // Run when executed directly (never when imported by the client or the tests).
119
+ if (process.argv[1] && import.meta.url === new URL(`file://${process.argv[1]}`).href) {
120
+ main(process.argv.slice(2)).then((code) => { process.exitCode = code; process.stdin.destroy(); }, (e) => { process.stderr.write(`signer: fatal ${e instanceof Error ? e.message : String(e)}\n`); process.exitCode = 1; });
121
+ }
@@ -0,0 +1,85 @@
1
+ // dexterm T2 — the DEVNET PROBE transaction builder (task-1328). Jupiter has no devnet, so the way to
2
+ // prove the real signing path end to end on devnet — keypress → Phantom sign → decode/verify →
3
+ // re-simulate → submit → confirm — without any value at risk is a transaction dexterm builds itself:
4
+ // the wrap-and-unwrap shape a Jupiter swap uses around its route, with NO route:
5
+ //
6
+ // AssociatedToken.CreateIdempotent(wallet's WSOL ATA) · System.Transfer(wallet → WSOL ATA, 0.001 SOL)
7
+ // Token.SyncNative(WSOL ATA) · Memo("dexterm devnet probe") · Token.CloseAccount(WSOL ATA → wallet)
8
+ //
9
+ // The close returns the lamports (and the ATA rent) to the wallet in the same transaction, so the only
10
+ // SOL that leaves is the network fee. The signer's policy for the probe kind refuses any other shape, and
11
+ // the probe is refused outright unless the RPC endpoint classifies as devnet. No key, no network write:
12
+ // this module only builds bytes; the executor simulates and the wallet signs.
13
+ import { base58Decode } from "../../sources/pda.js";
14
+ import { rpcNetwork } from "../rpc-exec.js";
15
+ import { associatedTokenAddress, ASSOCIATED_TOKEN_PROGRAM, SYSTEM_PROGRAM, TOKEN_PROGRAM, MEMO_PROGRAM, WSOL_MINT } from "../tx-decode.js";
16
+ export const PROBE_LAMPORTS = 1000000n; // 0.001 SOL, wrapped then unwrapped in the same tx
17
+ export const PROBE_MEMO = 'dexterm devnet probe';
18
+ function shortvec(n) {
19
+ const out = [];
20
+ do {
21
+ let b = n & 0x7f;
22
+ n >>= 7;
23
+ if (n)
24
+ b |= 0x80;
25
+ out.push(b);
26
+ } while (n);
27
+ return Buffer.from(out);
28
+ }
29
+ /**
30
+ * Compile a LEGACY message (no lookup tables needed) with the wallet as the single signer/fee payer.
31
+ * Key order follows the runtime's rule: writable signers, then writable non-signers, then readonly
32
+ * non-signers. Exported for the tests (they decode what this builds with the real decoder).
33
+ */
34
+ export function compileProbeMessage(user, blockhash, lamports = PROBE_LAMPORTS, memo = PROBE_MEMO) {
35
+ const wsolAta = associatedTokenAddress(user, WSOL_MINT, TOKEN_PROGRAM);
36
+ const transfer = Buffer.alloc(12);
37
+ transfer.writeUInt32LE(2, 0);
38
+ transfer.writeBigUInt64LE(lamports, 4);
39
+ const ixs = [
40
+ { program: ASSOCIATED_TOKEN_PROGRAM, accounts: [user, wsolAta, user, WSOL_MINT, SYSTEM_PROGRAM, TOKEN_PROGRAM], data: Buffer.from([1]) },
41
+ { program: SYSTEM_PROGRAM, accounts: [user, wsolAta], data: transfer },
42
+ { program: TOKEN_PROGRAM, accounts: [wsolAta], data: Buffer.from([17]) },
43
+ { program: MEMO_PROGRAM, accounts: [], data: Buffer.from(memo, 'utf8') },
44
+ { program: TOKEN_PROGRAM, accounts: [wsolAta, user, user], data: Buffer.from([9]) },
45
+ ];
46
+ const writable = [user, wsolAta];
47
+ const readonly = [WSOL_MINT, SYSTEM_PROGRAM, TOKEN_PROGRAM, ASSOCIATED_TOKEN_PROGRAM, MEMO_PROGRAM];
48
+ const keys = [...writable, ...readonly];
49
+ const index = (k) => { const i = keys.indexOf(k); if (i < 0)
50
+ throw new Error(`probe: unknown key ${k}`); return i; };
51
+ const parts = [Buffer.from([1, 0, readonly.length]), shortvec(keys.length)];
52
+ for (const k of keys)
53
+ parts.push(Buffer.from(base58Decode(k)));
54
+ parts.push(Buffer.from(base58Decode(blockhash)), shortvec(ixs.length));
55
+ for (const ix of ixs) {
56
+ const acc = ix.accounts.map(index);
57
+ parts.push(Buffer.from([index(ix.program)]), shortvec(acc.length), Buffer.from(acc), shortvec(ix.data.length), ix.data);
58
+ }
59
+ return Buffer.concat(parts);
60
+ }
61
+ /** The unsigned wire transaction (one zero signature slot + the message), base64. */
62
+ export function probeWire(message) {
63
+ return Buffer.concat([shortvec(1), Buffer.alloc(64), message]).toString('base64');
64
+ }
65
+ export class DevnetProbeBuilder {
66
+ rpc;
67
+ constructor(rpc) { this.rpc = rpc; }
68
+ /**
69
+ * The probe (0.001 SOL, the fixed memo) or — task-1363 — a DEVNET FILL for the fast wallet: the same
70
+ * wrap-and-unwrap shape sized to a plan's SOL leg, with a memo naming the plan leg. Devnet only either way.
71
+ */
72
+ async build(userPublicKey, opts = {}) {
73
+ if (rpcNetwork(this.rpc.url, this.rpc.network ?? null) !== 'devnet')
74
+ throw new Error('the probe builds only against a devnet RPC endpoint');
75
+ if (base58Decode(userPublicKey).length !== 32)
76
+ throw new Error('probe: bad wallet public key');
77
+ const lamports = opts.lamports ?? PROBE_LAMPORTS;
78
+ if (!(lamports > 0n))
79
+ throw new Error('probe: lamports must be positive');
80
+ const memo = (opts.memo ?? PROBE_MEMO).replace(/[^\x20-\x7e]/g, '?').slice(0, 96);
81
+ const { blockhash } = await this.rpc.latestBlockhash();
82
+ const message = compileProbeMessage(userPublicKey, blockhash, lamports, memo);
83
+ return { txBase64: probeWire(message), lamports };
84
+ }
85
+ }