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.
- package/README.md +41 -3
- package/README.zh-CN.md +294 -0
- package/package.json +10 -4
- package/patches/@opentui%2Fcore@0.3.2.patch +26 -69
- package/src/api-client/auth.ts +2 -0
- package/src/api-client/index.test.ts +4 -0
- package/src/api-client/types.ts +3 -0
- package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -0
- package/src/app/pane-template-dialog-wizard.test.tsx +36 -0
- package/src/app/pane-template-dialog-wizard.tsx +10 -3
- package/src/app/runtime/desktop-deeplink.test.ts +15 -0
- package/src/app/runtime/desktop-deeplink.ts +21 -6
- package/src/app.tsx +40 -33
- package/src/capabilities/service-capabilities.ts +7 -2
- package/src/cli/commands/automation.test.ts +202 -0
- package/src/cli/commands/automation.ts +153 -36
- package/src/cli/commands/market.ts +1 -14
- package/src/cli/commands/system.ts +4 -1
- package/src/cli/context.ts +1 -1
- package/src/cli/desktop-pane-shot.ts +65 -4
- package/src/cli/entry.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +532 -0
- package/src/cli/pane-functions/catalog.ts +39 -4
- package/src/cli/pane-functions/data.test.ts +17 -0
- package/src/cli/pane-functions/data.ts +55 -13
- package/src/cli/pane-functions/discovery.ts +36 -9
- package/src/cli/pane-functions/index.test.ts +104 -1
- package/src/cli/pane-functions/index.ts +64 -5
- package/src/cli/pane-functions/options.ts +21 -6
- package/src/cli/pane-functions/report.ts +735 -72
- package/src/cli/pane-functions/resolver.ts +70 -6
- package/src/cli/pane-functions/screenshot.test.ts +431 -0
- package/src/cli/pane-functions/screenshot.ts +952 -8
- package/src/components/chart/composite/column-layout.ts +215 -0
- package/src/components/chart/composite/composite-chart.test.tsx +630 -0
- package/src/components/chart/composite/composite-chart.tsx +1148 -0
- package/src/components/chart/composite/format.test.ts +137 -0
- package/src/components/chart/composite/format.ts +135 -0
- package/src/components/chart/composite/index.ts +38 -0
- package/src/components/chart/composite/interactions.test.ts +109 -0
- package/src/components/chart/composite/interactions.ts +247 -0
- package/src/components/chart/composite/price-series.test.ts +39 -0
- package/src/components/chart/composite/price-series.ts +78 -0
- package/src/components/chart/composite/rasterizer.ts +359 -0
- package/src/components/chart/composite/renderers.test.ts +610 -0
- package/src/components/chart/composite/scene.test.ts +464 -0
- package/src/components/chart/composite/scene.ts +553 -0
- package/src/components/chart/composite/text-renderer.ts +285 -0
- package/src/components/chart/composite/time-scale.test.ts +88 -0
- package/src/components/chart/composite/time-scale.ts +217 -0
- package/src/components/chart/composite/types.ts +141 -0
- package/src/components/chart/core/data.ts +81 -5
- package/src/components/chart/core/index.test.ts +128 -43
- package/src/components/chart/core/resolution.ts +0 -6
- package/src/components/chart/native/chart-rasterizer.ts +1 -3
- package/src/components/chart/native/kitty/index.test.ts +99 -120
- package/src/components/chart/native/kitty/manager.ts +3 -0
- package/src/components/chart/native/kitty/protocol.ts +4 -1
- package/src/components/chart/native/raster/crosshair.ts +79 -46
- package/src/components/chart/native/raster/types.ts +2 -11
- package/src/components/chart/native/surface/manager.ts +48 -3
- package/src/components/chart/price-axis-labels.tsx +18 -4
- package/src/components/command-bar/commands/direct/index.ts +25 -0
- package/src/components/command-bar/commands/registry.ts +9 -0
- package/src/components/command-bar/helpers.test.ts +4 -1
- package/src/components/command-bar/list/view.tsx +18 -6
- package/src/components/command-bar/multi-select-picker.tsx +6 -5
- package/src/components/command-bar/pane-settings.test.ts +141 -0
- package/src/components/command-bar/pane-settings.ts +61 -1
- package/src/components/command-bar/panel/body.tsx +7 -6
- package/src/components/command-bar/routes/root/command-items.ts +4 -4
- package/src/components/command-bar/routes/root/results.ts +10 -1
- package/src/components/command-bar/routes/root/selection.test.ts +54 -0
- package/src/components/command-bar/routes/root/selection.ts +13 -0
- package/src/components/command-bar/routing/actions.ts +18 -2
- package/src/components/command-bar/routing/list-state.ts +2 -1
- package/src/components/command-bar/surface/index.test.tsx +36 -4
- package/src/components/command-bar/surface/pane.test.tsx +93 -0
- package/src/components/command-bar/theme-picker.tsx +2 -1
- package/src/components/command-bar/view-model.ts +19 -20
- package/src/components/command-bar/workflow/body.tsx +2 -1
- package/src/components/command-bar/workflow/field-row.tsx +3 -2
- package/src/components/command-bar/workflow/fields.ts +4 -2
- package/src/components/command-bar/workflow/ops.test.ts +161 -0
- package/src/components/command-bar/workflow/ops.ts +20 -50
- package/src/components/command-bar/workflow/route-actions.test.ts +34 -0
- package/src/components/command-bar/workflow/route-actions.ts +4 -0
- package/src/components/command-bar/workflow/types.ts +1 -0
- package/src/components/feed-data-table/stack-view.tsx +6 -5
- package/src/components/index.ts +13 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -1
- package/src/components/layout/floating-pane.tsx +3 -0
- package/src/components/layout/header.tsx +15 -8
- package/src/components/layout/pane/footer/index.test.tsx +22 -3
- package/src/components/layout/pane/footer/index.tsx +3 -1
- package/src/components/layout/pane/footer/model.ts +9 -2
- package/src/components/layout/pane/header.tsx +4 -6
- package/src/components/layout/pane/index.tsx +3 -0
- package/src/components/layout/pane/sidebar.test.tsx +100 -0
- package/src/components/layout/pane/sidebar.tsx +239 -0
- package/src/components/layout/pane/title.ts +4 -3
- package/src/components/layout/shell/action-menu-overlay.tsx +5 -3
- package/src/components/layout/shell/drag/runtime.ts +2 -0
- package/src/components/layout/shell/menu.test.ts +19 -0
- package/src/components/layout/shell/menu.ts +8 -5
- package/src/components/layout/shell/native/pointer-runtime.ts +4 -1
- package/src/components/layout/shell/pane/layers.tsx +2 -2
- package/src/components/layout/status-bar.tsx +8 -7
- package/src/components/layout/window-controls.tsx +4 -3
- package/src/components/metric-treemap/index.tsx +3 -2
- package/src/components/onboarding/onboarding-steps.tsx +30 -29
- package/src/components/onboarding/onboarding-wizard.tsx +5 -4
- package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +15 -12
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +17 -16
- package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +6 -5
- package/src/components/onboarding/portfolio-step/choose-panel.tsx +5 -4
- package/src/components/pane-settings-dialog/desktop.tsx +25 -3
- package/src/components/pane-settings-dialog/field-dialogs.tsx +5 -4
- package/src/components/pane-settings-dialog/tui.tsx +11 -9
- package/src/components/pane-settings-dialog/value.ts +8 -5
- package/src/components/pane-settings-dialog.test.tsx +235 -0
- package/src/components/pane-settings-dialog.tsx +24 -0
- package/src/components/pane-template-wizard.tsx +6 -2
- package/src/components/ticker/list-table-view.tsx +3 -2
- package/src/components/ui/button.tsx +3 -1
- package/src/components/ui/choice-dialog.tsx +6 -1
- package/src/components/ui/confirm-dialog.tsx +3 -2
- package/src/components/ui/fields.tsx +10 -1
- package/src/components/ui/frame.tsx +4 -1
- package/src/components/ui/index.ts +5 -0
- package/src/components/ui/inline-quick-add.tsx +104 -0
- package/src/components/ui/list-view.tsx +5 -4
- package/src/components/ui/loading.tsx +2 -1
- package/src/components/ui/multi-select/dialog.tsx +188 -44
- package/src/components/ui/popover.tsx +10 -0
- package/src/components/ui/shortcut-hint.tsx +2 -0
- package/src/components/ui/status.tsx +4 -3
- package/src/components/ui/tabs.tsx +19 -8
- package/src/components/ui/toggle.tsx +44 -3
- package/src/components/ui/ui.test.tsx +67 -5
- package/src/core/app-services.ts +4 -6
- package/src/core/state/session-persistence.ts +3 -2
- package/src/data/config/chart-settings.ts +712 -0
- package/src/data/config/layout.ts +24 -5
- package/src/data/config/store/index.test.ts +377 -14
- package/src/data/config/store/normalize.ts +79 -65
- package/src/data/fred-series.ts +137 -0
- package/src/i18n/index.test.ts +114 -0
- package/src/i18n/index.ts +147 -0
- package/src/i18n/ja.ts +1013 -0
- package/src/i18n/ko.ts +1011 -0
- package/src/i18n/languages.test.ts +31 -0
- package/src/i18n/languages.ts +72 -0
- package/src/i18n/zh-cn.ts +1013 -0
- package/src/i18n/zh-tw.ts +1013 -0
- package/src/plugins/builtin/account-management/footer.ts +3 -2
- package/src/plugins/builtin/account-management/form-components.tsx +3 -2
- package/src/plugins/builtin/account-management/keyboard.ts +6 -0
- package/src/plugins/builtin/account-management/model.ts +25 -21
- package/src/plugins/builtin/account-management/navigation.test.ts +27 -0
- package/src/plugins/builtin/account-management/navigation.ts +24 -0
- package/src/plugins/builtin/account-management/pane.tsx +120 -85
- package/src/plugins/builtin/account-management/password-dialog.tsx +10 -9
- package/src/plugins/builtin/ai/agent-history.ts +139 -0
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +133 -51
- package/src/plugins/builtin/ai/ask-ai.test.tsx +83 -22
- package/src/plugins/builtin/ai/index.test.ts +302 -0
- package/src/plugins/builtin/ai/index.tsx +374 -40
- package/src/plugins/builtin/ai/pane-settings.test.ts +155 -0
- package/src/plugins/builtin/ai/pane-settings.ts +319 -0
- package/src/plugins/builtin/ai/pi/credential-store.test.ts +80 -0
- package/src/plugins/builtin/ai/pi/credential-store.ts +138 -0
- package/src/plugins/builtin/ai/pi/file-store.ts +138 -0
- package/src/plugins/builtin/ai/pi/host.test.ts +500 -0
- package/src/plugins/builtin/ai/pi/host.ts +564 -0
- package/src/plugins/builtin/ai/pi/index.ts +5 -0
- package/src/plugins/builtin/ai/pi/models-store.test.ts +44 -0
- package/src/plugins/builtin/ai/pi/models-store.ts +141 -0
- package/src/plugins/builtin/ai/pi/providers.ts +68 -0
- package/src/plugins/builtin/ai/pi/runtime.test.ts +240 -0
- package/src/plugins/builtin/ai/pi/runtime.ts +635 -0
- package/src/plugins/builtin/ai/providers.test.ts +68 -0
- package/src/plugins/builtin/ai/providers.ts +163 -38
- package/src/plugins/builtin/ai/runner-selection.test.ts +174 -0
- package/src/plugins/builtin/ai/runner-selection.ts +193 -0
- package/src/plugins/builtin/ai/runner-selector.tsx +153 -0
- package/src/plugins/builtin/ai/runner.test.ts +198 -0
- package/src/plugins/builtin/ai/runner.ts +276 -73
- package/src/plugins/builtin/ai/screener/contract.test.ts +26 -73
- package/src/plugins/builtin/ai/screener/contract.ts +14 -52
- package/src/plugins/builtin/ai/screener/editor-runtime.ts +55 -5
- package/src/plugins/builtin/ai/screener/editor.tsx +42 -36
- package/src/plugins/builtin/ai/screener/footer.ts +82 -67
- package/src/plugins/builtin/ai/screener/keyboard.ts +27 -11
- package/src/plugins/builtin/ai/screener/model.test.ts +24 -0
- package/src/plugins/builtin/ai/screener/model.ts +9 -3
- package/src/plugins/builtin/ai/screener/pane.test.tsx +142 -55
- package/src/plugins/builtin/ai/screener/pane.tsx +95 -97
- package/src/plugins/builtin/ai/screener/results-view.tsx +8 -41
- package/src/plugins/builtin/ai/screener/runner.ts +41 -25
- package/src/plugins/builtin/ai/screener/tabs-bar.tsx +2 -2
- package/src/plugins/builtin/ai/use-runtime-providers.ts +28 -0
- package/src/plugins/builtin/ai/workspace/model.test.ts +227 -0
- package/src/plugins/builtin/ai/workspace/model.ts +308 -0
- package/src/plugins/builtin/ai/workspace/pane.test.tsx +197 -0
- package/src/plugins/builtin/ai/workspace/pane.tsx +896 -0
- package/src/plugins/builtin/analytics/index.test.tsx +3 -3
- package/src/plugins/builtin/analytics/index.tsx +3 -8
- package/src/plugins/builtin/broker-manager/detail.tsx +25 -24
- package/src/plugins/builtin/broker-manager/index.test.tsx +1 -1
- package/src/plugins/builtin/broker-manager/index.tsx +9 -11
- package/src/plugins/builtin/broker-manager/model.ts +11 -10
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +22 -21
- package/src/plugins/builtin/broker-manager/table.ts +9 -8
- package/src/plugins/builtin/changelog/index.tsx +3 -8
- package/src/plugins/builtin/chart-composer/chart-spec.ts +89 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +98 -0
- package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +120 -0
- package/src/plugins/builtin/chart-composer/editor.test.tsx +332 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +1107 -0
- package/src/plugins/builtin/chart-composer/index.tsx +228 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +645 -0
- package/src/plugins/builtin/chart-composer/presets.test.ts +561 -0
- package/src/plugins/builtin/chart-composer/presets.ts +759 -0
- package/src/plugins/builtin/chart-composer/quick-add.test.tsx +245 -0
- package/src/plugins/builtin/chart-composer/quick-add.tsx +409 -0
- package/src/plugins/builtin/chart-composer/semantic.ts +86 -0
- package/src/plugins/builtin/chart-composer/series-catalog.test.ts +65 -0
- package/src/plugins/builtin/chart-composer/series-catalog.ts +295 -0
- package/src/plugins/builtin/chart-composer/settings.test.ts +163 -0
- package/src/plugins/builtin/chart-composer/settings.ts +397 -0
- package/src/plugins/builtin/chart-composer/shortcuts.test.ts +44 -0
- package/src/plugins/builtin/chart-composer/shortcuts.ts +35 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +103 -0
- package/src/plugins/builtin/chat/channel-labels.ts +5 -4
- package/src/plugins/builtin/chat/channels.ts +12 -11
- package/src/plugins/builtin/chat/chat.test.tsx +32 -0
- package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
- package/src/plugins/builtin/chat/content/composer.tsx +9 -8
- package/src/plugins/builtin/chat/content/index.tsx +36 -1
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +8 -7
- package/src/plugins/builtin/chat/content/transcript.tsx +9 -3
- package/src/plugins/builtin/chat/controller/utils.ts +10 -9
- package/src/plugins/builtin/chat/message/desktop.tsx +17 -10
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +122 -3
- package/src/plugins/builtin/chat/message/profile-popover.tsx +38 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +19 -10
- package/src/plugins/builtin/chat/pane-state.test.ts +31 -0
- package/src/plugins/builtin/chat/pane-state.ts +30 -0
- package/src/plugins/builtin/chat/pane.tsx +27 -4
- package/src/plugins/builtin/chat/profile-popover.ts +62 -5
- package/src/plugins/builtin/chat/sidebar.test.tsx +4 -7
- package/src/plugins/builtin/chat/sidebar.tsx +107 -168
- package/src/plugins/builtin/chat/test-harness.tsx +6 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +5 -4
- package/src/plugins/builtin/cloud/plugin.tsx +150 -127
- package/src/plugins/builtin/composite-plugins.ts +71 -0
- package/src/plugins/builtin/correlation/index.test.tsx +4 -4
- package/src/plugins/builtin/correlation/index.tsx +3 -8
- package/src/plugins/builtin/earnings/index.tsx +37 -21
- package/src/plugins/builtin/earnings/model.test.ts +52 -0
- package/src/plugins/builtin/earnings/model.ts +32 -0
- package/src/plugins/builtin/econ/detail-view.tsx +17 -4
- package/src/plugins/builtin/econ/fred-cache.test.ts +57 -11
- package/src/plugins/builtin/econ/index.tsx +16 -20
- package/src/plugins/builtin/fear-greed/index.tsx +2 -8
- package/src/plugins/builtin/fx-matrix/index.tsx +3 -8
- package/src/plugins/builtin/help/components.tsx +5 -4
- package/src/plugins/builtin/help/index.tsx +14 -17
- package/src/plugins/builtin/holders/index.tsx +2 -8
- package/src/plugins/builtin/insider/index.tsx +2 -7
- package/src/plugins/builtin/kelly-sizer/index.test.tsx +2 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +3 -7
- package/src/plugins/builtin/kelly-sizer/test-support.tsx +3 -3
- package/src/plugins/builtin/layout-manager/index.tsx +3 -7
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -8
- package/src/plugins/builtin/market-movers/index.tsx +3 -8
- package/src/plugins/builtin/news/index.tsx +12 -16
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +3 -3
- package/src/plugins/builtin/news/wire/index.tsx +83 -73
- package/src/plugins/builtin/options/index.tsx +2 -8
- package/src/plugins/builtin/plugin-module.test.ts +89 -0
- package/src/plugins/builtin/plugin-module.ts +116 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +4 -3
- package/src/plugins/builtin/portfolio-list/index.test.ts +17 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +3 -5
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +3 -3
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +53 -53
- package/src/plugins/builtin/portfolio-list/settings.ts +5 -3
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +4 -3
- package/src/plugins/builtin/research/index.tsx +2 -8
- package/src/plugins/builtin/sec/index.tsx +2 -8
- package/src/plugins/builtin/sectors/index.tsx +3 -8
- package/src/plugins/builtin/thirteenf/index.tsx +3 -8
- package/src/plugins/builtin/thirteenf/pane.test.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/chart-tab-switch.test.tsx +49 -23
- package/src/plugins/builtin/ticker-detail/index.test.tsx +15 -136
- package/src/plugins/builtin/ticker-detail/index.tsx +2 -93
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +6 -5
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -17
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -3
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -50
- package/src/plugins/builtin/ticker-research-backend-plugin.ts +17 -0
- package/src/plugins/builtin/ticker-research-plugin.ts +27 -0
- package/src/plugins/builtin/tv/channels.ts +27 -0
- package/src/plugins/builtin/tv/index.tsx +40 -0
- package/src/plugins/builtin/tv/pane.tsx +286 -0
- package/src/plugins/builtin/tv/youtube-stream.ts +100 -0
- package/src/plugins/builtin/world-indices/index.tsx +3 -8
- package/src/plugins/builtin/yield-curve/index.tsx +8 -8
- package/src/plugins/catalog-backend.test.ts +21 -0
- package/src/plugins/catalog-backend.ts +42 -0
- package/src/plugins/catalog-ui.ts +6 -14
- package/src/plugins/ibkr/instance-selection.test.ts +0 -1
- package/src/plugins/ownership.ts +143 -0
- package/src/plugins/prediction-markets/backend-plugin.ts +19 -0
- package/src/plugins/prediction-markets/chart.test.tsx +7 -5
- package/src/plugins/prediction-markets/chart.tsx +25 -7
- package/src/plugins/registry/index.test.ts +51 -0
- package/src/plugins/registry/index.ts +8 -0
- package/src/plugins/registry/pane-settings.ts +1 -1
- package/src/plugins/runtime/index.test.tsx +2 -2
- package/src/remote/app-host.tsx +16 -3
- package/src/remote/controller.test.ts +102 -1
- package/src/remote/controller.ts +122 -0
- package/src/remote/types.ts +11 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +123 -27
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/media.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/window-style.ts +4 -1
- package/src/renderers/electrobun/bun/desktop/workspace/index.test.ts +32 -1
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +10 -0
- package/src/renderers/electrobun/bun/index.ts +5 -0
- package/src/renderers/electrobun/bun/tui-entry.ts +1 -6
- package/src/renderers/electrobun/view/ai-host.ts +124 -19
- package/src/renderers/electrobun/view/backend-rpc.ts +4 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +151 -39
- package/src/renderers/electrobun/view/desktop/popover.tsx +166 -0
- package/src/renderers/electrobun/view/dialog-host.test.ts +25 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +75 -12
- package/src/renderers/electrobun/view/host/box.tsx +5 -0
- package/src/renderers/electrobun/view/host/chart-surface.tsx +10 -9
- package/src/renderers/electrobun/view/host/input.tsx +9 -19
- package/src/renderers/electrobun/view/host/media-surface.tsx +170 -0
- package/src/renderers/electrobun/view/host/mouse.ts +1 -0
- package/src/renderers/electrobun/view/host/style.ts +1 -1
- package/src/renderers/electrobun/view/host/tabs.tsx +4 -3
- package/src/renderers/electrobun/view/main.tsx +10 -3
- package/src/renderers/electrobun/view/styles.css +17 -0
- package/src/renderers/electrobun/view/ui-host.tsx +7 -0
- package/src/renderers/opentui/chart-surface.test.tsx +52 -6
- package/src/renderers/opentui/chart-surface.tsx +95 -11
- package/src/renderers/opentui/dialog-host.tsx +36 -5
- package/src/renderers/opentui/host.tsx +39 -0
- package/src/renderers/opentui/start.tsx +30 -0
- package/src/renderers/opentui/ui-host.tsx +2 -0
- package/src/sources/provider-router/index.test.ts +43 -0
- package/src/sources/provider-router/index.ts +4 -0
- package/src/sources/provider-router/supplemental.ts +46 -0
- package/src/sources/sec-edgar.test.ts +203 -2
- package/src/sources/sec-edgar.ts +70 -15
- package/src/state/app/context/index.tsx +13 -1
- package/src/state/app/context/selector.test.tsx +62 -1
- package/src/state/session-persistence.test.ts +13 -2
- package/src/sync/controller.test.ts +190 -3
- package/src/sync/controller.ts +76 -45
- package/src/sync/core-contributors.test.ts +121 -0
- package/src/sync/core-contributors.ts +104 -27
- package/src/sync/react.ts +6 -8
- package/src/sync/types.ts +3 -0
- package/src/time-series/alignment.ts +158 -0
- package/src/time-series/economic.ts +49 -0
- package/src/time-series/field-catalog.ts +306 -0
- package/src/time-series/fundamentals.test.ts +505 -0
- package/src/time-series/fundamentals.ts +842 -0
- package/src/time-series/hooks.ts +45 -0
- package/src/time-series/index.ts +14 -0
- package/src/time-series/live-quotes.test.ts +219 -0
- package/src/time-series/live-quotes.ts +203 -0
- package/src/time-series/market-resolution.ts +53 -0
- package/src/time-series/market.ts +125 -0
- package/src/time-series/reporting.ts +98 -0
- package/src/time-series/resolve.test.ts +1281 -0
- package/src/time-series/resolve.ts +783 -0
- package/src/time-series/spec-studies.test.ts +400 -0
- package/src/time-series/spec.ts +487 -0
- package/src/time-series/studies.ts +563 -0
- package/src/time-series/transforms.test.ts +105 -0
- package/src/time-series/transforms.ts +152 -0
- package/src/time-series/types.ts +162 -0
- package/src/time-series/use-chart-resolution.ts +166 -0
- package/src/types/config.ts +9 -11
- package/src/types/desktop-window.ts +1 -0
- package/src/types/financials.ts +4 -0
- package/src/types/media.ts +17 -0
- package/src/types/plugin.ts +32 -1
- package/src/ui/context-menu.tsx +15 -3
- package/src/ui/dialog.tsx +14 -2
- package/src/ui/host.tsx +40 -1
- package/src/ui/index.tsx +9 -0
- package/src/utils/exchanges.test.ts +13 -1
- package/src/utils/exchanges.ts +10 -0
- package/src/utils/financial-statements.test.ts +138 -0
- package/src/utils/financial-statements.ts +115 -8
- package/src/utils/format.test.ts +11 -1
- package/src/utils/format.ts +7 -0
- package/src/version.ts +1 -1
- package/src/components/chart/bar-chart-renderer.test.ts +0 -233
- package/src/components/chart/bar-chart-renderer.ts +0 -445
- package/src/components/chart/comparison/data.test.ts +0 -159
- package/src/components/chart/comparison/data.ts +0 -302
- package/src/components/chart/comparison/renderer.test.ts +0 -187
- package/src/components/chart/comparison/renderer.ts +0 -282
- package/src/components/chart/comparison/scene.ts +0 -178
- package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
- package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
- package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
- package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
- package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
- package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
- package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
- package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
- package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
- package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
- package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
- package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
- package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
- package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
- package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
- package/src/components/chart/comparison-stock-chart/types.ts +0 -22
- package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
- package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
- package/src/components/chart/comparison-stock-chart.tsx +0 -24
- package/src/components/chart/core/axis-measure.ts +0 -33
- package/src/components/chart/core/controller.test.ts +0 -157
- package/src/components/chart/core/controller.ts +0 -349
- package/src/components/chart/core/indicator-selector.tsx +0 -75
- package/src/components/chart/core/keyboard.ts +0 -24
- package/src/components/chart/core/pane-settings.test.ts +0 -52
- package/src/components/chart/core/pane-settings.ts +0 -141
- package/src/components/chart/core/scroll.test.ts +0 -64
- package/src/components/chart/core/scroll.ts +0 -82
- package/src/components/chart/core/window-viewport.test.ts +0 -69
- package/src/components/chart/core/window-viewport.ts +0 -162
- package/src/components/chart/display-cursor.ts +0 -153
- package/src/components/chart/indicators/bands.test.ts +0 -36
- package/src/components/chart/indicators/bands.ts +0 -29
- package/src/components/chart/indicators/moving-averages.test.ts +0 -39
- package/src/components/chart/indicators/moving-averages.ts +0 -42
- package/src/components/chart/indicators/options.test.ts +0 -32
- package/src/components/chart/indicators/options.ts +0 -107
- package/src/components/chart/indicators/oscillators.test.ts +0 -58
- package/src/components/chart/indicators/oscillators.ts +0 -92
- package/src/components/chart/native/canvas-bitmaps.ts +0 -140
- package/src/components/chart/native/raster/comparison-chart.ts +0 -162
- package/src/components/chart/native/surface/hooks.ts +0 -144
- package/src/components/chart/native/surface/rendering.ts +0 -211
- package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
- package/src/components/chart/static/bar-chart-surface.tsx +0 -292
- package/src/components/chart/stock-chart/auto.ts +0 -216
- package/src/components/chart/stock-chart/axis-width.ts +0 -146
- package/src/components/chart/stock-chart/bitmaps.ts +0 -22
- package/src/components/chart/stock-chart/canvas.ts +0 -202
- package/src/components/chart/stock-chart/controls.tsx +0 -149
- package/src/components/chart/stock-chart/cursor.ts +0 -60
- package/src/components/chart/stock-chart/data-runtime.ts +0 -128
- package/src/components/chart/stock-chart/footer.test.ts +0 -65
- package/src/components/chart/stock-chart/footer.ts +0 -303
- package/src/components/chart/stock-chart/geometry.ts +0 -160
- package/src/components/chart/stock-chart/index.test.ts +0 -532
- package/src/components/chart/stock-chart/index.tsx +0 -29
- package/src/components/chart/stock-chart/indicators.ts +0 -226
- package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
- package/src/components/chart/stock-chart/interactions.ts +0 -300
- package/src/components/chart/stock-chart/keyboard.ts +0 -373
- package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
- package/src/components/chart/stock-chart/layout.tsx +0 -177
- package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
- package/src/components/chart/stock-chart/presentation.ts +0 -67
- package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
- package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
- package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
- package/src/components/chart/stock-chart/rendering/data.ts +0 -370
- package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
- package/src/components/chart/stock-chart/rendering/output.ts +0 -304
- package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
- package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
- package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
- package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
- package/src/components/chart/stock-chart/rendering/state.ts +0 -283
- package/src/components/chart/stock-chart/requests.ts +0 -337
- package/src/components/chart/stock-chart/resolution-support.ts +0 -96
- package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
- package/src/components/chart/stock-chart/resolved.tsx +0 -359
- package/src/components/chart/stock-chart/settings.ts +0 -81
- package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
- package/src/components/chart/stock-chart/types.ts +0 -25
- package/src/components/chart/stock-chart/view.tsx +0 -155
- package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
- package/src/components/chart/stock-chart/viewport/index.ts +0 -144
- package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
- package/src/components/chart/time-axis-label.tsx +0 -175
- package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
- package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
- package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
- package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
- package/src/plugins/builtin/econ/fred-cache.ts +0 -106
- package/src/plugins/builtin/plugin-groups.ts +0 -130
- package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
- package/src/plugins/builtin/ticker-detail/data-panes.test.ts +0 -220
|
@@ -17,13 +17,37 @@ function createRegistryHarness() {
|
|
|
17
17
|
actions.push(action);
|
|
18
18
|
state = appReducer(state, action);
|
|
19
19
|
};
|
|
20
|
+
const invokedCapabilities: Array<{ capabilityId: string; operationId: string; payload: unknown }> = [];
|
|
21
|
+
const marketDataQueries: Array<{ operation: string; input: unknown }> = [];
|
|
20
22
|
const registry = {
|
|
21
23
|
panes: new Map(),
|
|
22
24
|
paneTemplates: new Map(),
|
|
23
25
|
commands: new Map(),
|
|
24
26
|
capabilities: {
|
|
25
27
|
manifests: () => [],
|
|
26
|
-
invoke: async (
|
|
28
|
+
invoke: async (capabilityId: string, operationId: string, payload: unknown) => {
|
|
29
|
+
invokedCapabilities.push({ capabilityId, operationId, payload });
|
|
30
|
+
return { invoked: true, capabilityId, operationId, payload };
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
marketData: {
|
|
34
|
+
search: async (query: string) => {
|
|
35
|
+
marketDataQueries.push({ operation: "search", input: { query } });
|
|
36
|
+
return [{
|
|
37
|
+
symbol: "NVDA",
|
|
38
|
+
name: "NVIDIA Corporation",
|
|
39
|
+
exchange: "NASDAQ",
|
|
40
|
+
type: "Equity",
|
|
41
|
+
}];
|
|
42
|
+
},
|
|
43
|
+
getQuote: async (symbol: string, exchange?: string) => {
|
|
44
|
+
marketDataQueries.push({ operation: "quote", input: { symbol, exchange } });
|
|
45
|
+
return { symbol, exchange, price: 180 };
|
|
46
|
+
},
|
|
47
|
+
getTickerFinancials: async (symbol: string, exchange?: string) => {
|
|
48
|
+
marketDataQueries.push({ operation: "financials", input: { symbol, exchange } });
|
|
49
|
+
return { quote: { symbol, exchange, price: 180 } };
|
|
50
|
+
},
|
|
27
51
|
},
|
|
28
52
|
resolvePaneSettings: () => null,
|
|
29
53
|
showPane: () => {},
|
|
@@ -62,7 +86,9 @@ function createRegistryHarness() {
|
|
|
62
86
|
actions,
|
|
63
87
|
controller,
|
|
64
88
|
getState: () => state,
|
|
89
|
+
invokedCapabilities,
|
|
65
90
|
invokedUiActions,
|
|
91
|
+
marketDataQueries,
|
|
66
92
|
setUiNodes: (nodes: RemoteUiNodeSnapshot[]) => {
|
|
67
93
|
uiNodes = nodes;
|
|
68
94
|
},
|
|
@@ -101,6 +127,47 @@ describe("createAppRemoteController", () => {
|
|
|
101
127
|
}
|
|
102
128
|
});
|
|
103
129
|
|
|
130
|
+
test("queries configured market data without dispatching app-control actions", async () => {
|
|
131
|
+
const { actions, controller, marketDataQueries } = createRegistryHarness();
|
|
132
|
+
|
|
133
|
+
const search = await controller.handle({
|
|
134
|
+
type: "data",
|
|
135
|
+
operation: "search",
|
|
136
|
+
query: "NVIDIA",
|
|
137
|
+
});
|
|
138
|
+
const quote = await controller.handle({
|
|
139
|
+
type: "data",
|
|
140
|
+
operation: "quote",
|
|
141
|
+
symbol: "nvda",
|
|
142
|
+
exchange: "nasdaq",
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(search).toMatchObject({
|
|
146
|
+
ok: true,
|
|
147
|
+
data: [{ symbol: "NVDA", exchange: "NASDAQ" }],
|
|
148
|
+
});
|
|
149
|
+
expect(quote).toMatchObject({
|
|
150
|
+
ok: true,
|
|
151
|
+
data: { symbol: "NVDA", exchange: "NASDAQ", price: 180 },
|
|
152
|
+
});
|
|
153
|
+
expect(marketDataQueries).toEqual([
|
|
154
|
+
{ operation: "search", input: { query: "NVIDIA" } },
|
|
155
|
+
{ operation: "quote", input: { symbol: "NVDA", exchange: "NASDAQ" } },
|
|
156
|
+
]);
|
|
157
|
+
expect(actions).toEqual([]);
|
|
158
|
+
|
|
159
|
+
const appControlAttempt = await controller.handle({
|
|
160
|
+
type: "data",
|
|
161
|
+
operation: "app.openCommandBar",
|
|
162
|
+
query: "NVDA",
|
|
163
|
+
} as never);
|
|
164
|
+
expect(appControlAttempt).toMatchObject({
|
|
165
|
+
ok: false,
|
|
166
|
+
error: { message: expect.stringContaining("Unknown market data operation") },
|
|
167
|
+
});
|
|
168
|
+
expect(actions).toEqual([]);
|
|
169
|
+
});
|
|
170
|
+
|
|
104
171
|
test("dispatches semantic app operations", async () => {
|
|
105
172
|
const { actions, controller, getState } = createRegistryHarness();
|
|
106
173
|
|
|
@@ -292,6 +359,40 @@ describe("createAppRemoteController", () => {
|
|
|
292
359
|
}
|
|
293
360
|
});
|
|
294
361
|
|
|
362
|
+
test("forwards plugin capability operations without narrowing their payload", async () => {
|
|
363
|
+
const { controller, invokedCapabilities } = createRegistryHarness();
|
|
364
|
+
const payload = {
|
|
365
|
+
instanceId: "broker-main",
|
|
366
|
+
operation: "placeOrder",
|
|
367
|
+
args: [{ symbol: "NVDA", side: "BUY", quantity: 5 }],
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const response = await controller.handle({
|
|
371
|
+
type: "call",
|
|
372
|
+
operation: "capability.invoke",
|
|
373
|
+
input: {
|
|
374
|
+
capabilityId: "desktop.broker",
|
|
375
|
+
operationId: "invoke",
|
|
376
|
+
payload,
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
expect(response.ok).toBe(true);
|
|
381
|
+
expect(invokedCapabilities).toEqual([{
|
|
382
|
+
capabilityId: "desktop.broker",
|
|
383
|
+
operationId: "invoke",
|
|
384
|
+
payload,
|
|
385
|
+
}]);
|
|
386
|
+
if (response.ok) {
|
|
387
|
+
expect(response.data).toEqual({
|
|
388
|
+
invoked: true,
|
|
389
|
+
capabilityId: "desktop.broker",
|
|
390
|
+
operationId: "invoke",
|
|
391
|
+
payload,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
295
396
|
test("runs sequential batches with halt-on-error and final state", async () => {
|
|
296
397
|
const { controller } = createRegistryHarness();
|
|
297
398
|
|
package/src/remote/controller.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
RemoteControlRequest,
|
|
18
18
|
RemoteControlResponse,
|
|
19
19
|
RemoteJsonPatchOperation,
|
|
20
|
+
RemoteMarketDataRequest,
|
|
20
21
|
RemoteStateInclude,
|
|
21
22
|
} from "./types";
|
|
22
23
|
import type { RemoteUiRegistry } from "./semantic-tree";
|
|
@@ -51,6 +52,26 @@ interface AppRemoteControllerOptions {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const DEFAULT_MUTATION_INCLUDE: RemoteStateInclude[] = ["app", "layout", "panes", "commandBar"];
|
|
55
|
+
const MAX_MARKET_DATA_SEARCH_RESULTS = 20;
|
|
56
|
+
const MAX_MARKET_DATA_FILINGS = 20;
|
|
57
|
+
const MAX_MARKET_DATA_EARNINGS_SYMBOLS = 25;
|
|
58
|
+
const MAX_MARKET_DATA_HOLDERS = 25;
|
|
59
|
+
const MAX_MARKET_DATA_EVENT_ROWS = 20;
|
|
60
|
+
|
|
61
|
+
function requiredMarketDataText(value: string, field: string): string {
|
|
62
|
+
const normalized = value.trim();
|
|
63
|
+
if (!normalized) throw new Error(`Market data ${field} is required.`);
|
|
64
|
+
return normalized;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizedMarketDataSymbol(symbol: string): string {
|
|
68
|
+
return requiredMarketDataText(symbol, "symbol").toUpperCase();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizedOptionalExchange(exchange: string | undefined): string | undefined {
|
|
72
|
+
const normalized = exchange?.trim().toUpperCase();
|
|
73
|
+
return normalized || undefined;
|
|
74
|
+
}
|
|
54
75
|
|
|
55
76
|
export function createAppRemoteController({
|
|
56
77
|
dispatch,
|
|
@@ -72,6 +93,105 @@ export function createAppRemoteController({
|
|
|
72
93
|
return mutationSummary(getState(), extra);
|
|
73
94
|
};
|
|
74
95
|
|
|
96
|
+
const queryMarketData = async (request: RemoteMarketDataRequest): Promise<unknown> => {
|
|
97
|
+
const marketData = pluginRegistry.marketData;
|
|
98
|
+
switch (request.operation) {
|
|
99
|
+
case "search":
|
|
100
|
+
return (await marketData.search(requiredMarketDataText(request.query, "query")))
|
|
101
|
+
.slice(0, MAX_MARKET_DATA_SEARCH_RESULTS)
|
|
102
|
+
.map((result) => ({
|
|
103
|
+
providerId: result.providerId,
|
|
104
|
+
symbol: result.symbol,
|
|
105
|
+
name: result.name,
|
|
106
|
+
exchange: result.exchange,
|
|
107
|
+
primaryExchange: result.primaryExchange,
|
|
108
|
+
type: result.type,
|
|
109
|
+
currency: result.currency,
|
|
110
|
+
}));
|
|
111
|
+
case "quote":
|
|
112
|
+
return marketData.getQuote(
|
|
113
|
+
normalizedMarketDataSymbol(request.symbol),
|
|
114
|
+
normalizedOptionalExchange(request.exchange),
|
|
115
|
+
);
|
|
116
|
+
case "financials": {
|
|
117
|
+
const financials = await marketData.getTickerFinancials(
|
|
118
|
+
normalizedMarketDataSymbol(request.symbol),
|
|
119
|
+
normalizedOptionalExchange(request.exchange),
|
|
120
|
+
);
|
|
121
|
+
return {
|
|
122
|
+
quote: financials.quote,
|
|
123
|
+
fundamentals: financials.fundamentals,
|
|
124
|
+
profile: financials.profile,
|
|
125
|
+
annualStatements: financials.annualStatements.slice(0, 5),
|
|
126
|
+
quarterlyStatements: financials.quarterlyStatements.slice(0, 8),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
case "secFilings": {
|
|
130
|
+
if (!marketData.getSecFilings) throw new Error("The configured market data sources do not provide SEC filings.");
|
|
131
|
+
const count = Math.max(1, Math.min(
|
|
132
|
+
MAX_MARKET_DATA_FILINGS,
|
|
133
|
+
Number.isFinite(request.count) ? Math.trunc(request.count!) : 10,
|
|
134
|
+
));
|
|
135
|
+
return (await marketData.getSecFilings(
|
|
136
|
+
normalizedMarketDataSymbol(request.symbol),
|
|
137
|
+
count,
|
|
138
|
+
normalizedOptionalExchange(request.exchange),
|
|
139
|
+
)).slice(0, count);
|
|
140
|
+
}
|
|
141
|
+
case "holders": {
|
|
142
|
+
if (!marketData.getHolders) throw new Error("The configured market data sources do not provide holder data.");
|
|
143
|
+
const holders = await marketData.getHolders(
|
|
144
|
+
normalizedMarketDataSymbol(request.symbol),
|
|
145
|
+
normalizedOptionalExchange(request.exchange),
|
|
146
|
+
);
|
|
147
|
+
return {
|
|
148
|
+
...holders,
|
|
149
|
+
holders: holders.holders.slice(0, MAX_MARKET_DATA_HOLDERS),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
case "analystResearch": {
|
|
153
|
+
if (!marketData.getAnalystResearch) throw new Error("The configured market data sources do not provide analyst research.");
|
|
154
|
+
const research = await marketData.getAnalystResearch(
|
|
155
|
+
normalizedMarketDataSymbol(request.symbol),
|
|
156
|
+
normalizedOptionalExchange(request.exchange),
|
|
157
|
+
);
|
|
158
|
+
return {
|
|
159
|
+
...research,
|
|
160
|
+
recommendations: research.recommendations.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
161
|
+
ratings: research.ratings.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
162
|
+
earningsEstimates: research.earningsEstimates.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
163
|
+
revenueEstimates: research.revenueEstimates.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case "corporateActions": {
|
|
167
|
+
if (!marketData.getCorporateActions) throw new Error("The configured market data sources do not provide corporate actions.");
|
|
168
|
+
const actions = await marketData.getCorporateActions(
|
|
169
|
+
normalizedMarketDataSymbol(request.symbol),
|
|
170
|
+
normalizedOptionalExchange(request.exchange),
|
|
171
|
+
);
|
|
172
|
+
return {
|
|
173
|
+
...actions,
|
|
174
|
+
dividends: actions.dividends.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
175
|
+
splits: actions.splits.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
176
|
+
earnings: actions.earnings.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
case "earningsCalendar": {
|
|
180
|
+
if (!marketData.getEarningsCalendar) throw new Error("The configured market data sources do not provide an earnings calendar.");
|
|
181
|
+
const symbols = request.symbols
|
|
182
|
+
.map((symbol) => normalizedMarketDataSymbol(symbol))
|
|
183
|
+
.filter((symbol, index, all) => all.indexOf(symbol) === index)
|
|
184
|
+
.slice(0, MAX_MARKET_DATA_EARNINGS_SYMBOLS);
|
|
185
|
+
if (symbols.length === 0) throw new Error("Market data symbols are required.");
|
|
186
|
+
return marketData.getEarningsCalendar(symbols);
|
|
187
|
+
}
|
|
188
|
+
default:
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Unknown market data operation "${String((request as { operation?: unknown }).operation)}".`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
75
195
|
const openCommandBar = async (input: Record<string, unknown>): Promise<unknown> => {
|
|
76
196
|
const mode = optionalString(input, "mode") ?? "command";
|
|
77
197
|
const query = optionalString(input, "query") ?? "";
|
|
@@ -370,6 +490,8 @@ export function createAppRemoteController({
|
|
|
370
490
|
const data = getResource(request.resource);
|
|
371
491
|
return ok(data, revisionFor(data), buildIncludedState(request.include));
|
|
372
492
|
}
|
|
493
|
+
case "data":
|
|
494
|
+
return ok(await queryMarketData(request));
|
|
373
495
|
case "call": {
|
|
374
496
|
const data = await call(request.operation, request.input, request.dryRun);
|
|
375
497
|
return ok(data, undefined, buildIncludedState(request.include, request.dryRun ? [] : DEFAULT_MUTATION_INCLUDE));
|
package/src/remote/types.ts
CHANGED
|
@@ -28,10 +28,21 @@ export type RemoteStateInclude =
|
|
|
28
28
|
| "help"
|
|
29
29
|
| "all";
|
|
30
30
|
|
|
31
|
+
export type RemoteMarketDataRequest =
|
|
32
|
+
| { type: "data"; operation: "search"; query: string }
|
|
33
|
+
| { type: "data"; operation: "quote"; symbol: string; exchange?: string }
|
|
34
|
+
| { type: "data"; operation: "financials"; symbol: string; exchange?: string }
|
|
35
|
+
| { type: "data"; operation: "secFilings"; symbol: string; exchange?: string; count?: number }
|
|
36
|
+
| { type: "data"; operation: "holders"; symbol: string; exchange?: string }
|
|
37
|
+
| { type: "data"; operation: "analystResearch"; symbol: string; exchange?: string }
|
|
38
|
+
| { type: "data"; operation: "corporateActions"; symbol: string; exchange?: string }
|
|
39
|
+
| { type: "data"; operation: "earningsCalendar"; symbols: string[] };
|
|
40
|
+
|
|
31
41
|
export type RemoteControlRequest =
|
|
32
42
|
| { type: "help"; topic?: string }
|
|
33
43
|
| { type: "schema" }
|
|
34
44
|
| { type: "get"; resource: string; include?: RemoteStateInclude[] }
|
|
45
|
+
| RemoteMarketDataRequest
|
|
35
46
|
| { type: "call"; operation: string; input?: unknown; dryRun?: boolean; include?: RemoteStateInclude[] }
|
|
36
47
|
| {
|
|
37
48
|
type: "patch";
|
|
@@ -8,8 +8,20 @@ import {
|
|
|
8
8
|
type PluginCapability,
|
|
9
9
|
} from "../../../capabilities";
|
|
10
10
|
import type { AppServices } from "../../../core/app-services";
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
isAiProviderId,
|
|
13
|
+
type AiProviderId,
|
|
14
|
+
} from "../../../plugins/builtin/ai/providers";
|
|
15
|
+
import { createPiAiHost } from "../../../plugins/builtin/ai/pi";
|
|
16
|
+
import {
|
|
17
|
+
isAiRunCancelled,
|
|
18
|
+
type AiConversationMessage,
|
|
19
|
+
type AiRuntimeAuthType,
|
|
20
|
+
} from "../../../plugins/builtin/ai/runner";
|
|
21
|
+
import {
|
|
22
|
+
normalizeAiAgentHistory,
|
|
23
|
+
type AiAgentHistoryMessage,
|
|
24
|
+
} from "../../../plugins/builtin/ai/agent-history";
|
|
13
25
|
import type { BrokerAdapter } from "../../../types/broker";
|
|
14
26
|
import type { AppConfig, BrokerInstanceConfig } from "../../../types/config";
|
|
15
27
|
|
|
@@ -265,48 +277,132 @@ function createNotesFilesCapability(): PluginCapability {
|
|
|
265
277
|
};
|
|
266
278
|
}
|
|
267
279
|
|
|
268
|
-
function getAiProviderAvailability(): Record<string, boolean> {
|
|
269
|
-
const availability: Record<string, boolean> = {};
|
|
270
|
-
const hasBunWhich = typeof Bun !== "undefined" && typeof Bun.which === "function";
|
|
271
|
-
for (const definition of getAiProviderDefinitions()) {
|
|
272
|
-
availability[definition.id] = hasBunWhich ? !!Bun.which(definition.command) : false;
|
|
273
|
-
}
|
|
274
|
-
return availability;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
280
|
function createAiRunnerCapability(options: CoreCapabilityOptions): PluginCapability {
|
|
281
|
+
const aiHost = createPiAiHost({
|
|
282
|
+
appKind: "desktop",
|
|
283
|
+
dataDir: options.getConfig().dataDir,
|
|
284
|
+
});
|
|
285
|
+
const requireProviderId = (value: unknown): AiProviderId => {
|
|
286
|
+
const providerId = requireString(value, "AI provider");
|
|
287
|
+
if (!isAiProviderId(providerId)) {
|
|
288
|
+
throw new Error(`Unknown AI provider: ${providerId}`);
|
|
289
|
+
}
|
|
290
|
+
return providerId;
|
|
291
|
+
};
|
|
292
|
+
const requireCatalogProvider = async (value: unknown): Promise<AiProviderId> => {
|
|
293
|
+
const providerId = requireProviderId(value);
|
|
294
|
+
const catalog = await aiHost.getCatalog?.();
|
|
295
|
+
if (!catalog?.providers.some((provider) => provider.providerId === providerId)) {
|
|
296
|
+
throw new Error(`Unknown AI provider: ${providerId}`);
|
|
297
|
+
}
|
|
298
|
+
return providerId;
|
|
299
|
+
};
|
|
300
|
+
const optionalAuthType = (value: unknown): AiRuntimeAuthType | undefined => {
|
|
301
|
+
if (value === undefined || value === null || value === "") return undefined;
|
|
302
|
+
if (value === "oauth" || value === "api_key") return value;
|
|
303
|
+
throw new Error(`Unknown AI authentication method: ${String(value)}`);
|
|
304
|
+
};
|
|
305
|
+
const optionalMessages = (value: unknown): AiConversationMessage[] | undefined => {
|
|
306
|
+
if (value === undefined || value === null) return undefined;
|
|
307
|
+
if (!Array.isArray(value)) throw new Error("AI conversation messages must be an array.");
|
|
308
|
+
return value.map((message, index) => {
|
|
309
|
+
if (!message || typeof message !== "object") {
|
|
310
|
+
throw new Error(`AI conversation message ${index + 1} is invalid.`);
|
|
311
|
+
}
|
|
312
|
+
const role = (message as { role?: unknown }).role;
|
|
313
|
+
const content = (message as { content?: unknown }).content;
|
|
314
|
+
if ((role !== "user" && role !== "assistant") || typeof content !== "string") {
|
|
315
|
+
throw new Error(`AI conversation message ${index + 1} is invalid.`);
|
|
316
|
+
}
|
|
317
|
+
return { role, content };
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
const optionalAgentMessages = (value: unknown): AiAgentHistoryMessage[] | undefined => {
|
|
321
|
+
if (value === undefined || value === null) return undefined;
|
|
322
|
+
if (!Array.isArray(value)) throw new Error("AI agent messages must be an array.");
|
|
323
|
+
const messages = normalizeAiAgentHistory(value);
|
|
324
|
+
if (messages.length !== value.length) {
|
|
325
|
+
throw new Error("AI agent message history is invalid.");
|
|
326
|
+
}
|
|
327
|
+
return messages;
|
|
328
|
+
};
|
|
329
|
+
|
|
278
330
|
return {
|
|
279
331
|
id: AI_RUNNER_CAPABILITY_ID,
|
|
280
332
|
kind: "ai-runner",
|
|
281
333
|
name: "AI Runner",
|
|
282
334
|
operations: {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const providerId =
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
335
|
+
getCatalog: op(async () => aiHost.getCatalog?.() ?? { providers: [], accounts: [], models: [] }),
|
|
336
|
+
connectProvider: stream(async (input: any, emit) => {
|
|
337
|
+
const providerId = await requireCatalogProvider(input.providerId);
|
|
338
|
+
const authType = optionalAuthType(input.authType);
|
|
339
|
+
if (!aiHost.connect) throw new Error("In-app AI sign-in is unavailable.");
|
|
340
|
+
let disposed = false;
|
|
341
|
+
aiHost.connect(providerId, authType, (event) => {
|
|
342
|
+
if (!disposed) emit({ kind: "account-auth", event });
|
|
343
|
+
}).then((catalog) => {
|
|
344
|
+
if (!disposed) emit({ kind: "account-connected", catalog });
|
|
345
|
+
}).catch((error) => {
|
|
346
|
+
if (!disposed) emit({
|
|
347
|
+
kind: "account-error",
|
|
348
|
+
error: error instanceof Error ? error.message : String(error),
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
return () => {
|
|
352
|
+
disposed = true;
|
|
353
|
+
};
|
|
354
|
+
}),
|
|
355
|
+
disconnectProvider: op(async (input: any) => {
|
|
356
|
+
const providerId = await requireCatalogProvider(input.providerId);
|
|
357
|
+
if (!aiHost.disconnect) throw new Error("In-app AI account disconnection is unavailable.");
|
|
358
|
+
return aiHost.disconnect(providerId);
|
|
359
|
+
}, "action"),
|
|
360
|
+
checkProviderStatus: op(async (input: any) => {
|
|
361
|
+
const providerId = await requireCatalogProvider(input.providerId);
|
|
362
|
+
if (!aiHost.checkStatus) {
|
|
363
|
+
throw new Error("AI provider status checks are unavailable.");
|
|
290
364
|
}
|
|
291
|
-
|
|
292
|
-
|
|
365
|
+
return aiHost.checkStatus(providerId);
|
|
366
|
+
}),
|
|
367
|
+
run: stream(async (input: any, emit) => {
|
|
368
|
+
const providerId = await requireCatalogProvider(input.providerId);
|
|
369
|
+
const prompt = requireString(input.prompt, "AI prompt");
|
|
370
|
+
const messages = optionalMessages(input.messages);
|
|
371
|
+
const agentMessages = optionalAgentMessages(input.agentMessages);
|
|
372
|
+
let completedAgentMessages: AiAgentHistoryMessage[] | undefined;
|
|
373
|
+
const modelId = optionalString(input.modelId);
|
|
374
|
+
const providerStatus = await aiHost.checkStatus?.(providerId);
|
|
375
|
+
if (providerStatus && !providerStatus.authenticated) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
providerStatus.message
|
|
378
|
+
?? `${providerId} is not connected.`,
|
|
379
|
+
);
|
|
293
380
|
}
|
|
294
381
|
|
|
295
382
|
let disposed = false;
|
|
296
|
-
const controller =
|
|
297
|
-
|
|
298
|
-
...providerDefinition,
|
|
299
|
-
available: true,
|
|
300
|
-
},
|
|
383
|
+
const controller = aiHost.run({
|
|
384
|
+
providerId,
|
|
301
385
|
prompt,
|
|
302
|
-
|
|
386
|
+
messages,
|
|
387
|
+
agentMessages,
|
|
388
|
+
modelId: modelId ?? undefined,
|
|
389
|
+
outputMode: input.outputMode === "structured" || input.outputMode === "screener"
|
|
390
|
+
? input.outputMode
|
|
391
|
+
: "plain",
|
|
303
392
|
onChunk: (output) => {
|
|
304
393
|
if (!disposed) emit({ kind: "chunk", output });
|
|
305
394
|
},
|
|
395
|
+
onAgentMessages: (nextMessages) => {
|
|
396
|
+
completedAgentMessages = nextMessages;
|
|
397
|
+
},
|
|
306
398
|
});
|
|
307
399
|
|
|
308
400
|
controller.done.then((output) => {
|
|
309
|
-
if (!disposed) emit({
|
|
401
|
+
if (!disposed) emit({
|
|
402
|
+
kind: "done",
|
|
403
|
+
output,
|
|
404
|
+
...(completedAgentMessages ? { agentMessages: completedAgentMessages } : {}),
|
|
405
|
+
});
|
|
310
406
|
}).catch((error) => {
|
|
311
407
|
if (disposed) return;
|
|
312
408
|
if (isAiRunCancelled(error)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAppServices, type AppServices } from "../../../../core/app-services";
|
|
2
|
+
import { getDesktopBackendPlugins } from "../../../../plugins/catalog-backend";
|
|
2
3
|
import type { AppSessionSnapshot } from "../../../../core/state/session-persistence";
|
|
3
4
|
import {
|
|
4
5
|
exportConfig,
|
|
@@ -80,7 +81,10 @@ async function importDesktopConfig({
|
|
|
80
81
|
setDesktopWorkspace(null);
|
|
81
82
|
teardownServices();
|
|
82
83
|
setCurrentConfig(await importConfig(payload.dataDir as string, payload.srcPath as string));
|
|
83
|
-
setServices(createAppServices({
|
|
84
|
+
setServices(createAppServices({
|
|
85
|
+
config: getConfig(),
|
|
86
|
+
plugins: getDesktopBackendPlugins(),
|
|
87
|
+
}));
|
|
84
88
|
syncConfigAccessors();
|
|
85
89
|
registerCoreCapabilities();
|
|
86
90
|
const desktopWorkspace = createDesktopWorkspace(getConfig(), getSessionSnapshot());
|
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync } from "fs";
|
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { createAppServices, type AppServices } from "../../../../core/app-services";
|
|
5
|
+
import { getDesktopBackendPlugins } from "../../../../plugins/catalog-backend";
|
|
5
6
|
import type { AppSessionSnapshot } from "../../../../core/state/session-persistence";
|
|
6
7
|
import {
|
|
7
8
|
getDataDir,
|
|
@@ -126,7 +127,10 @@ export async function initializeDesktopBackend<TRpc>(
|
|
|
126
127
|
const config = options.getCurrentConfig();
|
|
127
128
|
if (!config) throw new Error("Desktop config failed to initialize.");
|
|
128
129
|
|
|
129
|
-
const services = createAppServices({
|
|
130
|
+
const services = createAppServices({
|
|
131
|
+
config,
|
|
132
|
+
plugins: getDesktopBackendPlugins(),
|
|
133
|
+
});
|
|
130
134
|
options.setServices(services);
|
|
131
135
|
options.syncConfigAccessors();
|
|
132
136
|
options.registerCoreCapabilities();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LiveStreamResolveRequest, ResolvedLiveStream } from "../../../../types/media";
|
|
2
|
+
import { getTvChannel, TV_CHANNELS } from "../../../../plugins/builtin/tv/channels";
|
|
3
|
+
import { resolveTvStream } from "../../../../plugins/builtin/tv/youtube-stream";
|
|
4
|
+
|
|
5
|
+
export async function resolveDesktopLiveStream(payload: Record<string, unknown>): Promise<ResolvedLiveStream> {
|
|
6
|
+
const request = payload as Partial<LiveStreamResolveRequest>;
|
|
7
|
+
if (request.provider !== "youtube") {
|
|
8
|
+
throw new Error("Unsupported live-stream provider.");
|
|
9
|
+
}
|
|
10
|
+
const channel = TV_CHANNELS.find((item) => item.id === request.sourceId);
|
|
11
|
+
if (!channel) {
|
|
12
|
+
throw new Error("Unknown TV channel.");
|
|
13
|
+
}
|
|
14
|
+
return resolveTvStream(getTvChannel(channel.id), { force: request.force === true });
|
|
15
|
+
}
|
|
@@ -15,7 +15,10 @@ export function desktopTitleBarStyle(): DesktopTitleBarStyle {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function desktopWindowRenderer(): DesktopWindowRenderer {
|
|
18
|
-
if (process.platform === "win32")
|
|
18
|
+
if (process.platform === "win32") {
|
|
19
|
+
const hostArchitecture = process.env.PROCESSOR_ARCHITEW6432 ?? process.env.PROCESSOR_ARCHITECTURE;
|
|
20
|
+
return hostArchitecture?.toLowerCase() === "arm64" ? "native" : "cef";
|
|
21
|
+
}
|
|
19
22
|
return "native";
|
|
20
23
|
}
|
|
21
24
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { createDefaultConfig } from "../../../../../types/config";
|
|
2
|
+
import { cloneLayout, createDefaultConfig } from "../../../../../types/config";
|
|
3
3
|
import { createDesktopWorkspace } from "./index";
|
|
4
4
|
|
|
5
5
|
describe("desktop workspace", () => {
|
|
@@ -60,4 +60,35 @@ describe("desktop workspace", () => {
|
|
|
60
60
|
{ instanceId: "chat:main", x: 220, y: 260, width: 640, height: 480 },
|
|
61
61
|
]);
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
test("ignores main-window state that arrives behind a newer layout revision", () => {
|
|
65
|
+
const config = createDefaultConfig("/tmp/gloomberb-desktop-layout-revision");
|
|
66
|
+
const workspace = createDesktopWorkspace(config, null);
|
|
67
|
+
const initialSnapshot = workspace.getSnapshot();
|
|
68
|
+
const monitorLayout = config.layouts[1]!;
|
|
69
|
+
|
|
70
|
+
const newerSnapshot = {
|
|
71
|
+
...initialSnapshot,
|
|
72
|
+
config: {
|
|
73
|
+
...initialSnapshot.config,
|
|
74
|
+
layout: cloneLayout(monitorLayout.layout),
|
|
75
|
+
activeLayoutIndex: 1,
|
|
76
|
+
},
|
|
77
|
+
paneState: monitorLayout.paneState ?? {},
|
|
78
|
+
focusedPaneId: monitorLayout.focusedPaneId ?? null,
|
|
79
|
+
activePanel: monitorLayout.activePanel ?? "left" as const,
|
|
80
|
+
mainStateRevision: 2,
|
|
81
|
+
};
|
|
82
|
+
const staleSnapshot = {
|
|
83
|
+
...initialSnapshot,
|
|
84
|
+
mainStateRevision: 1,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
workspace.syncMainState(newerSnapshot);
|
|
88
|
+
const result = workspace.syncMainState(staleSnapshot);
|
|
89
|
+
|
|
90
|
+
expect(result.mainStateRevision).toBe(2);
|
|
91
|
+
expect(result.config.activeLayoutIndex).toBe(1);
|
|
92
|
+
expect(result.config.layout).toEqual(monitorLayout.layout);
|
|
93
|
+
});
|
|
63
94
|
});
|
|
@@ -62,6 +62,7 @@ export function createDesktopWorkspace(
|
|
|
62
62
|
focusedPaneId: initialFocusedPaneId,
|
|
63
63
|
activePanel: initialActivePanel,
|
|
64
64
|
statusBarVisible: sessionSnapshot?.statusBarVisible !== false,
|
|
65
|
+
mainStateRevision: 0,
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
const updateConfig = (nextConfig: AppConfig, options?: { layoutChanged?: boolean }) => {
|
|
@@ -94,12 +95,20 @@ export function createDesktopWorkspace(
|
|
|
94
95
|
focusedPaneId: sharedState.focusedPaneId,
|
|
95
96
|
activePanel: sharedState.activePanel,
|
|
96
97
|
statusBarVisible: sharedState.statusBarVisible,
|
|
98
|
+
mainStateRevision: sharedState.mainStateRevision,
|
|
97
99
|
layoutChanged: sharedState.layoutChanged,
|
|
98
100
|
});
|
|
99
101
|
|
|
100
102
|
return {
|
|
101
103
|
getSnapshot,
|
|
102
104
|
syncMainState(snapshot) {
|
|
105
|
+
const currentRevision = sharedState.mainStateRevision ?? 0;
|
|
106
|
+
if (
|
|
107
|
+
typeof snapshot.mainStateRevision === "number"
|
|
108
|
+
&& snapshot.mainStateRevision <= currentRevision
|
|
109
|
+
) {
|
|
110
|
+
return getSnapshot();
|
|
111
|
+
}
|
|
103
112
|
const syncedConfig = syncConfigActiveLayoutState(
|
|
104
113
|
snapshot.config,
|
|
105
114
|
snapshot.paneState,
|
|
@@ -116,6 +125,7 @@ export function createDesktopWorkspace(
|
|
|
116
125
|
focusedPaneId: snapshot.focusedPaneId,
|
|
117
126
|
activePanel: snapshot.activePanel,
|
|
118
127
|
statusBarVisible: snapshot.statusBarVisible,
|
|
128
|
+
mainStateRevision: snapshot.mainStateRevision ?? currentRevision,
|
|
119
129
|
layoutChanged: snapshot.layoutChanged,
|
|
120
130
|
};
|
|
121
131
|
return getSnapshot();
|
|
@@ -46,6 +46,7 @@ import { DesktopDetachedWindowManager } from "./desktop/detached-windows";
|
|
|
46
46
|
import { handleDesktopHostRequest } from "./desktop/host-requests";
|
|
47
47
|
import { handleDesktopWorkspaceRequest } from "./desktop/workspace/requests";
|
|
48
48
|
import { handleDesktopBackendRequest } from "./desktop/backend-requests";
|
|
49
|
+
import { resolveDesktopLiveStream } from "./desktop/media";
|
|
49
50
|
import { initializeDesktopBackend } from "./desktop/initialization";
|
|
50
51
|
import { applyWindowsCustomChrome } from "./desktop/windows-custom-chrome";
|
|
51
52
|
import { applyWindowsWindowIcon } from "./desktop/windows-icons";
|
|
@@ -429,6 +430,10 @@ async function handleBackendRequest(
|
|
|
429
430
|
|
|
430
431
|
if (method === "init") return initialize(rpc, payload);
|
|
431
432
|
if (method === "http.fetch") return handleHttpFetch(payload);
|
|
433
|
+
if (method === "media.resolveLiveStream") return resolveDesktopLiveStream(payload);
|
|
434
|
+
if (method === "remote.forward") {
|
|
435
|
+
return forwardRemoteControlRequest(payload.request as RemoteControlRequest);
|
|
436
|
+
}
|
|
432
437
|
if (method.startsWith("capability.")) return capabilityBridge.handle(rpc, method, payload);
|
|
433
438
|
if (method.startsWith("desktop.")) {
|
|
434
439
|
return handleDesktopWorkspaceRequest({
|