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
@@ -21,6 +21,7 @@ import type { SignedInBroker } from "./client";
21
21
  import {
22
22
  BrokerSignInController,
23
23
  runBrokerSignIn,
24
+ type BrokerSignInOptions,
24
25
  type BrokerSignInOutcome,
25
26
  type BrokerSignInSnapshot,
26
27
  } from "./sign-in";
@@ -59,9 +60,10 @@ export function useBrokerSignInAttempt(
59
60
  broker: SignedInBroker,
60
61
  write: boolean,
61
62
  onOutcome: (outcome: "connected" | "signed-out") => void,
63
+ options: BrokerSignInOptions = {},
62
64
  ): { snapshot: BrokerSignInSnapshot; restart(): void; finish(): void } {
63
65
  const controllerRef = useRef<BrokerSignInController | null>(null);
64
- if (!controllerRef.current) controllerRef.current = new BrokerSignInController(broker, write);
66
+ if (!controllerRef.current) controllerRef.current = new BrokerSignInController(broker, write, {}, options);
65
67
  const controller = controllerRef.current;
66
68
  const [snapshot, setSnapshot] = useState(controller.getSnapshot());
67
69
  const onOutcomeRef = useRef(onOutcome);
@@ -110,12 +112,13 @@ function BrokerSignInDialog({
110
112
  dismiss,
111
113
  broker,
112
114
  write,
113
- }: PromptContext<BrokerSignInOutcome> & { broker: SignedInBroker; write: boolean }) {
115
+ renew,
116
+ }: PromptContext<BrokerSignInOutcome> & { broker: SignedInBroker; write: boolean; renew: boolean }) {
114
117
  useAppLanguage();
115
118
  const { height: termHeight } = useViewport();
116
119
  // Close after a beat so "Connected" is visible; enter skips the wait. Signed
117
120
  // out closes at once, for the host to sign in to Gloom.
118
- const { snapshot, restart, finish } = useBrokerSignInAttempt(broker, write, resolve);
121
+ const { snapshot, restart, finish } = useBrokerSignInAttempt(broker, write, resolve, { renew });
119
122
 
120
123
  useDialogKeyboard((event) => {
121
124
  event.stopPropagation();
@@ -170,6 +173,8 @@ export interface BrokerSignInRequest {
170
173
  broker: SignedInBroker;
171
174
  /** Ask for trading as well as reading. Defaults to whether the broker takes orders. */
172
175
  write?: boolean;
176
+ /** Sign in again although the broker is connected. */
177
+ renew?: boolean;
173
178
  resolve: (connected: boolean) => void;
174
179
  }
175
180
 
@@ -178,13 +183,17 @@ const requestListeners = new Set<(request: BrokerSignInRequest) => void>();
178
183
  /**
179
184
  * Connects a signed-in broker, signing in to Gloom first when needed.
180
185
  * Resolves true once the broker is connected, false when the user cancels or
181
- * no dialog host is mounted.
186
+ * no dialog host is mounted. A broker that is already connected is done at
187
+ * once unless `renew` asks for a new sign-in.
182
188
  */
183
- export function requestBrokerSignIn(broker: SignedInBroker, options: { write?: boolean } = {}): Promise<boolean> {
189
+ export function requestBrokerSignIn(
190
+ broker: SignedInBroker,
191
+ options: { write?: boolean; renew?: boolean } = {},
192
+ ): Promise<boolean> {
184
193
  if (requestListeners.size === 0) return Promise.resolve(false);
185
194
  return new Promise((resolve) => {
186
195
  const write = options.write ?? Boolean(broker.capabilities.orders);
187
- for (const listener of requestListeners) listener({ broker, write, resolve });
196
+ for (const listener of requestListeners) listener({ broker, write, renew: options.renew, resolve });
188
197
  });
189
198
  }
190
199
 
@@ -203,13 +212,18 @@ export function BrokerSignInDialogHost() {
203
212
  void runBrokerSignIn(request.broker, request.write, {
204
213
  isSignedIn: () => apiClient.isSignedIn(),
205
214
  signInToGloom: () => promptGloomSignIn(dialog),
206
- connectBroker: async (broker, write) => await dialog.prompt<BrokerSignInOutcome>({
215
+ connectBroker: async (broker, write, renew) => await dialog.prompt<BrokerSignInOutcome>({
207
216
  size: "full",
208
217
  content: (context: unknown) => (
209
- <BrokerSignInDialog {...(context as PromptContext<BrokerSignInOutcome>)} broker={broker} write={write} />
218
+ <BrokerSignInDialog
219
+ {...(context as PromptContext<BrokerSignInOutcome>)}
220
+ broker={broker}
221
+ write={write}
222
+ renew={renew}
223
+ />
210
224
  ),
211
225
  }) ?? "cancelled",
212
- })
226
+ }, { renew: request.renew })
213
227
  .then(request.resolve, () => request.resolve(false))
214
228
  .finally(() => {
215
229
  openRef.current = false;
@@ -24,9 +24,21 @@ export interface BrokerSignInSnapshot {
24
24
  error: string | null;
25
25
  }
26
26
 
27
+ /** What a read of the account's connection tells the flow. */
28
+ export type BrokerConnectionRead = Pick<SignedInBrokerConnection, "status"> & { connectedAt?: string | null };
29
+
30
+ export interface BrokerSignInOptions {
31
+ /**
32
+ * Sign in again although the broker is connected, to start a new sign-in
33
+ * before the broker ends the old one. Without it a connected broker is done
34
+ * at once, as when a device adds a connection the account already holds.
35
+ */
36
+ renew?: boolean;
37
+ }
38
+
27
39
  export interface BrokerSignInIo {
28
40
  start(brokerId: string, write: boolean): Promise<{ connectUrl: string; code: string; expiresAt: string }>;
29
- fetchConnection(brokerId: string): Promise<Pick<SignedInBrokerConnection, "status">>;
41
+ fetchConnection(brokerId: string): Promise<BrokerConnectionRead>;
30
42
  now(): number;
31
43
  delay(ms: number): Promise<void>;
32
44
  }
@@ -58,10 +70,37 @@ function codeDeadline(expiresAt: string, now: number): number {
58
70
  return Number.isFinite(parsed) && parsed > now ? parsed : now + DEFAULT_CODE_TTL_MS;
59
71
  }
60
72
 
73
+ function stamp(value: string | null | undefined): number | null {
74
+ const parsed = value ? Date.parse(value) : Number.NaN;
75
+ return Number.isFinite(parsed) ? parsed : null;
76
+ }
77
+
78
+ /**
79
+ * Whether `now` shows a sign-in made after `before` was read. Renewing a
80
+ * connection takes more than connected: a broker that was connected when the
81
+ * flow began stays connected while the user signs in again, until Gloom stamps
82
+ * the new sign-in. Both reads are Gloom's, so a device clock that runs fast or
83
+ * slow cannot matter.
84
+ */
85
+ function signedInSince(before: BrokerConnectionRead, now: BrokerConnectionRead): boolean {
86
+ if (now.status !== "connected") return false;
87
+ if (before.status !== "connected") return true;
88
+ const at = stamp(now.connectedAt);
89
+ const was = stamp(before.connectedAt);
90
+ return at !== null && (was === null || at > was);
91
+ }
92
+
61
93
  export class BrokerSignInController {
62
94
  private readonly io: BrokerSignInIo;
63
95
  private readonly listeners = new Set<(snapshot: BrokerSignInSnapshot) => void>();
64
96
  private generation = 0;
97
+ /**
98
+ * Renewing only: the account's connection as first seen. It outlives a
99
+ * restarted code, so a sign-in finished while "r" asked for a new one still
100
+ * counts.
101
+ */
102
+ private before: BrokerConnectionRead | null = null;
103
+ private readonly renew: boolean;
65
104
  private snapshot: BrokerSignInSnapshot = { phase: "starting", connectUrl: null, code: null, error: null };
66
105
 
67
106
  constructor(
@@ -69,8 +108,10 @@ export class BrokerSignInController {
69
108
  /** Ask for trading as well as reading when the broker offers it. */
70
109
  private readonly write: boolean,
71
110
  io: Partial<BrokerSignInIo> = {},
111
+ options: BrokerSignInOptions = {},
72
112
  ) {
73
113
  this.io = { ...defaultIo, ...io };
114
+ this.renew = options.renew === true;
74
115
  }
75
116
 
76
117
  getSnapshot(): BrokerSignInSnapshot {
@@ -102,6 +143,14 @@ export class BrokerSignInController {
102
143
  private async run(generation: number): Promise<void> {
103
144
  const stale = () => this.generation !== generation;
104
145
  let retryMs = POLL_INTERVAL_MS;
146
+ // Renewing: read before a code exists, so nothing the user does can come
147
+ // first. A failed read leaves the first poll to stand in for it: no
148
+ // sign-in finishes within a poll interval.
149
+ if (this.renew && !this.before) {
150
+ const read = await this.io.fetchConnection(this.broker.id).catch(() => null);
151
+ if (stale()) return;
152
+ this.before ??= read;
153
+ }
105
154
  // Each iteration is one code: start, poll until connected, and loop again
106
155
  // when the code expired so the link refreshes instead of dead-ending.
107
156
  while (!stale()) {
@@ -132,8 +181,14 @@ export class BrokerSignInController {
132
181
  await this.io.delay(POLL_INTERVAL_MS);
133
182
  if (stale()) return;
134
183
  try {
184
+ const connection = await this.io.fetchConnection(this.broker.id);
135
185
  // Connected is enough: a user who declined trading still gets a read-only connection.
136
- if ((await this.io.fetchConnection(this.broker.id)).status === "connected") {
186
+ let done = connection.status === "connected";
187
+ if (this.renew) {
188
+ this.before ??= connection;
189
+ done = signedInSince(this.before, connection);
190
+ }
191
+ if (done) {
137
192
  if (!stale()) this.update({ phase: "connected", error: null });
138
193
  this.generation += 1;
139
194
  return;
@@ -154,7 +209,7 @@ export interface BrokerSignInSteps {
154
209
  isSignedIn(): boolean;
155
210
  /** The Gloom device sign-in; true once signed in. */
156
211
  signInToGloom(): Promise<boolean>;
157
- connectBroker(broker: SignedInBroker, write: boolean): Promise<BrokerSignInOutcome>;
212
+ connectBroker(broker: SignedInBroker, write: boolean, renew: boolean): Promise<BrokerSignInOutcome>;
158
213
  }
159
214
 
160
215
  /**
@@ -166,13 +221,15 @@ export async function runBrokerSignIn(
166
221
  broker: SignedInBroker,
167
222
  write: boolean | undefined,
168
223
  steps: BrokerSignInSteps,
224
+ options: BrokerSignInOptions = {},
169
225
  ): Promise<boolean> {
170
226
  if (!steps.isSignedIn() && !await steps.signInToGloom()) return false;
171
227
  // Trading is asked for only where the broker takes orders from Gloom.
172
228
  const scope = write ?? Boolean(broker.capabilities.orders);
173
- let outcome = await steps.connectBroker(broker, scope);
229
+ const renew = options.renew === true;
230
+ let outcome = await steps.connectBroker(broker, scope, renew);
174
231
  if (outcome === "signed-out" && await steps.signInToGloom()) {
175
- outcome = await steps.connectBroker(broker, scope);
232
+ outcome = await steps.connectBroker(broker, scope, renew);
176
233
  }
177
234
  return outcome === "connected";
178
235
  }
@@ -159,7 +159,14 @@ export class CapabilityRegistry {
159
159
 
160
160
  private resolveOperation(capabilityId: string, operationId: string, options: { renderer?: boolean }) {
161
161
  const entry = this.capabilities.get(capabilityId);
162
- if (!entry || !this.isEnabled(entry)) throw new Error(`Capability "${capabilityId}" is not available.`);
162
+ if (!entry || !this.isEnabled(entry)) {
163
+ const pluginName = entry && this.options.isPluginEnabled?.(entry.pluginId) === false
164
+ ? this.options.pluginName?.(entry.pluginId)
165
+ : undefined;
166
+ throw new Error(pluginName
167
+ ? `Turn on ${pluginName} to use ${entry!.capability.name}.`
168
+ : `Capability "${capabilityId}" is not available.`);
169
+ }
163
170
  const operation = entry.capability.operations[operationId];
164
171
  if (!operation) throw new Error(`Capability operation "${capabilityId}.${operationId}" is not available.`);
165
172
  if (options.renderer && operation.rendererSafe !== true) {
@@ -132,6 +132,8 @@ export interface CapabilityManifest {
132
132
 
133
133
  export interface CapabilityRegistryOptions {
134
134
  isPluginEnabled?(pluginId: string): boolean;
135
+ /** Names the plugin a switched-off capability belongs to, so the error can say what to turn on. */
136
+ pluginName?(pluginId: string): string | undefined;
135
137
  isCapabilityEnabled?(capability: PluginCapability, pluginId: string): boolean;
136
138
  connectionHealth?: ConnectionHealthRegistry;
137
139
  }
@@ -0,0 +1,74 @@
1
+ import type { CliCommandDef } from "../types/plugin";
2
+ import { fail } from "./errors";
3
+ import { suggestCliOption } from "./help";
4
+
5
+ /** A built-in command, which may say more about an option it does not take than the list of those it does. */
6
+ export interface BuiltinCliCommandDef extends CliCommandDef {
7
+ /** A sentence for an unknown option people reach for, such as `--from` on `history`; null for the rest. */
8
+ unknownOptionHint?(flag: string): string | null;
9
+ }
10
+
11
+ interface DeclaredOption {
12
+ flag: string;
13
+ takesValue: boolean;
14
+ }
15
+
16
+ /** `--range`, `--range=5Y`: an option. `-5`, `---`, `--` and plain words are not. */
17
+ const OPTION_TOKEN = /^--[a-z]/i;
18
+
19
+ /**
20
+ * The options a command's help declares, from each spelling such as
21
+ * `--range <range>` (takes a value) or `--all` (a switch). Null when it takes
22
+ * any option: `--<option> <value>` on `fn` and `shot`.
23
+ */
24
+ function declaredOptions(command: CliCommandDef): Map<string, DeclaredOption> | null {
25
+ const declared = new Map<string, DeclaredOption>();
26
+ for (const option of command.help?.options ?? []) {
27
+ for (const spelling of option.flags.split(",")) {
28
+ const trimmed = spelling.trim();
29
+ if (trimmed.startsWith("--<")) return null;
30
+ const match = /^(--?[a-z][\w-]*)(?:[ =](.+))?$/i.exec(trimmed);
31
+ if (match) declared.set(match[1]!, { flag: match[1]!, takesValue: match[2] != null });
32
+ }
33
+ }
34
+ return declared;
35
+ }
36
+
37
+ function failUnknownOption(command: BuiltinCliCommandDef, flag: string, declared: Map<string, DeclaredOption>): never {
38
+ const flags = [...declared.keys()];
39
+ const suggestion = suggestCliOption(flag, flags);
40
+ const help = `See gloomberb help ${command.name}.`;
41
+ fail(`Unknown option ${flag} for ${command.name}.`, [
42
+ command.unknownOptionHint?.(flag) ?? "",
43
+ suggestion ? `Did you mean ${suggestion}?` : "",
44
+ flags.length > 0 ? `Options: ${flags.join(", ")}. ${help}` : `${command.name} takes no options. ${help}`,
45
+ ].filter(Boolean).join("\n"));
46
+ }
47
+
48
+ /**
49
+ * Fails on the first `--option` a built-in command does not declare, which it
50
+ * would otherwise ignore and answer as if it had not been given: `history
51
+ * --from 1995-01-01` printing the default year. Global flags are already out
52
+ * of `args`; a declared option's value is skipped whatever it looks like, and
53
+ * nothing from `literalStart` on (after a bare `--`) is read as an option.
54
+ */
55
+ export function checkCliCommandOptions(
56
+ command: BuiltinCliCommandDef,
57
+ args: readonly string[],
58
+ literalStart = args.length,
59
+ ): void {
60
+ const declared = declaredOptions(command);
61
+ if (!declared) return;
62
+ for (let index = 0; index < Math.min(literalStart, args.length); index += 1) {
63
+ const arg = args[index]!;
64
+ if (!OPTION_TOKEN.test(arg)) continue;
65
+ const equals = arg.indexOf("=");
66
+ const flag = equals >= 0 ? arg.slice(0, equals) : arg;
67
+ const option = declared.get(flag);
68
+ if (!option) failUnknownOption(command, flag, declared);
69
+ if (!option.takesValue && equals >= 0) {
70
+ fail(`${flag} takes no value.`, `Write it alone, as ${flag}. See gloomberb help ${command.name}.`);
71
+ }
72
+ if (option.takesValue && equals < 0) index += 1;
73
+ }
74
+ }
@@ -46,6 +46,35 @@ export function requireArg(value: string | undefined, usage: string, ctx: CliCom
46
46
  return value;
47
47
  }
48
48
 
49
+ /**
50
+ * Fails on arguments past the `max` a command takes rather than dropping them:
51
+ * `fx NGN ZAR` printing NGN alone reads as an answer for both. `usage` is the
52
+ * command's usage line without `gloomberb`, `takes` what it takes ("one
53
+ * currency", "no arguments").
54
+ */
55
+ export function rejectExtraArgs(
56
+ args: readonly string[],
57
+ max: number,
58
+ { usage, takes, advice }: { usage: string; takes: string; advice?: string },
59
+ ctx: Pick<CliCommandContext, "fail">,
60
+ ): void {
61
+ if (args.length <= max) return;
62
+ const command = usage.split(" ")[0];
63
+ ctx.fail(`${command} takes ${takes}; got ${args.join(" ")}.`, [advice, `Usage: gloomberb ${usage}`].filter(Boolean).join("\n"));
64
+ }
65
+
66
+ /** The one argument a command takes, such as a symbol: missing fails with the usage line, extras are not dropped. */
67
+ export function requireOneArg(
68
+ args: readonly string[],
69
+ usage: string,
70
+ noun: string,
71
+ ctx: CliCommandContext,
72
+ ): string {
73
+ const value = requireArg(args[0], `Usage: gloomberb ${usage}`, ctx);
74
+ rejectExtraArgs(args, 1, { usage, takes: `one ${noun}`, advice: `Run it once per ${noun}.` }, ctx);
75
+ return value;
76
+ }
77
+
49
78
  export function isoDate(value: unknown): string {
50
79
  if (value instanceof Date) return value.toISOString();
51
80
  if (typeof value === "number") return new Date(value).toISOString();
@@ -1,6 +1,7 @@
1
1
  import { isCryptoInstrumentType } from "../../tickers/search/ranking";
2
2
  import type { Quote } from "../../types/financials";
3
3
  import { isCryptoPairSymbol } from "../../utils/crypto-pair";
4
+ import { isNotATickerMessage } from "../not-a-ticker";
4
5
 
5
6
  /** Where a crypto pair that will not load can still be read. `fn CRYP` takes no argument. */
6
7
  export const CRYPTO_BOARD_HINT = "Crypto prices may be briefly unavailable; the CRYP function (gloomberb fn CRYP) shows the crypto board.";
@@ -42,11 +43,15 @@ interface QuoteNoteResult {
42
43
  error: string | null;
43
44
  }
44
45
 
45
- /** The router words a failure as "<reason> for <symbol>"; drop the symbol so equal reasons group. */
46
+ /**
47
+ * The router words a failure as "<reason> for <symbol>"; drop the symbol so equal reasons group.
48
+ * Any other line, such as a sentence the data service wrote, is kept as it reads.
49
+ */
46
50
  function failureReason(message: string, symbol: string): string {
47
- const line = (message.split("\n")[0] ?? "").trim().replace(/\.+$/, "");
51
+ const line = (message.split("\n")[0] ?? "").trim();
52
+ const bare = line.replace(/\.+$/, "");
48
53
  const suffix = ` for ${symbol}`;
49
- return line.toLowerCase().endsWith(suffix.toLowerCase()) ? line.slice(0, -suffix.length) : line;
54
+ return bare.toLowerCase().endsWith(suffix.toLowerCase()) ? bare.slice(0, -suffix.length) : line;
50
55
  }
51
56
 
52
57
  /**
@@ -63,7 +68,10 @@ export function quoteNotes(results: QuoteNoteResult[], options: { exchange?: str
63
68
  if (!symbols.includes(result.target.symbol)) failures.set(reason, [...symbols, result.target.symbol]);
64
69
  }
65
70
  const notes = [...failures].map(([reason, symbols]) => (
66
- `${symbols.join(", ")}: ${reason}${symbols.some(isCryptoPairSymbol) ? `. ${CRYPTO_BOARD_HINT}` : ""}`
71
+ // "Not a ticker: APPLE." already names its symbol.
72
+ isNotATickerMessage(reason)
73
+ ? reason
74
+ : `${symbols.join(", ")}: ${reason}${symbols.some(isCryptoPairSymbol) ? `${/[.!?]$/.test(reason) ? "" : "."} ${CRYPTO_BOARD_HINT}` : ""}`
67
75
  ));
68
76
  if (!options.exchange) {
69
77
  for (const result of results) {