gloomberb 0.9.9 → 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 (519) 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 +40 -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 +190 -3
  368. package/src/sync/controller.ts +76 -45
  369. package/src/sync/core-contributors.test.ts +121 -0
  370. package/src/sync/core-contributors.ts +104 -27
  371. package/src/sync/react.ts +6 -8
  372. package/src/sync/types.ts +3 -0
  373. package/src/time-series/alignment.ts +158 -0
  374. package/src/time-series/economic.ts +49 -0
  375. package/src/time-series/field-catalog.ts +306 -0
  376. package/src/time-series/fundamentals.test.ts +505 -0
  377. package/src/time-series/fundamentals.ts +842 -0
  378. package/src/time-series/hooks.ts +45 -0
  379. package/src/time-series/index.ts +14 -0
  380. package/src/time-series/live-quotes.test.ts +219 -0
  381. package/src/time-series/live-quotes.ts +203 -0
  382. package/src/time-series/market-resolution.ts +53 -0
  383. package/src/time-series/market.ts +125 -0
  384. package/src/time-series/reporting.ts +98 -0
  385. package/src/time-series/resolve.test.ts +1281 -0
  386. package/src/time-series/resolve.ts +783 -0
  387. package/src/time-series/spec-studies.test.ts +400 -0
  388. package/src/time-series/spec.ts +487 -0
  389. package/src/time-series/studies.ts +563 -0
  390. package/src/time-series/transforms.test.ts +105 -0
  391. package/src/time-series/transforms.ts +152 -0
  392. package/src/time-series/types.ts +162 -0
  393. package/src/time-series/use-chart-resolution.ts +166 -0
  394. package/src/types/config.ts +9 -11
  395. package/src/types/desktop-window.ts +1 -0
  396. package/src/types/financials.ts +4 -0
  397. package/src/types/media.ts +17 -0
  398. package/src/types/plugin.ts +32 -1
  399. package/src/ui/context-menu.tsx +15 -3
  400. package/src/ui/dialog.tsx +14 -2
  401. package/src/ui/host.tsx +40 -1
  402. package/src/ui/index.tsx +9 -0
  403. package/src/utils/exchanges.test.ts +13 -1
  404. package/src/utils/exchanges.ts +10 -0
  405. package/src/utils/financial-statements.test.ts +138 -0
  406. package/src/utils/financial-statements.ts +115 -8
  407. package/src/utils/format.test.ts +11 -1
  408. package/src/utils/format.ts +7 -0
  409. package/src/version.ts +1 -1
  410. package/src/components/chart/bar-chart-renderer.test.ts +0 -233
  411. package/src/components/chart/bar-chart-renderer.ts +0 -445
  412. package/src/components/chart/comparison/data.test.ts +0 -159
  413. package/src/components/chart/comparison/data.ts +0 -302
  414. package/src/components/chart/comparison/renderer.test.ts +0 -187
  415. package/src/components/chart/comparison/renderer.ts +0 -282
  416. package/src/components/chart/comparison/scene.ts +0 -178
  417. package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
  418. package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
  419. package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
  420. package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
  421. package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
  422. package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
  423. package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
  424. package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
  425. package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
  426. package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
  427. package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
  428. package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
  429. package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
  430. package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
  431. package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
  432. package/src/components/chart/comparison-stock-chart/types.ts +0 -22
  433. package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
  434. package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
  435. package/src/components/chart/comparison-stock-chart.tsx +0 -24
  436. package/src/components/chart/core/axis-measure.ts +0 -33
  437. package/src/components/chart/core/controller.test.ts +0 -157
  438. package/src/components/chart/core/controller.ts +0 -349
  439. package/src/components/chart/core/indicator-selector.tsx +0 -75
  440. package/src/components/chart/core/keyboard.ts +0 -24
  441. package/src/components/chart/core/pane-settings.test.ts +0 -52
  442. package/src/components/chart/core/pane-settings.ts +0 -141
  443. package/src/components/chart/core/scroll.test.ts +0 -64
  444. package/src/components/chart/core/scroll.ts +0 -82
  445. package/src/components/chart/core/window-viewport.test.ts +0 -69
  446. package/src/components/chart/core/window-viewport.ts +0 -162
  447. package/src/components/chart/display-cursor.ts +0 -153
  448. package/src/components/chart/indicators/bands.test.ts +0 -36
  449. package/src/components/chart/indicators/bands.ts +0 -29
  450. package/src/components/chart/indicators/moving-averages.test.ts +0 -39
  451. package/src/components/chart/indicators/moving-averages.ts +0 -42
  452. package/src/components/chart/indicators/options.test.ts +0 -32
  453. package/src/components/chart/indicators/options.ts +0 -107
  454. package/src/components/chart/indicators/oscillators.test.ts +0 -58
  455. package/src/components/chart/indicators/oscillators.ts +0 -92
  456. package/src/components/chart/native/canvas-bitmaps.ts +0 -140
  457. package/src/components/chart/native/raster/comparison-chart.ts +0 -162
  458. package/src/components/chart/native/surface/hooks.ts +0 -144
  459. package/src/components/chart/native/surface/rendering.ts +0 -211
  460. package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
  461. package/src/components/chart/static/bar-chart-surface.tsx +0 -292
  462. package/src/components/chart/stock-chart/auto.ts +0 -216
  463. package/src/components/chart/stock-chart/axis-width.ts +0 -146
  464. package/src/components/chart/stock-chart/bitmaps.ts +0 -22
  465. package/src/components/chart/stock-chart/canvas.ts +0 -202
  466. package/src/components/chart/stock-chart/controls.tsx +0 -149
  467. package/src/components/chart/stock-chart/cursor.ts +0 -60
  468. package/src/components/chart/stock-chart/data-runtime.ts +0 -128
  469. package/src/components/chart/stock-chart/footer.test.ts +0 -65
  470. package/src/components/chart/stock-chart/footer.ts +0 -303
  471. package/src/components/chart/stock-chart/geometry.ts +0 -160
  472. package/src/components/chart/stock-chart/index.test.ts +0 -532
  473. package/src/components/chart/stock-chart/index.tsx +0 -29
  474. package/src/components/chart/stock-chart/indicators.ts +0 -226
  475. package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
  476. package/src/components/chart/stock-chart/interactions.ts +0 -300
  477. package/src/components/chart/stock-chart/keyboard.ts +0 -373
  478. package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
  479. package/src/components/chart/stock-chart/layout.tsx +0 -177
  480. package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
  481. package/src/components/chart/stock-chart/presentation.ts +0 -67
  482. package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
  483. package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
  484. package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
  485. package/src/components/chart/stock-chart/rendering/data.ts +0 -370
  486. package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
  487. package/src/components/chart/stock-chart/rendering/output.ts +0 -304
  488. package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
  489. package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
  490. package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
  491. package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
  492. package/src/components/chart/stock-chart/rendering/state.ts +0 -283
  493. package/src/components/chart/stock-chart/requests.ts +0 -337
  494. package/src/components/chart/stock-chart/resolution-support.ts +0 -96
  495. package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
  496. package/src/components/chart/stock-chart/resolved.tsx +0 -359
  497. package/src/components/chart/stock-chart/settings.ts +0 -81
  498. package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
  499. package/src/components/chart/stock-chart/types.ts +0 -25
  500. package/src/components/chart/stock-chart/view.tsx +0 -155
  501. package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
  502. package/src/components/chart/stock-chart/viewport/index.ts +0 -144
  503. package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
  504. package/src/components/chart/time-axis-label.tsx +0 -175
  505. package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
  506. package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
  507. package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
  508. package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
  509. package/src/plugins/builtin/econ/fred-cache.ts +0 -106
  510. package/src/plugins/builtin/plugin-groups.ts +0 -130
  511. package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
  512. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
  513. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
  514. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
  515. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
  516. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
  517. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
  518. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
  519. 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();