gloomberb 0.9.10 → 0.10.0

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 (516) hide show
  1. package/README.md +41 -3
  2. package/README.zh-CN.md +294 -0
  3. package/package.json +10 -4
  4. package/patches/@opentui%2Fcore@0.3.2.patch +26 -69
  5. package/src/api-client/auth.ts +2 -0
  6. package/src/api-client/index.test.ts +4 -0
  7. package/src/api-client/types.ts +3 -0
  8. package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -0
  9. package/src/app/pane-template-dialog-wizard.test.tsx +36 -0
  10. package/src/app/pane-template-dialog-wizard.tsx +10 -3
  11. package/src/app/runtime/desktop-deeplink.test.ts +15 -0
  12. package/src/app/runtime/desktop-deeplink.ts +21 -6
  13. package/src/app.tsx +39 -33
  14. package/src/capabilities/service-capabilities.ts +7 -2
  15. package/src/cli/commands/automation.test.ts +202 -0
  16. package/src/cli/commands/automation.ts +153 -36
  17. package/src/cli/commands/market.ts +1 -14
  18. package/src/cli/commands/system.ts +4 -1
  19. package/src/cli/context.ts +1 -1
  20. package/src/cli/desktop-pane-shot.ts +65 -4
  21. package/src/cli/entry.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +532 -0
  23. package/src/cli/pane-functions/catalog.ts +39 -4
  24. package/src/cli/pane-functions/data.test.ts +17 -0
  25. package/src/cli/pane-functions/data.ts +55 -13
  26. package/src/cli/pane-functions/discovery.ts +36 -9
  27. package/src/cli/pane-functions/index.test.ts +104 -1
  28. package/src/cli/pane-functions/index.ts +64 -5
  29. package/src/cli/pane-functions/options.ts +21 -6
  30. package/src/cli/pane-functions/report.ts +735 -72
  31. package/src/cli/pane-functions/resolver.ts +70 -6
  32. package/src/cli/pane-functions/screenshot.test.ts +431 -0
  33. package/src/cli/pane-functions/screenshot.ts +952 -8
  34. package/src/components/chart/composite/column-layout.ts +215 -0
  35. package/src/components/chart/composite/composite-chart.test.tsx +630 -0
  36. package/src/components/chart/composite/composite-chart.tsx +1148 -0
  37. package/src/components/chart/composite/format.test.ts +137 -0
  38. package/src/components/chart/composite/format.ts +135 -0
  39. package/src/components/chart/composite/index.ts +38 -0
  40. package/src/components/chart/composite/interactions.test.ts +109 -0
  41. package/src/components/chart/composite/interactions.ts +247 -0
  42. package/src/components/chart/composite/price-series.test.ts +39 -0
  43. package/src/components/chart/composite/price-series.ts +78 -0
  44. package/src/components/chart/composite/rasterizer.ts +359 -0
  45. package/src/components/chart/composite/renderers.test.ts +610 -0
  46. package/src/components/chart/composite/scene.test.ts +464 -0
  47. package/src/components/chart/composite/scene.ts +553 -0
  48. package/src/components/chart/composite/text-renderer.ts +285 -0
  49. package/src/components/chart/composite/time-scale.test.ts +88 -0
  50. package/src/components/chart/composite/time-scale.ts +217 -0
  51. package/src/components/chart/composite/types.ts +141 -0
  52. package/src/components/chart/core/data.ts +81 -5
  53. package/src/components/chart/core/index.test.ts +128 -43
  54. package/src/components/chart/core/resolution.ts +0 -6
  55. package/src/components/chart/native/chart-rasterizer.ts +1 -3
  56. package/src/components/chart/native/kitty/index.test.ts +99 -120
  57. package/src/components/chart/native/kitty/manager.ts +3 -0
  58. package/src/components/chart/native/kitty/protocol.ts +4 -1
  59. package/src/components/chart/native/raster/crosshair.ts +79 -46
  60. package/src/components/chart/native/raster/types.ts +2 -11
  61. package/src/components/chart/native/surface/manager.ts +48 -3
  62. package/src/components/chart/price-axis-labels.tsx +18 -4
  63. package/src/components/command-bar/commands/direct/index.ts +25 -0
  64. package/src/components/command-bar/commands/registry.ts +9 -0
  65. package/src/components/command-bar/helpers.test.ts +4 -1
  66. package/src/components/command-bar/list/view.tsx +18 -6
  67. package/src/components/command-bar/multi-select-picker.tsx +6 -5
  68. package/src/components/command-bar/pane-settings.test.ts +141 -0
  69. package/src/components/command-bar/pane-settings.ts +61 -1
  70. package/src/components/command-bar/panel/body.tsx +7 -6
  71. package/src/components/command-bar/routes/root/command-items.ts +4 -4
  72. package/src/components/command-bar/routes/root/results.ts +10 -1
  73. package/src/components/command-bar/routes/root/selection.test.ts +54 -0
  74. package/src/components/command-bar/routes/root/selection.ts +13 -0
  75. package/src/components/command-bar/routing/actions.ts +18 -2
  76. package/src/components/command-bar/routing/list-state.ts +2 -1
  77. package/src/components/command-bar/surface/index.test.tsx +36 -4
  78. package/src/components/command-bar/surface/pane.test.tsx +93 -0
  79. package/src/components/command-bar/theme-picker.tsx +2 -1
  80. package/src/components/command-bar/view-model.ts +19 -20
  81. package/src/components/command-bar/workflow/body.tsx +2 -1
  82. package/src/components/command-bar/workflow/field-row.tsx +3 -2
  83. package/src/components/command-bar/workflow/fields.ts +4 -2
  84. package/src/components/command-bar/workflow/ops.test.ts +161 -0
  85. package/src/components/command-bar/workflow/ops.ts +20 -50
  86. package/src/components/command-bar/workflow/route-actions.test.ts +34 -0
  87. package/src/components/command-bar/workflow/route-actions.ts +4 -0
  88. package/src/components/command-bar/workflow/types.ts +1 -0
  89. package/src/components/feed-data-table/stack-view.tsx +6 -5
  90. package/src/components/index.ts +13 -1
  91. package/src/components/layout/detached-pane-shell.tsx +2 -1
  92. package/src/components/layout/floating-pane.tsx +3 -0
  93. package/src/components/layout/header.tsx +15 -8
  94. package/src/components/layout/pane/footer/index.test.tsx +22 -3
  95. package/src/components/layout/pane/footer/index.tsx +3 -1
  96. package/src/components/layout/pane/footer/model.ts +9 -2
  97. package/src/components/layout/pane/header.tsx +4 -6
  98. package/src/components/layout/pane/index.tsx +3 -0
  99. package/src/components/layout/pane/sidebar.test.tsx +100 -0
  100. package/src/components/layout/pane/sidebar.tsx +239 -0
  101. package/src/components/layout/pane/title.ts +4 -3
  102. package/src/components/layout/shell/action-menu-overlay.tsx +5 -3
  103. package/src/components/layout/shell/drag/runtime.ts +2 -0
  104. package/src/components/layout/shell/menu.test.ts +19 -0
  105. package/src/components/layout/shell/menu.ts +8 -5
  106. package/src/components/layout/shell/native/pointer-runtime.ts +4 -1
  107. package/src/components/layout/shell/pane/layers.tsx +2 -2
  108. package/src/components/layout/status-bar.tsx +8 -7
  109. package/src/components/layout/window-controls.tsx +4 -3
  110. package/src/components/metric-treemap/index.tsx +3 -2
  111. package/src/components/onboarding/onboarding-steps.tsx +30 -29
  112. package/src/components/onboarding/onboarding-wizard.tsx +5 -4
  113. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +15 -12
  114. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +17 -16
  115. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +6 -5
  116. package/src/components/onboarding/portfolio-step/choose-panel.tsx +5 -4
  117. package/src/components/pane-settings-dialog/desktop.tsx +25 -3
  118. package/src/components/pane-settings-dialog/field-dialogs.tsx +5 -4
  119. package/src/components/pane-settings-dialog/tui.tsx +11 -9
  120. package/src/components/pane-settings-dialog/value.ts +8 -5
  121. package/src/components/pane-settings-dialog.test.tsx +235 -0
  122. package/src/components/pane-settings-dialog.tsx +24 -0
  123. package/src/components/pane-template-wizard.tsx +6 -2
  124. package/src/components/ticker/list-table-view.tsx +3 -2
  125. package/src/components/ui/button.tsx +3 -1
  126. package/src/components/ui/choice-dialog.tsx +6 -1
  127. package/src/components/ui/confirm-dialog.tsx +3 -2
  128. package/src/components/ui/fields.tsx +10 -1
  129. package/src/components/ui/frame.tsx +4 -1
  130. package/src/components/ui/index.ts +5 -0
  131. package/src/components/ui/inline-quick-add.tsx +104 -0
  132. package/src/components/ui/list-view.tsx +5 -4
  133. package/src/components/ui/loading.tsx +2 -1
  134. package/src/components/ui/multi-select/dialog.tsx +188 -44
  135. package/src/components/ui/popover.tsx +10 -0
  136. package/src/components/ui/shortcut-hint.tsx +2 -0
  137. package/src/components/ui/status.tsx +4 -3
  138. package/src/components/ui/tabs.tsx +19 -8
  139. package/src/components/ui/toggle.tsx +44 -3
  140. package/src/components/ui/ui.test.tsx +67 -5
  141. package/src/core/app-services.ts +4 -6
  142. package/src/core/state/session-persistence.ts +3 -2
  143. package/src/data/config/chart-settings.ts +712 -0
  144. package/src/data/config/layout.ts +24 -5
  145. package/src/data/config/store/index.test.ts +377 -14
  146. package/src/data/config/store/normalize.ts +79 -65
  147. package/src/data/fred-series.ts +137 -0
  148. package/src/i18n/index.test.ts +114 -0
  149. package/src/i18n/index.ts +147 -0
  150. package/src/i18n/ja.ts +1013 -0
  151. package/src/i18n/ko.ts +1011 -0
  152. package/src/i18n/languages.test.ts +31 -0
  153. package/src/i18n/languages.ts +72 -0
  154. package/src/i18n/zh-cn.ts +1013 -0
  155. package/src/i18n/zh-tw.ts +1013 -0
  156. package/src/plugins/builtin/account-management/footer.ts +3 -2
  157. package/src/plugins/builtin/account-management/form-components.tsx +3 -2
  158. package/src/plugins/builtin/account-management/keyboard.ts +6 -0
  159. package/src/plugins/builtin/account-management/model.ts +25 -21
  160. package/src/plugins/builtin/account-management/navigation.test.ts +27 -0
  161. package/src/plugins/builtin/account-management/navigation.ts +24 -0
  162. package/src/plugins/builtin/account-management/pane.tsx +120 -85
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +10 -9
  164. package/src/plugins/builtin/ai/agent-history.ts +139 -0
  165. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +133 -51
  166. package/src/plugins/builtin/ai/ask-ai.test.tsx +83 -22
  167. package/src/plugins/builtin/ai/index.test.ts +302 -0
  168. package/src/plugins/builtin/ai/index.tsx +374 -40
  169. package/src/plugins/builtin/ai/pane-settings.test.ts +155 -0
  170. package/src/plugins/builtin/ai/pane-settings.ts +319 -0
  171. package/src/plugins/builtin/ai/pi/credential-store.test.ts +80 -0
  172. package/src/plugins/builtin/ai/pi/credential-store.ts +138 -0
  173. package/src/plugins/builtin/ai/pi/file-store.ts +138 -0
  174. package/src/plugins/builtin/ai/pi/host.test.ts +500 -0
  175. package/src/plugins/builtin/ai/pi/host.ts +564 -0
  176. package/src/plugins/builtin/ai/pi/index.ts +5 -0
  177. package/src/plugins/builtin/ai/pi/models-store.test.ts +44 -0
  178. package/src/plugins/builtin/ai/pi/models-store.ts +141 -0
  179. package/src/plugins/builtin/ai/pi/providers.ts +68 -0
  180. package/src/plugins/builtin/ai/pi/runtime.test.ts +240 -0
  181. package/src/plugins/builtin/ai/pi/runtime.ts +635 -0
  182. package/src/plugins/builtin/ai/providers.test.ts +68 -0
  183. package/src/plugins/builtin/ai/providers.ts +163 -38
  184. package/src/plugins/builtin/ai/runner-selection.test.ts +174 -0
  185. package/src/plugins/builtin/ai/runner-selection.ts +193 -0
  186. package/src/plugins/builtin/ai/runner-selector.tsx +153 -0
  187. package/src/plugins/builtin/ai/runner.test.ts +198 -0
  188. package/src/plugins/builtin/ai/runner.ts +276 -73
  189. package/src/plugins/builtin/ai/screener/contract.test.ts +26 -73
  190. package/src/plugins/builtin/ai/screener/contract.ts +14 -52
  191. package/src/plugins/builtin/ai/screener/editor-runtime.ts +55 -5
  192. package/src/plugins/builtin/ai/screener/editor.tsx +42 -36
  193. package/src/plugins/builtin/ai/screener/footer.ts +82 -67
  194. package/src/plugins/builtin/ai/screener/keyboard.ts +27 -11
  195. package/src/plugins/builtin/ai/screener/model.test.ts +24 -0
  196. package/src/plugins/builtin/ai/screener/model.ts +9 -3
  197. package/src/plugins/builtin/ai/screener/pane.test.tsx +142 -55
  198. package/src/plugins/builtin/ai/screener/pane.tsx +95 -97
  199. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -41
  200. package/src/plugins/builtin/ai/screener/runner.ts +41 -25
  201. package/src/plugins/builtin/ai/screener/tabs-bar.tsx +2 -2
  202. package/src/plugins/builtin/ai/use-runtime-providers.ts +28 -0
  203. package/src/plugins/builtin/ai/workspace/model.test.ts +227 -0
  204. package/src/plugins/builtin/ai/workspace/model.ts +308 -0
  205. package/src/plugins/builtin/ai/workspace/pane.test.tsx +197 -0
  206. package/src/plugins/builtin/ai/workspace/pane.tsx +896 -0
  207. package/src/plugins/builtin/analytics/index.test.tsx +3 -3
  208. package/src/plugins/builtin/analytics/index.tsx +3 -8
  209. package/src/plugins/builtin/broker-manager/detail.tsx +25 -24
  210. package/src/plugins/builtin/broker-manager/index.test.tsx +1 -1
  211. package/src/plugins/builtin/broker-manager/index.tsx +9 -11
  212. package/src/plugins/builtin/broker-manager/model.ts +11 -10
  213. package/src/plugins/builtin/broker-manager/pane-actions.tsx +22 -21
  214. package/src/plugins/builtin/broker-manager/table.ts +9 -8
  215. package/src/plugins/builtin/changelog/index.tsx +3 -8
  216. package/src/plugins/builtin/chart-composer/chart-spec.ts +89 -0
  217. package/src/plugins/builtin/chart-composer/cli-options.ts +98 -0
  218. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +120 -0
  219. package/src/plugins/builtin/chart-composer/editor.test.tsx +332 -0
  220. package/src/plugins/builtin/chart-composer/editor.tsx +1107 -0
  221. package/src/plugins/builtin/chart-composer/index.tsx +228 -0
  222. package/src/plugins/builtin/chart-composer/pane.tsx +645 -0
  223. package/src/plugins/builtin/chart-composer/presets.test.ts +561 -0
  224. package/src/plugins/builtin/chart-composer/presets.ts +759 -0
  225. package/src/plugins/builtin/chart-composer/quick-add.test.tsx +245 -0
  226. package/src/plugins/builtin/chart-composer/quick-add.tsx +409 -0
  227. package/src/plugins/builtin/chart-composer/semantic.ts +86 -0
  228. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +65 -0
  229. package/src/plugins/builtin/chart-composer/series-catalog.ts +295 -0
  230. package/src/plugins/builtin/chart-composer/settings.test.ts +163 -0
  231. package/src/plugins/builtin/chart-composer/settings.ts +397 -0
  232. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +44 -0
  233. package/src/plugins/builtin/chart-composer/shortcuts.ts +35 -0
  234. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +103 -0
  235. package/src/plugins/builtin/chat/channel-labels.ts +5 -4
  236. package/src/plugins/builtin/chat/channels.ts +12 -11
  237. package/src/plugins/builtin/chat/chat.test.tsx +32 -0
  238. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  239. package/src/plugins/builtin/chat/content/composer.tsx +9 -8
  240. package/src/plugins/builtin/chat/content/index.tsx +36 -1
  241. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +8 -7
  242. package/src/plugins/builtin/chat/content/transcript.tsx +9 -3
  243. package/src/plugins/builtin/chat/controller/utils.ts +10 -9
  244. package/src/plugins/builtin/chat/message/desktop.tsx +17 -10
  245. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +122 -3
  246. package/src/plugins/builtin/chat/message/profile-popover.tsx +38 -1
  247. package/src/plugins/builtin/chat/message/terminal.tsx +19 -10
  248. package/src/plugins/builtin/chat/pane-state.test.ts +31 -0
  249. package/src/plugins/builtin/chat/pane-state.ts +30 -0
  250. package/src/plugins/builtin/chat/pane.tsx +27 -4
  251. package/src/plugins/builtin/chat/profile-popover.ts +62 -5
  252. package/src/plugins/builtin/chat/sidebar.test.tsx +4 -7
  253. package/src/plugins/builtin/chat/sidebar.tsx +107 -168
  254. package/src/plugins/builtin/chat/test-harness.tsx +6 -0
  255. package/src/plugins/builtin/cloud/auth-actions.tsx +5 -4
  256. package/src/plugins/builtin/cloud/plugin.tsx +150 -127
  257. package/src/plugins/builtin/composite-plugins.ts +71 -0
  258. package/src/plugins/builtin/correlation/index.test.tsx +4 -4
  259. package/src/plugins/builtin/correlation/index.tsx +3 -8
  260. package/src/plugins/builtin/earnings/index.tsx +37 -21
  261. package/src/plugins/builtin/earnings/model.test.ts +52 -0
  262. package/src/plugins/builtin/earnings/model.ts +32 -0
  263. package/src/plugins/builtin/econ/detail-view.tsx +17 -4
  264. package/src/plugins/builtin/econ/fred-cache.test.ts +57 -11
  265. package/src/plugins/builtin/econ/index.tsx +16 -20
  266. package/src/plugins/builtin/fear-greed/index.tsx +2 -8
  267. package/src/plugins/builtin/fx-matrix/index.tsx +3 -8
  268. package/src/plugins/builtin/help/components.tsx +5 -4
  269. package/src/plugins/builtin/help/index.tsx +14 -17
  270. package/src/plugins/builtin/holders/index.tsx +2 -8
  271. package/src/plugins/builtin/insider/index.tsx +2 -7
  272. package/src/plugins/builtin/kelly-sizer/index.test.tsx +2 -2
  273. package/src/plugins/builtin/kelly-sizer/index.tsx +3 -7
  274. package/src/plugins/builtin/kelly-sizer/test-support.tsx +3 -3
  275. package/src/plugins/builtin/layout-manager/index.tsx +3 -7
  276. package/src/plugins/builtin/market-heatmap/index.tsx +3 -8
  277. package/src/plugins/builtin/market-movers/index.tsx +3 -8
  278. package/src/plugins/builtin/news/index.tsx +12 -16
  279. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +3 -3
  280. package/src/plugins/builtin/news/wire/index.tsx +83 -73
  281. package/src/plugins/builtin/options/index.tsx +2 -8
  282. package/src/plugins/builtin/plugin-module.test.ts +89 -0
  283. package/src/plugins/builtin/plugin-module.ts +116 -0
  284. package/src/plugins/builtin/portfolio-list/header.tsx +4 -3
  285. package/src/plugins/builtin/portfolio-list/index.test.ts +17 -0
  286. package/src/plugins/builtin/portfolio-list/index.tsx +3 -5
  287. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +3 -3
  288. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +53 -53
  289. package/src/plugins/builtin/portfolio-list/settings.ts +5 -3
  290. package/src/plugins/builtin/portfolio-list/summary/index.tsx +4 -3
  291. package/src/plugins/builtin/research/index.tsx +2 -8
  292. package/src/plugins/builtin/sec/index.tsx +2 -8
  293. package/src/plugins/builtin/sectors/index.tsx +3 -8
  294. package/src/plugins/builtin/thirteenf/index.tsx +3 -8
  295. package/src/plugins/builtin/thirteenf/pane.test.tsx +1 -1
  296. package/src/plugins/builtin/ticker-detail/chart-tab-switch.test.tsx +49 -23
  297. package/src/plugins/builtin/ticker-detail/index.test.tsx +15 -136
  298. package/src/plugins/builtin/ticker-detail/index.tsx +2 -93
  299. package/src/plugins/builtin/ticker-detail/overview/components.tsx +6 -5
  300. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -17
  301. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -3
  302. package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +1 -1
  303. package/src/plugins/builtin/ticker-detail/settings.ts +3 -50
  304. package/src/plugins/builtin/ticker-research-backend-plugin.ts +17 -0
  305. package/src/plugins/builtin/ticker-research-plugin.ts +27 -0
  306. package/src/plugins/builtin/tv/channels.ts +27 -0
  307. package/src/plugins/builtin/tv/index.tsx +40 -0
  308. package/src/plugins/builtin/tv/pane.tsx +286 -0
  309. package/src/plugins/builtin/tv/youtube-stream.ts +100 -0
  310. package/src/plugins/builtin/world-indices/index.tsx +3 -8
  311. package/src/plugins/builtin/yield-curve/index.tsx +8 -8
  312. package/src/plugins/catalog-backend.test.ts +21 -0
  313. package/src/plugins/catalog-backend.ts +42 -0
  314. package/src/plugins/catalog-ui.ts +6 -14
  315. package/src/plugins/ibkr/instance-selection.test.ts +0 -1
  316. package/src/plugins/ownership.ts +143 -0
  317. package/src/plugins/prediction-markets/backend-plugin.ts +19 -0
  318. package/src/plugins/prediction-markets/chart.test.tsx +7 -5
  319. package/src/plugins/prediction-markets/chart.tsx +25 -7
  320. package/src/plugins/registry/index.test.ts +51 -0
  321. package/src/plugins/registry/index.ts +8 -0
  322. package/src/plugins/registry/pane-settings.ts +1 -1
  323. package/src/plugins/runtime/index.test.tsx +2 -2
  324. package/src/remote/app-host.tsx +16 -3
  325. package/src/remote/controller.test.ts +102 -1
  326. package/src/remote/controller.ts +122 -0
  327. package/src/remote/types.ts +11 -0
  328. package/src/renderers/electrobun/bun/core-capabilities.ts +123 -27
  329. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +5 -1
  330. package/src/renderers/electrobun/bun/desktop/initialization.ts +5 -1
  331. package/src/renderers/electrobun/bun/desktop/media.ts +15 -0
  332. package/src/renderers/electrobun/bun/desktop/window-style.ts +4 -1
  333. package/src/renderers/electrobun/bun/desktop/workspace/index.test.ts +32 -1
  334. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +10 -0
  335. package/src/renderers/electrobun/bun/index.ts +5 -0
  336. package/src/renderers/electrobun/bun/tui-entry.ts +1 -6
  337. package/src/renderers/electrobun/view/ai-host.ts +124 -19
  338. package/src/renderers/electrobun/view/backend-rpc.ts +4 -0
  339. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +151 -39
  340. package/src/renderers/electrobun/view/desktop/popover.tsx +166 -0
  341. package/src/renderers/electrobun/view/dialog-host.test.ts +25 -0
  342. package/src/renderers/electrobun/view/dialog-host.tsx +75 -12
  343. package/src/renderers/electrobun/view/host/box.tsx +5 -0
  344. package/src/renderers/electrobun/view/host/chart-surface.tsx +10 -9
  345. package/src/renderers/electrobun/view/host/input.tsx +9 -19
  346. package/src/renderers/electrobun/view/host/media-surface.tsx +170 -0
  347. package/src/renderers/electrobun/view/host/mouse.ts +1 -0
  348. package/src/renderers/electrobun/view/host/style.ts +1 -1
  349. package/src/renderers/electrobun/view/host/tabs.tsx +4 -3
  350. package/src/renderers/electrobun/view/main.tsx +10 -3
  351. package/src/renderers/electrobun/view/styles.css +17 -0
  352. package/src/renderers/electrobun/view/ui-host.tsx +7 -0
  353. package/src/renderers/opentui/chart-surface.test.tsx +52 -6
  354. package/src/renderers/opentui/chart-surface.tsx +95 -11
  355. package/src/renderers/opentui/dialog-host.tsx +36 -5
  356. package/src/renderers/opentui/host.tsx +39 -0
  357. package/src/renderers/opentui/start.tsx +30 -0
  358. package/src/renderers/opentui/ui-host.tsx +2 -0
  359. package/src/sources/provider-router/index.test.ts +43 -0
  360. package/src/sources/provider-router/index.ts +4 -0
  361. package/src/sources/provider-router/supplemental.ts +46 -0
  362. package/src/sources/sec-edgar.test.ts +203 -2
  363. package/src/sources/sec-edgar.ts +70 -15
  364. package/src/state/app/context/index.tsx +13 -1
  365. package/src/state/app/context/selector.test.tsx +62 -1
  366. package/src/state/session-persistence.test.ts +13 -2
  367. package/src/sync/controller.test.ts +67 -0
  368. package/src/sync/core-contributors.test.ts +121 -0
  369. package/src/sync/core-contributors.ts +67 -14
  370. package/src/time-series/alignment.ts +158 -0
  371. package/src/time-series/economic.ts +49 -0
  372. package/src/time-series/field-catalog.ts +306 -0
  373. package/src/time-series/fundamentals.test.ts +505 -0
  374. package/src/time-series/fundamentals.ts +842 -0
  375. package/src/time-series/hooks.ts +45 -0
  376. package/src/time-series/index.ts +14 -0
  377. package/src/time-series/live-quotes.test.ts +219 -0
  378. package/src/time-series/live-quotes.ts +203 -0
  379. package/src/time-series/market-resolution.ts +53 -0
  380. package/src/time-series/market.ts +125 -0
  381. package/src/time-series/reporting.ts +98 -0
  382. package/src/time-series/resolve.test.ts +1281 -0
  383. package/src/time-series/resolve.ts +783 -0
  384. package/src/time-series/spec-studies.test.ts +400 -0
  385. package/src/time-series/spec.ts +487 -0
  386. package/src/time-series/studies.ts +563 -0
  387. package/src/time-series/transforms.test.ts +105 -0
  388. package/src/time-series/transforms.ts +152 -0
  389. package/src/time-series/types.ts +162 -0
  390. package/src/time-series/use-chart-resolution.ts +166 -0
  391. package/src/types/config.ts +9 -11
  392. package/src/types/desktop-window.ts +1 -0
  393. package/src/types/financials.ts +4 -0
  394. package/src/types/media.ts +17 -0
  395. package/src/types/plugin.ts +32 -1
  396. package/src/ui/context-menu.tsx +15 -3
  397. package/src/ui/dialog.tsx +14 -2
  398. package/src/ui/host.tsx +40 -1
  399. package/src/ui/index.tsx +9 -0
  400. package/src/utils/exchanges.test.ts +13 -1
  401. package/src/utils/exchanges.ts +10 -0
  402. package/src/utils/financial-statements.test.ts +138 -0
  403. package/src/utils/financial-statements.ts +115 -8
  404. package/src/utils/format.test.ts +11 -1
  405. package/src/utils/format.ts +7 -0
  406. package/src/version.ts +1 -1
  407. package/src/components/chart/bar-chart-renderer.test.ts +0 -233
  408. package/src/components/chart/bar-chart-renderer.ts +0 -445
  409. package/src/components/chart/comparison/data.test.ts +0 -159
  410. package/src/components/chart/comparison/data.ts +0 -302
  411. package/src/components/chart/comparison/renderer.test.ts +0 -187
  412. package/src/components/chart/comparison/renderer.ts +0 -282
  413. package/src/components/chart/comparison/scene.ts +0 -178
  414. package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
  415. package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
  416. package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
  417. package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
  418. package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
  419. package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
  420. package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
  421. package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
  422. package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
  423. package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
  424. package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
  425. package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
  426. package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
  427. package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
  428. package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
  429. package/src/components/chart/comparison-stock-chart/types.ts +0 -22
  430. package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
  431. package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
  432. package/src/components/chart/comparison-stock-chart.tsx +0 -24
  433. package/src/components/chart/core/axis-measure.ts +0 -33
  434. package/src/components/chart/core/controller.test.ts +0 -157
  435. package/src/components/chart/core/controller.ts +0 -349
  436. package/src/components/chart/core/indicator-selector.tsx +0 -75
  437. package/src/components/chart/core/keyboard.ts +0 -24
  438. package/src/components/chart/core/pane-settings.test.ts +0 -52
  439. package/src/components/chart/core/pane-settings.ts +0 -141
  440. package/src/components/chart/core/scroll.test.ts +0 -64
  441. package/src/components/chart/core/scroll.ts +0 -82
  442. package/src/components/chart/core/window-viewport.test.ts +0 -69
  443. package/src/components/chart/core/window-viewport.ts +0 -162
  444. package/src/components/chart/display-cursor.ts +0 -153
  445. package/src/components/chart/indicators/bands.test.ts +0 -36
  446. package/src/components/chart/indicators/bands.ts +0 -29
  447. package/src/components/chart/indicators/moving-averages.test.ts +0 -39
  448. package/src/components/chart/indicators/moving-averages.ts +0 -42
  449. package/src/components/chart/indicators/options.test.ts +0 -32
  450. package/src/components/chart/indicators/options.ts +0 -107
  451. package/src/components/chart/indicators/oscillators.test.ts +0 -58
  452. package/src/components/chart/indicators/oscillators.ts +0 -92
  453. package/src/components/chart/native/canvas-bitmaps.ts +0 -140
  454. package/src/components/chart/native/raster/comparison-chart.ts +0 -162
  455. package/src/components/chart/native/surface/hooks.ts +0 -144
  456. package/src/components/chart/native/surface/rendering.ts +0 -211
  457. package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
  458. package/src/components/chart/static/bar-chart-surface.tsx +0 -292
  459. package/src/components/chart/stock-chart/auto.ts +0 -216
  460. package/src/components/chart/stock-chart/axis-width.ts +0 -146
  461. package/src/components/chart/stock-chart/bitmaps.ts +0 -22
  462. package/src/components/chart/stock-chart/canvas.ts +0 -202
  463. package/src/components/chart/stock-chart/controls.tsx +0 -149
  464. package/src/components/chart/stock-chart/cursor.ts +0 -60
  465. package/src/components/chart/stock-chart/data-runtime.ts +0 -128
  466. package/src/components/chart/stock-chart/footer.test.ts +0 -65
  467. package/src/components/chart/stock-chart/footer.ts +0 -303
  468. package/src/components/chart/stock-chart/geometry.ts +0 -160
  469. package/src/components/chart/stock-chart/index.test.ts +0 -532
  470. package/src/components/chart/stock-chart/index.tsx +0 -29
  471. package/src/components/chart/stock-chart/indicators.ts +0 -226
  472. package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
  473. package/src/components/chart/stock-chart/interactions.ts +0 -300
  474. package/src/components/chart/stock-chart/keyboard.ts +0 -373
  475. package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
  476. package/src/components/chart/stock-chart/layout.tsx +0 -177
  477. package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
  478. package/src/components/chart/stock-chart/presentation.ts +0 -67
  479. package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
  480. package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
  481. package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
  482. package/src/components/chart/stock-chart/rendering/data.ts +0 -370
  483. package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
  484. package/src/components/chart/stock-chart/rendering/output.ts +0 -304
  485. package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
  486. package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
  487. package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
  488. package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
  489. package/src/components/chart/stock-chart/rendering/state.ts +0 -283
  490. package/src/components/chart/stock-chart/requests.ts +0 -337
  491. package/src/components/chart/stock-chart/resolution-support.ts +0 -96
  492. package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
  493. package/src/components/chart/stock-chart/resolved.tsx +0 -359
  494. package/src/components/chart/stock-chart/settings.ts +0 -81
  495. package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
  496. package/src/components/chart/stock-chart/types.ts +0 -25
  497. package/src/components/chart/stock-chart/view.tsx +0 -155
  498. package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
  499. package/src/components/chart/stock-chart/viewport/index.ts +0 -144
  500. package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
  501. package/src/components/chart/time-axis-label.tsx +0 -175
  502. package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
  503. package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
  504. package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
  505. package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
  506. package/src/plugins/builtin/econ/fred-cache.ts +0 -106
  507. package/src/plugins/builtin/plugin-groups.ts +0 -130
  508. package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
  509. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
  510. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
  511. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
  512. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
  513. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
  514. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
  515. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
  516. package/src/plugins/builtin/ticker-detail/data-panes.test.ts +0 -220
