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.
Files changed (377) 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 +394 -0
  4. package/dist/packages/agents/src/executor.js +202 -0
  5. package/dist/packages/agents/src/gate.js +179 -0
  6. package/dist/packages/agents/src/identity.js +218 -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 +119 -0
  12. package/dist/packages/agents/src/tools.js +72 -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 +75 -0
  18. package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -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 +336 -0
  26. package/dist/packages/cli/src/agent-roster.js +26 -0
  27. package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
  28. package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
  29. package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
  30. package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
  31. package/dist/packages/cli/src/alerts/creds.js +83 -0
  32. package/dist/packages/cli/src/alerts/format.js +41 -0
  33. package/dist/packages/cli/src/alerts/index.js +10 -0
  34. package/dist/packages/cli/src/alerts/notifier.js +53 -0
  35. package/dist/packages/cli/src/alerts/route.js +19 -0
  36. package/dist/packages/cli/src/alerts/types.js +1 -0
  37. package/dist/packages/cli/src/alerts-cmd.js +212 -0
  38. package/dist/packages/cli/src/analyst/backend.js +526 -0
  39. package/dist/packages/cli/src/analyst/results.js +38 -0
  40. package/dist/packages/cli/src/analyst/session.js +319 -0
  41. package/dist/packages/cli/src/audit.js +51 -0
  42. package/dist/packages/cli/src/backtest-cmd.js +208 -0
  43. package/dist/packages/cli/src/boot-state.js +62 -0
  44. package/dist/packages/cli/src/coin-target.js +2 -0
  45. package/dist/packages/cli/src/custom.js +261 -0
  46. package/dist/packages/cli/src/display.js +191 -0
  47. package/dist/packages/cli/src/doctor.js +375 -0
  48. package/dist/packages/cli/src/export.js +235 -0
  49. package/dist/packages/cli/src/feed.js +634 -0
  50. package/dist/packages/cli/src/fixtures.js +275 -0
  51. package/dist/packages/cli/src/follow-cmd.js +221 -0
  52. package/dist/packages/cli/src/images-config.js +59 -0
  53. package/dist/packages/cli/src/index.js +31 -0
  54. package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
  55. package/dist/packages/cli/src/larp-record.js +21 -0
  56. package/dist/packages/cli/src/live/copy.js +80 -0
  57. package/dist/packages/cli/src/live/flow.js +233 -0
  58. package/dist/packages/cli/src/live/index.js +511 -0
  59. package/dist/packages/cli/src/live/intel.js +227 -0
  60. package/dist/packages/cli/src/live/newpairs.js +334 -0
  61. package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
  62. package/dist/packages/cli/src/live/screens/token-live.js +459 -0
  63. package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
  64. package/dist/packages/cli/src/live/token.js +585 -0
  65. package/dist/packages/cli/src/live/trade.js +929 -0
  66. package/dist/packages/cli/src/live/watchlist.js +178 -0
  67. package/dist/packages/cli/src/main.js +5785 -0
  68. package/dist/packages/cli/src/mcp-cmd.js +326 -0
  69. package/dist/packages/cli/src/narrative-cmd.js +76 -0
  70. package/dist/packages/cli/src/pack-cmd.js +323 -0
  71. package/dist/packages/cli/src/plan-cmd.js +278 -0
  72. package/dist/packages/cli/src/popup.js +810 -0
  73. package/dist/packages/cli/src/rules-cmd.js +131 -0
  74. package/dist/packages/cli/src/rules.js +358 -0
  75. package/dist/packages/cli/src/scan/command.js +57 -0
  76. package/dist/packages/cli/src/scan/engine.js +419 -0
  77. package/dist/packages/cli/src/scan/observe.js +64 -0
  78. package/dist/packages/cli/src/scan/ui.js +98 -0
  79. package/dist/packages/cli/src/screener/filter.js +241 -0
  80. package/dist/packages/cli/src/screener/presets.js +80 -0
  81. package/dist/packages/cli/src/screener/rows.js +146 -0
  82. package/dist/packages/cli/src/screener/rug.js +50 -0
  83. package/dist/packages/cli/src/screener/sort.js +52 -0
  84. package/dist/packages/cli/src/screener/view.js +183 -0
  85. package/dist/packages/cli/src/screens/analyst-session.js +343 -0
  86. package/dist/packages/cli/src/screens/analyst.js +326 -0
  87. package/dist/packages/cli/src/screens/boot.js +74 -0
  88. package/dist/packages/cli/src/screens/bubbles.js +255 -0
  89. package/dist/packages/cli/src/screens/coin.js +33 -0
  90. package/dist/packages/cli/src/screens/follows.js +201 -0
  91. package/dist/packages/cli/src/screens/kol.js +90 -0
  92. package/dist/packages/cli/src/screens/narrative.js +90 -0
  93. package/dist/packages/cli/src/screens/newpairs.js +150 -0
  94. package/dist/packages/cli/src/screens/overlay.js +257 -0
  95. package/dist/packages/cli/src/screens/plans.js +126 -0
  96. package/dist/packages/cli/src/screens/positions.js +176 -0
  97. package/dist/packages/cli/src/screens/rules.js +170 -0
  98. package/dist/packages/cli/src/screens/screener.js +459 -0
  99. package/dist/packages/cli/src/screens/social-panel.js +107 -0
  100. package/dist/packages/cli/src/screens/ticket.js +342 -0
  101. package/dist/packages/cli/src/screens/token.js +312 -0
  102. package/dist/packages/cli/src/screens/trending.js +181 -0
  103. package/dist/packages/cli/src/screens/watchlist.js +104 -0
  104. package/dist/packages/cli/src/skill-eval-build.js +109 -0
  105. package/dist/packages/cli/src/social/demo.js +96 -0
  106. package/dist/packages/cli/src/social/view.js +250 -0
  107. package/dist/packages/cli/src/state.js +463 -0
  108. package/dist/packages/cli/src/theme-cmd.js +126 -0
  109. package/dist/packages/cli/src/trade.js +319 -0
  110. package/dist/packages/cli/src/update.js +294 -0
  111. package/dist/packages/cli/src/version.js +3 -0
  112. package/dist/packages/cli/src/voice-cmd.js +119 -0
  113. package/dist/packages/cli/src/voice-hud.js +42 -0
  114. package/dist/packages/cli/src/wallet-cmd.js +440 -0
  115. package/dist/packages/cli/src/wallet-command.js +31 -0
  116. package/dist/packages/cli/src/watch-cmd.js +190 -0
  117. package/dist/packages/cli/src/widgets.js +208 -0
  118. package/dist/packages/cli/src/workspace-cmd.js +68 -0
  119. package/dist/packages/cli/src/x-cmd.js +154 -0
  120. package/dist/packages/custom/src/config.js +213 -0
  121. package/dist/packages/custom/src/index.js +10 -0
  122. package/dist/packages/custom/src/keymap.js +107 -0
  123. package/dist/packages/custom/src/layout.js +235 -0
  124. package/dist/packages/custom/src/packs.js +633 -0
  125. package/dist/packages/custom/src/rules.js +704 -0
  126. package/dist/packages/custom/src/schema.js +538 -0
  127. package/dist/packages/custom/src/themes.js +176 -0
  128. package/dist/packages/custom/src/widget-html.js +222 -0
  129. package/dist/packages/custom/src/workspace.js +141 -0
  130. package/dist/packages/data/src/analyst-actions.js +133 -0
  131. package/dist/packages/data/src/budget.js +88 -0
  132. package/dist/packages/data/src/cache.js +142 -0
  133. package/dist/packages/data/src/candles.js +168 -0
  134. package/dist/packages/data/src/clock.js +64 -0
  135. package/dist/packages/data/src/coin-target.js +82 -0
  136. package/dist/packages/data/src/credits.js +137 -0
  137. package/dist/packages/data/src/detail.js +97 -0
  138. package/dist/packages/data/src/fast-lane.js +321 -0
  139. package/dist/packages/data/src/fixtures.js +135 -0
  140. package/dist/packages/data/src/health.js +245 -0
  141. package/dist/packages/data/src/http.js +277 -0
  142. package/dist/packages/data/src/images/cache.js +141 -0
  143. package/dist/packages/data/src/images/decode-worker.js +28 -0
  144. package/dist/packages/data/src/images/decode.js +81 -0
  145. package/dist/packages/data/src/images/fetch.js +162 -0
  146. package/dist/packages/data/src/images/gif.js +214 -0
  147. package/dist/packages/data/src/images/index.js +10 -0
  148. package/dist/packages/data/src/images/jpeg.js +544 -0
  149. package/dist/packages/data/src/images/png.js +310 -0
  150. package/dist/packages/data/src/images/service.js +334 -0
  151. package/dist/packages/data/src/images/types.js +27 -0
  152. package/dist/packages/data/src/index.js +211 -0
  153. package/dist/packages/data/src/intel/helius-history.js +70 -0
  154. package/dist/packages/data/src/intel/holder-class.js +125 -0
  155. package/dist/packages/data/src/intel/rules.js +421 -0
  156. package/dist/packages/data/src/intel/scan.js +170 -0
  157. package/dist/packages/data/src/larp/evidence.js +288 -0
  158. package/dist/packages/data/src/larp/link-baseline.js +196 -0
  159. package/dist/packages/data/src/larp/links.js +137 -0
  160. package/dist/packages/data/src/larp/robots.js +90 -0
  161. package/dist/packages/data/src/larp/safe-fetch.js +389 -0
  162. package/dist/packages/data/src/limiter.js +341 -0
  163. package/dist/packages/data/src/model.js +12 -0
  164. package/dist/packages/data/src/paper.js +331 -0
  165. package/dist/packages/data/src/rpc-config.js +126 -0
  166. package/dist/packages/data/src/rpc-pool.js +166 -0
  167. package/dist/packages/data/src/social/chain.js +83 -0
  168. package/dist/packages/data/src/social/dedupe.js +205 -0
  169. package/dist/packages/data/src/social/feed.js +462 -0
  170. package/dist/packages/data/src/social/firewall.js +110 -0
  171. package/dist/packages/data/src/social/index.js +11 -0
  172. package/dist/packages/data/src/social/kol.js +269 -0
  173. package/dist/packages/data/src/social/model.js +11 -0
  174. package/dist/packages/data/src/social/rank.js +184 -0
  175. package/dist/packages/data/src/social/rss.js +113 -0
  176. package/dist/packages/data/src/social/summary.js +104 -0
  177. package/dist/packages/data/src/social/venues.js +186 -0
  178. package/dist/packages/data/src/sources/dexscreener.js +184 -0
  179. package/dist/packages/data/src/sources/geckoterminal.js +133 -0
  180. package/dist/packages/data/src/sources/goplus.js +161 -0
  181. package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
  182. package/dist/packages/data/src/sources/jupiter.js +198 -0
  183. package/dist/packages/data/src/sources/pda.js +113 -0
  184. package/dist/packages/data/src/sources/pump-curve.js +58 -0
  185. package/dist/packages/data/src/sources/pumpportal.js +252 -0
  186. package/dist/packages/data/src/sources/rugcheck.js +71 -0
  187. package/dist/packages/data/src/sources/solana-rpc.js +299 -0
  188. package/dist/packages/data/src/sources/solana-ws.js +287 -0
  189. package/dist/packages/data/src/store.js +289 -0
  190. package/dist/packages/data/src/trade/bridge.js +464 -0
  191. package/dist/packages/data/src/trade/caps.js +323 -0
  192. package/dist/packages/data/src/trade/copy/auto.js +385 -0
  193. package/dist/packages/data/src/trade/copy/follows.js +103 -0
  194. package/dist/packages/data/src/trade/copy/index.js +9 -0
  195. package/dist/packages/data/src/trade/copy/mirror.js +91 -0
  196. package/dist/packages/data/src/trade/copy/stats.js +120 -0
  197. package/dist/packages/data/src/trade/copy/tracker.js +158 -0
  198. package/dist/packages/data/src/trade/copy/trades.js +87 -0
  199. package/dist/packages/data/src/trade/copy/types.js +6 -0
  200. package/dist/packages/data/src/trade/executor.js +933 -0
  201. package/dist/packages/data/src/trade/kill-switch.js +47 -0
  202. package/dist/packages/data/src/trade/link-store.js +52 -0
  203. package/dist/packages/data/src/trade/plans/confirm.js +157 -0
  204. package/dist/packages/data/src/trade/plans/engine.js +784 -0
  205. package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
  206. package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
  207. package/dist/packages/data/src/trade/plans/executor.js +11 -0
  208. package/dist/packages/data/src/trade/plans/index.js +12 -0
  209. package/dist/packages/data/src/trade/plans/journal.js +77 -0
  210. package/dist/packages/data/src/trade/plans/mandate.js +129 -0
  211. package/dist/packages/data/src/trade/plans/plan.js +179 -0
  212. package/dist/packages/data/src/trade/plans/service.js +168 -0
  213. package/dist/packages/data/src/trade/plans/types.js +6 -0
  214. package/dist/packages/data/src/trade/rpc-exec.js +229 -0
  215. package/dist/packages/data/src/trade/signer/client.js +197 -0
  216. package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
  217. package/dist/packages/data/src/trade/signer/host.js +97 -0
  218. package/dist/packages/data/src/trade/signer/main.js +121 -0
  219. package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
  220. package/dist/packages/data/src/trade/signer/protocol.js +92 -0
  221. package/dist/packages/data/src/trade/signer/service.js +400 -0
  222. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
  223. package/dist/packages/data/src/trade/sol-usd.js +86 -0
  224. package/dist/packages/data/src/trade/swap-build.js +108 -0
  225. package/dist/packages/data/src/trade/token-delta.js +201 -0
  226. package/dist/packages/data/src/trade/tx-decode.js +632 -0
  227. package/dist/packages/data/src/util.js +24 -0
  228. package/dist/packages/data/src/watchlist.js +176 -0
  229. package/dist/packages/data/src/x/api.js +256 -0
  230. package/dist/packages/data/src/x/budget.js +66 -0
  231. package/dist/packages/data/src/x/config.js +49 -0
  232. package/dist/packages/data/src/x/index.js +10 -0
  233. package/dist/packages/data/src/x/mock-server.js +217 -0
  234. package/dist/packages/data/src/x/oauth.js +242 -0
  235. package/dist/packages/data/src/x/pkce.js +21 -0
  236. package/dist/packages/data/src/x/source.js +285 -0
  237. package/dist/packages/data/src/x/token-store.js +134 -0
  238. package/dist/packages/data/src/x/transport.js +71 -0
  239. package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
  240. package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
  241. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
  242. package/dist/packages/mcp/assets/cases.json +1194 -0
  243. package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
  244. package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
  245. package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
  246. package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
  247. package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
  248. package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
  249. package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
  250. package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
  251. package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
  252. package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
  253. package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
  254. package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
  255. package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
  256. package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
  257. package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
  258. package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
  259. package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
  260. package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
  261. package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
  262. package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
  263. package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
  264. package/dist/packages/mcp/src/dossier.js +78 -0
  265. package/dist/packages/mcp/src/firewall.js +21 -0
  266. package/dist/packages/mcp/src/index.js +22 -0
  267. package/dist/packages/mcp/src/server.js +936 -0
  268. package/dist/packages/mcp/src/user-content.js +434 -0
  269. package/dist/packages/packs/src/backtest.js +183 -0
  270. package/dist/packages/packs/src/digest.js +95 -0
  271. package/dist/packages/packs/src/firewall.js +57 -0
  272. package/dist/packages/packs/src/index.js +20 -0
  273. package/dist/packages/packs/src/larp-eval.js +100 -0
  274. package/dist/packages/packs/src/narrative.js +309 -0
  275. package/dist/packages/packs/src/reason.js +135 -0
  276. package/dist/packages/packs/src/rug-model.js +137 -0
  277. package/dist/packages/packs/src/schema.js +54 -0
  278. package/dist/packages/packs/src/skill-backtest.js +300 -0
  279. package/dist/packages/packs/src/token-dossier.js +174 -0
  280. package/dist/packages/packs/src/verdicts.js +17 -0
  281. package/dist/packages/packs/src/wallet-dossier.js +105 -0
  282. package/dist/packages/popup/src/config.js +71 -0
  283. package/dist/packages/popup/src/dock.js +271 -0
  284. package/dist/packages/popup/src/host.js +210 -0
  285. package/dist/packages/popup/src/index.js +12 -0
  286. package/dist/packages/popup/src/loopback.js +53 -0
  287. package/dist/packages/popup/src/open.js +183 -0
  288. package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
  289. package/dist/packages/popup/src/pages/bubbles.css +23 -0
  290. package/dist/packages/popup/src/pages/bubbles.html +36 -0
  291. package/dist/packages/popup/src/pages/bubbles.js +214 -0
  292. package/dist/packages/popup/src/pages/chart-lib.js +262 -0
  293. package/dist/packages/popup/src/pages/chart.css +50 -0
  294. package/dist/packages/popup/src/pages/chart.html +56 -0
  295. package/dist/packages/popup/src/pages/chart.js +554 -0
  296. package/dist/packages/popup/src/pages/common.css +34 -0
  297. package/dist/packages/popup/src/pages/common.js +187 -0
  298. package/dist/packages/popup/src/pages/feeds.html +20 -0
  299. package/dist/packages/popup/src/pages/feeds.js +77 -0
  300. package/dist/packages/popup/src/pages/narrative.html +20 -0
  301. package/dist/packages/popup/src/pages/narrative.js +61 -0
  302. package/dist/packages/popup/src/pages/plans.css +30 -0
  303. package/dist/packages/popup/src/pages/plans.html +26 -0
  304. package/dist/packages/popup/src/pages/plans.js +82 -0
  305. package/dist/packages/popup/src/pages/roster.css +41 -0
  306. package/dist/packages/popup/src/pages/roster.html +25 -0
  307. package/dist/packages/popup/src/pages/roster.js +75 -0
  308. package/dist/packages/popup/src/pages/token.html +20 -0
  309. package/dist/packages/popup/src/pages/token.js +112 -0
  310. package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
  311. package/dist/packages/popup/src/pages/widget.css +5 -0
  312. package/dist/packages/popup/src/pages/widget.html +21 -0
  313. package/dist/packages/popup/src/pages/widget.js +85 -0
  314. package/dist/packages/popup/src/protocol.js +31 -0
  315. package/dist/packages/popup/src/selftest.js +156 -0
  316. package/dist/packages/popup/src/server.js +582 -0
  317. package/dist/packages/popup/src/sinkproxy.js +167 -0
  318. package/dist/packages/popup/src/widget-html.js +231 -0
  319. package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
  320. package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
  321. package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
  322. package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
  323. package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
  324. package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
  325. package/dist/packages/recorder/src/cli.js +113 -0
  326. package/dist/packages/recorder/src/export.js +71 -0
  327. package/dist/packages/recorder/src/index.js +20 -0
  328. package/dist/packages/recorder/src/labels.js +151 -0
  329. package/dist/packages/recorder/src/open.js +29 -0
  330. package/dist/packages/recorder/src/recorder.js +553 -0
  331. package/dist/packages/recorder/src/schedule.js +43 -0
  332. package/dist/packages/recorder/src/schema.js +135 -0
  333. package/dist/packages/recorder/src/snapshot.js +191 -0
  334. package/dist/packages/recorder/src/sweep.js +52 -0
  335. package/dist/packages/recorder/src/verdicts.js +52 -0
  336. package/dist/packages/tui/src/bubbles.js +415 -0
  337. package/dist/packages/tui/src/canvas.js +370 -0
  338. package/dist/packages/tui/src/chrome.js +85 -0
  339. package/dist/packages/tui/src/color.js +133 -0
  340. package/dist/packages/tui/src/flush.js +62 -0
  341. package/dist/packages/tui/src/fmt.js +188 -0
  342. package/dist/packages/tui/src/fx-trade.js +278 -0
  343. package/dist/packages/tui/src/fx.js +527 -0
  344. package/dist/packages/tui/src/images/blocks.js +141 -0
  345. package/dist/packages/tui/src/images/coin.js +22 -0
  346. package/dist/packages/tui/src/images/detect.js +140 -0
  347. package/dist/packages/tui/src/images/index.js +7 -0
  348. package/dist/packages/tui/src/images/layer.js +146 -0
  349. package/dist/packages/tui/src/images/pixels.js +157 -0
  350. package/dist/packages/tui/src/images/protocols.js +87 -0
  351. package/dist/packages/tui/src/index.js +18 -0
  352. package/dist/packages/tui/src/keymap.js +357 -0
  353. package/dist/packages/tui/src/layout.js +11 -0
  354. package/dist/packages/tui/src/raster-font.js +195 -0
  355. package/dist/packages/tui/src/raster.js +213 -0
  356. package/dist/packages/tui/src/sound.js +177 -0
  357. package/dist/packages/tui/src/theme.js +182 -0
  358. package/dist/packages/tui/src/widgets.js +598 -0
  359. package/dist/packages/voice/src/controller.js +407 -0
  360. package/dist/packages/voice/src/doctor.js +131 -0
  361. package/dist/packages/voice/src/download.js +184 -0
  362. package/dist/packages/voice/src/engines.js +84 -0
  363. package/dist/packages/voice/src/index.js +20 -0
  364. package/dist/packages/voice/src/router.js +502 -0
  365. package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
  366. package/dist/packages/voice/src/stt/apple.js +179 -0
  367. package/dist/packages/voice/src/stt/fake.js +34 -0
  368. package/dist/packages/voice/src/stt/whisper.js +112 -0
  369. package/dist/packages/voice/src/summary.js +44 -0
  370. package/dist/packages/voice/src/tts/espeak.js +131 -0
  371. package/dist/packages/voice/src/tts/fake.js +30 -0
  372. package/dist/packages/voice/src/tts/piper.js +206 -0
  373. package/dist/packages/voice/src/tts/say.js +132 -0
  374. package/dist/packages/voice/src/tts/voices.js +206 -0
  375. package/dist/packages/voice/src/types.js +3 -0
  376. package/package.json +38 -4
  377. package/README.md +0 -3
