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