gloomberb 0.16.2 → 0.16.4

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 (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -1,4 +1,5 @@
1
1
  import { apiClient } from "../../api-client";
2
+ import { ApiRequestError } from "../../api-client/errors";
2
3
  import type { CliCommandDef } from "../../types/plugin";
3
4
  import { withCliServices } from "../context";
4
5
  import { formatCompact } from "../../utils/format";
@@ -10,8 +11,18 @@ import {
10
11
  type ScreenerCategory,
11
12
  } from "../../plugins/builtin/market-movers/screener";
12
13
  import { formatMoverPrice, moverReferencePrice } from "../../plugins/builtin/market-movers/model";
13
- import { loadCalendar, matchesCountry, matchesImpact, type CountryFilter, type ImpactFilter } from "../../plugins/builtin/econ/calendar-model";
14
- import { isoDate, requireArg, takeOption } from "./command-utils";
14
+ import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, type ExtendedSession } from "../../market-data/market/status";
15
+ import {
16
+ COUNTRY_CYCLE,
17
+ FILTER_CYCLE,
18
+ loadCalendar,
19
+ matchesCountry,
20
+ matchesImpact,
21
+ shortCalendarEnd,
22
+ type CountryFilter,
23
+ type ImpactFilter,
24
+ } from "../../plugins/builtin/econ/calendar-model";
25
+ import { isoDate, rejectExtraArgs, requireArg, requireOneArg, takeFlag, takeOption } from "./command-utils";
15
26
  import { buildCorrelationSeries } from "../../plugins/builtin/correlation/matrix/model";
16
27
  import { alignDailyCloses, correlateDailyCloses } from "../../plugins/builtin/correlation/compute";
17
28
  import { mixedSessionCloseNote } from "../../market-data/market/session-close-note";
@@ -31,39 +42,89 @@ const BASKET_NAMES = new Map<string, string>([
31
42
  ...SECTOR_COLLECTIONS.flatMap((collection) => collection.items.map((item) => [item.etf, item.name] as const)),
32
43
  ]);
33
44
  // Priced like the MOST pane. Index and yield levels (^GSPC, ^TNX) carry no currency sign.
34
- const PRICE_COLUMN = { key: "price", header: "Last", align: "right" as const,
35
- format: (value: unknown, row: Record<string, unknown>) => typeof value === "number"
45
+ function formatRowPrice(value: unknown, row: Record<string, unknown>): string {
46
+ return typeof value === "number"
36
47
  ? formatMoverPrice(
37
48
  value,
38
49
  typeof row.currency === "string" && !String(row.symbol ?? "").startsWith("^") ? row.currency : "",
39
50
  moverReferencePrice(row as Parameters<typeof moverReferencePrice>[0]),
40
- ) : "" };
51
+ ) : "";
52
+ }
53
+ const PRICE_COLUMN = { key: "price", header: "Last", align: "right" as const, format: formatRowPrice };
54
+
55
+ /**
56
+ * A pre-market or after-hours print and its move from the regular close, as
57
+ * `quote` gives it, in its own column beside Last and Chg%. Text drops a
58
+ * column no row fills, so a table without an extended print stays as before.
59
+ */
60
+ function extendedColumn(session: ExtendedSession) {
61
+ return {
62
+ key: session === "PRE" ? "preMarket" : "afterHours",
63
+ header: EXTENDED_SESSION_LABELS[session],
64
+ align: "right" as const,
65
+ value: (row: Record<string, unknown>) => row.extendedSession === session ? row.extendedPrice : null,
66
+ format: (value: unknown, row: Record<string, unknown>) => typeof value === "number"
67
+ ? [formatRowPrice(value, row), formatChangePercentCell(row.extendedChangePercent)].join(" ").trim()
68
+ : "",
69
+ };
70
+ }
71
+ const EXTENDED_COLUMNS = [extendedColumn("PRE"), extendedColumn("POST")];
72
+ // A narrow terminal drops these before it cuts the names short.
73
+ const MARKET_CAP_CELL = { key: "marketCap", header: "Mkt Cap", align: "right" as const, optional: true, dropPriority: 1 };
41
74
  const MOVER_COLUMNS = [
42
75
  { key: "symbol", header: "Symbol" },
43
76
  { key: "name", header: "Name" },
44
77
  PRICE_COLUMN,
45
78
  { key: "changePercent", header: "Chg%", align: "right" as const, format: formatChangePercentCell },
79
+ ...EXTENDED_COLUMNS,
46
80
  { key: "volume", header: "Volume", align: "right" as const, format: formatCompactCell },
47
- { key: "marketCap", header: "Mkt Cap", align: "right" as const, format: formatCompactCell },
81
+ { ...MARKET_CAP_CELL, format: formatCompactCell },
48
82
  ];
