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
package/src/cli/help.ts
CHANGED
|
@@ -34,15 +34,25 @@ const INDENT = 2;
|
|
|
34
34
|
const MAX_HELP_WIDTH = 100;
|
|
35
35
|
const PIPED_HELP_WIDTH = 80;
|
|
36
36
|
|
|
37
|
+
/** Commands that run as written, for someone who has just installed it. */
|
|
38
|
+
const START_HERE: Array<[string, string]> = [
|
|
39
|
+
["gloomberb search apple", "Find a ticker from a company name"],
|
|
40
|
+
["gloomberb quote AAPL", "Show the latest price"],
|
|
41
|
+
["gloomberb ticker AAPL", "Open the full research report"],
|
|
42
|
+
["gloomberb catalog earnings", "Find a function, then run it with fn"],
|
|
43
|
+
];
|
|
44
|
+
|
|
37
45
|
const GLOBAL_OPTIONS: Array<[string, string]> = [
|
|
38
46
|
["--json", "Print the full result as JSON"],
|
|
39
47
|
["--csv", "Print rows as CSV under the columns the text shows; fn reports and fundamentals put each table under # section: and end with # source lines"],
|
|
40
48
|
["--ndjson", "Print one JSON object per table row"],
|
|
41
|
-
["--limit <n>", "Show at most n rows"],
|
|
49
|
+
["--limit <n>", "Show at most n rows, the first n as printed"],
|
|
50
|
+
["--tail <n>", "Show the newest n rows of a dated series (history, fred, econ) in their printed order, or the last n of another list"],
|
|
42
51
|
["--refresh", "Fetch fresh data instead of reading the cache"],
|
|
43
52
|
["--dry-run", "Preview config, cache, notes, alerts, plugin on/off, and remote changes without saving"],
|
|
44
53
|
["-q, --quiet", "Print no results or errors in text mode, for commands that print results"],
|
|
45
54
|
["--color, --no-color", "Force or turn off colors (NO_COLOR=1 also turns them off)"],
|
|
55
|
+
["--width <n>", "Fit tables to n columns (default: the terminal width; piped output is not fitted). shot takes its own --width in pixels"],
|
|
46
56
|
];
|
|
47
57
|
|
|
48
58
|
export interface CliHelpEntry {
|
|
@@ -98,6 +108,9 @@ export function renderCliHelp(entries: CliHelpEntry[], version: string, descript
|
|
|
98
108
|
`${cliStyles.bold("gloomberb")} ${cliStyles.muted(version)}`,
|
|
99
109
|
...wrapText(description, width),
|
|
100
110
|
"",
|
|
111
|
+
renderSection("Start here"),
|
|
112
|
+
...renderDefinitions(START_HERE, { width, termStyle: cliStyles.command }),
|
|
113
|
+
"",
|
|
101
114
|
renderSection("Usage"),
|
|
102
115
|
...renderDefinitions([
|
|
103
116
|
["gloomberb", "Open the terminal UI"],
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Well-known index roots that people type bare. As a bare symbol each one is
|
|
3
|
+
* another instrument (MOVE is Corvex, MID a fund) or nothing at all (VIX); the
|
|
4
|
+
* index itself is quoted as ^ROOT. Every entry is offered as a command to run,
|
|
5
|
+
* so only roots whose ^ symbol returns a quote belong here.
|
|
6
|
+
*/
|
|
7
|
+
const INDEX_ROOTS = new Map([
|
|
8
|
+
["VIX", "Cboe Volatility Index"],
|
|
9
|
+
["VVIX", "Cboe VIX of VIX Index"],
|
|
10
|
+
["VIX9D", "Cboe S&P 500 9-Day Volatility Index"],
|
|
11
|
+
["VIX3M", "Cboe S&P 500 3-Month Volatility Index"],
|
|
12
|
+
["VXN", "Cboe Nasdaq-100 Volatility Index"],
|
|
13
|
+
["OVX", "Cboe Crude Oil ETF Volatility Index"],
|
|
14
|
+
["GVZ", "Cboe Gold ETF Volatility Index"],
|
|
15
|
+
["SKEW", "Cboe SKEW Index"],
|
|
16
|
+
["TNX", "Cboe 10-Year Treasury Yield Index"],
|
|
17
|
+
["SPX", "S&P 500 Index"],
|
|
18
|
+
["OEX", "S&P 100 Index"],
|
|
19
|
+
["MID", "S&P MidCap 400 Index"],
|
|
20
|
+
["DJI", "Dow Jones Industrial Average"],
|
|
21
|
+
["NDX", "Nasdaq-100 Index"],
|
|
22
|
+
["SOX", "PHLX Semiconductor Index"],
|
|
23
|
+
["RUT", "Russell 2000 Index"],
|
|
24
|
+
["RUI", "Russell 1000 Index"],
|
|
25
|
+
["RUA", "Russell 3000 Index"],
|
|
26
|
+
["MOVE", "ICE BofA MOVE Index"],
|
|
27
|
+
["NYA", "NYSE Composite Index"],
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
export interface IndexRoot {
|
|
31
|
+
/** The index's own symbol: ^VIX. */
|
|
32
|
+
symbol: string;
|
|
33
|
+
name: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The index a bare root names (VIX is ^VIX); null for any other symbol, ^VIX included. */
|
|
37
|
+
export function indexRootFor(symbol: string): IndexRoot | null {
|
|
38
|
+
const root = symbol.trim().toUpperCase();
|
|
39
|
+
const name = INDEX_ROOTS.get(root);
|
|
40
|
+
return name ? { symbol: `^${root}`, name } : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** "^VIX Cboe Volatility Index (try: gloomberb quote ^VIX)", under a bare root that found nothing. */
|
|
44
|
+
export function indexRootTryLine(root: IndexRoot, command = "quote"): string {
|
|
45
|
+
return `${root.symbol} ${root.name} (try: gloomberb ${command} ${root.symbol})`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** "also ^MOVE ICE BofA MOVE Index (gloomberb quote ^MOVE)", under a bare root that quoted another instrument. */
|
|
49
|
+
export function indexRootAlsoLine(root: IndexRoot, command = "quote"): string {
|
|
50
|
+
return `also ${root.symbol} ${root.name} (gloomberb ${command} ${root.symbol})`;
|
|
51
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
} from "./help";
|
|
22
22
|
import { parsePaneFunctionArgs } from "./pane-functions/options";
|
|
23
23
|
import { checkCliCommandOptions } from "./command-options";
|
|
24
|
-
import { fail, inferCliErrorOptions, printCliError } from "./errors";
|
|
25
|
-
import { setCliColorEnabledOverride } from "../utils/cli-output";
|
|
24
|
+
import { asUsageError, fail, inferCliErrorOptions, printCliError } from "./errors";
|
|
25
|
+
import { setCliColorEnabledOverride, setCliWidthOverride } from "../utils/cli-output";
|
|
26
26
|
import { search, searchCandidatesForCli, buildSearchReport } from "./commands/search";
|
|
27
27
|
import { ticker } from "./commands/ticker";
|
|
28
28
|
import { requireOneArg, takeOption } from "./commands/command-utils";
|
|
@@ -176,9 +176,10 @@ function createCoreCliCommands(
|
|
|
176
176
|
{ flags: "--output <path>", description: "PNG to write; defaults to gloomberb-<function>-<argument>.png in this folder" },
|
|
177
177
|
{ flags: "--width <px>", description: "Image width, 720 to 2400 (default 1280)" },
|
|
178
178
|
{ flags: "--height <px>", description: "Image height, 360 to 1800 (default 720)" },
|
|
179
|
-
{ flags: "--theme <id>", description: "Render with another theme, such as amber or
|
|
179
|
+
{ flags: "--theme <id>", description: "Render with another theme, such as amber or colorblind; gloomberb config themes lists them" },
|
|
180
180
|
{ flags: "--scale <n>", description: "Text scale from 0.5 to 4 (default 1)" },
|
|
181
181
|
{ flags: "--watermark <label>", description: "Label drawn in the pane title bar" },
|
|
182
|
+
{ flags: "--no-status", description: "Leave out the dated status line (as-of, delay, market hours) drawn in the pane footer" },
|
|
182
183
|
{ flags: "--<option> <value>", description: "A function setting; gloomberb catalog <function> lists them" },
|
|
183
184
|
EXCHANGE_OPTION,
|
|
184
185
|
],
|
|
@@ -187,6 +188,7 @@ function createCoreCliCommands(
|
|
|
187
188
|
"shot HP BHP:ASX",
|
|
188
189
|
"shot DDIS MSFT --tab history",
|
|
189
190
|
"shot HP NVDA --width 1600 --theme green",
|
|
191
|
+
"shot QQ AAPL,MSFT --no-status",
|
|
190
192
|
],
|
|
191
193
|
},
|
|
192
194
|
execute: async (args, ctx) => {
|
|
@@ -336,11 +338,12 @@ export async function dispatchCli(args: string[], options: DispatchCliOptions =
|
|
|
336
338
|
try {
|
|
337
339
|
parsed = parseCliGlobalArgs(args);
|
|
338
340
|
} catch (error) {
|
|
339
|
-
printCliError(error, inferCliErrorOptions(args));
|
|
341
|
+
printCliError(asUsageError(error), inferCliErrorOptions(args));
|
|
340
342
|
process.exitCode = 1;
|
|
341
343
|
return { kind: "handled" };
|
|
342
344
|
}
|
|
343
345
|
setCliColorEnabledOverride(parsed.options.color);
|
|
346
|
+
setCliWidthOverride(parsed.options.width ?? null);
|
|
344
347
|
const command = parsed.args[0] ?? (parsed.help ? "help" : undefined);
|
|
345
348
|
if (!command) {
|
|
346
349
|
return { kind: "unhandled" };
|
|
@@ -361,11 +364,11 @@ export async function dispatchCli(args: string[], options: DispatchCliOptions =
|
|
|
361
364
|
if (!helpOnly && resolved.builtin) checkCliCommandOptions(resolved.command, commandArgs, parsed.literalStart - 1);
|
|
362
365
|
const result = await target.command.execute(
|
|
363
366
|
commandArgs,
|
|
364
|
-
createCliCommandContext(target.ownerId, registry, parsed.options),
|
|
367
|
+
createCliCommandContext(target.ownerId, registry, parsed.options, commandArgs),
|
|
365
368
|
);
|
|
366
369
|
return normalizeCliDispatchResult(result);
|
|
367
370
|
} catch (error) {
|
|
368
|
-
printCliError(error, parsed.options, { command: resolved.command.name });
|
|
371
|
+
printCliError(error, parsed.options, { command: resolved.command.name, args: commandArgs });
|
|
369
372
|
process.exitCode = 1;
|
|
370
373
|
return { kind: "handled" };
|
|
371
374
|
}
|
package/src/cli/listing-arg.ts
CHANGED
|
@@ -8,10 +8,13 @@ import {
|
|
|
8
8
|
canonicalExchange,
|
|
9
9
|
exchangeLabel,
|
|
10
10
|
isKnownExchangeCode,
|
|
11
|
+
isUsListingExchange,
|
|
11
12
|
parsePublicTickerKey,
|
|
12
13
|
publicTickerKey,
|
|
14
|
+
resolveExchangeTimeZone,
|
|
13
15
|
} from "../utils/exchanges";
|
|
14
16
|
import { withMarketData } from "./scoped-context";
|
|
17
|
+
import { isNoProviderMessage } from "../sources/provider-errors";
|
|
15
18
|
|
|
16
19
|
/** The `--exchange` option of every command that takes a ticker. */
|
|
17
20
|
export const EXCHANGE_OPTION = {
|
|
@@ -156,32 +159,237 @@ export async function failIfNotTraded(
|
|
|
156
159
|
if (message) ctx.fail(message);
|
|
157
160
|
}
|
|
158
161
|
|
|
162
|
+
/** What a command asked a listing for, to name it when the listing has none: `history` asks for "history". */
|
|
163
|
+
export interface ListingDataRequest {
|
|
164
|
+
/** The command a retry runs, with any options to repeat: `history`, `history --range 5Y`. */
|
|
165
|
+
command: string;
|
|
166
|
+
/** What the listing has none of: "history", "a quote". */
|
|
167
|
+
noun: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The router's answer when no source serves a symbol: "No history provider available for 2222",
|
|
172
|
+
* or "Not a ticker: 2222." when the service found no listing for it.
|
|
173
|
+
*/
|
|
174
|
+
export function isNoProviderError(error: unknown): boolean {
|
|
175
|
+
return isNoProviderMessage(error instanceof Error ? error.message : String(error ?? ""));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const MAX_OTHER_LISTINGS = 5;
|
|
179
|
+
|
|
180
|
+
type BareListing = Pick<CliListing, "symbol" | "exchange"> & Partial<Pick<CliListing, "saved">>;
|
|
181
|
+
|
|
182
|
+
/** Where a bare symbol went and where else it trades, in the order they are tried and named. */
|
|
183
|
+
interface BareListingVenues {
|
|
184
|
+
/** Canonical code of the venue the symbol went to. */
|
|
185
|
+
exchange: string;
|
|
186
|
+
/** That venue as search lists it; null for a saved venue search no longer lists. */
|
|
187
|
+
resolved: ListingVenue | null;
|
|
188
|
+
/** The symbol's other venues, in search order. */
|
|
189
|
+
others: ListingVenue[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** A bare symbol goes to its saved listing, else to the one search lists first. Null when it named its exchange. */
|
|
193
|
+
async function bareListingVenues(listing: BareListing, deps: ListingDeps): Promise<BareListingVenues | null> {
|
|
194
|
+
if (listing.exchange) return null;
|
|
195
|
+
const venues = await listingVenues(listing.symbol, deps).catch(() => []);
|
|
196
|
+
const savedExchange = canonicalExchange(listing.saved?.metadata.exchange);
|
|
197
|
+
const resolved = venues.find((venue) => venue.exchange === savedExchange) ?? (savedExchange ? null : venues[0] ?? null);
|
|
198
|
+
const exchange = resolved?.exchange ?? savedExchange;
|
|
199
|
+
if (!exchange) return null;
|
|
200
|
+
return { exchange, resolved, others: venues.filter((venue) => venue !== resolved && venue.exchange !== savedExchange) };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
interface OtherListingsMessage {
|
|
204
|
+
message: string;
|
|
205
|
+
details: string;
|
|
206
|
+
/** SAN:EPA, for each other listing. */
|
|
207
|
+
others: string[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function describeOtherListings(
|
|
211
|
+
listing: BareListing,
|
|
212
|
+
{ exchange, resolved, others }: BareListingVenues,
|
|
213
|
+
request: ListingDataRequest,
|
|
214
|
+
): OtherListingsMessage | null {
|
|
215
|
+
if (others.length === 0) return null;
|
|
216
|
+
const name = resolved?.name || listing.saved?.metadata.name?.trim() || "";
|
|
217
|
+
const [command, ...options] = request.command.split(" ");
|
|
218
|
+
const retry = (key: string) => ["gloomberb", command, key, ...options].join(" ");
|
|
219
|
+
const lines = others.slice(0, MAX_OTHER_LISTINGS).map((venue) => {
|
|
220
|
+
const key = `${listing.symbol}:${venue.exchange}`;
|
|
221
|
+
return `${key}${venue.name ? ` ${venue.name}` : ""} (try: ${retry(key)})`;
|
|
222
|
+
});
|
|
223
|
+
if (others.length > MAX_OTHER_LISTINGS) lines.push(`and ${others.length - MAX_OTHER_LISTINGS} more: gloomberb search ${listing.symbol}`);
|
|
224
|
+
return {
|
|
225
|
+
message: `${listing.symbol} resolved to ${name ? `${name} (${exchange})` : exchange}, which has no ${request.noun}.`,
|
|
226
|
+
details: lines.length === 1 ? `Other listings: ${lines[0]}` : ["Other listings:", ...lines.map((line) => ` ${line}`)].join("\n"),
|
|
227
|
+
others: others.map((venue) => `${listing.symbol}:${venue.exchange}`),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* For a bare symbol whose request found no source: the listing it went to and
|
|
233
|
+
* the symbol's other listings, each with the command that asks for it, such
|
|
234
|
+
* as "2222 resolved to Kotobuki Spirits Co., Ltd. (JPX), which has no
|
|
235
|
+
* history." and "2222:TADAWUL Saudi Arabian Oil Co. (try: gloomberb history
|
|
236
|
+
* 2222:TADAWUL)". Null when the symbol named its exchange or has no other
|
|
237
|
+
* listing. It searches, so ask only after a request failed.
|
|
238
|
+
*/
|
|
239
|
+
async function otherListingsMessage(
|
|
240
|
+
listing: BareListing,
|
|
241
|
+
deps: ListingDeps,
|
|
242
|
+
request: ListingDataRequest,
|
|
243
|
+
): Promise<OtherListingsMessage | null> {
|
|
244
|
+
const venues = await bareListingVenues(listing, deps);
|
|
245
|
+
return venues && describeOtherListings(listing, venues, request);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The listing on one venue, as `SYM:EXCH` names it. */
|
|
249
|
+
function namedListing(listing: CliListing, exchange: string): CliListing {
|
|
250
|
+
const key = `${listing.symbol}:${exchange}`;
|
|
251
|
+
const saved = canonicalExchange(listing.saved?.metadata.exchange) === exchange ? listing.saved : null;
|
|
252
|
+
return { symbol: listing.symbol, exchange, key, saved, request: { symbol: key, exchange } };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function quoteListings(
|
|
256
|
+
dataProvider: DataProvider,
|
|
257
|
+
listings: readonly CliListing[],
|
|
258
|
+
refresh: boolean,
|
|
259
|
+
): Promise<Array<Quote | null>> {
|
|
260
|
+
if (listings.length === 0) return [];
|
|
261
|
+
const targets = listings.map((listing) => listing.request);
|
|
262
|
+
if (dataProvider.getQuotesBatch) {
|
|
263
|
+
const results = await dataProvider.getQuotesBatch(targets, { forceRefresh: refresh }).catch(() => []);
|
|
264
|
+
return targets.map((target, index) => (results.find((result) => result.target === target) ?? results[index])?.quote ?? null);
|
|
265
|
+
}
|
|
266
|
+
return Promise.all(targets.map((target) => dataProvider.getQuote(target.symbol, target.exchange, {
|
|
267
|
+
cacheMode: refresh ? "refresh" : "default",
|
|
268
|
+
}).catch(() => null)));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export type BareListingQuote =
|
|
272
|
+
| {
|
|
273
|
+
kind: "quoted";
|
|
274
|
+
/** The listing that has a quote: SXR8:XETRA. */
|
|
275
|
+
listing: CliListing;
|
|
276
|
+
quote: Quote;
|
|
277
|
+
/** "SXR8 -> XETRA (other listings: BUD, MUNICH)". */
|
|
278
|
+
note: string;
|
|
279
|
+
}
|
|
280
|
+
| ({ kind: "missing" } & OtherListingsMessage);
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The quote for a bare symbol no source quoted. The data service reads a
|
|
284
|
+
* symbol with no US listing on its home listing abroad (`bareListingNote`),
|
|
285
|
+
* so this is left for one it reads on none, as a code several companies list
|
|
286
|
+
* (2222 is Kotobuki Spirits in Tokyo and Saudi Aramco in Riyadh), and for a
|
|
287
|
+
* home listing with no quote. Search knows where the symbol trades: each
|
|
288
|
+
* listing is asked again by its own key (2222:JPX), as a named one is. The
|
|
289
|
+
* listing the symbol resolved to goes first, then the others together, and
|
|
290
|
+
* the first in search order with a quote is the answer. When none has one,
|
|
291
|
+
* the `otherListingsMessage` failure. Null when the symbol named its exchange
|
|
292
|
+
* (a named venue never switches) or search knows no listing of it. It
|
|
293
|
+
* searches, so ask only after a request failed.
|
|
294
|
+
*/
|
|
295
|
+
export async function quoteBareListing(
|
|
296
|
+
listing: CliListing,
|
|
297
|
+
deps: ListingDeps,
|
|
298
|
+
request: ListingDataRequest,
|
|
299
|
+
{ refresh = false }: { refresh?: boolean } = {},
|
|
300
|
+
): Promise<BareListingQuote | null> {
|
|
301
|
+
const venues = await bareListingVenues(listing, deps);
|
|
302
|
+
if (!venues) return null;
|
|
303
|
+
const first = [namedListing(listing, venues.exchange)];
|
|
304
|
+
const rest = venues.others.map((venue) => namedListing(listing, venue.exchange));
|
|
305
|
+
let tried = first;
|
|
306
|
+
let quotes = await quoteListings(deps.dataProvider, first, refresh);
|
|
307
|
+
if (!quotes[0] && rest.length > 0) {
|
|
308
|
+
tried = [...first, ...rest];
|
|
309
|
+
quotes = [...quotes, ...await quoteListings(deps.dataProvider, rest, refresh)];
|
|
310
|
+
}
|
|
311
|
+
const found = quotes.findIndex(Boolean);
|
|
312
|
+
if (found < 0) {
|
|
313
|
+
const other = describeOtherListings(listing, venues, request);
|
|
314
|
+
return other && { kind: "missing", ...other };
|
|
315
|
+
}
|
|
316
|
+
const answer = tried[found]!;
|
|
317
|
+
const without = tried.slice(0, found).map((entry) => entry.exchange);
|
|
318
|
+
// Listings not asked, or asked and quoted, are still there to name.
|
|
319
|
+
const others = [...first, ...rest]
|
|
320
|
+
.filter((entry, index) => entry !== answer && (index >= tried.length || quotes[index]))
|
|
321
|
+
.map((entry) => entry.exchange);
|
|
322
|
+
const more = others.length - MAX_OTHER_LISTINGS;
|
|
323
|
+
const aside = [
|
|
324
|
+
without.length > 0 ? `no quote on ${without.join(", ")}` : "",
|
|
325
|
+
others.length > 0
|
|
326
|
+
? `other listings: ${others.slice(0, MAX_OTHER_LISTINGS).join(", ")}${more > 0 ? ` and ${more} more: gloomberb search ${listing.symbol}` : ""}`
|
|
327
|
+
: "",
|
|
328
|
+
].filter(Boolean).join("; ");
|
|
329
|
+
return {
|
|
330
|
+
kind: "quoted",
|
|
331
|
+
listing: answer,
|
|
332
|
+
quote: quotes[found]!,
|
|
333
|
+
note: `${listing.symbol} -> ${answer.exchange}${aside ? ` (${aside})` : ""}`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// The zones of the US exchanges; a venue in another is abroad.
|
|
338
|
+
const US_TIME_ZONES = new Set(["America/New_York", "America/Chicago"]);
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* "SXR8 -> XETRA" for a bare symbol the data service read on its home listing
|
|
342
|
+
* abroad, as it reads a symbol with no US listing whose listings belong to one
|
|
343
|
+
* company or fund: a row that shows the symbol as typed says which listing it
|
|
344
|
+
* is. Null for a named or saved venue, and for a venue the app does not place
|
|
345
|
+
* outside the US (a US exchange, OTC, a futures exchange).
|
|
346
|
+
*/
|
|
347
|
+
export function bareListingNote(
|
|
348
|
+
listing: Pick<CliListing, "symbol" | "request">,
|
|
349
|
+
quote: Pick<Quote, "listingExchangeName" | "exchangeName"> | null | undefined,
|
|
350
|
+
): string | null {
|
|
351
|
+
if (listing.request.exchange || !quote || !/^[A-Z0-9]{1,10}$/.test(listing.symbol)) return null;
|
|
352
|
+
const venue = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
|
|
353
|
+
const zone = resolveExchangeTimeZone(venue);
|
|
354
|
+
return zone && !US_TIME_ZONES.has(zone) && !isUsListingExchange(venue) ? `${listing.symbol} -> ${venue}` : null;
|
|
355
|
+
}
|
|
356
|
+
|
|
159
357
|
/**
|
|
160
358
|
* A data request for the listings a command named. When it throws, or `isMiss`
|
|
161
359
|
* calls what it returned empty, and a listing's exchange is one its symbol does
|
|
162
360
|
* not trade on, the command ends in that message instead of the request's own
|
|
163
|
-
* "unavailable".
|
|
361
|
+
* "unavailable". With `request`, a bare symbol no source serves ends naming
|
|
362
|
+
* its other listings (`otherListingsMessage`). A request that succeeds costs no
|
|
363
|
+
* extra lookup.
|
|
164
364
|
*/
|
|
165
365
|
export async function loadForListing<T>(
|
|
166
|
-
listings:
|
|
366
|
+
listings: ListingForLoad | readonly ListingForLoad[],
|
|
167
367
|
deps: ListingDeps,
|
|
168
368
|
ctx: Pick<CliCommandContext, "fail">,
|
|
169
369
|
load: () => Promise<T>,
|
|
170
370
|
isMiss?: (value: T) => boolean,
|
|
371
|
+
request?: ListingDataRequest,
|
|
171
372
|
): Promise<T> {
|
|
172
|
-
const
|
|
373
|
+
const all = "symbol" in listings ? [listings] : listings;
|
|
374
|
+
const named = all.filter((listing) => listing.exchange);
|
|
173
375
|
const checkVenues = () => Promise.all(named.map((listing) => failIfNotTraded(listing, deps, ctx)));
|
|
174
376
|
let value: T;
|
|
175
377
|
try {
|
|
176
378
|
value = await load();
|
|
177
379
|
} catch (error) {
|
|
178
380
|
await checkVenues();
|
|
381
|
+
if (request && all.length === 1 && isNoProviderError(error)) {
|
|
382
|
+
const other = await otherListingsMessage(all[0]!, deps, request);
|
|
383
|
+
if (other) ctx.fail(other.message, other.details);
|
|
384
|
+
}
|
|
179
385
|
throw error;
|
|
180
386
|
}
|
|
181
387
|
if (isMiss?.(value)) await checkVenues();
|
|
182
388
|
return value;
|
|
183
389
|
}
|
|
184
390
|
|
|
391
|
+
type ListingForLoad = Pick<ListingArg, "symbol" | "exchange"> & Partial<Pick<CliListing, "saved">>;
|
|
392
|
+
|
|
185
393
|
/** The saved ticker for a listing: under its own key, or a saved symbol whose venue it is. */
|
|
186
394
|
export async function loadSavedListing(store: ListingStore, listing: ListingArg): Promise<TickerRecord | null> {
|
|
187
395
|
if (!listing.exchange) return store.loadTicker(listing.symbol);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { notATickerSymbol } from "../sources/provider-errors";
|
|
2
|
+
|
|
3
|
+
// "Not a ticker: APPLE." as the router words it; the symbol ends at the sentence's own period.
|
|
4
|
+
const NOT_A_TICKER_SENTENCE = /Not a ticker: ((?:(?!\.\s)[^\n`\x1b])+?)\.(?=\s|\x1b|$)(?! Try `gloomberb search)/g;
|
|
5
|
+
|
|
6
|
+
/** The symbol as the user typed it on the command line (`Apple`), not as the router saw it (`APPLE`). */
|
|
7
|
+
function typedSymbol(symbol: string, args: readonly string[]): string {
|
|
8
|
+
const wanted = symbol.toUpperCase();
|
|
9
|
+
for (const arg of args) {
|
|
10
|
+
for (const part of arg.split(",")) {
|
|
11
|
+
const typed = part.trim().replace(/^\$/, "");
|
|
12
|
+
if (typed.toUpperCase() === wanted) return typed;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return symbol;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** "Try `gloomberb search Apple`." naming what the user typed; a venue (`Apple:NYSE`) is not part of a search. */
|
|
19
|
+
function searchHint(typed: string): string {
|
|
20
|
+
const query = typed.split(":")[0]!.trim() || typed;
|
|
21
|
+
return `Try \`gloomberb search ${/\s/.test(query) ? JSON.stringify(query) : query}\`.`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Adds the search hint after each "Not a ticker: APPLE." in text meant for a person. The router
|
|
26
|
+
* only knows the symbol upper-cased, so the hint reads the user's own spelling from `args`.
|
|
27
|
+
*/
|
|
28
|
+
export function withSearchHints(text: string, args: readonly string[]): string {
|
|
29
|
+
return text.includes("Not a ticker: ")
|
|
30
|
+
? text.replace(NOT_A_TICKER_SENTENCE, (sentence, symbol: string) => `${sentence} ${searchHint(typedSymbol(symbol, args))}`)
|
|
31
|
+
: text;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Whether an error message says the symbol is not a ticker. */
|
|
35
|
+
export function isNotATickerMessage(message: string | null | undefined): boolean {
|
|
36
|
+
return message != null && notATickerSymbol(message) !== null;
|
|
37
|
+
}
|
package/src/cli/options.ts
CHANGED
|
@@ -5,6 +5,10 @@ export interface CliGlobalOptions {
|
|
|
5
5
|
quiet: boolean;
|
|
6
6
|
color: boolean | null;
|
|
7
7
|
limit?: number;
|
|
8
|
+
/** `--tail <n>`: the newest n rows of a dated series, in their printed order. Never set with `limit`. */
|
|
9
|
+
tail?: number;
|
|
10
|
+
/** `--width <n>`: fit tables to n columns. Unset, a terminal's own width fits them and piped output is not fitted. */
|
|
11
|
+
width?: number;
|
|
8
12
|
refresh: boolean;
|
|
9
13
|
dryRun: boolean;
|
|
10
14
|
yes: boolean;
|
|
@@ -32,20 +36,44 @@ export const DEFAULT_CLI_OPTIONS: CliGlobalOptions = {
|
|
|
32
36
|
yes: false,
|
|
33
37
|
};
|
|
34
38
|
|
|
35
|
-
function
|
|
36
|
-
if (!value) throw new Error(
|
|
39
|
+
function parseRowCount(flag: "--limit" | "--tail", value: string | undefined): number {
|
|
40
|
+
if (!value) throw new Error(`Missing value for ${flag}.`);
|
|
37
41
|
const parsed = Number(value);
|
|
38
42
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
39
|
-
throw new Error(
|
|
43
|
+
throw new Error(`${flag} must be a positive integer.`);
|
|
40
44
|
}
|
|
41
45
|
return parsed;
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
/** Commands whose own `--width` is a size in pixels, so it is not the table width. */
|
|
49
|
+
const PIXEL_WIDTH_COMMANDS = new Set(["shot", "screenshot"]);
|
|
50
|
+
const MIN_TABLE_WIDTH = 20;
|
|
51
|
+
|
|
52
|
+
function parseColumnCount(value: string | undefined): number {
|
|
53
|
+
const parsed = Number(value);
|
|
54
|
+
if (!value || !Number.isInteger(parsed) || parsed < MIN_TABLE_WIDTH) {
|
|
55
|
+
throw new Error(`--width must be a whole number of columns, ${MIN_TABLE_WIDTH} or more, such as --width 80.`);
|
|
56
|
+
}
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The first word that is not an option or the value of one: the command the arguments are for. */
|
|
61
|
+
function commandWord(rawArgs: readonly string[]): string | undefined {
|
|
62
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
63
|
+
const arg = rawArgs[index]!;
|
|
64
|
+
if (arg === "--") return rawArgs[index + 1];
|
|
65
|
+
if (arg === "--limit" || arg === "--tail" || arg === "--width") index += 1;
|
|
66
|
+
else if (!arg.startsWith("-")) return arg;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
export function parseCliGlobalArgs(rawArgs: string[]): ParsedCliArgs {
|
|
45
72
|
const options: CliGlobalOptions = { ...DEFAULT_CLI_OPTIONS };
|
|
46
73
|
const args: string[] = [];
|
|
47
74
|
let help = false;
|
|
48
75
|
let literalStart: number | null = null;
|
|
76
|
+
const ownsWidth = PIXEL_WIDTH_COMMANDS.has(commandWord(rawArgs)?.toLowerCase() ?? "");
|
|
49
77
|
|
|
50
78
|
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
51
79
|
const arg = rawArgs[index]!;
|
|
@@ -94,17 +122,33 @@ export function parseCliGlobalArgs(rawArgs: string[]): ParsedCliArgs {
|
|
|
94
122
|
options.yes = true;
|
|
95
123
|
continue;
|
|
96
124
|
}
|
|
97
|
-
if (arg === "--
|
|
125
|
+
if (!ownsWidth && arg === "--width") {
|
|
98
126
|
index += 1;
|
|
99
|
-
options.
|
|
127
|
+
options.width = parseColumnCount(rawArgs[index]);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!ownsWidth && arg.startsWith("--width=")) {
|
|
131
|
+
options.width = parseColumnCount(arg.slice("--width=".length));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (arg === "--limit" || arg === "--tail") {
|
|
135
|
+
index += 1;
|
|
136
|
+
options[arg === "--limit" ? "limit" : "tail"] = parseRowCount(arg, rawArgs[index]);
|
|
100
137
|
continue;
|
|
101
138
|
}
|
|
102
139
|
if (arg.startsWith("--limit=")) {
|
|
103
|
-
options.limit =
|
|
140
|
+
options.limit = parseRowCount("--limit", arg.slice("--limit=".length));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (arg.startsWith("--tail=")) {
|
|
144
|
+
options.tail = parseRowCount("--tail", arg.slice("--tail=".length));
|
|
104
145
|
continue;
|
|
105
146
|
}
|
|
106
147
|
args.push(arg);
|
|
107
148
|
}
|
|
149
|
+
if (options.limit != null && options.tail != null) {
|
|
150
|
+
throw new Error("Use --limit or --tail, not both: --limit keeps the first rows, --tail the newest.");
|
|
151
|
+
}
|
|
108
152
|
|
|
109
153
|
return { args, options, help, literalStart: literalStart ?? args.length };
|
|
110
154
|
}
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
export type PaneFunctionReadiness = "ready" | "partial" | "live-dom" | "unsupported";
|
|
10
10
|
type PaneFunctionScreenshotReadiness = PaneFunctionReadiness;
|
|
11
11
|
type PaneFunctionTickerCardinality = "none" | "one" | "one-or-more" | "two-or-more" | "one-or-two";
|
|
12
|
+
/** A headless option, or a rendered view's, which may name its value, show an example and check what is typed. */
|
|
12
13
|
export type PaneFunctionOptionDef = HeadlessPaneOptionDef;
|
|
13
14
|
export type NormalizedPaneFunctionOptions = Record<string, string | number | boolean>;
|
|
14
15
|
|
|
@@ -64,7 +65,7 @@ function fallbackCapability(
|
|
|
64
65
|
limitations: [dataPane
|
|
65
66
|
? RENDERED_VIEW_LIMITATION
|
|
66
67
|
: "This pane is an interactive surface and does not expose a data report."],
|
|
67
|
-
options: [],
|
|
68
|
+
options: [...(pane.reportOptions ?? [])],
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -159,6 +160,7 @@ function normalizeOptionValue(option: PaneFunctionOptionDef, rawValue: string |
|
|
|
159
160
|
throw new Error(`Invalid --${option.key} value "${rawValue}". Use true or false.`);
|
|
160
161
|
}
|
|
161
162
|
if (rawValue === true) throw new Error(`--${option.key} requires a value.`);
|
|
163
|
+
if (option.normalize) return option.normalize(rawValue);
|
|
162
164
|
if (option.type === "enum") return normalizeEnumValue(option, rawValue);
|
|
163
165
|
if (option.type === "integer") {
|
|
164
166
|
const value = Number(rawValue);
|
|
@@ -180,7 +182,11 @@ export function normalizeCapabilityOptions(
|
|
|
180
182
|
settings: { strict?: boolean } = {},
|
|
181
183
|
): NormalizedPaneFunctionOptions {
|
|
182
184
|
if (!capability.botSafe) {
|
|
183
|
-
|
|
185
|
+
// A rendered view takes any pane setting; the options it declares are checked.
|
|
186
|
+
return Object.fromEntries(Object.entries(options).map(([key, value]) => {
|
|
187
|
+
const option = resolveOptionDef(capability, key);
|
|
188
|
+
return option ? [option.key, normalizeOptionValue(option, value)] : [key, value];
|
|
189
|
+
}));
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
const normalized: Record<string, string | number | boolean> = {};
|
|
@@ -259,6 +259,11 @@ function paneCatalogSearchScore(entry: PaneCatalogEntry, query: string): number
|
|
|
259
259
|
].filter((value): value is string => !!value).join(" ").toLowerCase();
|
|
260
260
|
|
|
261
261
|
let score = searchable.includes(query.toLowerCase()) ? 12 : 0;
|
|
262
|
+
// A query that is one of the function's own keywords ("hedge" for OSA) beats one found inside a
|
|
263
|
+
// longer keyword ("hedge funds" for 13F).
|
|
264
|
+
const keywords = [entry.label, ...entry.keywords, ...entry.capability.aliases]
|
|
265
|
+
.filter((value): value is string => !!value).map((value) => value.toLowerCase().replace(/\s+/g, " "));
|
|
266
|
+
if (keywords.includes(terms.join(" "))) score += 4;
|
|
262
267
|
let matchedTerms = 0;
|
|
263
268
|
for (const term of terms) {
|
|
264
269
|
const normalized = normalizeLookupToken(term);
|
|
@@ -320,7 +325,8 @@ function optionChoices(option: PaneFunctionOptionDef): string[] {
|
|
|
320
325
|
|
|
321
326
|
function optionFlags(option: PaneFunctionOptionDef): string {
|
|
322
327
|
const choices = optionChoices(option).join("|");
|
|
323
|
-
|
|
328
|
+
const value = choices && choices.length <= MAX_INLINE_CHOICES ? choices : option.values?.length ? "value" : option.placeholder ?? option.type;
|
|
329
|
+
return `--${option.key} <${value}>`;
|
|
324
330
|
}
|
|
325
331
|
|
|
326
332
|
function optionDescription(option: PaneFunctionOptionDef): string {
|
|
@@ -365,7 +371,12 @@ function renderCatalogEntry(entry: PaneCatalogEntry): string {
|
|
|
365
371
|
}
|
|
366
372
|
|
|
367
373
|
lines.push("", renderSection("Examples"));
|
|
368
|
-
if (capability.reportReadiness !== "unsupported")
|
|
374
|
+
if (capability.reportReadiness !== "unsupported") {
|
|
375
|
+
lines.push(...wrapCommandLine(`gloomberb fn ${invocation}`, width));
|
|
376
|
+
for (const option of capability.options) {
|
|
377
|
+
if (option.example) lines.push(...wrapCommandLine(`gloomberb fn ${invocation} ${option.example}`, width));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
369
380
|
lines.push(...wrapCommandLine(`gloomberb shot ${invocation} --output ${entry.token.toLowerCase()}.png`, width));
|
|
370
381
|
return lines.join("\n");
|
|
371
382
|
}
|