@@ -0,0 +1,202 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { PiCatalog, PiProviderSummary } from "../../plugins/builtin/ai/pi";
3
+ import type { AiProviderId } from "../../plugins/builtin/ai/providers";
4
+ import { createDefaultConfig } from "../../types/config";
5
+ import type { CliCommandContext } from "../../types/plugin";
6
+ import { createAiCliCommand } from "./automation";
7
+
8
+ function piProvider(
9
+ id: AiProviderId,
10
+ connection: PiProviderSummary["connection"],
11
+ ): PiProviderSummary {
12
+ return {
13
+ id,
14
+ label: id,
15
+ name: id,
16
+ authMethods: [],
17
+ connection,
18
+ models: [],
19
+ };
20
+ }
21
+
22
+ function createHarness(options: {
23
+ catalog: PiCatalog;
24
+ configuredProviderId?: string;
25
+ configuredModelId?: string;
26
+ response?: string;
27
+ }) {
28
+ const config = createDefaultConfig("/tmp/gloomberb-ai-cli");
29
+ config.pluginConfig.ai = {
30
+ ...(options.configuredProviderId
31
+ ? { defaultProviderId: options.configuredProviderId }
32
+ : {}),
33
+ ...(options.configuredModelId
34
+ ? { defaultModelId: options.configuredModelId }
35
+ : {}),
36
+ };
37
+ const output: unknown[] = [];
38
+ const runs: Array<{ providerId: string; modelId?: string; prompt: string }> = [];
39
+ let closed = 0;
40
+ let runtimeDataDir = "";
41
+ const command = createAiCliCommand({
42
+ createRuntime(dataDir) {
43
+ runtimeDataDir = dataDir;
44
+ return {
45
+ getCatalog: async () => options.catalog,
46
+ runText(request) {
47
+ runs.push(request);
48
+ return {
49
+ done: Promise.resolve(options.response ?? "answer"),
50
+ cancel() {},
51
+ };
52
+ },
53
+ };
54
+ },
55
+ });
56
+ const context = {
57
+ initConfigData: async () => ({
58
+ config,
59
+ dataDir: config.dataDir,
60
+ persistence: { close: () => { closed += 1; } },
61
+ store: {},
62
+ }),
63
+ printResult: (result: { data: unknown }) => { output.push(result.data); },
64
+ fail(message: string): never {
65
+ throw new Error(message);
66
+ },
67
+ } as unknown as CliCommandContext;
68
+
69
+ return {
70
+ command,
71
+ context,
72
+ output,
73
+ runs,
74
+ closed: () => closed,
75
+ runtimeDataDir: () => runtimeDataDir,
76
+ };
77
+ }
78
+
79
+ describe("headless Pi AI command", () => {
80
+ test("lists canonical Pi providers and their connection state", async () => {
81
+ const harness = createHarness({
82
+ catalog: {
83
+ providers: [
84
+ piProvider("anthropic", {
85
+ state: "connected",
86
+ type: "oauth",
87
+ source: "Claude Pro/Max",
88
+ origin: "stored",
89
+ disconnectable: true,
90
+ }),
91
+ piProvider("openai-codex", { state: "not_connected" }),
92
+ ],
93
+ refreshErrors: {},
94
+ },
95
+ });
96
+
97
+ await harness.command.execute(["providers"], harness.context);
98
+
99
+ expect(harness.runtimeDataDir()).toBe("/tmp/gloomberb-ai-cli");
100
+ expect(harness.output[0]).toEqual([
101
+ {
102
+ id: "anthropic",
103
+ name: "anthropic",
104
+ connectionState: "connected",
105
+ connectionSource: "Claude Pro/Max",
106
+ connectionError: "",
107
+ availableModels: 0,
108
+ },
109
+ {
110
+ id: "openai-codex",
111
+ name: "openai-codex",
112
+ connectionState: "not_connected",
113
+ connectionSource: "",
114
+ connectionError: "",
115
+ availableModels: 0,
116
+ },
117
+ ]);
118
+ expect(harness.closed()).toBe(1);
119
+ });
120
+
121
+ test("uses the configured connected provider and model through Pi", async () => {
122
+ const harness = createHarness({
123
+ catalog: {
124
+ providers: [
125
+ piProvider("google", {
126
+ state: "connected",
127
+ type: "oauth",
128
+ origin: "stored",
129
+ disconnectable: true,
130
+ }),
131
+ piProvider("anthropic", {
132
+ state: "connected",
133
+ type: "oauth",
134
+ origin: "stored",
135
+ disconnectable: true,
136
+ }),
137
+ ],
138
+ refreshErrors: {},
139
+ },
140
+ configuredProviderId: "claude",
141
+ configuredModelId: "claude-opus-4-8",
142
+ response: "Pi answer",
143
+ });
144
+
145
+ await harness.command.execute(["ask", "Explain", "cash", "flow"], harness.context);
146
+
147
+ expect(harness.runs).toEqual([{
148
+ providerId: "anthropic",
149
+ modelId: "claude-opus-4-8",
150
+ prompt: "Explain cash flow",
151
+ }]);
152
+ expect(harness.output[0]).toEqual({
153
+ provider: "anthropic",
154
+ model: "claude-opus-4-8",
155
+ text: "Pi answer",
156
+ });
157
+ expect(harness.closed()).toBe(1);
158
+ });
159
+
160
+ test("supports explicit canonicalized overrides and rejects disconnected accounts", async () => {
161
+ const connected = createHarness({
162
+ catalog: {
163
+ providers: [piProvider("google", {
164
+ state: "connected",
165
+ type: "oauth",
166
+ origin: "stored",
167
+ disconnectable: true,
168
+ })],
169
+ refreshErrors: {},
170
+ },
171
+ });
172
+
173
+ await connected.command.execute([
174
+ "ask",
175
+ "--provider",
176
+ "gemini",
177
+ "--model",
178
+ "gemini-2.5-pro",
179
+ "Find",
180
+ "compounders",
181
+ ], connected.context);
182
+
183
+ expect(connected.runs).toEqual([{
184
+ providerId: "google",
185
+ modelId: "gemini-2.5-pro",
186
+ prompt: "Find compounders",
187
+ }]);
188
+
189
+ const disconnected = createHarness({
190
+ catalog: {
191
+ providers: [piProvider("anthropic", { state: "not_connected" })],
192
+ refreshErrors: {},
193
+ },
194
+ });
195
+ await expect(disconnected.command.execute(
196
+ ["ask", "--provider", "claude", "Hello"],
197
+ disconnected.context,
198
+ )).rejects.toThrow("is not connected");
199
+ expect(disconnected.runs).toEqual([]);
200
+ expect(disconnected.closed()).toBe(1);
201
+ });
202
+ });
@@ -1,5 +1,13 @@
1
- import { detectProviders, getAiProvider } from "../../plugins/builtin/ai/providers";
2
- import { runAiPrompt } from "../../plugins/builtin/ai/runner";
1
+ import {
2
+ PiAiRuntime,
3
+ type PiCatalog,
4
+ type PiTextRunController,
5
+ } from "../../plugins/builtin/ai/pi";
6
+ import {
7
+ isAiProviderId,
8
+ migrateLegacyAiProviderId,
9
+ type AiProviderId,
10
+ } from "../../plugins/builtin/ai/providers";
3
11
  import { createRssNewsCapability } from "../../plugins/builtin/news/wire/rss/source";
