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,75 @@
1
+ // dexterm popup — agent roster page (task-1345). Renders the RosterVM: connected agents with tier, last
2
+ // activity, reads/proposals, rate usage, and the audit log, with per-agent pause and kick. Every untrusted
3
+ // string (agent name, client label, audit detail) is rendered with textContent only — never innerHTML. The
4
+ // only messages this page sends are the allow-listed roster.pause / roster.resume / roster.kick events; the
5
+ // page can never arm, sign, change a cap, or touch a wallet (no such control exists and the server drops
6
+ // anything off the allow-list).
7
+ (() => {
8
+ 'use strict';
9
+ const DT = window.DT; DT.page = 'roster';
10
+ const $ = (id) => document.getElementById(id);
11
+ const el = (tag, cls, text) => { const e = document.createElement(tag); if (cls) e.className = cls; if (text != null) e.textContent = String(text); return e; };
12
+ const ago = (t) => { if (!t) return '—'; const s = Math.max(0, Math.round((Date.now() - t) / 1000)); return s < 60 ? `${s}s` : s < 3600 ? `${Math.floor(s / 60)}m` : `${Math.floor(s / 3600)}h`; };
13
+ const TIER_LABEL = { observe: 'observe', annotate: 'annotate', propose: 'propose' };
14
+
15
+ let vm = null;
16
+
17
+ function render() {
18
+ if (!vm) return;
19
+ const sum = $('summary'); sum.textContent = '';
20
+ const live = vm.agents.length;
21
+ sum.appendChild(el('span', 'roster-k', `${live} connected`));
22
+ sum.appendChild(el('span', 'roster-dim', ` · bus ws://127.0.0.1:${Number(vm.busPort) || 0} · untrusted by default`));
23
+
24
+ const host = $('agents'); host.textContent = '';
25
+ if (!vm.agents.length) host.appendChild(el('div', 'roster-dim', 'no agents connected. register one: dexterm agent add <name> --tier observe'));
26
+ for (const a of vm.agents) {
27
+ const row = el('div', 'roster-row' + (a.paused ? ' paused' : ''));
28
+ const head = el('div', 'roster-row-head');
29
+ head.appendChild(el('span', 'roster-name', a.name)); // textContent — untrusted name is safe
30
+ head.appendChild(el('span', 'roster-tier tier-' + a.tier, TIER_LABEL[a.tier] || a.tier));
31
+ if (a.paused) head.appendChild(el('span', 'roster-badge', a.floodPausedUntil ? 'flood-paused' : 'paused'));
32
+ head.appendChild(el('span', 'roster-dim', ` ${a.sessions} session${a.sessions === 1 ? '' : 's'} · ${a.client || 'unknown'} · active ${ago(a.lastActivity)}`));
33
+ row.appendChild(head);
34
+
35
+ const stats = el('div', 'roster-stats');
36
+ stats.appendChild(el('span', 'roster-stat', `reads ${a.reads}/${a.readsLimit}·min`));
37
+ stats.appendChild(el('span', 'roster-stat', `proposals ${a.proposals}/${a.proposalsLimit}·h`));
38
+ stats.appendChild(el('span', 'roster-stat' + (a.refused ? ' bad' : ''), `refused ${a.refused}`));
39
+ if (a.topics && a.topics.length) stats.appendChild(el('span', 'roster-stat', `subs: ${a.topics.join(',')}`));
40
+ row.appendChild(stats);
41
+
42
+ const ctl = el('div', 'roster-ctl');
43
+ const pause = el('button', 'roster-btn', a.paused ? 'resume' : 'pause'); pause.type = 'button';
44
+ pause.addEventListener('click', () => DT.send({ type: 'event', name: a.paused ? 'roster.resume' : 'roster.pause', payload: { agent: a.name } }));
45
+ const kick = el('button', 'roster-btn danger', 'kick'); kick.type = 'button';
46
+ kick.addEventListener('click', () => DT.send({ type: 'event', name: 'roster.kick', payload: { agent: a.name } }));
47
+ ctl.appendChild(pause); ctl.appendChild(kick);
48
+ row.appendChild(ctl);
49
+ host.appendChild(row);
50
+ }
51
+
52
+ const audit = $('audit'); audit.textContent = '';
53
+ for (const r of (vm.audit || []).slice(0, 80)) {
54
+ const line = el('div', 'roster-audit-row' + (r.ok ? '' : ' bad'));
55
+ line.appendChild(el('span', 'roster-audit-t', ago(r.t)));
56
+ line.appendChild(el('span', 'roster-audit-agent', r.agent));
57
+ line.appendChild(el('span', 'roster-audit-kind', r.kind));
58
+ line.appendChild(el('span', 'roster-audit-tool', r.tool));
59
+ line.appendChild(el('span', 'roster-audit-detail', r.detail)); // textContent — firewalled upstream, safe here
60
+ audit.appendChild(line);
61
+ }
62
+ DT.status(`${live} agent${live === 1 ? '' : 's'}`, live ? 'up' : 'muted');
63
+ }
64
+
65
+ DT.onHello(() => { DT.mountRain('rain'); const r = DT.rain(); if (r) { r.setActivity(0.25); r.setMood('rain'); } });
66
+ DT.onData((payload) => { vm = payload; render(); });
67
+ DT.onUpdate(() => { /* roster is pushed as full `data` frames; nothing incremental */ });
68
+ DT.wireChrome();
69
+ // A slow rAF loop keeps the rain alive and refreshes relative times once a second; the roster content
70
+ // itself only changes when a new `data` frame arrives.
71
+ let lastTick = 0;
72
+ DT.rainEvery = 2; DT.fpsCap = 30;
73
+ DT.loop((now) => { if (now - lastTick > 1000) { lastTick = now; if (vm) render(); } });
74
+ DT.connect();
75
+ })();
@@ -0,0 +1,20 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="dexterm-ws" content="__WS_TOKEN__">
6
+ <meta name="dexterm-popup" content="__POPUP_ID__">
7
+ <meta name="dexterm-port" content="__PORT__">
8
+ <title>dexterm · token</title>
9
+ <style nonce="__NONCE__">/*__CSS__*/</style>
10
+ </head>
11
+ <body>
12
+ <div id="hud" class="hud">
13
+ <div id="drag" class="drag"><span class="mark">✦</span><span id="title" class="title">dexterm · TOKEN</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
14
+ <div class="stage"><canvas id="rain"></canvas><div class="body"><div id="rows" class="rows"></div></div></div>
15
+ <div id="foot" class="foot"><span>token detail</span><span>esc close</span><span id="fps" class="fps">— fps</span></div>
16
+ </div>
17
+ <script nonce="__NONCE__">/*__COMMON_JS__*/</script>
18
+ <script nonce="__NONCE__">/*__JS__*/</script>
19
+ </body>
20
+ </html>
@@ -0,0 +1,112 @@
1
+ // dexterm popup — TOKEN DETAIL HUD (key/value table over full-panel rain; task-1290).
2
+ // Inlined after common.js under a CSP nonce. No remote content, no innerHTML: every untrusted string
3
+ // (symbol, name, mint, risk/flag text) is rendered with textContent only. Data is a TokenDetailVM over the WS.
4
+ (() => {
5
+ 'use strict';
6
+ const DT = window.DT; DT.page = 'token';
7
+ const $ = (id) => document.getElementById(id);
8
+ const titleEl = $('title'), rowsEl = $('rows');
9
+ const rain = DT.mountRain('rain');
10
+ let vm = null;
11
+
12
+ function fmtPrice(p) {
13
+ if (p == null) return '—';
14
+ if (p >= 1) return '$' + p.toFixed(3);
15
+ if (p >= 0.0001) return '$' + p.toFixed(6);
16
+ return '$' + p.toExponential(2);
17
+ }
18
+ // task-1325 fix: TokenView.chg is already in percent (DexScreener priceChange), not a fraction.
19
+ function fmtPct(x) { return x == null ? '—' : (x >= 0 ? '+' : '') + x.toFixed(Math.abs(x) >= 1000 ? 0 : 1) + '%'; }
20
+ function fmtUsd(x) {
21
+ if (x == null) return '—';
22
+ const a = Math.abs(x);
23
+ if (a >= 1e9) return '$' + (x / 1e9).toFixed(2) + 'B';
24
+ if (a >= 1e6) return '$' + (x / 1e6).toFixed(2) + 'M';
25
+ if (a >= 1e3) return '$' + (x / 1e3).toFixed(1) + 'K';
26
+ return '$' + x.toFixed(0);
27
+ }
28
+ const fmtNum = (x) => x == null ? '—' : x.toLocaleString('en-US');
29
+ const fmtPctRaw = (x) => x == null ? '—' : x.toFixed(1) + '%';
30
+
31
+ function resize() { if (rain) rain.resize(window.innerWidth, window.innerHeight); }
32
+ window.addEventListener('resize', resize);
33
+
34
+ // ---- row builders: every value is textContent only.
35
+ function clear(el) { while (el.firstChild) el.removeChild(el.firstChild); }
36
+ function addRow(label, value, cls) {
37
+ const row = document.createElement('div'); row.className = 'row';
38
+ const k = document.createElement('div'); k.className = 'k'; k.textContent = label;
39
+ const v = document.createElement('div'); v.className = 'v' + (cls ? ' ' + cls : ''); v.textContent = value == null ? '—' : String(value);
40
+ row.appendChild(k); row.appendChild(v); rowsEl.appendChild(row);
41
+ }
42
+ const chgCls = (x) => x == null ? 'muted' : x >= 0 ? 'up' : 'down';
43
+ const yesNoCls = (b) => b === true ? 'warn' : b === false ? 'up' : 'muted';
44
+ const yesNo = (b) => b == null ? '—' : b ? 'yes' : 'no';
45
+
46
+ function render() {
47
+ clear(rowsEl);
48
+ if (!vm) { addRow('', 'waiting for data…', 'muted'); return; }
49
+ const chg = vm.chg || {};
50
+ addRow('token', `${vm.symbol ? '$' + String(vm.symbol).replace(/^\$/, '') : '?'} ${vm.name || ''}`.trim());
51
+ if (vm.launchpad) addRow('launchpad', vm.launchpad, 'muted');
52
+ if (vm.mint) addRow('mint', DT.shortAddr(vm.mint, 6), 'muted');
53
+ if (vm.mint) addRow('', vm.mint, 'muted');
54
+ addRow('price', fmtPrice(vm.price));
55
+ addRow('24h', fmtPct(chg.h24), chgCls(chg.h24));
56
+ addRow('6h', fmtPct(chg.h6), chgCls(chg.h6));
57
+ addRow('1h', fmtPct(chg.h1), chgCls(chg.h1));
58
+ addRow('5m', fmtPct(chg.m5), chgCls(chg.m5));
59
+ addRow('mcap', fmtUsd(vm.mcap));
60
+ addRow('fdv', fmtUsd(vm.fdv));
61
+ addRow('liq', fmtUsd(vm.liq));
62
+ addRow('vol 24h', fmtUsd(vm.vol24));
63
+ addRow('holders', fmtNum(vm.holders));
64
+ if (vm.holdersSample) addRow('', vm.holdersSample, 'muted');
65
+ addRow('top 10', fmtPctRaw(vm.top10), vm.top10 != null && vm.top10 >= 40 ? 'warn' : null);
66
+ addRow('dev', fmtPctRaw(vm.dev), vm.dev != null && vm.dev >= 10 ? 'warn' : null);
67
+ addRow('lp locked', fmtPctRaw(vm.lpLocked), vm.lpLocked != null && vm.lpLocked < 50 ? 'warn' : 'up');
68
+ addRow('mint auth', yesNo(vm.mintAuth), yesNoCls(vm.mintAuth));
69
+ addRow('freeze auth', yesNo(vm.freezeAuth), yesNoCls(vm.freezeAuth));
70
+ // task-1359: snipers / bundled from the wallet-intel scan (a reason instead of a number when not measured)
71
+ if (vm.intel) {
72
+ const lv = (l) => l === 'bad' ? 'down' : l === 'warn' ? 'warn' : l === 'ok' ? 'up' : 'muted';
73
+ addRow('snipers', vm.intel.snipers, lv(vm.intel.snipersLevel));
74
+ addRow('bundled', vm.intel.bundle, lv(vm.intel.bundleLevel));
75
+ }
76
+ addRow('rug score', vm.rugScore == null ? '—' : String(vm.rugScore), vm.rugScore != null && vm.rugScore >= 50 ? 'warn' : vm.rugScore != null ? 'up' : 'muted');
77
+ if (Array.isArray(vm.risks) && vm.risks.length) {
78
+ for (const r of vm.risks) {
79
+ const lvl = r && r.level ? String(r.level) : '';
80
+ const cls = /high|danger|crit/i.test(lvl) ? 'down' : /med|warn/i.test(lvl) ? 'warn' : 'muted';
81
+ addRow('risk', `${r && r.name ? r.name : ''}${lvl ? ' (' + lvl + ')' : ''}`.trim(), cls);
82
+ }
83
+ }
84
+ if (Array.isArray(vm.flags) && vm.flags.length) {
85
+ for (const f of vm.flags) addRow('flag', String(f), 'muted');
86
+ }
87
+ if (vm.updatedAt) addRow('updated', new Date(vm.updatedAt).toISOString().slice(11, 19), 'muted');
88
+ }
89
+
90
+ function setData(a) {
91
+ vm = a;
92
+ const sym = String(vm.symbol || '?').replace(/^\$/, '');
93
+ titleEl.textContent = `dexterm · $${sym} · TOKEN`;
94
+ document.title = `dexterm · ${sym}`;
95
+ const dir = (vm.chg && (vm.chg.h24 ?? vm.chg.h1)) || 0;
96
+ if (rain) {
97
+ rain.setMood(dir > 0.01 ? 'rainPump' : dir < -0.01 ? 'rainDump' : 'rain');
98
+ rain.setActivity(Math.min(1, 0.4 + Math.abs(dir)));
99
+ rain.setSources([sym, String(vm.mint || '').slice(0, 6)]);
100
+ }
101
+ DT.status(vm.price != null ? fmtPrice(vm.price) : 'no price', dir >= 0 ? 'up' : 'down');
102
+ render();
103
+ }
104
+
105
+ DT.onData(setData);
106
+ DT.onTheme(render);
107
+ DT.wireChrome();
108
+ resize();
109
+ render();
110
+ DT.connect();
111
+ DT.loop(() => {});
112
+ })();
@@ -0,0 +1,224 @@
1
+ // dexterm popup — PACK WIDGET SDK (task-1358 Tier 5, r2, r3, r4). Inlined by the popup server into a pack's widget page,
2
+ // ahead of the pack's own markup, and allow-listed in the page's CSP by its sha256 (a widget page carries no nonce;
3
+ // every inline block, this one included, runs by hash). It is the FIRST script of the frame document and it does two
4
+ // things: it seals the DOM (r4), then it defines ONE frozen global, `window.dexterm`:
5
+ //
6
+ // dexterm.on(topic, fn) fn(payload) on every push of a topic the widget declared (rows · token · narrative ·
7
+ // positions · alerts · theme); the current value is replayed to a late listener
8
+ // dexterm.off(topic, fn)
9
+ // dexterm.theme the terminal's theme tokens (also set as CSS variables: --bg --panel --border
10
+ // --borderFocus --text --muted --dim --accent --up --down --warn --ai --pump --dbc)
11
+ // dexterm.topics the topics this widget subscribed to (from its widget.json)
12
+ // dexterm.key '<pack>/<widget>[/<id>]'
13
+ // dexterm.close() ask the terminal to close this window (Esc does the same)
14
+ // dexterm.log(text) a line in the terminal's popup log (sanitised there, 200 chars)
15
+ //
16
+ // That is the whole API. (r3) The page is the document inside the host's wrapper iframe: it has NO network of its own
17
+ // (connect-src 'none' — no socket, no fetch, nothing), an opaque origin (sandbox without allow-same-origin: no cookies,
18
+ // no storage, no reach into the wrapper) and may only be shown inside the host's own origin. View models arrive from the
19
+ // wrapper by postMessage (accepted only from the parent window AND the host origin named in <head>); the only messages
20
+ // this page can send are ready / log / close, and only to that origin. No imports or remote scripts (script-src is the
21
+ // hash list and nothing else), no eval, no inline handlers, no remote images or fonts, no runtime script injection
22
+ // (Trusted Types), no popups, forms or downloads (sandbox), and no navigating away: the wrapper's frame-src names the one
23
+ // document this frame may show, the engine refuses any other, and the wrapper then closes the widget.
24
+ //
25
+ // (r4) THE DOM SEAL. One thing no CSP directive covers: a `<link rel=preconnect|dns-prefetch>` built at run time makes
26
+ // WebKit resolve a name and open a bare TCP connection to a host:port the page chose (nothing is fetched, so no
27
+ // directive applies; Trusted Types do not see it; the install-time audit cannot, the element does not exist yet). The
28
+ // page cannot obtain a second realm (nested frames are created but sealed off from it by the sandbox; every HTML-parsing
29
+ // sink is a Trusted Types sink), so this script — first to run, hash-listed — replaces the DOM's insertion methods on
30
+ // the prototypes with non-configurable, non-writable guards that refuse any node whose subtree holds a LOADER element
31
+ // (link · meta · base · iframe · frame · frameset · object · embed · applet · portal · fencedframe · script), makes the
32
+ // HTML-string sinks throw outright (innerHTML / outerHTML setters, insertAdjacentHTML, document.write, setHTML,
33
+ // setHTMLUnsafe, createContextualFragment, DOMParser) so the rule holds on an engine without Trusted Types too, and
34
+ // removes WebRTC (the other connection CSP has no directive for). The natives live only in this closure: a pack script
35
+ // runs after it and cannot get them back. (r5, after the fourth verifier) The STATIC parsers (`Document.parseHTMLUnsafe`,
36
+ // `Document.parseHTML`) and `document.execCommand` throw too; `adoptNode` / `importNode` and the element-taking setters
37
+ // (`table.caption` / `tHead` / `tFoot`, `document.body`, `select.add`, `options.add`) refuse a loader subtree at the door —
38
+ // the system WKWebView on macOS 14/15 (Safari < 26) has NO Trusted Types, so this seal must be complete on its own; a
39
+ // MutationObserver removes any loader element that ever appears and tells the wrapper, which closes the widget (after the
40
+ // fact: a belt, not the control), and `ready` reports whether Trusted Types exist. (r6, after the fifth verifier) A click
41
+ // on a link that targets ANOTHER window (`<a target=_blank>`) makes WebKit open a bare TCP connection to the link's
42
+ // host:port even though the sandbox refuses the popup — an `<a>` is not a loader, so the seal never saw it. Now a
43
+ // capture-phase listener on `window`, registered before any pack script exists (it runs first and nothing can remove
44
+ // it), cancels every click / auxclick / submit whose path holds a link with a target other than `_self`, a `download`
45
+ // or `ping`, or a form; `attachShadow` is forced open so a closed shadow root cannot hide such a link from the guard's
46
+ // composedPath; the belt treats a `<meta>` as a breach only when it carries `http-equiv`. Render untrusted strings
47
+ // (symbols, names, alert text) with textContent. Nothing in a view model names a key, a signer, a cap, a mandate or the gate.
48
+ (() => {
49
+ 'use strict';
50
+ let sealBreach = null, sealNote = null; // set once the SDK can talk to the wrapper
51
+ // ------------------------------------------------------------------ the DOM seal (r4, r5)
52
+ (() => {
53
+ const LOADERS = new Set(['link', 'meta', 'base', 'iframe', 'frame', 'frameset', 'object', 'embed', 'applet', 'portal', 'fencedframe', 'script']);
54
+ const SEL = [...LOADERS].join(',');
55
+ const N = Node.prototype, E = Element.prototype, D = Document.prototype, F = DocumentFragment.prototype, CD = CharacterData.prototype, DT = DocumentType.prototype;
56
+ const desc = (o, k) => Object.getOwnPropertyDescriptor(o, k);
57
+ const nodeType = desc(N, 'nodeType').get, localName = desc(E, 'localName').get;
58
+ const qsaE = E.querySelectorAll, qsaF = F.querySelectorAll, hasAttribute = E.hasAttribute, getAttribute = E.getAttribute;
59
+ const apply = Reflect.apply;
60
+ const refuse = (what) => { throw new DOMException(`dexterm widget: ${what} is sealed (loaders, nested documents and HTML-string sinks are refused in a pack page; see WIDGETS.md)`, 'NotAllowedError'); };
61
+ // the loader element (local name) a node carries, or null; a non-node is left to the native to reject
62
+ const findIn = (n, set, sel) => {
63
+ if (!n || typeof n !== 'object') return null;
64
+ let t; try { t = apply(nodeType, n, []); } catch { return null; }
65
+ if (t === 1) { const ln = apply(localName, n, []); if (set.has(ln)) return ln; const hit = apply(qsaE, n, [sel]); return hit.length ? apply(localName, hit[0], []) : null; }
66
+ if (t === 11) { const hit = apply(qsaF, n, [sel]); return hit.length ? apply(localName, hit[0], []) : null; }
67
+ return null;
68
+ };
69
+ const loaderIn = (n) => findIn(n, LOADERS, SEL);
70
+ // the kill switch watches the parser too, and the pack's own (hashed) <script> elements arrive through the parser: a
71
+ // runtime <script> has no hash (CSP refuses it) and the insertion guards refuse it anyway, so the watch excludes script
72
+ // (r6) a plain <meta> the parser reaches after this script (a page without </head>, or one in <body>) is harmless — the
73
+ // audit refuses <meta http-equiv> and so does the watch; any other meta is not a breach
74
+ const WATCHED = new Set([...LOADERS].filter((x) => x !== 'script' && x !== 'meta')), WATCH_SEL = [...WATCHED, 'meta[http-equiv]'].join(',');
75
+ const breachIn = (n) => {
76
+ const w = findIn(n, WATCHED, WATCH_SEL); if (w) return w;
77
+ try { if (apply(nodeType, n, []) === 1 && apply(localName, n, []) === 'meta' && apply(hasAttribute, n, ['http-equiv'])) return 'meta'; } catch { /* not an element */ }
78
+ return null;
79
+ };
80
+ const lock = (proto, name, value) => Object.defineProperty(proto, name, { value, writable: false, configurable: false, enumerable: true });
81
+ // which argument(s) a method inserts: 0 = the first, 1 = the second, -1 = all of them
82
+ const guard = (proto, name, which) => {
83
+ const d = desc(proto, name); if (!d || typeof d.value !== 'function') return;
84
+ const native = d.value;
85
+ lock(proto, name, function (...args) {
86
+ if (which === -1) { for (const a of args) { const w = loaderIn(a); if (w) refuse(`inserting <${w}>`); } }
87
+ else { const w = loaderIn(args[which]); if (w) refuse(`inserting <${w}>`); }
88
+ return apply(native, this, args);
89
+ });
90
+ };
91
+ guard(N, 'appendChild', 0); guard(N, 'insertBefore', 0); guard(N, 'replaceChild', 0);
92
+ for (const p of [E, D, F]) { guard(p, 'append', -1); guard(p, 'prepend', -1); guard(p, 'replaceChildren', -1); guard(p, 'moveBefore', 0); }
93
+ for (const p of [E, CD, DT]) { guard(p, 'before', -1); guard(p, 'after', -1); guard(p, 'replaceWith', -1); }
94
+ guard(E, 'insertAdjacentElement', 1);
95
+ if (typeof Range !== 'undefined') { guard(Range.prototype, 'insertNode', 0); guard(Range.prototype, 'surroundContents', 0); }
96
+ // HTML-string sinks: refused outright (Trusted Types refuse them too where the engine has them; this holds everywhere)
97
+ const sinkMethod = (proto, name) => { if (proto && desc(proto, name)) lock(proto, name, function () { refuse(`${name}()`); }); };
98
+ const sinkSetter = (proto, name) => { const d = proto && desc(proto, name); if (d && d.set) Object.defineProperty(proto, name, { get: d.get, set() { refuse(`the ${name} setter`); }, configurable: false, enumerable: d.enumerable }); };
99
+ sinkSetter(E, 'innerHTML'); sinkSetter(E, 'outerHTML'); sinkSetter(typeof ShadowRoot !== 'undefined' ? ShadowRoot.prototype : null, 'innerHTML');
100
+ sinkMethod(E, 'insertAdjacentHTML'); sinkMethod(E, 'setHTML'); sinkMethod(E, 'setHTMLUnsafe');
101
+ if (typeof ShadowRoot !== 'undefined') { sinkMethod(ShadowRoot.prototype, 'setHTML'); sinkMethod(ShadowRoot.prototype, 'setHTMLUnsafe'); }
102
+ sinkMethod(D, 'write'); sinkMethod(D, 'writeln'); sinkMethod(D, 'open');
103
+ if (typeof Range !== 'undefined') sinkMethod(Range.prototype, 'createContextualFragment');
104
+ if (typeof DOMParser !== 'undefined') sinkMethod(DOMParser.prototype, 'parseFromString');
105
+ if (typeof XSLTProcessor !== 'undefined') { sinkMethod(XSLTProcessor.prototype, 'transformToFragment'); sinkMethod(XSLTProcessor.prototype, 'transformToDocument'); }
106
+ // r5: the STATIC parsers (not on any prototype) and the editor command that parses HTML into the document
107
+ for (const name of ['parseHTMLUnsafe', 'parseHTML']) { if (typeof Document[name] === 'function') lock(Document, name, function () { refuse(`Document.${name}()`); }); }
108
+ sinkMethod(D, 'execCommand');
109
+ // r5: a node from ANOTHER document (a parsed one, an XML one) enters this one by adoptNode / importNode, by an insertion
110
+ // method (guarded above) or by an element-taking setter: refuse a loader subtree at every door
111
+ guard(D, 'adoptNode', 0); guard(D, 'importNode', 0);
112
+ const guardSetter = (proto, name) => {
113
+ const d = proto && desc(proto, name); if (!d || !d.set) return;
114
+ const set = d.set;
115
+ Object.defineProperty(proto, name, { get: d.get, set(v) { const w = loaderIn(v); if (w) refuse(`setting ${name} to a subtree with <${w}>`); return apply(set, this, [v]); }, configurable: false, enumerable: d.enumerable });
116
+ };
117
+ if (typeof HTMLTableElement !== 'undefined') for (const k of ['caption', 'tHead', 'tFoot']) guardSetter(HTMLTableElement.prototype, k);
118
+ guardSetter(D, 'body');
119
+ if (typeof HTMLSelectElement !== 'undefined') guard(HTMLSelectElement.prototype, 'add', 0);
120
+ if (typeof HTMLOptionsCollection !== 'undefined') guard(HTMLOptionsCollection.prototype, 'add', 0);
121
+ // r5: the last line — if a loader element ever becomes part of the document anyway (a parsing path this list does not
122
+ // name on some engine, or a parser/audit disagreement in the markup itself), it is removed at once and the wrapper
123
+ // closes the widget. After the fact (the engine may already have acted on it), so a belt, not the control.
124
+ const removeChild = N.removeChild;
125
+ const mo = new MutationObserver((recs) => {
126
+ for (const r of recs) for (const n of r.addedNodes) {
127
+ const w = breachIn(n);
128
+ if (w) { try { const p = n.parentNode; if (p) apply(removeChild, p, [n]); } catch { /* already gone */ } if (sealBreach) sealBreach(w); }
129
+ }
130
+ });
131
+ mo.observe(document, { childList: true, subtree: true });
132
+ // r6: ACTIVATIONS. A click on a link that targets another window makes WebKit open a bare TCP connection to the link's
133
+ // host:port before the sandbox refuses the popup. This listener sits on `window` in the capture phase, registered
134
+ // before any pack script exists: it runs first and nothing can remove it (no reference). It cancels a click /
135
+ // auxclick whose composed path holds a link (`a` / `area`, HTML or SVG) with a target other than `_self` / empty, or a
136
+ // `download` / `ping` attribute, and every form submit (the sandbox refuses forms anyway). A link to our own origin
137
+ // with no target navigates the frame, which the wrapper's frame-src governs (r3).
138
+ const composedPath = Event.prototype.composedPath, preventDefault = Event.prototype.preventDefault, stopImmediate = Event.prototype.stopImmediatePropagation;
139
+ let noted = 0;
140
+ const cancel = (e, why) => { apply(preventDefault, e, []); apply(stopImmediate, e, []); if (sealNote && noted < 5) { noted++; sealNote(why); } };
141
+ const activation = (e) => {
142
+ let path; try { path = apply(composedPath, e, []); } catch { path = []; }
143
+ for (const n of path) {
144
+ let t; try { t = apply(nodeType, n, []); } catch { continue; }
145
+ if (t !== 1) continue;
146
+ const ln = apply(localName, n, []);
147
+ if (ln === 'a' || ln === 'area') {
148
+ const target = (apply(hasAttribute, n, ['target']) ? String(apply(getAttribute, n, ['target']) || '') : '').trim().toLowerCase();
149
+ if ((target && target !== '_self') || apply(hasAttribute, n, ['download']) || apply(hasAttribute, n, ['ping'])) { cancel(e, `refused a ${e.type} on a link that targets another window (${target || 'download/ping'})`); return; }
150
+ }
151
+ if (ln === 'form') { cancel(e, `refused a form ${e.type}`); return; }
152
+ }
153
+ };
154
+ for (const type of ['click', 'auxclick', 'submit']) window.addEventListener(type, activation, { capture: true });
155
+ // a closed shadow root would hide its links from composedPath: every shadow root a pack page makes is open
156
+ { const d = desc(E, 'attachShadow'); if (d && typeof d.value === 'function') { const native = d.value; lock(E, 'attachShadow', function (init, ...rest) { const i = init && typeof init === 'object' ? { ...init, mode: 'open' } : init; return apply(native, this, [i, ...rest]); }); } }
157
+ // WebRTC: a connection with no CSP directive; a pack page has no use for it
158
+ for (const name of ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceTransport', 'RTCDtlsTransport', 'RTCSctpTransport']) {
159
+ if (name in window) lock(window, name, function () { refuse(`${name}`); });
160
+ }
161
+ Object.defineProperty(window, 'dextermSealed', { value: true, writable: false, configurable: false, enumerable: true });
162
+ })();
163
+
164
+ // ------------------------------------------------------------------ the SDK
165
+ const meta = (n) => (document.querySelector(`meta[name="${n}"]`) || {}).content || '';
166
+ const KEY = meta('dexterm-widget'), TOPICS = Object.freeze(meta('dexterm-topics').split(',').filter(Boolean)), HOST = meta('dexterm-host');
167
+ // the wrapper is the parent window; a page opened any other way has nobody to talk to and stays inert
168
+ const host = window.parent !== window && /^http:\/\/127\.0\.0\.1:\d+$/.test(HOST) ? window.parent : null;
169
+ const listeners = new Map();
170
+ const last = new Map();
171
+ let theme = {};
172
+ const CSS_TOKENS = ['bg', 'panel', 'panelAlt', 'border', 'borderFocus', 'text', 'muted', 'dim', 'accent', 'up', 'down', 'warn', 'ai', 'pump', 'dbc', 'glow'];
173
+ function send(o) { if (host) { try { host.postMessage(o, HOST); } catch { /* wrapper gone */ } } }
174
+ function applyTheme(t) {
175
+ theme = Object.freeze({ ...theme, ...(t || {}) });
176
+ const r = document.documentElement.style;
177
+ for (const k of CSS_TOKENS) if (typeof theme[k] === 'string') r.setProperty(`--${k}`, theme[k]);
178
+ fire('theme', theme);
179
+ }
180
+ function fire(topic, payload) {
181
+ last.set(topic, payload);
182
+ const fns = listeners.get(topic);
183
+ if (!fns) return;
184
+ for (const fn of [...fns]) { try { fn(payload); } catch (err) { send({ type: 'log', msg: `widget ${KEY} ${topic} handler: ${err && err.message}` }); } }
185
+ }
186
+ function requestClose() { send({ type: 'close' }); }
187
+ sealBreach = (what) => send({ type: 'breach', what: String(what).slice(0, 24) });
188
+ sealNote = (why) => send({ type: 'log', msg: `widget ${KEY}: ${why}`.slice(0, 200) });
189
+ window.addEventListener('message', (e) => {
190
+ if (!host || e.source !== host || e.origin !== HOST) return;
191
+ const m = e.data;
192
+ if (!m || typeof m !== 'object' || typeof m.type !== 'string') return;
193
+ switch (m.type) {
194
+ case 'hello': if (m.theme) applyTheme(m.theme); break;
195
+ case 'theme': applyTheme(m.tokens || {}); break;
196
+ case 'data': if (typeof m.topic === 'string' && TOPICS.includes(m.topic)) fire(m.topic, m.payload); break;
197
+ case 'ping': send({ type: 'pong' }); break; // r6: the wrapper checks the document is still ours after a frame `load`
198
+ case 'close': document.documentElement.dataset.dexterm = 'closed'; break; // the host closes the window itself
199
+ default: break;
200
+ }
201
+ });
202
+ const api = Object.freeze({
203
+ on(topic, fn) {
204
+ if (typeof fn !== 'function' || typeof topic !== 'string') return api;
205
+ if (!listeners.has(topic)) listeners.set(topic, new Set());
206
+ listeners.get(topic).add(fn);
207
+ if (last.has(topic)) { try { fn(last.get(topic)); } catch (err) { send({ type: 'log', msg: `widget ${KEY} ${topic} handler: ${err && err.message}` }); } }
208
+ return api;
209
+ },
210
+ off(topic, fn) { const s = listeners.get(topic); if (s) s.delete(fn); return api; },
211
+ get theme() { return theme; },
212
+ get topics() { return TOPICS; },
213
+ get key() { return KEY; },
214
+ close: requestClose,
215
+ log(text) { send({ type: 'log', msg: String(text).slice(0, 200) }); },
216
+ });
217
+ Object.defineProperty(window, 'dexterm', { value: api, writable: false, configurable: false, enumerable: true });
218
+ window.addEventListener('keydown', (e) => { if (e.key === 'Escape') requestClose(); });
219
+ window.addEventListener('error', (e) => send({ type: 'log', msg: `widget ${KEY} error: ${e.message}`.slice(0, 200) }));
220
+ // the pack's own scripts run after this one; `ready` goes out once the document has parsed, so their listeners exist
221
+ // before the wrapper replays the current view models
222
+ const ready = () => send({ type: 'ready', page: `widget:${KEY}`.slice(0, 24), tt: typeof trustedTypes !== 'undefined' });
223
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ready); else ready();
224
+ })();
@@ -0,0 +1,5 @@
1
+ /* dexterm popup — pack WIDGET wrapper (task-1358 r3). The pack page lives in the sandboxed iframe; this page is ours. */
2
+ .widget { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; background: transparent; }
3
+ .blocked { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 20px; text-align: center;
4
+ color: var(--down); background: var(--bg); font-size: 12px; z-index: 2; }
5
+ .blocked[hidden] { display: none; }
@@ -0,0 +1,21 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="dexterm-ws" content="__WS_TOKEN__">
6
+ <meta name="dexterm-popup" content="__POPUP_ID__">
7
+ <meta name="dexterm-port" content="__PORT__">
8
+ <meta name="dexterm-widget" content="__WIDGET_KEY__">
9
+ <meta name="dexterm-frame" content="__FRAME_URL__">
10
+ <title>dexterm · widget</title>
11
+ <style nonce="__NONCE__">/*__CSS__*/</style>
12
+ </head>
13
+ <body>
14
+ <div id="hud" class="hud">
15
+ <div id="drag" class="drag"><span class="mark">✦</span><span id="title" class="title">__WIDGET_TITLE__</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
16
+ <div class="stage"><iframe id="widget" class="widget" sandbox="allow-scripts" referrerpolicy="no-referrer" title="pack widget" src="__FRAME_URL__"></iframe><div id="blocked" class="blocked" hidden></div></div>
17
+ </div>
18
+ <script nonce="__NONCE__">/*__COMMON_JS__*/</script>
19
+ <script nonce="__NONCE__">/*__JS__*/</script>
20
+ </body>
21
+ </html>
@@ -0,0 +1,85 @@
1
+ // dexterm popup — pack WIDGET wrapper page (task-1358 r3). OUR document (nonce CSP like every built-in page): it owns
2
+ // the WebSocket, the drag strip, the close button and Escape, and holds the pack page in `<iframe sandbox="allow-scripts">`
3
+ // whose only permitted document is the host's widget-frame route (the page CSP's `frame-src`). The pack page has no
4
+ // network of its own: everything it sees comes through here by postMessage, and the only things it can say back are
5
+ // ready / log / close.
6
+ //
7
+ // Why a frame: a document may always navigate ITSELF, and `sandbox` only stops it navigating others — so a pack page
8
+ // served top-level could carry data to a remote URL in the address bar (the r2 verifier's finding, browser fallback).
9
+ // A nested browsing context's navigations are governed by the PARENT's `frame-src`, whoever initiates them: the engine
10
+ // refuses a remote URL before any request leaves, and this page hears the violation and closes the widget.
11
+ (() => {
12
+ 'use strict';
13
+ const meta = (n) => document.querySelector(`meta[name="${n}"]`)?.content || '';
14
+ const KEY = meta('dexterm-widget');
15
+ DT.page = `widget:${KEY}`.slice(0, 24);
16
+ const frame = document.getElementById('widget'), blockedEl = document.getElementById('blocked');
17
+ let child = null, loads = 0, dead = false, frameReady = false;
18
+ const last = new Map(); // the latest payload per topic, replayed once the frame says ready (pushes may arrive first)
19
+
20
+ // targetOrigin must be '*' for an opaque-origin (sandboxed) frame; the only document that can be in there is ours
21
+ // (frame-src), and the frame is dropped the moment anything else is seen.
22
+ const post = (m) => { if (!dead && child && frameReady) { try { child.postMessage(m, '*'); } catch { /* frame gone */ } } };
23
+ const kill = (why) => {
24
+ if (dead) return;
25
+ dead = true; child = null; frameReady = false;
26
+ try { frame.removeAttribute('src'); frame.remove(); } catch { /* already gone */ }
27
+ blockedEl.textContent = `widget blocked: ${why}`; blockedEl.hidden = false;
28
+ DT.status('blocked', 'down');
29
+ DT.send({ type: 'log', msg: `widget ${KEY} blocked: ${why}`.slice(0, 200) });
30
+ DT.requestClose();
31
+ };
32
+
33
+ // r6: a second `load` is NOT proof of a navigation — WebKit fires it for a same-document fragment navigation too (an
34
+ // in-page `<a href="#section">`), which used to close honest widgets. So on a second load the wrapper asks the document
35
+ // to identify itself: our SDK answers a `ping` with `pong` at once; an error document committed after a refused
36
+ // navigation (frame-src), a spent-token 404 or about:blank cannot — then the widget is closed. Pushes pause meanwhile.
37
+ let pingTimer = null;
38
+ const liveness = () => {
39
+ frameReady = false; child = frame.contentWindow;
40
+ if (pingTimer) clearTimeout(pingTimer);
41
+ try { child && child.postMessage({ type: 'ping' }, '*'); } catch { /* no window */ }
42
+ pingTimer = setTimeout(() => { pingTimer = null; if (!dead) kill('the page tried to leave its frame (the navigation was refused)'); }, 400);
43
+ };
44
+ frame.addEventListener('load', () => {
45
+ loads++;
46
+ if (loads > 1) { liveness(); return; }
47
+ child = frame.contentWindow;
48
+ });
49
+ // frame-src violations are reported to the EMBEDDING document's policy: this page hears a pack page's attempt to go elsewhere
50
+ document.addEventListener('securitypolicyviolation', (e) => {
51
+ const d = String(e.effectiveDirective || e.violatedDirective || '');
52
+ if (d.startsWith('frame-src') || d.startsWith('child-src')) kill(`tried to navigate to ${String(e.blockedURI || '?').slice(0, 80)} (refused by frame-src)`);
53
+ });
54
+ window.addEventListener('message', (e) => {
55
+ // identity is the window object; the sandboxed frame's origin is the literal 'null'
56
+ if (dead || !child || e.source !== child) return;
57
+ const m = e.data;
58
+ if (!m || typeof m !== 'object' || typeof m.type !== 'string') return;
59
+ switch (m.type) {
60
+ case 'ready':
61
+ frameReady = true;
62
+ // r5: the frame says whether this WebView has Trusted Types; without them the SDK's seal is the only HTML-sink guard
63
+ if (m.tt === false) { DT.status('live · no Trusted Types', 'warn'); DT.send({ type: 'log', msg: `widget ${KEY}: this WebView has no Trusted Types — the SDK seal is the HTML-sink guard`.slice(0, 200) }); }
64
+ else DT.status('live', 'up');
65
+ post({ type: 'hello', theme: DT.theme });
66
+ for (const [topic, payload] of last) post({ type: 'data', topic, payload });
67
+ break;
68
+ case 'pong': // r6: the document is still ours after a second load (a fragment navigation) — keep feeding it
69
+ if (pingTimer) { clearTimeout(pingTimer); pingTimer = null; }
70
+ frameReady = true; break;
71
+ case 'log': DT.send({ type: 'log', msg: String(m.msg).slice(0, 200) }); break;
72
+ case 'close': DT.requestClose(); break;
73
+ // r5: the seal's kill switch saw a loader element appear in the page — it was removed; close the widget
74
+ case 'breach': kill(`a <${String(m.what).replace(/[^a-z0-9-]/gi, '').slice(0, 24)}> element appeared in the page (removed; the seal reported it)`); break;
75
+ default: break; // anything else a page invents is ignored here and never reaches the socket
76
+ }
77
+ });
78
+
79
+ DT.onTheme((t) => post({ type: 'theme', tokens: t }));
80
+ DT.onData((payload, topic) => { if (typeof topic !== 'string') return; last.set(topic, payload); post({ type: 'data', topic, payload }); });
81
+ DT.onClose(() => { post({ type: 'close' }); });
82
+ DT.wireChrome();
83
+ DT.loop(() => {});
84
+ DT.connect();
85
+ })();
@@ -0,0 +1,31 @@
1
+ // dexterm popup protocol v1 (task-1290). Types only; the runtime lives in server.ts (TUI side),
2
+ // src/pages/*.js (popup side) and shell/wry (window side). Three layers, three channels:
3
+ //
4
+ // TUI ──stdin JSON lines──▶ SHELL (window process: wry) ──stdout JSON lines──▶ TUI
5
+ // TUI ◀──── WebSocket (loopback, ticket → one-time token) ────▶ POPUP PAGE (served by the TUI's server)
6
+ //
7
+ // Invariants (security model, carried over from the spike doc §5):
8
+ // * popups never receive keys, signer state, mandates or trade capability; they receive VIEW MODELS and theme tokens;
9
+ // * popups can only send UI events from `PopupToTui`; anything else is dropped and reported as `popup.dropped`;
10
+ // * the popup URL carries a single-use 30 s ticket and travels TUI → shell over stdin (never argv, never a log);
11
+ // * every untrusted string in a view model is rendered via textContent / fillText, never markup; CSP blocks the rest.
12
+ //
13
+ // This file ports and narrows research/popup-host/popup-protocol.ts to the three in-app popups.
14
+ // ------------------------------------------------------------------ pack widgets (task-1358 Tier 5)
15
+ /**
16
+ * The topics a pack widget may subscribe to. Each is a PUBLIC view model the terminal already shows: nothing in
17
+ * them names a key, a signer, a cap, a mandate or the gate, and a widget can only receive — the only messages a
18
+ * widget page may send are the generic ready/perf/log + close/focus/drag events every popup has.
19
+ */
20
+ export const WIDGET_TOPICS = ['rows', 'token', 'narrative', 'positions', 'alerts', 'theme'];
21
+ /** Hard cap on a widget page (the pack layer caps files at 512 KB; the server refuses anything larger at ticket time). */
22
+ export const WIDGET_HTML_MAX = 512_000;
23
+ // ------------------------------------------------------------------ chart v2 (task-1325): TradingView Lightweight Charts page
24
+ /** The six chart timeframes, same order and keys (1–6) as the terminal. 1s is deliberately absent (see README). */
25
+ export const CHART_INTERVALS = ['1m', '5m', '15m', '1h', '4h', '1d'];
26
+ /** Indicator / overlay ids the chart page may toggle. Anything else is dropped by the server. */
27
+ export const CHART_INDICATORS = ['ema9', 'ema21', 'vwap', 'volma', 'volume', 'tape'];
28
+ export const CHART_STYLES = ['candles', 'line', 'area'];
29
+ /** Price-scale mode: auto (normal, autoscaled), logarithmic, percent (from the first visible bar). */
30
+ export const CHART_SCALES = ['auto', 'log', 'percent'];
31
+ export const PROTOCOL_VERSION = 'dexterm-popup/1';