49
83
  const START_OPTION = { flags: "--start <yyyy-mm-dd>", description: "First observation date (default 2021-01-01)" };
50
84
 
51
- function screenerCategory(value: string | undefined): ScreenerCategory | "trending" {
52
- if (value === "losers") return "day_losers";
53
- if (value === "active" || value === "most-active") return "most_actives";
54
- if (value === "trending") return "trending";
55
- return "day_gainers";
85
+ const MOVER_LISTS: Record<string, ScreenerCategory | "trending"> = {
86
+ gainers: "day_gainers",
87
+ losers: "day_losers",
88
+ active: "most_actives",
89
+ "most-active": "most_actives",
90
+ trending: "trending",
91
+ };
92
+ const MOVERS_USAGE = "movers [gainers|losers|active|trending]";
93
+
94
+ function screenerCategory(args: readonly string[], ctx: Parameters<CliCommandDef["execute"]>[1]): ScreenerCategory | "trending" {
95
+ rejectExtraArgs(args, 1, { usage: MOVERS_USAGE, takes: "one list" }, ctx);
96
+ const list = args[0]?.toLowerCase() ?? "gainers";
97
+ const category = MOVER_LISTS[list];
98
+ if (!category) ctx.fail(`Unknown list "${args[0]}".`, "Use gainers, losers, active or trending.");
99
+ return category;
100
+ }
101
+
102
+ /** A command that takes no arguments fails on one, rather than answering as if it were not there. */
103
+ function rejectArgs(args: readonly string[], usage: string, ctx: Parameters<CliCommandDef["execute"]>[1], advice?: string): void {
104
+ rejectExtraArgs(args, 0, { usage, takes: "no arguments", advice }, ctx);
105
+ }
106
+
107
+ /** A percent to two decimals, as the table prints it. */
108
+ function roundedPercent(value: number | null): number | null {
109
+ return value == null ? null : Number(value.toFixed(2));
56
110
  }
57
111
 
58
112
  function quoteRows(results: Awaited<ReturnType<NonNullable<import("../../types/data-provider").AssetDataProvider["getQuotesBatch"]>>>) {
59
113
  return results.map((result) => {
60
114
  const quote = result.quote;
115
+ // Last and Chg% are the regular session, as `ticker` reads it; a pre-market or
116
+ // after-hours print is its own column, measured from that session's close.
117
+ const session = getQuoteSessionFields(quote);
61
118
  return {
62
119
  symbol: result.target.symbol,
63
120
  name: quote?.name || BASKET_NAMES.get(result.target.symbol) || "",
64
- price: quote?.price ?? null,
65
- change: quote?.change ?? null,
66
- changePercent: quote?.changePercent == null ? null : Number(quote.changePercent.toFixed(2)),
121
+ price: session.price,
122
+ change: session.change,
123
+ changePercent: roundedPercent(session.changePercent),
124
+ extendedSession: session.extendedSession,
125
+ extendedPrice: session.extendedPrice,
126
+ extendedChange: session.extendedChange,
127
+ extendedChangePercent: roundedPercent(session.extendedChangePercent),
67
128
  currency: quote?.currency ?? "",
68
129
  providerId: quote?.providerId ?? "",
69
130
  marketCap: quote?.marketCap ?? null,
@@ -72,7 +133,7 @@ function quoteRows(results: Awaited<ReturnType<NonNullable<import("../../types/d
72
133
  }
73
134
 
74
135
  async function runMoverCommand(args: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
75
- const category = screenerCategory(args[0]);
136
+ const category = screenerCategory(args, ctx);
76
137
  const limit = ctx.cliOptions.limit ?? 25;
77
138
  if (category === "trending") {
78
139
  await withCliServices(ctx, async (services) => {
@@ -112,7 +173,8 @@ async function runQuoteBasket(symbols: string[], ctx: Parameters<CliCommandDef["
112
173
  { key: "name", header: "Name" },
113
174
  PRICE_COLUMN,
114
175
  { key: "changePercent", header: "Chg%", align: "right", format: formatChangePercentCell },
115
- { key: "marketCap", header: "Mkt Cap", align: "right", value: (row) => row.marketCap == null ? "" : formatCompact(Number(row.marketCap)) },
176
+ ...EXTENDED_COLUMNS,
177
+ { ...MARKET_CAP_CELL, value: (row) => row.marketCap == null ? "" : formatCompact(Number(row.marketCap)) },
116
178
  ],
117
179
  });
118
180
  });
@@ -126,16 +188,61 @@ function utcDateTimePart(value: unknown, part: "date" | "time"): string {
126
188
  return part === "date" ? iso.slice(0, 10) : `${iso.slice(11, 16)} UTC`;
127
189
  }
128
190
 
191
+ const ECON_USAGE = "econ [--country <region>] [--impact <level>] [--from <yyyy-mm-dd>]";
192
+ const DAY_MS = 86_400_000;
193
+
194
+ /**
195
+ * What an empty calendar says. A window that starts after the last day listed
196
+ * says where the listing stops, whatever the filters, rather than nothing.
197
+ */
198
+ function econEmptyMessage(from: string | null, listedThrough: string | null, stopsShort: boolean): string {
199
+ if (!listedThrough) return "No events listed.";
200
+ if (from != null && from > listedThrough) return `No events listed after ${listedThrough}.`;
201
+ const range = from ? ` from ${from}` : "";
202
+ return stopsShort
203
+ ? `No matching events${range}; none listed after ${listedThrough}.`
204
+ : `No matching events${range} through ${listedThrough}.`;
205
+ }
206
+
207
+ /** A `yyyy-mm-dd` option, checked here; null when it was not given. */
208
+ function parseDateOption(value: string | undefined, flag: string, ctx: Parameters<CliCommandDef["execute"]>[1]): string | null {
209
+ if (value == null) return null;
210
+ const date = value.trim();
211
+ const time = /^\d{4}-\d{2}-\d{2}$/.test(date) ? Date.parse(`${date}T00:00:00Z`) : Number.NaN;
212
+ if (!Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== date) {
213
+ ctx.fail(`${flag} takes a date as yyyy-mm-dd, got "${value}".`);
214
+ }
215
+ return date;
216
+ }
217
+
218
+ /** One of a filter's values, matched without regard to case; anything else fails with the list. */
219
+ function parseFilter<T extends string>(value: string | undefined, values: readonly T[], label: string, ctx: Parameters<CliCommandDef["execute"]>[1]): T {
220
+ if (value == null) return values[0]!;
221
+ const match = values.find((candidate) => candidate.toLowerCase() === value.trim().toLowerCase());
222
+ if (!match) ctx.fail(`Unknown ${label} "${value}".`, `Use ${values.join(", ")}.`);
223
+ return match;
224
+ }
225
+
129
226
  async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
130
227
  const rawArgs = [...args];
131
- const country = (takeOption(rawArgs, "--country") ?? "all") as CountryFilter;
132
- const impact = (takeOption(rawArgs, "--impact") ?? "all") as ImpactFilter;
133
- await withCliServices(ctx, async (services) => {
134
- const { data: events } = await loadCalendar(ctx.cliOptions.refresh);
228
+ const country: CountryFilter = parseFilter(takeOption(rawArgs, "--country"), COUNTRY_CYCLE, "country", ctx);
229
+ const impact: ImpactFilter = parseFilter(takeOption(rawArgs, "--impact"), FILTER_CYCLE, "impact", ctx);
230
+ const fromOption = parseDateOption(takeOption(rawArgs, "--from"), "--from", ctx);
231
+ rejectArgs(rawArgs, ECON_USAGE, ctx, "Filter with --country and --impact.");
232
+ await withCliServices(ctx, async () => {
233
+ const { data: events, fetchedAt, stale } = await loadCalendar(ctx.cliOptions.refresh);
234
+ const todayStart = Math.floor(Date.now() / DAY_MS) * DAY_MS;
235
+ // The calendar lists from today (UTC). --from moves the first day; --tail
236
+ // alone keeps the newest events of the whole calendar, the past week's too.
237
+ const from = fromOption ?? (ctx.cliOptions.tail != null ? null : new Date(todayStart).toISOString().slice(0, 10));
238
+ const fromTime = from == null ? Number.NEGATIVE_INFINITY : Date.parse(`${from}T00:00:00Z`);
239
+ const lastEvent = events.reduce<Date | null>((last, event) => (!last || event.date > last ? event.date : last), null);
240
+ const listedThrough = lastEvent ? lastEvent.toISOString().slice(0, 10) : null;
241
+ // A calendar that stops short of a week ahead says so under the table.
242
+ const listedAfter = shortCalendarEnd(events, Math.max(todayStart, fromTime)) ? `No events listed after ${listedThrough}` : null;
135
243
  const rows = events
136
- .filter((event) => matchesCountry(event, country) && matchesImpact(event, impact))
244
+ .filter((event) => event.date.getTime() >= fromTime && matchesCountry(event, country) && matchesImpact(event, impact))
137
245
  .sort((left, right) => left.date.getTime() - right.date.getTime())
138
- .slice(0, ctx.cliOptions.limit ?? 50)
139
246
  .map((event) => ({
140
247
  date: isoDate(event.date),
141
248
  time: event.time,
@@ -148,9 +255,14 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
148
255
  }));
149
256
  ctx.printResult({
150
257
  data: rows,
151
- metadata: { country, impact },
152
- freshness: rowsFreshness(rows, { status: "not-a-feed", basis: "calendar", observedKey: "date", oldest: null }),
258
+ metadata: { country, impact, from, listedThrough },
259
+ // The calendar as read, not its rows: those are scheduled, mostly ahead.
260
+ freshness: rowsFreshness([], { status: "not-a-feed", basis: "calendar", asOf: fetchedAt }, { stale }),
153
261
  }, {
262
+ dateKey: "date",
263
+ defaultLimit: 50,
264
+ empty: econEmptyMessage(from, listedThrough, listedAfter != null),
265
+ ...(listedAfter ? { footnote: listedAfter } : {}),
154
266
  columns: [
155
267
  // Text shows both halves of the event timestamp in UTC, never the host's zone; exports keep the source values.
156
268
  { key: "date", header: "Date", format: (value) => utcDateTimePart(value, "date") },
@@ -166,18 +278,70 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
166
278
  });
167
279
  }
168
280
 
281
+ const FRED_USAGE = "fred <series-id> [--start <yyyy-mm-dd>] [--sort desc|asc]";
282
+ const FRED_LIST_USAGE = "fred --list [filter]";
283
+
284
+ /** `--start`, checked here: Gloom Cloud cannot read anything but yyyy-mm-dd. */
285
+ function parseStartDate(value: string | undefined, ctx: Parameters<CliCommandDef["execute"]>[1]): string {
286
+ return parseDateOption(value, "--start", ctx) ?? "2021-01-01";
287
+ }
288
+
289
+ /** Gloom Cloud serves a curated set of FRED series and answers 400 for any other id. */
290
+ function isUnsupportedSeries(error: unknown): boolean {
291
+ if (!(error instanceof ApiRequestError) || error.status !== 400) return false;
292
+ const message = typeof error.details?.message === "string" ? error.details.message : error.message;
293
+ return error.details?.code === "unsupported_series" || /^Unsupported FRED series\b/i.test(message);
294
+ }
295
+
296
+ async function runFredList(filter: string, ctx: Parameters<CliCommandDef["execute"]>[1]) {
297
+ let catalog;
298
+ try {
299
+ catalog = await apiClient.getCloudFredSeriesCatalog();
300
+ } catch (error) {
301
+ if (error instanceof ApiRequestError && error.status === 404) ctx.fail("The list of supported FRED series is not available yet.");
302
+ throw error;
303
+ }
304
+ const terms = filter.toLowerCase().split(/\s+/).filter(Boolean);
305
+ const rows = catalog.series
306
+ .map((series) => ({ id: series.id, title: series.title?.trim() ?? "", group: series.group?.trim() ?? "" }))
307
+ .filter((series) => {
308
+ const text = `${series.id} ${series.title} ${series.group}`.toLowerCase();
309
+ return terms.every((term) => text.includes(term));
310
+ });
311
+ ctx.printResult({ data: rows, metadata: { filter: filter || null, total: catalog.series.length } }, {
312
+ columns: [
313
+ { key: "id", header: "ID", shrink: false },
314
+ { key: "title", header: "Title" },
315
+ { key: "group", header: "Group" },
316
+ ],
317
+ empty: filter ? `No supported FRED series match "${filter}". Run gloomberb fred --list to see them all.` : "No FRED series are listed.",
318
+ });
319
+ }
320
+
169
321
  async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
170
322
  const args = [...rawArgs];
171
- const startDate = takeOption(args, "--start") ?? "2021-01-01";
172
- const sortOrder = (takeOption(args, "--sort") ?? "desc") as "asc" | "desc";
173
- const seriesId = requireArg(args[0]?.toUpperCase(), "Usage: gloomberb fred <series-id> [--start <yyyy-mm-dd>]", ctx);
174
- const data = await apiClient.getCloudFredSeries(seriesId, { startDate, sortOrder });
175
- const rows = data.observations.slice(0, ctx.cliOptions.limit ?? data.observations.length);
323
+ const list = takeFlag(args, "--list");
324
+ const start = takeOption(args, "--start");
325
+ const sort = takeOption(args, "--sort");
326
+ if (list) {
327
+ if (start != null || sort != null) ctx.fail("--list takes a filter, not --start or --sort.", `Usage: gloomberb ${FRED_LIST_USAGE}`);
328
+ return runFredList(args.join(" ").trim(), ctx);
329
+ }
330
+ const startDate = parseStartDate(start, ctx);
331
+ const sortOrder = (sort ?? "desc").trim().toLowerCase();
332
+ if (sortOrder !== "asc" && sortOrder !== "desc") ctx.fail(`Unknown sort order "${sort}".`, "Use desc (newest first) or asc.");
333
+ const seriesId = requireOneArg(args, FRED_USAGE, "series", ctx).toUpperCase();
334
+ const data = await apiClient.getCloudFredSeries(seriesId, { startDate, sortOrder }).catch((error: unknown) => {
335
+ if (isUnsupportedSeries(error)) ctx.fail(`Unsupported FRED series ${seriesId}. List supported series with: gloomberb fred --list`);
336
+ throw error;
337
+ });
338
+ const rows = data.observations;
176
339
  ctx.printResult({
177
340
  data: rows,
178
341
  metadata: { info: data.info, seriesId, startDate, sortOrder },
179
342
  freshness: rowsFreshness(rows, { source: "FRED", status: "not-a-feed", basis: "published statistics", observedKey: "date", oldest: null }),
180
343
  }, {
344
+ dateKey: "date",
181
345
  textColumns: [
182
346
  { key: "date", header: "Date" },
183
347
  { key: "value", header: data.info?.units ? `Value (${data.info.units})` : "Value", align: "right" },
@@ -187,8 +351,10 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
187
351
 
188
352
  const YIELD_TENORS: Record<string, string> = { DGS3MO: "3M", DGS2: "2Y", DGS10: "10Y", DGS30: "30Y" };
189
353
 
190
- async function runYieldCurve(args: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
191
- const startDate = takeOption(args, "--start") ?? "2021-01-01";
354
+ async function runYieldCurve(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
355
+ const args = [...rawArgs];
356
+ const startDate = parseStartDate(takeOption(args, "--start"), ctx);
357
+ rejectArgs(args, "yield-curve [--start <yyyy-mm-dd>]", ctx);
192
358
  const series = Object.keys(YIELD_TENORS);
193
359
  const results = await Promise.all(series.map(async (seriesId) => {
194
360
  const data = await apiClient.getCloudFredSeries(seriesId, { startDate, sortOrder: "desc" });
@@ -217,9 +383,10 @@ async function runYieldCurve(args: string[], ctx: Parameters<CliCommandDef["exec
217
383
  async function runCorrelation(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
218
384
  const args = [...rawArgs];
219
385
  const exchange = takeOption(args, "--exchange");
220
- const usage = "Usage: gloomberb correlation <symbol-a> <symbol-b>";
221
- requireArg(args[0], usage, ctx);
222
- requireArg(args[1], usage, ctx);
386
+ const usage = "correlation <symbol-a> <symbol-b>";
387
+ requireArg(args[0], `Usage: gloomberb ${usage}`, ctx);
388
+ requireArg(args[1], `Usage: gloomberb ${usage}`, ctx);
389
+ rejectExtraArgs(args, 2, { usage, takes: "two symbols", advice: "Run it once per pair." }, ctx);
223
390
  await withCliServices(ctx, async (services) => {
224
391
  // --exchange is for a symbol that names no exchange of its own.
225
392
  const [leftListing, rightListing] = await Promise.all([args[0]!, args[1]!].map((raw) => (
@@ -274,7 +441,7 @@ export const overviewCliCommands: CliCommandDef[] = [
274
441
  description: "Show gainers, losers, most active, or trending stocks",
275
442
  help: {
276
443
  group: CLI_COMMAND_GROUPS.markets,
277
- usage: ["movers [gainers|losers|active|trending]"],
444
+ usage: [MOVERS_USAGE],
278
445
  examples: ["movers", "movers losers --limit 10", "movers trending"],
279
446
  },
280
447
  execute: runMoverCommand,
@@ -283,39 +450,53 @@ export const overviewCliCommands: CliCommandDef[] = [
283
450
  name: "indices",
284
451
  description: "Show the major US stock indices",
285
452
  help: { group: CLI_COMMAND_GROUPS.markets, usage: ["indices"] },
286
- execute: (_args, ctx) => runQuoteBasket([...MARKET_SUMMARY_SYMBOLS], ctx, { group: "indices" }),
453
+ execute: (args, ctx) => {
454
+ rejectArgs(args, "indices", ctx);
455
+ return runQuoteBasket([...MARKET_SUMMARY_SYMBOLS], ctx, { group: "indices" });
456
+ },
287
457
  },
288
458
  {
289
459
  name: "sectors",
290
460
  description: "Show the SPDR sector ETFs",
291
461
  help: { group: CLI_COMMAND_GROUPS.markets, usage: ["sectors"] },
292
- execute: (_args, ctx) => runQuoteBasket(getSectorCollection("sectors").items.map((item) => item.etf), ctx, { group: "sectors" }),
462
+ execute: (args, ctx) => {
463
+ rejectArgs(args, "sectors", ctx);
464
+ return runQuoteBasket(getSectorCollection("sectors").items.map((item) => item.etf), ctx, { group: "sectors" });
465
+ },
293
466
  },
294
467
  {
295
468
  name: "econ",
296
469
  description: "List upcoming economic calendar events",
297
470
  help: {
298
471
  group: CLI_COMMAND_GROUPS.markets,
299
- usage: ["econ [--country <region>] [--impact <level>]"],
472
+ usage: [ECON_USAGE],
300
473
  options: [
301
474
  { flags: "--country <region>", description: "US, G7, EU, or all (default all)" },
302
475
  { flags: "--impact <level>", description: "high, medium, low, or all (default all)" },
476
+ { flags: "--from <yyyy-mm-dd>", description: "First day listed (default today, UTC); an earlier day reaches past events" },
303
477
  ],
304
- examples: ["econ", "econ --country US --impact high"],
478
+ examples: ["econ", "econ --country US --impact high", "econ --tail 20"],
305
479
  },
306
480
  execute: runEcon,
307
481
  },
308
482
  {
309
483
  name: "fred",
310
- description: "Fetch a FRED economic series (needs a Gloom Cloud sign-in)",
484
+ description: "Fetch a FRED economic series, or list the ones served",
311
485
  help: {
312
486
  group: CLI_COMMAND_GROUPS.markets,
313
- usage: ["fred <series-id> [--start <yyyy-mm-dd>]"],
487
+ usage: [FRED_USAGE, FRED_LIST_USAGE],
314
488
  options: [
315
489
  START_OPTION,
316
490
  { flags: "--sort <order>", description: "desc for newest first (default) or asc" },
491
+ { flags: "--list", description: "List the series Gloom Cloud serves (ID, title, group); words after it filter the list" },
317
492
  ],
318
- examples: ["fred CPIAUCSL", "fred UNRATE --start 2020-01-01 --csv"],
493
+ sections: [{
494
+ title: "Series",
495
+ lines: [
496
+ "Gloom Cloud serves a curated set of FRED series and needs no sign-in for them. gloomberb fred --list prints them; any other ID fails with that pointer.",
497
+ ],
498
+ }],
499
+ examples: ["fred CPIAUCSL", "fred DGS10 --tail 5", "fred UNRATE --start 2020-01-01 --csv", "fred --list", "fred --list fx"],
319
500
  },
320
501
  execute: runFred,
321
502
  },
@@ -13,6 +13,7 @@ import { initMarketData, withMarketData } from "../context";
13
13
  import { fail } from "../errors";
14
14
  import type { MarketContext } from "../types";
15
15
  import type { CliCommandContext } from "../../types/plugin";
16
+ import { indexRootFor, indexRootTryLine } from "../index-roots";
16
17
 
17
18
  interface SearchCommandDependencies {
18
19
  initMarketData?: () => Promise<MarketContext>;
@@ -52,6 +53,11 @@ export async function searchCandidatesForCli({
52
53
  }
53
54
  }
54
55
 
56
+ /** "Saved", or the broker a row came from; never the data service's internal id. */
57
+ function searchSourceLabel(candidate: TickerSearchCandidate): string {
58
+ return candidate.kind === "ticker" ? "Saved" : candidate.result?.brokerLabel || "";
59
+ }
60
+
55
61
  function resolveSearchName(candidate: TickerSearchCandidate): string {
56
62
  return candidate.detail.split(" | ")[0] || candidate.detail || "—";
57
63
  }
@@ -70,14 +76,22 @@ function searchCandidateRows(candidates: TickerSearchCandidate[]) {
70
76
  || candidate.result?.brokerContract?.secType
71
77
  || candidate.ticker?.metadata.assetCategory
72
78
  || "",
73
- source: candidate.kind === "ticker"
74
- ? "Saved"
75
- : candidate.result?.brokerLabel || candidate.result?.providerId || "Provider",
79
+ source: searchSourceLabel(candidate),
76
80
  providerId: candidate.result?.providerId ?? "",
77
81
  saved: candidate.kind === "ticker",
78
82
  }));
79
83
  }
80
84
 
85
+ /**
86
+ * "^VIX Cboe Volatility Index (try: gloomberb quote ^VIX)" for a bare index root, whose
87
+ * search finds funds named after the index but not the index, unless it does list it.
88
+ */
89
+ function indexRootNote(query: string, candidates: TickerSearchCandidate[]): string | null {
90
+ const root = indexRootFor(query);
91
+ if (!root || candidates.some((candidate) => candidate.label.toUpperCase() === root.symbol)) return null;
92
+ return indexRootTryLine(root);
93
+ }
94
+
81
95
  export function buildSearchReport({
82
96
  query,
83
97
  candidates,
@@ -86,6 +100,8 @@ export function buildSearchReport({
86
100
  candidates: TickerSearchCandidate[];
87
101
  }): string {
88
102
  const lines = [renderSection(`Search: ${query}`)];
103
+ const indexNote = indexRootNote(query, candidates);
104
+ if (indexNote) lines.push("", indexNote);
89
105
 
90
106
  if (candidates.length === 0) {
91
107
  lines.push(cliStyles.muted("No matches found."));
@@ -93,6 +109,8 @@ export function buildSearchReport({
93
109
  }
94
110
 
95
111
  const categories = Array.from(new Set(candidates.map((candidate) => candidate.category)));
112
+ // Where a row came from, only when one is saved or from a broker.
113
+ const showSource = candidates.some((candidate) => searchSourceLabel(candidate));
96
114
  for (const category of categories) {
97
115
  const categoryRows = candidates.filter((candidate) => candidate.category === category);
98
116
  lines.push("");
@@ -103,7 +121,7 @@ export function buildSearchReport({
103
121
  { header: "Name" },
104
122
  { header: "Exchange" },
105
123
  { header: "Type" },
106
- { header: "Source" },
124
+ ...(showSource ? [{ header: "Source" }] : []),
107
125
  ],
108
126
  categoryRows.map((candidate) => [
109
127
  candidate.label,
@@ -117,9 +135,7 @@ export function buildSearchReport({
117
135
  || candidate.result?.brokerContract?.secType
118
136
  || candidate.ticker?.metadata.assetCategory
119
137
  || "—",
120
- candidate.kind === "ticker"
121
- ? "Saved"
122
- : candidate.result?.brokerLabel || candidate.result?.providerId || "Provider",
138
+ ...(showSource ? [searchSourceLabel(candidate) || "—"] : []),
123
139
  ]),
124
140
  ));
125
141
  }
@@ -144,7 +160,12 @@ export async function search(query: string, dependencies: SearchCommandDependenc
144
160
  });
145
161
 
146
162
  if (dependencies.printResult) {
147
- dependencies.printResult({ data: searchCandidateRows(candidates), metadata: { query: trimmedQuery } }, {
163
+ const indexNote = indexRootNote(trimmedQuery, candidates);
164
+ dependencies.printResult({
165
+ data: searchCandidateRows(candidates),
166
+ metadata: { query: trimmedQuery },
167
+ warnings: indexNote ? [indexNote] : undefined,
168
+ }, {
148
169
  columns: [
149
170
  { key: "category", header: "Category" },
150
171
  { key: "symbol", header: "Symbol" },