4
12
  import type { CliCommandDef } from "../../types/plugin";
5
13
  import { requireArg, takeOption } from "./command-utils";
@@ -84,42 +92,151 @@ export const ibkrCliCommand: CliCommandDef = {
84
92
  },
85
93
  };
86
94
 
87
- export const aiCliCommand: CliCommandDef = {
88
- name: "ai",
89
- description: "Inspect AI providers and run guarded headless AI prompts",
90
- help: { usage: ["ai providers", "ai ask [--provider id] <prompt>", "ai screen list|show|delete|refresh|export"] },
91
- execute: async (args, ctx) => {
92
- const action = args[0] ?? "providers";
93
- if (action === "providers") {
94
- ctx.printResult({ data: detectProviders().map((provider) => ({
95
- id: provider.id,
96
- name: provider.name,
97
- command: provider.command,
98
- available: provider.available,
99
- })) });
100
- return;
101
- }
102
- if (action === "ask") {
95
+ interface HeadlessPiRuntime {
96
+ getCatalog(): Promise<PiCatalog>;
97
+ runText(options: {
98
+ providerId: AiProviderId;
99
+ modelId?: string;
100
+ prompt: string;
101
+ }): PiTextRunController;
102
+ }
103
+
104
+ interface CreateAiCliCommandOptions {
105
+ createRuntime?: (dataDir: string) => HeadlessPiRuntime;
106
+ }
107
+
108
+ function configuredAiSelection(config: Record<string, unknown> | undefined): {
109
+ providerId: AiProviderId | null;
110
+ modelId: string | null;
111
+ } {
112
+ const rawProviderId = typeof config?.defaultProviderId === "string"
113
+ ? config.defaultProviderId.trim()
114
+ : "";
115
+ const providerId = migrateLegacyAiProviderId(rawProviderId);
116
+ const modelId = typeof config?.defaultModelId === "string"
117
+ ? config.defaultModelId.trim()
118
+ : "";
119
+ return {
120
+ providerId: isAiProviderId(providerId) ? providerId : null,
121
+ modelId: modelId || null,
122
+ };
123
+ }
124
+
125
+ function connectionFailure(provider: PiCatalog["providers"][number]): string {
126
+ if (provider.connection.state === "error") {
127
+ return `${provider.label} could not connect: ${provider.connection.message}`;
128
+ }
129
+ return `${provider.label} is not connected. Connect it from AI pane settings first.`;
130
+ }
131
+
132
+ export function createAiCliCommand(options: CreateAiCliCommandOptions = {}): CliCommandDef {
133
+ const createRuntime = options.createRuntime ?? ((dataDir: string) => new PiAiRuntime({ dataDir }));
134
+ return {
135
+ name: "ai",
136
+ description: "Inspect AI providers and run guarded headless AI prompts",
137
+ help: {
138
+ usage: [
139
+ "ai providers",
140
+ "ai ask [--provider id] [--model id] <prompt>",
141
+ "ai screen list|show|delete|refresh|export",
142
+ ],
143
+ },
144
+ execute: async (args, ctx) => {
145
+ const action = args[0] ?? "providers";
146
+ if (action === "screen") {
147
+ ctx.printResult({
148
+ data: deferredResult("ai screen", "not_implemented", "AI screener tabs are still pane-state backed; expose them through a shared non-UI screener store before headless mutation."),
149
+ });
150
+ return;
151
+ }
152
+ if (action !== "providers" && action !== "ask") {
153
+ ctx.fail("Usage: gloomberb ai providers|ask|screen");
154
+ }
155
+
103
156
  const rawArgs = args.slice(1);
104
- const providerId = takeOption(rawArgs, "--provider") ?? detectProviders().find((provider) => provider.available)?.id;
105
- const selectedProvider = getAiProvider(providerId) ?? ctx.fail("Unknown AI provider.");
106
- if (!selectedProvider.available) ctx.fail(`${selectedProvider.name} is not installed or not available in PATH.`);
157
+ const requestedProvider = takeOption(rawArgs, "--provider")?.trim();
158
+ const requestedModel = takeOption(rawArgs, "--model")?.trim();
107
159
  const prompt = rawArgs.join(" ").trim();
108
- if (!prompt) ctx.fail("Usage: gloomberb ai ask [--provider id] <prompt>");
109
- const controller = runAiPrompt({ provider: selectedProvider, prompt, cwd: process.cwd() });
110
- const text = await controller.done;
111
- ctx.printResult({ data: { provider: selectedProvider.id, text } });
112
- return;
113
- }
114
- if (action === "screen") {
115
- ctx.printResult({
116
- data: deferredResult("ai screen", "not_implemented", "AI screener tabs are still pane-state backed; expose them through a shared non-UI screener store before headless mutation."),
117
- });
118
- return;
119
- }
120
- ctx.fail("Usage: gloomberb ai providers|ask|screen");
121
- },
122
- };
160
+ if (action === "ask" && !prompt) {
161
+ ctx.fail("Usage: gloomberb ai ask [--provider id] [--model id] <prompt>");
162
+ }
163
+
164
+ const context = await ctx.initConfigData();
165
+ try {
166
+ const runtime = createRuntime(context.dataDir);
167
+ const catalog = await runtime.getCatalog();
168
+ if (action === "providers") {
169
+ ctx.printResult({
170
+ data: catalog.providers.map((provider) => ({
171
+ id: provider.id,
172
+ name: provider.label,
173
+ connectionState: provider.connection.state,
174
+ connectionSource: provider.connection.state === "connected"
175
+ ? provider.connection.source ?? ""
176
+ : "",
177
+ connectionError: provider.connection.state === "error"
178
+ ? provider.connection.message
179
+ : "",
180
+ availableModels: provider.models.filter((model) => model.available).length,
181
+ })),
182
+ });
183
+ return;
184
+ }
185
+
186
+ const configured = configuredAiSelection(context.config.pluginConfig.ai);
187
+ const canonicalRequestedId = requestedProvider
188
+ ? migrateLegacyAiProviderId(requestedProvider)
189
+ : null;
190
+ const requestedProviderId = canonicalRequestedId && isAiProviderId(canonicalRequestedId)
191
+ ? canonicalRequestedId
192
+ : null;
193
+ const requested = requestedProviderId
194
+ ? catalog.providers.find((provider) => provider.id === requestedProviderId)
195
+ : null;
196
+ if (requestedProvider && (!requestedProviderId || !requested)) {
197
+ ctx.fail(`Unknown AI provider: ${requestedProvider}.`);
198
+ }
199
+
200
+ const configuredProvider = configured.providerId
201
+ ? catalog.providers.find((provider) => (
202
+ provider.id === configured.providerId
203
+ && provider.connection.state === "connected"
204
+ ))
205
+ : null;
206
+ const selected = requested
207
+ ?? configuredProvider
208
+ ?? catalog.providers.find((provider) => provider.connection.state === "connected");
209
+ if (!selected) {
210
+ ctx.fail("No AI provider is connected. Connect an account from AI pane settings first.");
211
+ throw new Error("AI provider selection failed.");
212
+ }
213
+ if (selected.connection.state !== "connected") {
214
+ ctx.fail(connectionFailure(selected));
215
+ }
216
+
217
+ const modelId = requestedModel
218
+ || (selected.id === configured.providerId ? configured.modelId : null)
219
+ || undefined;
220
+ const text = await runtime.runText({
221
+ providerId: selected.id,
222
+ modelId,
223
+ prompt,
224
+ }).done;
225
+ ctx.printResult({
226
+ data: {
227
+ provider: selected.id,
228
+ model: modelId ?? null,
229
+ text,
230
+ },
231
+ });
232
+ } finally {
233
+ context.persistence.close();
234
+ }
235
+ },
236
+ };
237
+ }
238
+
239
+ export const aiCliCommand = createAiCliCommand();
123
240
 