@@ -0,0 +1,257 @@
1
+ // dexterm — modal overlays (task-1305): the first-run WELCOME card (shown once, then reachable with
2
+ // `?`) and the `?` HELP card that lists every key for the screen underneath it. Both are drawn last,
3
+ // after the effects pass, so the card is crisp and no rain/scanline crosses it. Any key closes the
4
+ // overlay; `?` toggles help.
5
+ import { Canvas, DEFAULT_KEYMAP, mix } from '../../../tui/src/index.js';
6
+ /** Pretty single-key display for a keymap id ('enter' → '⏎', 'pagedown' → 'PgDn', 'ctrl+f' → '^F'). */
7
+ function prettyKey(id) {
8
+ switch (id) {
9
+ case 'enter': return '⏎';
10
+ case 'esc': return 'esc';
11
+ case 'tab': return 'tab';
12
+ case 'space': return 'spc';
13
+ case 'up': return '↑';
14
+ case 'down': return '↓';
15
+ case 'left': return '←';
16
+ case 'right': return '→';
17
+ case 'home': return 'home';
18
+ case 'end': return 'end';
19
+ case 'pageup': return 'PgUp';
20
+ case 'pagedown': return 'PgDn';
21
+ case 'backspace': return '⌫';
22
+ }
23
+ // task-1358 chords: "ctrl+w s" → "^W s"
24
+ if (id.includes(' '))
25
+ return id.split(' ').map(prettyKey).join(' ');
26
+ if (id.startsWith('ctrl+'))
27
+ return '^' + id.slice(5).toUpperCase();
28
+ if (id.startsWith('alt+'))
29
+ return '⌥' + id.slice(4);
30
+ return id;
31
+ }
32
+ /** Collapse a binding's key list to a compact display, dropping synonyms that render the same. */
33
+ function keysDisplay(b) {
34
+ const seen = new Set();
35
+ const parts = [];
36
+ for (const k of b.keys) {
37
+ const p = prettyKey(k);
38
+ if (seen.has(p))
39
+ continue;
40
+ seen.add(p);
41
+ parts.push(p);
42
+ }
43
+ return parts.join('/');
44
+ }
45
+ /**
46
+ * Extra behaviours handled directly in the interactive loop (not in the keymap), surfaced in help so
47
+ * the overlay really does list every key. `fixtures` adds the demo-event keys that only fire on
48
+ * `--fixtures`.
49
+ */
50
+ export function OVERLAY_HINTS(screen, fixtures) {
51
+ const rows = [
52
+ { keys: 'c', label: 'calm mode (effects off)' },
53
+ { keys: '?', label: 'this help' },
54
+ ];
55
+ if (screen === 'analyst')
56
+ rows.unshift({ keys: 'type', label: 'ask a follow-up · ⏎ send · esc clear/back' });
57
+ if (fixtures)
58
+ rows.push({ keys: 'n/u/d/x', label: 'demo: new pair · pump · dump · rug' });
59
+ return rows;
60
+ }
61
+ /** The help rows for a screen: the keymap bindings that apply there, plus the loop-handled extras.
62
+ * A global key shadowed by a screen-specific binding (e.g. `1` = timeframe on the token screen, not
63
+ * "screener") is attributed to the screen binding, mirroring Keymap.resolve's precedence. */
64
+ export function helpRows(screen, fixtures, keymap = DEFAULT_KEYMAP, overridden = new Set()) {
65
+ const bindings = keymap.forScreen(screen);
66
+ const screenKeys = new Set();
67
+ for (const b of bindings)
68
+ if (b.screen === screen)
69
+ for (const k of b.keys)
70
+ screenKeys.add(k);
71
+ const order = [];
72
+ const rowFor = new Map();
73
+ for (const b of bindings) {
74
+ if (!b.label)
75
+ continue;
76
+ for (const k of b.keys) {
77
+ if (!b.screen && screenKeys.has(k))
78
+ continue; // this global key is overridden on this screen
79
+ let r = rowFor.get(b.action);
80
+ if (!r) {
81
+ r = { label: b.label, keys: [] };
82
+ rowFor.set(b.action, r);
83
+ order.push(b.action);
84
+ }
85
+ r.keys.push(k);
86
+ }
87
+ }
88
+ const rows = [];
89
+ for (const action of order) {
90
+ const r = rowFor.get(action);
91
+ if (!r.keys.length)
92
+ continue;
93
+ // task-1358: a `·` marks a binding the user changed in config.keymap
94
+ const mark = overridden.has(`|${action}`) || overridden.has(`${screen}|${action}`) ? ' ·' : '';
95
+ rows.push({ keys: keysDisplay({ keys: r.keys, action }), label: r.label + mark });
96
+ }
97
+ return [...rows, ...OVERLAY_HINTS(screen, fixtures)];
98
+ }
99
+ const SCREEN_TITLE = {
100
+ screener: 'Screener', token: 'Token detail', newpairs: 'New pairs', watchlist: 'Watchlist', analyst: 'Analyst', boot: 'Screener',
101
+ ticket: 'Paper trade ticket', positions: 'Paper positions',
102
+ trending: 'Trending in the space', kol: 'KOL lists', bubbles: 'Bubble map', narrative: 'Narrative board',
103
+ plans: 'Order plans & mandates', follows: 'Followed wallets', rules: 'Rules (alerts + proposals only)',
104
+ };
105
+ /** Draw a centered modal card and return its inner rect, or null when the terminal is too small. */
106
+ function card(c, want, title, caption) {
107
+ const w = Math.min(want.w, c.w - 2);
108
+ const h = Math.min(want.h, c.h - 2);
109
+ if (w < 24 || h < 6)
110
+ return null;
111
+ const x = Math.max(0, Math.floor((c.w - w) / 2));
112
+ const y = Math.max(0, Math.floor((c.h - h) / 2));
113
+ c.noRain(x, y, w, h);
114
+ const box = c.box(x, y, w, h, { title, caption, focus: true, bg: c.theme.panelAlt });
115
+ return box;
116
+ }
117
+ function renderWelcome(c, state) {
118
+ const t = c.theme;
119
+ const box = card(c, { w: 72, h: 22 }, 'WELCOME TO DEXTERM', 'press any key to start');
120
+ if (!box)
121
+ return;
122
+ const bg = box.bg;
123
+ let y = box.y;
124
+ const line = (s, st = { fg: t.text, bg }) => { c.text(box.x + 2, y, s, st, box.w - 4); y += 1; };
125
+ const gap = () => { y += 1; };
126
+ line('A terminal dex screener for Solana — live, keyless, read-only.', { fg: t.muted, bg });
127
+ line('Browse tokens, open a token for safety & holders, ask a local', { fg: t.muted, bg });
128
+ line('Claude to scan the trenches. No wallet, no keys, no trades.', { fg: t.muted, bg });
129
+ gap();
130
+ c.text(box.x + 2, y, 'GET AROUND', { fg: t.accent, bg, b: true });
131
+ y += 1;
132
+ const hints = [
133
+ ['1-4', 'switch screens (screener · new pairs · watchlist · analyst)'],
134
+ ['j/k', 'move · ⏎ open a token · esc go back'],
135
+ ['/', 'filter · s sort · [ ] change the feed'],
136
+ ['a', 'ask Claude about what you are looking at'],
137
+ ['?', 'show every key for the current screen, anytime'],
138
+ ['q', 'quit (your terminal is always restored)'],
139
+ ];
140
+ for (const [k, what] of hints) {
141
+ c.text(box.x + 3, y, k.padEnd(5), { fg: t.borderFocus, bg, b: true });
142
+ c.text(box.x + 9, y, what, { fg: t.text, bg }, box.w - 11);
143
+ y += 1;
144
+ }
145
+ gap();
146
+ c.hline(box.x, y, box.w, { fg: mix(t.border, t.warn, 0.4), bg, soft: true });
147
+ y += 1;
148
+ line('Safety: dexterm never signs, holds keys, or sends funds. Trading is PAPER (simulated).', { fg: t.warn, bg });
149
+ line('Nothing here is financial advice — do your own research.', { fg: t.warn, bg });
150
+ }
151
+ function renderHelp(c, state) {
152
+ const t = c.theme;
153
+ const screen = state.screen === 'boot' ? 'screener' : state.screen;
154
+ const fixtures = state.screener.source === 'fixtures';
155
+ const ov = new Set((state.custom?.keymapResult.overridden ?? []).map((o) => `${o.screen ?? ''}|${o.action}`));
156
+ const rows = helpRows(screen, fixtures, state.custom?.keymap ?? DEFAULT_KEYMAP, ov);
157
+ // task-1330: the global key table outgrew a 40-row terminal (T trending, K KOL lists); when one column
158
+ // cannot show every row the card goes two columns wide (110) so the last rows (the fixture demo keys) are never cut.
159
+ const maxRows = c.h - 2 - 7;
160
+ const columns = rows.length > maxRows && c.w >= 112 ? 2 : 1;
161
+ const perCol = columns === 2 ? Math.ceil(rows.length / 2) : rows.length;
162
+ const want = { w: columns === 2 ? 110 : 64, h: perCol + 7 };
163
+ const box = card(c, want, `KEYS — ${SCREEN_TITLE[screen]}`, 'esc or ? to close');
164
+ if (!box)
165
+ return;
166
+ const bg = box.bg;
167
+ const keyW = Math.min(12, Math.max(...rows.map((r) => r.keys.length)) + 1);
168
+ const colW = Math.floor((box.w - 2) / columns);
169
+ rows.forEach((r, i) => {
170
+ const col = Math.floor(i / perCol);
171
+ const y = box.y + (i % perCol);
172
+ if (y >= box.y + box.h - 2)
173
+ return;
174
+ const x = box.x + 2 + col * colW;
175
+ c.text(x, y, r.keys.padEnd(keyW), { fg: t.borderFocus, bg, b: true });
176
+ c.text(x + keyW + 1, y, r.label, { fg: t.text, bg }, colW - keyW - 3);
177
+ });
178
+ const fy = box.y + box.h - 1;
179
+ c.hline(box.x, fy - 1, box.w, { fg: mix(t.border, t.warn, 0.4), bg, soft: true });
180
+ c.text(box.x + 2, fy, 'read-only · no keys · no trades · not financial advice', { fg: t.warn, bg, i: true }, box.w - 4);
181
+ }
182
+ /** task-1358: the WORKSPACES card — 1-9 pick, j/k move + ⏎, esc close. Built-ins first, then ~/.dexterm/workspaces/*.json. */
183
+ function renderWorkspaces(c, state) {
184
+ const t = c.theme;
185
+ const ui = state.custom;
186
+ const items = ui?.workspaces ?? [];
187
+ const box = card(c, { w: 76, h: Math.min(c.h - 2, items.length + 7) }, 'WORKSPACES', '1-9 pick · j/k + ⏎ · ^W s save current · esc close');
188
+ if (!box)
189
+ return;
190
+ const bg = box.bg;
191
+ if (!items.length) {
192
+ c.text(box.x + 2, box.y, 'no workspaces loaded (config not read on this run)', { fg: t.muted, bg }, box.w - 4);
193
+ return;
194
+ }
195
+ const cursor = Math.max(0, Math.min(items.length - 1, ui?.listCursor ?? 0));
196
+ let slot = 0;
197
+ items.forEach((w, i) => {
198
+ const y = box.y + i;
199
+ if (y >= box.y + box.h - 2)
200
+ return;
201
+ const active = ui?.active === w.name;
202
+ const picked = i === cursor;
203
+ const n = w.error ? ' ' : slot < 9 ? String(++slot) : ' ';
204
+ c.text(box.x + 2, y, `${picked ? '›' : ' '} ${n} `, { fg: picked ? t.accent : t.dim, bg, b: picked });
205
+ c.text(box.x + 7, y, w.name.padEnd(22), { fg: w.error ? t.down : active ? t.borderFocus : t.text, bg, b: active });
206
+ const what = w.error ? `⚠ ${w.error}` : `${w.builtin ? 'built-in' : 'file'} · ${w.workspace.description ?? (w.workspace.start ? `starts on ${w.workspace.start}` : '')}${w.workspace.popups?.length ? ` · ${w.workspace.popups.length} popup${w.workspace.popups.length > 1 ? 's' : ''}` : ''}`;
207
+ c.text(box.x + 30, y, what, { fg: w.error ? t.down : t.muted, bg }, box.w - 32);
208
+ });
209
+ const fy = box.y + box.h - 1;
210
+ c.hline(box.x, fy - 1, box.w, { fg: mix(t.border, t.warn, 0.4), bg, soft: true });
211
+ c.text(box.x + 2, fy, `active: ${ui?.active ?? 'default'} · a workspace only navigates, re-themes and opens read-only popups`, { fg: t.warn, bg, i: true }, box.w - 4);
212
+ }
213
+ /**
214
+ * task-1388: the GO-TO prompt — a one-line "paste a contract address (or a dexscreener / pump.fun / solscan
215
+ * URL) to open any coin" input, opened with `g`. Read-only: ⏎ opens the coin's live token screen, esc cancels.
216
+ * Drawn as a centered card so it works the same on every screen that offers it.
217
+ */
218
+ export function renderGotoPrompt(c, state) {
219
+ const g = state.goto;
220
+ if (!g)
221
+ return;
222
+ const t = c.theme;
223
+ const box = card(c, { w: 68, h: 7 }, 'GO TO COIN', '⏎ open · esc cancel');
224
+ if (!box)
225
+ return;
226
+ const bg = box.bg;
227
+ c.text(box.x + 2, box.y, 'paste or type a contract address (or a coin URL):', { fg: t.muted, bg }, box.w - 4);
228
+ const shown = g.input.length > box.w - 6 ? '…' + g.input.slice(-(box.w - 7)) : g.input;
229
+ c.text(box.x + 2, box.y + 2, '› ', { fg: t.accent, bg, b: true });
230
+ c.text(box.x + 4, box.y + 2, shown, { fg: t.text, bg, b: true }, box.w - 7);
231
+ c.text(box.x + 4 + shown.length, box.y + 2, '▏', { fg: t.borderFocus, bg });
232
+ const fy = box.y + box.h - 1;
233
+ c.hline(box.x, fy - 1, box.w, { fg: mix(t.border, t.warn, 0.4), bg, soft: true });
234
+ c.text(box.x + 2, fy, g.notice ?? 'read-only — opens the token screen, never trades or signs', { fg: g.notice ? t.down : t.warn, bg, i: true }, box.w - 4);
235
+ }
236
+ /** Draw the active overlay (welcome or help). Called by renderFrame after the effects pass. */
237
+ export function renderOverlay(c, state) {
238
+ if (state.overlay === 'welcome')
239
+ renderWelcome(c, state);
240
+ else if (state.overlay === 'help')
241
+ renderHelp(c, state);
242
+ else if (state.overlay === 'workspaces')
243
+ renderWorkspaces(c, state);
244
+ else if (state.overlay === 'links' && state.linkMenu) {
245
+ const menu = state.linkMenu;
246
+ const box = card(c, { w: 100, h: Math.min(24, menu.items.length * 2 + 6) }, 'ADVERTISED LINKS (UNTRUSTED)', 'j/k select · enter open · esc close');
247
+ if (!box)
248
+ return;
249
+ const count = Math.max(1, Math.floor((box.h - 2) / 2));
250
+ const start = Math.max(0, menu.selected - count + 1);
251
+ menu.items.slice(start, start + count).forEach((item, i) => {
252
+ const selected = start + i === menu.selected;
253
+ c.text(box.x + 1, box.y + i * 2, `${selected ? '›' : ' '} ${item.kind}`, { fg: selected ? c.theme.accent : c.theme.muted, bg: box.bg }, box.w - 2);
254
+ c.text(box.x + 3, box.y + i * 2 + 1, item.url.replace(/[\p{Cc}\p{Cf}]/gu, ''), { fg: c.theme.text, bg: box.bg }, box.w - 4);
255
+ });
256
+ }
257
+ }
@@ -0,0 +1,126 @@
1
+ // dexterm — ORDER PLANS & STANDING MANDATES screen (key O, task-1355; SPEC §6.13). Lists the conditional
2
+ // order plans and standing mandates held in the signer trust domain, and lets the user arm (hold-to-confirm,
3
+ // physical keypress only), cancel/revoke, and panic. The screen NEVER signs, holds a key, raises a cap, or
4
+ // arms by anything but a captured physical keypress — voice and agents can only PROPOSE, never reach here.
5
+ // Default runs are PAPER (no key, no wallet, no mainnet); the on-disk journal is the audit log + recovery.
6
+ import { Canvas, header, footer, breakpoint, fmtUsd } from '../../../tui/src/index.js';
7
+ import { headerModel } from "../state.js";
8
+ import { planStatusLabel } from '../../../data/src/trade/plans/index.js';
9
+ import { planLevels, priceModeOf } from "../display.js";
10
+ function fmtTtl(expiresAt, now) {
11
+ const ms = expiresAt - now;
12
+ if (ms <= 0)
13
+ return 'expired';
14
+ const h = Math.floor(ms / 3600_000);
15
+ const m = Math.floor((ms % 3600_000) / 60_000);
16
+ return h > 0 ? `${h}h${m}m` : `${m}m`;
17
+ }
18
+ export function renderPlans(c, state) {
19
+ const t = c.theme;
20
+ const bp = breakpoint(c.w);
21
+ header(c, headerModel(state));
22
+ const ui = state.plansUi ?? null;
23
+ const svc = state.plansSvc ?? null;
24
+ const tab = ui?.tab ?? 'plans';
25
+ const plans = svc ? svc.listPlans() : [];
26
+ const mandates = svc ? svc.listMandates() : [];
27
+ const top = 2, bottom = c.h - 1;
28
+ const risk = svc ? svc.riskStatus() : { gateOff: false, acceptedAt: null, expiresAt: null, active: false };
29
+ const gateLabel = risk.active ? 'HANDS-FREE' : 'GATE ON';
30
+ // task-1363: the fire path. Anything not provably paper renders LIVE (fail-safe).
31
+ const liveFire = svc ? svc.mode() === 'live' : false;
32
+ const fireLabel = liveFire ? '◉ LIVE · DEVNET · FAST WALLET' : 'PAPER';
33
+ const title = `ORDER PLANS & MANDATES · ${tab === 'plans' ? `${plans.length} plans` : `${mandates.length} mandates`} · ${fireLabel} · ${gateLabel}`;
34
+ const caption = bp === 'compact' ? undefined : 'voice/agents PROPOSE only · arming needs a physical keypress hold · caps are signer-side · panic revokes all';
35
+ const box = c.box(0, top, c.w, bottom - top, { title, caption, focus: true });
36
+ const bg = box.bg;
37
+ const now = state.now.getTime();
38
+ // Tab strip
39
+ let cy = box.y;
40
+ const tabPlans = `PLANS (${plans.length})`;
41
+ const tabMand = `MANDATES (${mandates.length})`;
42
+ c.text(box.x + 1, cy, tabPlans, { fg: tab === 'plans' ? t.accent : t.dim, bg, b: tab === 'plans' });
43
+ c.text(box.x + 2 + tabPlans.length + 2, cy, tabMand, { fg: tab === 'mandates' ? t.accent : t.dim, bg, b: tab === 'mandates' });
44
+ cy += 1;
45
+ c.hline(box.x, cy, box.w, { fg: t.border, bg });
46
+ cy += 1;
47
+ const rows = tab === 'plans' ? plans : mandates;
48
+ if (!rows.length) {
49
+ c.textC(box.x, box.y + 3, box.w, tab === 'plans' ? 'no order plans — propose one with `dexterm plan propose`, or by voice ("buy this if it dips to 300k")' : 'no standing mandates — draft one with `dexterm mandate draft`', { fg: t.muted, bg, i: true });
50
+ c.textC(box.x, box.y + 5, box.w, 'a proposal is INERT until you arm it here with a physical keypress hold; nothing arms by voice or agent', { fg: t.dim, bg });
51
+ footer(c, [['tab', 'plans/mandates'], ['esc', 'back'], ['q', 'quit']], t.name);
52
+ return;
53
+ }
54
+ const sel = Math.max(0, Math.min(rows.length - 1, ui?.selected ?? 0));
55
+ const scroll = Math.max(0, sel - (box.h - 6));
56
+ for (let i = scroll; i < rows.length; i++) {
57
+ if (cy >= box.y + box.h - 2)
58
+ break;
59
+ const selected = i === sel;
60
+ const rb = selected ? t.selBg : bg;
61
+ c.fill(box.x, cy, box.w, 1, ' ', { bg: rb });
62
+ if (selected)
63
+ c.put(box.x, cy, '▎', { fg: t.accent, bg: rb, soft: true });
64
+ if (tab === 'plans') {
65
+ const r = rows[i];
66
+ // status colour: armed=up, proposed=muted, terminal/refused=down
67
+ const armed = r.status === 'armed' || r.status.startsWith('filled');
68
+ const dead = r.status === 'cancelled' || r.status === 'failed' || r.status === 'expired' || r.status === 'done';
69
+ const sfg = r.paused ? t.down : armed ? t.up : dead ? t.dim : t.muted;
70
+ c.text(box.x + 1, cy, r.id, { fg: selected ? t.borderFocus : t.text, bg: rb }, 20);
71
+ c.text(box.x + 22, cy, planStatusLabel(r), { fg: sfg, bg: rb }, box.w - 24);
72
+ }
73
+ else {
74
+ const r = rows[i];
75
+ const armed = r.status === 'armed';
76
+ const sfg = armed ? t.up : r.status === 'revoked' || r.status === 'expired' ? t.dim : t.muted;
77
+ c.text(box.x + 1, cy, r.id, { fg: selected ? t.borderFocus : t.text, bg: rb }, 22);
78
+ c.text(box.x + 24, cy, r.status.toUpperCase(), { fg: sfg, bg: rb }, 8);
79
+ const detail = `${fmtUsd(r.totalUsd)} tot · ${fmtUsd(r.reservedUsd)} rsv · ${fmtUsd(r.spentUsd)} spent · ${r.maxTier} · ${fmtTtl(r.expiresAt, now)}`;
80
+ c.text(box.x + 33, cy, detail, { fg: t.muted, bg: rb }, box.w - 35);
81
+ }
82
+ cy += 1;
83
+ }
84
+ // The hold-to-arm modal (captured by the loop; this just paints the prompt).
85
+ if (ui?.arming) {
86
+ const a = ui.arming;
87
+ const mw = Math.min(box.w - 4, 72), mh = 7;
88
+ const mx = box.x + Math.floor((box.w - mw) / 2), my = box.y + Math.floor((box.h - mh) / 2);
89
+ const mbox = c.box(mx, my, mw, mh, { title: '⚠ ARM — physical keypress', focus: true });
90
+ const label = a.kind === 'plan' ? `Arm plan ${a.id} into ${a.mandateId}?` : `Arm standing mandate ${a.id}?`;
91
+ c.textC(mbox.x, mbox.y, mbox.w, label, { fg: t.text, bg: mbox.bg, b: true });
92
+ c.textC(mbox.x, mbox.y + 1, mbox.w, a.kind === 'mandate' ? 'armed plans can fire at machine speed from the capped fast wallet' : 'the signer re-checks caps on every fill; voice/agents can never arm', { fg: t.muted, bg: mbox.bg });
93
+ c.textC(mbox.x, mbox.y + 3, mbox.w, 'Hold and release [enter] or [y] to confirm · [esc] cancels · a pipe/agent cannot arm', { fg: t.accent, bg: mbox.bg });
94
+ }
95
+ // The "less safe" hands-free opt-in modal (Tier 4): keypress-only, journaled, auto-expires 24h, reversible.
96
+ if (ui?.riskModal) {
97
+ const mw = Math.min(box.w - 4, 74), mh = 8;
98
+ const mx = box.x + Math.floor((box.w - mw) / 2), my = box.y + Math.floor((box.h - mh) / 2);
99
+ const mbox = c.box(mx, my, mw, mh, { title: '⚠ LESS SAFE — turn OFF the keypress gate', focus: true });
100
+ c.textC(mbox.x, mbox.y, mbox.w, risk.active ? 'Hands-free is ON. Restore the keypress gate?' : 'Allow hands-free (spoken) confirmation without a keypress?', { fg: t.text, bg: mbox.bg, b: true });
101
+ c.textC(mbox.x, mbox.y + 2, mbox.w, risk.active ? 'armed plans could then confirm by voice alone — restoring the gate is always safe' : 'armed plans could then fire from a spoken phrase, with NO physical keypress', { fg: risk.active ? t.muted : t.down, bg: mbox.bg });
102
+ c.textC(mbox.x, mbox.y + 3, mbox.w, 'signer caps, allow-lists and panic still apply · this auto-expires in 24h · logged to the journal', { fg: t.muted, bg: mbox.bg });
103
+ c.textC(mbox.x, mbox.y + 5, mbox.w, risk.active ? 'Hold+release [enter]/[y] to restore the gate · [esc] cancels' : 'Hold+release [enter]/[y] to accept (keypress only) · [esc] cancels · a pipe/agent cannot', { fg: t.accent, bg: mbox.bg });
104
+ }
105
+ // task-1363: the FIRE PATH modal (paper ⇄ live). Switched by a captured keypress in the loop; this paints the prompt.
106
+ if (ui?.liveModal) {
107
+ const mw = Math.min(box.w - 4, 78), mh = 9;
108
+ const mx = box.x + Math.floor((box.w - mw) / 2), my = box.y + Math.floor((box.h - mh) / 2);
109
+ const mbox = c.box(mx, my, mw, mh, { title: liveFire ? 'FIRE PATH — back to PAPER' : '⚠ FIRE PATH — go LIVE on DEVNET', focus: true });
110
+ c.textC(mbox.x, mbox.y, mbox.w, liveFire ? 'Switch plan fills back to the paper executor?' : 'Fire armed plans through the SIGNER from the capped fast wallet (devnet)?', { fg: t.text, bg: mbox.bg, b: true });
111
+ c.textC(mbox.x, mbox.y + 2, mbox.w, liveFire ? 'fills are then simulated again at the tick price; nothing on chain' : 'real devnet lamports move (fees of a wrap-and-unwrap sized to the plan); the token leg is modelled — Jupiter has no devnet', { fg: liveFire ? t.muted : t.down, bg: mbox.bg });
112
+ c.textC(mbox.x, mbox.y + 3, mbox.w, 'the signer re-checks policy, simulation, USD per-trade/daily caps and the 0.05 SOL per-fill ceiling on every fill', { fg: t.muted, bg: mbox.bg });
113
+ c.textC(mbox.x, mbox.y + 4, mbox.w, 'needs: a live run with the signer, `dexterm wallet fast create`, no armed/filled plans · never mainnet · journaled', { fg: t.muted, bg: mbox.bg });
114
+ c.textC(mbox.x, mbox.y + 6, mbox.w, 'Hold+release [enter]/[y] to switch (keypress only) · [esc] cancels · a pipe/agent/voice cannot', { fg: t.accent, bg: mbox.bg });
115
+ }
116
+ // task-1366: the selected plan's levels with BOTH market cap and price (display only; mc first in mcap mode)
117
+ if (tab === 'plans' && svc && !ui?.notice) {
118
+ const r = rows[sel];
119
+ const plan = r ? svc.engine.plan(r.id) : null;
120
+ if (plan)
121
+ c.text(box.x + 1, box.y + box.h - 1, planLevels(plan, priceModeOf(state)), { fg: t.muted, bg }, box.w - 2);
122
+ }
123
+ if (ui?.notice)
124
+ c.text(box.x + 1, box.y + box.h - 1, ui.notice, { fg: t.accent, bg });
125
+ footer(c, [['↑↓', 'move'], ['tab', 'plans/mandates'], ['a', 'arm (hold)'], tab === 'plans' ? ['c', 'cancel'] : ['r', 'revoke'], ['L', liveFire ? 'paper' : 'live (devnet)'], ['G', risk.active ? 'gate on' : 'hands-free'], ['!', 'PANIC'], ['esc', 'back'], ['q', 'quit']], t.name);
126
+ }
@@ -0,0 +1,176 @@
1
+ // dexterm — PAPER positions screen (task-1310 T0, SPEC §6.1 "book"). The simulated wallet: balance,
2
+ // equity, open positions with live PnL (marked from the keyless price feed) and the trade history.
3
+ // Labelled PAPER on every frame; nothing here signs, holds a key or sends anything.
4
+ import { Canvas, header, footer, breakpoint, mix, isCalm, fmtPriceBare, fmtMcap, fmtNum, fmtPct, fmtAgeSec, shortAddr, } from '../../../tui/src/index.js';
5
+ import { lamportsToSol, rawToTokens, positionPnl } from '../../../data/src/index.js';
6
+ import { headerModel } from "../state.js";
7
+ import { priceModeOf, supplyRatio, FALLBACK_MARK } from "../display.js";
8
+ export function sol(n, digits = 4) {
9
+ return `${n >= 0 ? '' : '-'}${Math.abs(n).toFixed(digits)}`;
10
+ }
11
+ export function signedSol(n, digits = 4) {
12
+ return `${n >= 0 ? '+' : '-'}${Math.abs(n).toFixed(digits)}`;
13
+ }
14
+ /**
15
+ * USD for position-sized amounts (task-1323): cents below $1K (`$192.50`), compact above (`$12.3K`).
16
+ * `fmtUsd` rounds small values to whole dollars, which hides a paper PnL of a few dollars.
17
+ */
18
+ export function fmtUsdFine(n) {
19
+ const a = Math.abs(n);
20
+ const sign = n < 0 ? '-' : '';
21
+ if (a >= 1e9)
22
+ return `${sign}$${(a / 1e9).toFixed(2)}B`;
23
+ if (a >= 1e6)
24
+ return `${sign}$${(a / 1e6).toFixed(2)}M`;
25
+ if (a >= 1e3)
26
+ return `${sign}$${(a / 1e3).toFixed(a >= 1e4 ? 1 : 2)}K`;
27
+ return `${sign}$${a.toFixed(2)}`;
28
+ }
29
+ /** Signed USD: `+$42.00` / `-$3.10` (zero reads `+$0.00`). */
30
+ export function signedUsd(n) {
31
+ return `${n >= 0 ? '+' : '-'}${fmtUsdFine(Math.abs(n))}`;
32
+ }
33
+ /** Signed percent of a fraction: 0.28 → `+28.0%`, -0.082 → `-8.2%`. */
34
+ export function signedPctOf(frac, digits = 1) {
35
+ const p = frac * 100;
36
+ const a = Math.abs(p);
37
+ return `${p >= 0 ? '+' : '-'}${a.toFixed(a >= 1000 ? 0 : digits)}%`;
38
+ }
39
+ export function renderPositions(c, state) {
40
+ const t = c.theme;
41
+ header(c, headerModel(state));
42
+ const bp = breakpoint(c.w);
43
+ const v = (state.live ? state.live.positions.view() : state.positions) ?? null;
44
+ const top = 2;
45
+ c.fill(0, top, c.w, 1, ' ', { bg: mix(t.bg, t.warn, 0.16) });
46
+ c.text(2, top, ' ◈ PAPER PORTFOLIO — simulated wallet · no real funds, keys or signing ', { fg: t.warn, bg: mix(t.bg, t.warn, 0.16), b: true });
47
+ const linked = state.live?.linkedWallet;
48
+ if (linked)
49
+ c.textR(2, top, c.w - 3, `linked: ${linked.publicKey.slice(0, 4)}…${linked.publicKey.slice(-4)} `, { fg: t.muted, bg: mix(t.bg, t.warn, 0.16) });
50
+ c.noRain(0, top, c.w, 1);
51
+ if (!v) {
52
+ c.text(2, 4, 'No paper wallet in this mode. Run live to paper-trade (B/S on a token).', { fg: t.muted, bg: t.bg });
53
+ footer(c, [['esc', 'back'], ['q', 'quit']], t.name, isCalm(state.vfx));
54
+ return;
55
+ }
56
+ // summary strip
57
+ let y = 4;
58
+ const totalColor = v.totalPct === null ? t.muted : v.totalPct >= 0 ? t.up : t.down;
59
+ let x = c.text(2, y, 'Equity', { fg: t.dim, bg: t.bg });
60
+ x = c.text(x + 1, y, `${sol(v.equitySol)} SOL`, { fg: t.text, bg: t.bg, b: true });
61
+ x = c.text(x + 3, y, 'Cash', { fg: t.dim, bg: t.bg });
62
+ x = c.text(x + 1, y, `${sol(v.balanceSol)} SOL`, { fg: t.muted, bg: t.bg });
63
+ x = c.text(x + 3, y, 'Start', { fg: t.dim, bg: t.bg });
64
+ x = c.text(x + 1, y, `${sol(v.startSol)} SOL`, { fg: t.muted, bg: t.bg });
65
+ x = c.text(x + 3, y, 'Total PnL', { fg: t.dim, bg: t.bg });
66
+ x = c.text(x + 1, y, `${signedSol(v.equitySol - v.startSol)} SOL${v.totalPct !== null ? ` (${fmtPct(v.totalPct * 100)})` : ''}`, { fg: totalColor, bg: t.bg, b: true });
67
+ if (v.solPriceUsd)
68
+ c.textR(2, y, c.w - 3, `SOL $${v.solPriceUsd.toFixed(2)}${v.loading ? ' · marking…' : ''}`, { fg: t.dim, bg: t.bg });
69
+ y += 1;
70
+ c.text(2, y, `realized ${signedSol(v.totalRealizedSol)} · unrealized ${signedSol(v.totalUnrealizedSol)} SOL`, { fg: t.dim, bg: t.bg });
71
+ y += 2;
72
+ // positions table
73
+ const bottom = c.h - 1;
74
+ const half = Math.max(8, Math.floor((bottom - y) * 0.5));
75
+ // task-1366: mcap mode reads AVG / PRICE as market cap = SOL price × SOL/USD × the token's CURRENT supply (from the
76
+ // screener rows); a token without a supply (or no SOL/USD yet) keeps its SOL price, marked ᵖ. PnL stays SOL.
77
+ const mode = priceModeOf(state);
78
+ const supplyOf = (mint) => { const row = state.screener.rows.find((x) => x.mint === mint); return row ? supplyRatio(row.price, row.mcap) : null; };
79
+ const mcCell = (pSol, mint) => {
80
+ if (pSol === null)
81
+ return '—';
82
+ if (mode !== 'mcap')
83
+ return fmtPriceBare(pSol);
84
+ const s = supplyOf(mint);
85
+ return s !== null && v.solPriceUsd ? fmtMcap(pSol * v.solPriceUsd * s) : fmtPriceBare(pSol) + FALLBACK_MARK;
86
+ };
87
+ const pb = c.box(0, y, c.w, half, { title: 'POSITIONS', caption: mode === 'mcap' ? 'marked from keyless price feed · mcap at current supply' : 'marked from keyless price feed' });
88
+ let py = pb.y;
89
+ const cols = bp === 'compact'
90
+ ? [['TOKEN', 16], ['AMOUNT', 16], ['VALUE', 14], ['uPnL', 16]]
91
+ : [['TOKEN', 20], ['AMOUNT', 18], [mode === 'mcap' ? 'AVG MC' : 'AVG', 14], [mode === 'mcap' ? 'MCAP' : 'PRICE', 14], ['VALUE', 14], ['uPnL SOL', 18], ['REALIZED', 14]];
92
+ let hx = pb.x + 1;
93
+ for (const [label, wdt] of cols) {
94
+ c.text(hx, py, label, { fg: t.dim, bg: pb.bg }, wdt);
95
+ hx += wdt;
96
+ }
97
+ py += 1;
98
+ const open = v.rows.filter((r) => r.tokens > 0);
99
+ if (open.length === 0) {
100
+ c.text(pb.x + 1, py, 'no open positions — B paper-buy on a token', { fg: t.dim, bg: pb.bg });
101
+ }
102
+ for (const r of open) {
103
+ if (py >= pb.y + pb.h - 1)
104
+ break;
105
+ const uColor = r.unrealizedSol === null ? t.muted : r.unrealizedSol >= 0 ? t.up : t.down;
106
+ let cx = pb.x + 1;
107
+ cx = c.text(cx, py, `$${r.symbol}`, { fg: t.text, bg: pb.bg, b: true }, cols[0][1]);
108
+ cx = pb.x + 1 + cols[0][1];
109
+ c.text(cx, py, fmtNum(r.tokens), { fg: t.muted, bg: pb.bg }, cols[1][1] - 1);
110
+ cx += cols[1][1];
111
+ if (!bp || bp !== 'compact') {
112
+ c.text(cx, py, mcCell(r.avgEntrySol, r.mint), { fg: t.dim, bg: pb.bg }, cols[2][1] - 1);
113
+ cx += cols[2][1];
114
+ c.text(cx, py, mcCell(r.priceSol, r.mint), { fg: t.muted, bg: pb.bg }, cols[3][1] - 1);
115
+ cx += cols[3][1];
116
+ c.text(cx, py, r.valueSol !== null ? sol(r.valueSol) : '—', { fg: t.text, bg: pb.bg }, cols[4][1] - 1);
117
+ cx += cols[4][1];
118
+ c.text(cx, py, r.unrealizedSol !== null ? `${signedSol(r.unrealizedSol)}${r.unrealizedPct !== null ? ` ${fmtPct(r.unrealizedPct * 100)}` : ''}` : '—', { fg: uColor, bg: pb.bg, b: true }, cols[5][1] - 1);
119
+ cx += cols[5][1];
120
+ c.text(cx, py, signedSol(r.realizedSol), { fg: r.realizedSol >= 0 ? t.muted : t.down, bg: pb.bg }, cols[6][1] - 1);
121
+ }
122
+ else {
123
+ c.text(cx, py, r.valueSol !== null ? sol(r.valueSol) : '—', { fg: t.text, bg: pb.bg }, cols[2][1] - 1);
124
+ cx += cols[2][1];
125
+ c.text(cx, py, r.unrealizedSol !== null ? signedSol(r.unrealizedSol) : '—', { fg: uColor, bg: pb.bg, b: true }, cols[3][1] - 1);
126
+ }
127
+ py += 1;
128
+ }
129
+ // task-1328: LIVE positions (the signer's ledger file, read-only), only when there are any.
130
+ const liveSrc = state.live?.livePositions ?? null;
131
+ const livePos = liveSrc ? liveSrc.positions().filter((p) => p.tokensRaw > 0n) : [];
132
+ y += half;
133
+ if (livePos.length > 0) {
134
+ const lh = Math.min(livePos.length + 3, Math.max(5, Math.floor((bottom - y) * 0.4)));
135
+ const lb = c.box(0, y, c.w, lh, { title: '◉ LIVE POSITIONS', titleFg: t.up, caption: 'confirmed on-chain fills · signer ledger' });
136
+ let ly = lb.y;
137
+ for (const p of livePos) {
138
+ if (ly >= lb.y + lb.h)
139
+ break;
140
+ const mark = liveSrc?.held(p.mint)?.markSol ?? null;
141
+ const pnl = positionPnl(p, mark);
142
+ let cx = c.text(lb.x + 1, ly, `$${p.symbol}`, { fg: t.text, bg: lb.bg, b: true });
143
+ cx = c.text(cx + 2, ly, fmtNum(rawToTokens(p.tokensRaw, p.decimals)), { fg: t.muted, bg: lb.bg });
144
+ cx = c.text(cx + 2, ly, `cost ${sol(pnl.costSol)} SOL`, { fg: t.dim, bg: lb.bg });
145
+ if (pnl.avgEntrySol !== null)
146
+ cx = c.text(cx + 2, ly, `avg ${fmtPriceBare(pnl.avgEntrySol)}`, { fg: t.dim, bg: lb.bg });
147
+ if (pnl.unrealizedSol !== null)
148
+ c.text(cx + 2, ly, `uPnL ${signedSol(pnl.unrealizedSol)} SOL${pnl.unrealizedPct !== null ? ` ${fmtPct(pnl.unrealizedPct * 100)}` : ''}`, { fg: pnl.unrealizedSol >= 0 ? t.up : t.down, bg: lb.bg, b: true });
149
+ c.textR(lb.x + 1, ly, lb.w - 2, `${shortAddr(p.mint, 4)} · LIVE`, { fg: t.up, bg: lb.bg });
150
+ ly += 1;
151
+ }
152
+ y += lh;
153
+ }
154
+ // history
155
+ const hb = c.box(0, y, c.w, bottom - y, { title: 'TRADE HISTORY', caption: `${v.history.length} paper fills${liveSrc ? ` · ${liveSrc.records(50).length} live` : ''}` });
156
+ let yy = hb.y;
157
+ for (const h of v.history) {
158
+ if (yy >= hb.y + hb.h)
159
+ break;
160
+ const sideColor = h.side === 'buy' ? t.up : t.down;
161
+ const gotTokens = h.side === 'buy' ? rawToTokens(h.tokenDelta, h.decimals) : rawToTokens(-h.tokenDelta, h.decimals);
162
+ const solAmt = lamportsToSol(h.solDelta);
163
+ let cx = c.text(hb.x + 1, yy, fmtAgeSec(Math.max(0, Math.round((state.now.getTime() - h.t) / 1000))).padStart(4), { fg: t.dim, bg: hb.bg });
164
+ cx = c.text(cx + 1, yy, h.side === 'buy' ? '▲ BUY ' : '▼ SELL', { fg: sideColor, bg: hb.bg, b: true });
165
+ cx = c.text(cx + 1, yy, `$${h.symbol}`, { fg: t.text, bg: hb.bg, b: true });
166
+ cx = c.text(cx + 1, yy, `${fmtNum(gotTokens)} @ ${fmtPriceBare(h.priceSol)} SOL`, { fg: t.muted, bg: hb.bg });
167
+ cx = c.text(cx + 2, yy, `${signedSol(solAmt)} SOL`, { fg: solAmt >= 0 ? t.up : t.muted, bg: hb.bg });
168
+ if (h.side === 'sell')
169
+ c.text(cx + 2, yy, `pnl ${signedSol(lamportsToSol(h.realizedLamports))}`, { fg: h.realizedLamports >= 0n ? t.up : t.down, bg: hb.bg });
170
+ c.textR(hb.x + 1, yy, hb.w - 2, `${shortAddr(h.mint, 4)} · PAPER`, { fg: t.dim, bg: hb.bg });
171
+ yy += 1;
172
+ }
173
+ if (v.history.length === 0)
174
+ c.text(hb.x + 1, hb.y, 'no paper trades yet', { fg: t.dim, bg: hb.bg });
175
+ footer(c, [['esc', 'back'], ['r', 'refresh'], ['1-4', 'screens'], ['q', 'quit']], t.name, isCalm(state.vfx));
176
+ }