124
241
  export const rssCliCommand: CliCommandDef = {
125
242
  name: "rss",
@@ -466,20 +466,7 @@ async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["exe
466
466
  if (symbols.length === 0) ctx.fail("Usage: gloomberb earnings <symbol...>");
467
467
  const services = await ctx.initServices();
468
468
  try {
469
- let events: EarningsEvent[] = [];
470
- for (const { capability } of services.services.pluginRegistry.capabilities.list("asset-data")) {
471
- if (!capability.operations.getEarningsCalendar?.handler) continue;
472
- try {
473
- events = await services.services.pluginRegistry.capabilities.invoke<EarningsEvent[]>(
474
- capability.id,
475
- "getEarningsCalendar",
476
- { symbols },
477
- );
478
- } catch {
479
- continue;
480
- }
481
- if (events.length > 0) break;
482
- }
469
+ const events = await services.dataProvider.getEarningsCalendar(symbols);
483
470
  ctx.printResult({ data: events }, {
484
471
  rows: earningsRows,
485
472
  columns: [
@@ -242,6 +242,9 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
242
242
  }
243
243
  if (action === "enable" || action === "disable") {
244
244
  const id = requireArg(args[1], `Usage: gloomberb plugin ${action} <id>`, ctx);
245
+ const plugin = services.services.pluginRegistry.allPlugins.get(id);
246
+ if (!plugin) ctx.fail(`Plugin "${id}" is not available.`);
247
+ if (plugin?.toggleable !== true) ctx.fail(`Plugin "${id}" is part of the application and cannot be disabled.`);
245
248
  const disabled = new Set(services.config.disabledPlugins ?? []);
246
249
  const before = disabled.has(id);
247
250
  if (action === "enable") disabled.delete(id);
@@ -291,7 +294,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
291
294
  kind: "pane",
292
295
  id,
293
296
  name: pane.name,
294
- owner: "",
297
+ owner: services.services.pluginRegistry.getPanePluginId(id) ?? "",
295
298
  })),
296
299
  ...[...services.services.pluginRegistry.paneTemplates.entries()].map(([id, template]) => ({
297
300
  kind: "template",
@@ -80,7 +80,7 @@ export async function initCliServices(options: CliServicesOptions = {}) {
80
80
  const config = await loadConfig(dataDir);
81
81
  const services = createAppServices({
82
82
  config,
83
- externalPlugins: options.externalPlugins ?? [],
83
+ plugins: getLoadablePlugins(options.externalPlugins ?? []),
84
84
  });
85
85
  services.providerRouter.setConfigAccessor(() => config);
86
86
  await services.ready;
@@ -6,6 +6,8 @@ import type { AppConfig } from "../types/config";
6
6
  import type { OptionsChain, TickerFinancials } from "../types/financials";
7
7
  import type { TickerRecord } from "../types/ticker";
8
8
  import type { PaneRuntimeState } from "../core/state/app/state";
9
+ import type { RemoteUiNodeSnapshot } from "../remote/types";
10
+ import type { FredSeriesCacheEntry } from "../data/fred-series";
9
11
  import {
10
12
  electrobunViewPath,
11
13
  writeElectrobunViewPage,
@@ -21,9 +23,17 @@ export interface DesktopPaneShotPayload {
21
23
  tickers: TickerRecord[];
22
24
  financials: Array<[string, TickerFinancials]>;
23
25
  optionsChains: Array<[string, OptionsChain]>;
26
+ fredSeries: Array<[string, FredSeriesCacheEntry]>;
24
27
  paneState: Record<string, PaneRuntimeState>;
25
28
  }
26
29
 
30
+ export interface DesktopPaneShotRenderResult {
31
+ visibleText: string;
32
+ emptyStateDetected: boolean;
33
+ emptyStateMarkers: string[];
34
+ semanticUi: RemoteUiNodeSnapshot[];
35
+ }
36
+
27
37
  type CdpResponse = {
28
38
  id?: number;
29
39
  result?: unknown;
@@ -43,14 +53,14 @@ const SHOT_DEVICE_SCALE_FACTOR = 2;
43
53
  export async function renderDesktopPaneScreenshot(
44
54
  payload: DesktopPaneShotPayload,
45
55
  outputPath: string,
46
- ): Promise<void> {
56
+ ): Promise<DesktopPaneShotRenderResult> {
47
57
  const tempDir = await mkdtemp(join(tmpdir(), "gloom-pane-shot-"));
48
58
  try {
49
59
  const outdir = join(tempDir, "assets");
50
60
  await mkdir(outdir, { recursive: true });
51
61
  const htmlPath = await buildShotPage(outdir, payload);
52
62
  const chrome = await findChromeExecutable();
53
- await capturePageScreenshot({
63
+ return await capturePageScreenshot({
54
64
  chrome,
55
65
  url: pathToFileURL(htmlPath).href,
56
66
  outputPath,
@@ -119,7 +129,7 @@ async function capturePageScreenshot({
119
129
  widthPx: number;
120
130
  heightPx: number;
121
131
  userDataDir: string;
122
- }): Promise<void> {
132
+ }): Promise<DesktopPaneShotRenderResult> {
123
133
  await mkdir(userDataDir, { recursive: true });
124
134
  const port = 43000 + Math.floor(Math.random() * 10000);
125
135
  const proc = Bun.spawn([
@@ -158,13 +168,15 @@ async function capturePageScreenshot({
158
168
  mobile: false,
159
169
  });
160
170
  await waitForShotReady(session);
171
+ const rendered = await readRenderedPaneState(session);
161
172
  const screenshot = await session.send("Page.captureScreenshot", {
162
173
  format: "png",
163
174
  fromSurface: true,
164
175
  captureBeyondViewport: false,
165
176
  }) as { data?: string };
166
177
  if (!screenshot.data) throw new Error("Chrome did not return screenshot data.");
167
- await writeFile(outputPath, Buffer.from(screenshot.data, "base64"));
178
+ await writeFile(outputPath, Uint8Array.from(Buffer.from(screenshot.data, "base64")));
179
+ return rendered;
168
180
  } catch (error) {
169
181
  const message = error instanceof Error ? error.message : String(error);
170
182
  throw new Error(message);
@@ -175,6 +187,55 @@ async function capturePageScreenshot({
175
187
  }
176
188
  }
177
189
 
190
+ const EMPTY_STATE_PATTERNS = [
191
+ /\bNo chart data\b/gi,
192
+ /\bNo graph data\b/gi,
193
+ /\bNo historical prices\b/gi,
194
+ /\bNo financial statement rows\b/gi,
195
+ /\bNo comparison tickers configured\b/gi,
196
+ /\bNo relationship tickers configured\b/gi,
197
+ /\bNo overlapping price history\b/gi,
198
+ /\bLoading chart\.{0,3}\b/gi,
199
+ /\bRendering pane\.{0,3}\b/gi,
200
+ /\bNo tickers selected\b/gi,
201
+ /\bMarket data unavailable\b/gi,
202
+ /\bNo data(?: available)?\b/gi,
203
+ ];
204
+
205
+ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneShotRenderResult> {
206
+ const result = await session.send("Runtime.evaluate", {
207
+ expression: `(() => {
208
+ const root = document.getElementById("root") || document.body;
209
+ return {
210
+ visibleText: root.innerText || root.textContent || "",
211
+ error: window.__GLOOM_CLI_SHOT_ERROR__ || "",
212
+ semanticUi: window.__GLOOM_CLI_SHOT_SEMANTIC_UI__ || [],
213
+ };
214
+ })()`,
215
+ returnByValue: true,
216
+ }) as {
217
+ result?: {
218
+ value?: {
219
+ visibleText?: string;
220
+ error?: string;
221
+ semanticUi?: RemoteUiNodeSnapshot[];
222
+ };
223
+ };
224
+ };
225
+ const value = result.result?.value;
226
+ if (value?.error) throw new Error(value.error);
227
+ const visibleText = (value?.visibleText ?? "").replace(/\s+/g, " ").trim();
228
+ const emptyStateMarkers = [...new Set(EMPTY_STATE_PATTERNS.flatMap((pattern) => (
229
+ [...visibleText.matchAll(pattern)].map((match) => match[0])
230
+ )))];
231
+ return {
232
+ visibleText,
233
+ emptyStateDetected: emptyStateMarkers.length > 0,
234
+ emptyStateMarkers,
235
+ semanticUi: Array.isArray(value?.semanticUi) ? value.semanticUi : [],
236
+ };
237
+ }
238
+
178
239
  async function waitForPageWebSocket(port: number, targetUrl: string): Promise<string> {
179
240
  for (let attempt = 0; attempt < CHROME_POLL_ATTEMPTS; attempt += 1) {
180
241
  try {
package/src/cli/entry.ts CHANGED
@@ -23,7 +23,7 @@ export async function runCliEntrypoint(rawArgs = process.argv.slice(2)): Promise
23
23
 
24
24
  if (command === OPEN_TUI_NATIVE_SMOKE_COMMAND) {
25
25
  await smokeOpenTuiNative();
26
- return;
26
+ process.exit(0);
27
27
  }
28
28
 
29
29
  const externalPlugins = await loadExternalPlugins();