@gloom-sh/terminal 0.19.1 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +58 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +6 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +49 -6
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +6 -4
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ/detail-view.tsx +16 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +19 -3
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/client.ts +4 -6
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +64 -42
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +5 -2
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/electrobun/view/ui-host.tsx +9 -2
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/time-series/studies.ts +0 -1628
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
candidateExchange,
|
|
17
17
|
LISTING_SEARCH_LIMIT,
|
|
18
18
|
listingDistance,
|
|
19
|
+
primaryCompanyLine,
|
|
19
20
|
searchTickerCandidates,
|
|
20
21
|
type TickerSearchCandidate,
|
|
21
22
|
} from "../../tickers/search";
|
|
@@ -23,7 +24,7 @@ import { initMarketData, withMarketData } from "../context";
|
|
|
23
24
|
import { listingChangeLine, loadListingFate } from "../../tickers/listing-fate";
|
|
24
25
|
import { formatMarketPriceWithCurrency, quoteFormatOptions } from "../../market-data/market/format";
|
|
25
26
|
import { formerIssuerLine, startTickerChangeLookup, type TickerChangeLookup } from "../../tickers/ticker-changes";
|
|
26
|
-
import { fail, failRetryable } from "../errors";
|
|
27
|
+
import { fail, failRetryable, failWithError, isCliFailure, unreachableError } from "../errors";
|
|
27
28
|
import { isProviderBusy, SEARCH_BUSY_CODE, SEARCH_BUSY_MESSAGE } from "../../sources/provider-errors";
|
|
28
29
|
import { isNetworkFailure } from "../../utils/network-failure";
|
|
29
30
|
import type { MarketContext } from "../types";
|
|
@@ -34,8 +35,8 @@ import { readBrokerIndexCode } from "../listing-arg";
|
|
|
34
35
|
import { knownGapMessage, commandPointer } from "../known-gaps";
|
|
35
36
|
import { instrumentTypeLabel } from "../../tickers/instrument-kind";
|
|
36
37
|
import { canonicalExchange, isUsListingExchange } from "../../utils/exchanges";
|
|
37
|
-
import { companyNameExtraWords, groupByCompany, sameCompany } from "../../tickers/company-name";
|
|
38
|
-
import {
|
|
38
|
+
import { companyLines, companyNameExtraWords, groupByCompany, namedByService, sameCompany } from "../../tickers/company-name";
|
|
39
|
+
import { isLowQualityTickerName } from "../../tickers/search/result";
|
|
39
40
|
import { findCatalogSeries, renderSeriesMatches, type SeriesMatch } from "../series-search";
|
|
40
41
|
import { centralBankAskedAbout, centralBankCiting, type CentralBank } from "../../utils/central-banks";
|
|
41
42
|
import { createPaneCatalog } from "../pane-functions/discovery";
|
|
@@ -133,8 +134,8 @@ export async function searchCandidatesForCli({
|
|
|
133
134
|
const quote = BARE_SYMBOL.test(symbol) && candidates.some((candidate) => candidate.symbol.toUpperCase() === symbol)
|
|
134
135
|
? await (quoted ?? loadSearchedQuote(symbol, savedExchange, dataProvider))
|
|
135
136
|
: null;
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
const ordered = withQuotedNames(symbol, withUsLineOfPrimary(withHomeListingPrimary(trimmedQuery, withPrimaryListingFirst(symbol, candidates, quote))), quote);
|
|
138
|
+
return withOtherCompaniesApart(ordered).slice(0, totalLimit);
|
|
138
139
|
} catch (error) {
|
|
139
140
|
// Not "No matches found": a script or a reader would take that for a name nothing lists.
|
|
140
141
|
if (isProviderBusy(error)) failRetryable(SEARCH_BUSY_MESSAGE, SEARCH_BUSY_CODE);
|
|
@@ -283,17 +284,17 @@ function withHomeListingPrimary(query: string, candidates: TickerSearchCandidate
|
|
|
283
284
|
|| primary.symbol.toUpperCase() === query.toUpperCase()) return candidates;
|
|
284
285
|
const company = resolveSearchName(primary);
|
|
285
286
|
const marked = !!primary.result && namedByService(query, primary.result);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
&& (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (
|
|
287
|
+
// Never a line of another business its name is in (`companyLines`): Siemens Energy AG is not Siemens AG's.
|
|
288
|
+
const lines = companyLines(primary, candidates.filter((candidate) => candidate === primary || (candidate.kind === "search"
|
|
289
|
+
&& candidate.category !== "Funds & Derivatives" && (candidate.instrumentClass === "equity" || candidate.instrumentClass === "other"))),
|
|
290
|
+
(candidate) => ({
|
|
291
|
+
name: resolveSearchName(candidate), exchange: candidateExchange(candidate),
|
|
292
|
+
receipt: listingDistance(candidate) === 2, home: candidate.result?.homeListing === true,
|
|
293
|
+
})).filter((candidate) => candidate === primary || !marked || companyNameExtraWords(resolveSearchName(candidate), company) != null);
|
|
294
|
+
const home = primaryCompanyLine(primary, lines, (candidate) => ({
|
|
295
|
+
distance: listingDistance(candidate), score: candidate.result?.popularity ?? candidate.popularity ?? null,
|
|
296
|
+
}), { named: marked });
|
|
297
|
+
if (home === primary) return candidates;
|
|
297
298
|
const rest = candidates.filter((candidate) => candidate !== home);
|
|
298
299
|
const at = rest.indexOf(primary);
|
|
299
300
|
return [...rest.slice(0, at), { ...home, category: "Primary Listing" as const }, { ...primary, category: "Other Listings" as const }, ...rest.slice(at + 1)];
|
|
@@ -346,6 +347,35 @@ function withQuotedNames(symbol: string, candidates: TickerSearchCandidate[], qu
|
|
|
346
347
|
});
|
|
347
348
|
}
|
|
348
349
|
|
|
350
|
+
/** A symbol without a venue's suffix or code: DK for DK.BA or DK:BYMA. */
|
|
351
|
+
const baseSymbol = (symbol: string) => symbol.toUpperCase().replace(/[:.][A-Z]{1,4}$/, "");
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Other Listings are the primary listing's company's own lines alone: by its
|
|
355
|
+
* name, a name it had (Cassava Sciences for Filana's FLNA), or, for a line
|
|
356
|
+
* search names no company for, its symbol on another venue. Another company's
|
|
357
|
+
* line, or a nameless line under another symbol (DKC1O on BYMA under
|
|
358
|
+
* `search DK`), is an Other Match, listed after them.
|
|
359
|
+
*/
|
|
360
|
+
function withOtherCompaniesApart(candidates: TickerSearchCandidate[]): TickerSearchCandidate[] {
|
|
361
|
+
// A line search names no company for: no name, or only its symbol (DKC1O).
|
|
362
|
+
const nameless = (candidate: TickerSearchCandidate) =>
|
|
363
|
+
isLowQualityTickerName(candidate.result ? candidate.result.name ?? "" : resolveSearchName(candidate), candidate.symbol);
|
|
364
|
+
const primary = candidates.find((candidate) => candidate.category === "Primary Listing");
|
|
365
|
+
// A primary line search names no company for gives nothing to tell its company's lines by.
|
|
366
|
+
if (!primary || nameless(primary)) return candidates;
|
|
367
|
+
const names = [resolveSearchName(primary), primary.result?.formerName, ...primary.nameAliases ?? []];
|
|
368
|
+
const ownLine = (candidate: TickerSearchCandidate) => nameless(candidate)
|
|
369
|
+
? baseSymbol(candidate.symbol) === baseSymbol(primary.symbol)
|
|
370
|
+
: names.some((company) => sameCompany(resolveSearchName(candidate), company));
|
|
371
|
+
const others = candidates.filter((candidate) => candidate.category === "Other Listings" && !ownLine(candidate));
|
|
372
|
+
if (others.length === 0) return candidates;
|
|
373
|
+
const kept = candidates.filter((candidate) => !others.includes(candidate));
|
|
374
|
+
const at = kept.findLastIndex((candidate) => candidate === primary || candidate.category === "Other Listings") + 1;
|
|
375
|
+
const apart = others.map((candidate) => ({ ...candidate, category: "Other Matches" as const }));
|
|
376
|
+
return [...kept.slice(0, at), ...apart, ...kept.slice(at)];
|
|
377
|
+
}
|
|
378
|
+
|
|
349
379
|
/** "Saved", or the broker a row came from; never the data service's internal id. */
|
|
350
380
|
function searchSourceLabel(candidate: TickerSearchCandidate): string {
|
|
351
381
|
return candidate.kind === "ticker" ? "Saved" : candidate.result?.brokerLabel || "";
|
|
@@ -498,18 +528,19 @@ export function buildSearchReport({
|
|
|
498
528
|
/** What the query was searched as: "Read VIX.X as ^VIX (Cboe Volatility Index)." */
|
|
499
529
|
readAs?: string;
|
|
500
530
|
}): string {
|
|
531
|
+
// Every note is prose: it wraps to a narrow --width.
|
|
501
532
|
const lines = [renderSection(`Search: ${query}`)];
|
|
502
|
-
if (readAs) lines.push("", readAs);
|
|
533
|
+
if (readAs) lines.push("", wrapToTerminal(readAs));
|
|
503
534
|
const notes = [...fate.notes, ...formerIssuerNotes(candidates, fate)];
|
|
504
|
-
if (notes.length > 0) lines.push("", ...notes);
|
|
535
|
+
if (notes.length > 0) lines.push("", ...notes.map((note) => wrapToTerminal(note)));
|
|
505
536
|
const gap = knownGapMessage(query);
|
|
506
537
|
const indexNote = gap ? null : indexRootNote(query, candidates);
|
|
507
|
-
if (indexNote) lines.push("", indexNote);
|
|
508
|
-
if (gap) lines.push("", gap);
|
|
538
|
+
if (indexNote) lines.push("", wrapToTerminal(indexNote));
|
|
539
|
+
if (gap) lines.push("", wrapToTerminal(gap));
|
|
509
540
|
const bank = searchedCentralBank(query);
|
|
510
541
|
if (bank) lines.push("", wrapToTerminal(centralBankLine(bank)));
|
|
511
542
|
const setting = commandPointer(query);
|
|
512
|
-
if (setting) lines.push("", setting);
|
|
543
|
+
if (setting) lines.push("", wrapToTerminal(setting));
|
|
513
544
|
const charted = renderSeriesMatches(query, series, SEARCH_SERIES_LIMIT, cliTerminalWidth() ?? 100, "catalog");
|
|
514
545
|
// A query that names the data ("carbon", "day-ahead") lists its series first; words that happen to
|
|
515
546
|
// be in a series name ("apple") list them after the tickers.
|
|
@@ -523,9 +554,9 @@ export function buildSearchReport({
|
|
|
523
554
|
if (functions.length > 0) lines.push("", renderFunctionMatches(functions));
|
|
524
555
|
else if (!gap && !bank && !setting && !charted) {
|
|
525
556
|
return [...lines, ...hasNonLatinLetters(query) ? [cliStyles.muted(OTHER_SCRIPT_NO_MATCH)]
|
|
526
|
-
: [cliStyles.muted("No matches found."), cliStyles.muted(catalogHint(query))]].join("\n");
|
|
557
|
+
: [cliStyles.muted("No matches found."), cliStyles.muted(wrapToTerminal(catalogHint(query)))]].join("\n");
|
|
527
558
|
}
|
|
528
|
-
if (pointToCatalog) lines.push("", cliStyles.muted(catalogHint(query)));
|
|
559
|
+
if (pointToCatalog) lines.push("", cliStyles.muted(wrapToTerminal(catalogHint(query))));
|
|
529
560
|
return lines.join("\n");
|
|
530
561
|
}
|
|
531
562
|
|
|
@@ -558,11 +589,39 @@ export function buildSearchReport({
|
|
|
558
589
|
));
|
|
559
590
|
}
|
|
560
591
|
if (charted && !seriesFirst) lines.push("", charted);
|
|
561
|
-
if (pointToCatalog) lines.push("", cliStyles.muted(catalogHint(query)));
|
|
592
|
+
if (pointToCatalog) lines.push("", cliStyles.muted(wrapToTerminal(catalogHint(query))));
|
|
562
593
|
|
|
563
594
|
return lines.join("\n");
|
|
564
595
|
}
|
|
565
596
|
|
|
597
|
+
/**
|
|
598
|
+
* What a search says from its own text, whatever the ticker lookup does: the
|
|
599
|
+
* data no source carries and its proxies ("baltic dry"), the central bank the
|
|
600
|
+
* query names, the setting or report it names.
|
|
601
|
+
*/
|
|
602
|
+
function localSearchNotes(query: string): string[] {
|
|
603
|
+
const bank = searchedCentralBank(query);
|
|
604
|
+
return [knownGapMessage(query), bank ? centralBankLine(bank) : null, commandPointer(query)].filter((note): note is string => !!note);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* A ticker lookup that was busy or never reached the data service fails only
|
|
609
|
+
* the ticker part of a search: the notes from local text print first (text) or
|
|
610
|
+
* ride in the failure's details (JSON), then the failure says that no tickers
|
|
611
|
+
* were looked up. A search with no such note fails as before.
|
|
612
|
+
*/
|
|
613
|
+
function failTickerLookup(error: unknown, query: string, printNotes: boolean): never {
|
|
614
|
+
const notes = localSearchNotes(query);
|
|
615
|
+
const busy = isCliFailure(error) && error.code === SEARCH_BUSY_CODE;
|
|
616
|
+
if (notes.length === 0 || !(busy || isNetworkFailure(error))) throw error;
|
|
617
|
+
if (printNotes) console.log([renderSection(`Search: ${query}`), "", ...notes.map((note) => wrapToTerminal(note))].join("\n"));
|
|
618
|
+
const failure = busy ? { code: SEARCH_BUSY_CODE, message: TICKER_SEARCH_BUSY_MESSAGE, retryable: true } : unreachableError();
|
|
619
|
+
return failWithError(printNotes ? failure : { ...failure, details: [failure.details, ...notes].filter(Boolean).join("\n") });
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** The busy line after a search's own notes printed: only the ticker lookup is missing. */
|
|
623
|
+
const TICKER_SEARCH_BUSY_MESSAGE = "Ticker search is busy, try again in a moment.";
|
|
624
|
+
|
|
566
625
|
/** The functions a search lists when it found no ticker, unless a known gap or a central bank already says where to look. */
|
|
567
626
|
function searchedFunctions(
|
|
568
627
|
query: string,
|
|
@@ -613,7 +672,7 @@ export async function search(query: string, dependencies: SearchCommandDependenc
|
|
|
613
672
|
tickers,
|
|
614
673
|
dataProvider,
|
|
615
674
|
...quote ? { quote } : {},
|
|
616
|
-
});
|
|
675
|
+
}).catch((error: unknown) => failTickerLookup(error, trimmedQuery, !dependencies.printResult));
|
|
617
676
|
const fate = quote && changes ? await searchFate(symbol, candidates, await quote, dataProvider, changes) : NO_SEARCH_FATE;
|
|
618
677
|
|
|
619
678
|
const series = findCatalogSeries(dependencies.plugins ?? [], trimmedQuery, "names");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "path";
|
|
2
2
|
import { logFailure } from "../../utils/log-failure";
|
|
3
|
-
import {
|
|
3
|
+
import { versionCommand } from "./version";
|
|
4
4
|
import { saveConfig } from "../../data/config/store";
|
|
5
5
|
import { builtinPluginGroupMembers } from "../../plugins/ownership";
|
|
6
6
|
import type { AppConfig, TelemetryConfig } from "../../types/config";
|
|
@@ -9,7 +9,7 @@ import { readBaseCurrency } from "../../utils/base-currency";
|
|
|
9
9
|
import { NIGHTLY_CHANNEL_SUMMARY, parseUpdateChannel, withUpdateChannel } from "../../updater/channel";
|
|
10
10
|
import type { CliCommandDef } from "../../types/plugin";
|
|
11
11
|
import type { BuiltinCliCommandDef } from "../command-options";
|
|
12
|
-
import {
|
|
12
|
+
import { withCliServices, withConfigData } from "../context";
|
|
13
13
|
import { CLI_COMMAND_GROUPS } from "../help";
|
|
14
14
|
import { dryRunNote, formatBytes, formatStatusCell } from "../helpers";
|
|
15
15
|
import { cliStyles, cliTerminalWidth, renderSection, renderTable, wrapText } from "../../utils/cli-output";
|
|
@@ -97,6 +97,21 @@ function describeTimezone(value: unknown): string {
|
|
|
97
97
|
return typeof value === "string" && value ? value : "not set, times print in UTC alone";
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/** What `config get telemetry.attention` prints while the setting is untouched. */
|
|
101
|
+
const ATTENTION_DEFAULT = "default";
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Attention Counts as set, not as "on": unset is the default, which counts an
|
|
105
|
+
* account only once the app has shown it its notice, so it is not `true`.
|
|
106
|
+
*/
|
|
107
|
+
function attentionSetting(telemetry: TelemetryConfig | undefined): boolean | typeof ATTENTION_DEFAULT {
|
|
108
|
+
return telemetry?.attention ?? ATTENTION_DEFAULT;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function describeAttentionSetting(value: unknown): string {
|
|
112
|
+
return value === ATTENTION_DEFAULT ? "default: on once the app has shown its notice" : describeConfigValue(value);
|
|
113
|
+
}
|
|
114
|
+
|
|
100
115
|
function describeConfigValue(value: unknown): string {
|
|
101
116
|
if (value == null) return "nothing";
|
|
102
117
|
if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "nothing";
|
|
@@ -174,32 +189,6 @@ function resourceCacheStats(
|
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
export function createSystemCliCommands(): CliCommandDef[] {
|
|
177
|
-
const versionCommand: CliCommandDef = {
|
|
178
|
-
name: "version",
|
|
179
|
-
aliases: ["--version", "-v"],
|
|
180
|
-
description: "Print the version, runtime, and data folder",
|
|
181
|
-
help: { group: CLI_COMMAND_GROUPS.app, usage: ["version"] },
|
|
182
|
-
execute: async (_args, ctx) => {
|
|
183
|
-
// Where the data lives, or will once a command or the app first runs:
|
|
184
|
-
// printing the version never creates the folder.
|
|
185
|
-
let dataDir: string | null = null;
|
|
186
|
-
try {
|
|
187
|
-
dataDir = await resolveCliDataDir();
|
|
188
|
-
} catch (error) {
|
|
189
|
-
logFailure("cli-system", "Resolve data directory failed")(error);
|
|
190
|
-
dataDir = null;
|
|
191
|
-
}
|
|
192
|
-
ctx.printResult({
|
|
193
|
-
data: [{
|
|
194
|
-
version: VERSION,
|
|
195
|
-
bun: typeof Bun === "undefined" ? null : Bun.version,
|
|
196
|
-
platform: process.platform,
|
|
197
|
-
arch: process.arch,
|
|
198
|
-
dataDir,
|
|
199
|
-
}],
|
|
200
|
-
}, { layout: "record" });
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
192
|
|
|
204
193
|
const doctorCommand: CliCommandDef = {
|
|
205
194
|
name: "doctor",
|
|
@@ -259,6 +248,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
|
|
|
259
248
|
"config set theme <id>",
|
|
260
249
|
"config set telemetry.crashReports false",
|
|
261
250
|
"config set telemetry.usage false",
|
|
251
|
+
"config set telemetry.attention false",
|
|
262
252
|
"config set starPrompt.enabled false",
|
|
263
253
|
"config set timezone <Region/City>|none",
|
|
264
254
|
"config set updates.channel stable|nightly",
|
|
@@ -333,7 +323,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
|
|
|
333
323
|
valueFlashingEnabled: context.config.valueFlashingEnabled,
|
|
334
324
|
"telemetry.crashReports": context.config.telemetry?.crashReports !== false,
|
|
335
325
|
"telemetry.usage": context.config.telemetry?.usage !== false,
|
|
336
|
-
"telemetry.attention": context.config.telemetry
|
|
326
|
+
"telemetry.attention": attentionSetting(context.config.telemetry),
|
|
337
327
|
[STAR_PROMPT_CONFIG_KEY]: context.config.starPrompt?.enabled !== false,
|
|
338
328
|
[TIMEZONE_CONFIG_KEY]: context.config.timezone ?? null,
|
|
339
329
|
[UPDATE_CHANNEL_CONFIG_KEY]: context.config.updates?.channel ?? "stable",
|
|
@@ -348,6 +338,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
|
|
|
348
338
|
...(key === "theme" ? { format: (value: unknown) => describeThemeId(String(value)) } : {}),
|
|
349
339
|
...(key === TIMEZONE_CONFIG_KEY ? { format: describeTimezone } : {}),
|
|
350
340
|
...(key === "baseCurrency" ? { format: describeBaseCurrency } : {}),
|
|
341
|
+
...(key === "telemetry.attention" ? { format: describeAttentionSetting } : {}),
|
|
351
342
|
})),
|
|
352
343
|
});
|
|
353
344
|
return;
|
|
@@ -367,7 +358,8 @@ export function createSystemCliCommands(): CliCommandDef[] {
|
|
|
367
358
|
ctx.printResult({ data: { key, value: safeConfig[key] } }, {
|
|
368
359
|
text: (data) => key === KEYBINDINGS_CONFIG_KEY ? renderKeybindings(data.value)
|
|
369
360
|
: key === TIMEZONE_CONFIG_KEY ? describeTimezone(data.value)
|
|
370
|
-
: key === "baseCurrency" ? describeBaseCurrency(data.value)
|
|
361
|
+
: key === "baseCurrency" ? describeBaseCurrency(data.value)
|
|
362
|
+
: key === "telemetry.attention" ? describeAttentionSetting(data.value) : describeConfigValue(data.value),
|
|
371
363
|
});
|
|
372
364
|
return;
|
|
373
365
|
}
|
|
@@ -72,6 +72,8 @@ import {
|
|
|
72
72
|
quoteChangeText,
|
|
73
73
|
} from "../helpers";
|
|
74
74
|
import { NotesFiles } from "../../plugins/builtin/notes/files";
|
|
75
|
+
import { getFormDescription } from "../../plugins/builtin/sec/model";
|
|
76
|
+
import { rowSummary, rowSummaryTarget } from "../../plugins/builtin/sec/row-summaries";
|
|
75
77
|
import { isKnownNonUsListing, secFilingItemCodes, US_FUND_FACTS_ONLY } from "../../utils/sec";
|
|
76
78
|
import { currentReportItemsLabel, hiddenFilingsNote, loadLatestFilings, type HiddenFilings } from "../../utils/sec-filings";
|
|
77
79
|
import { canonicalExchange, exchangeLabel, isKnownExchangeCode, isUsListingExchange } from "../../utils/exchanges";
|
|
@@ -80,6 +82,7 @@ import {
|
|
|
80
82
|
resolveCliListing, type CliListing, type ListingDataRequest,
|
|
81
83
|
} from "../listing-arg";
|
|
82
84
|
import { movedAwayChange } from "../../tickers/ticker-changes";
|
|
85
|
+
import { listingChangeLine } from "../../tickers/listing-fate";
|
|
83
86
|
import { companyMemberships, loadCompanyMemberships, type CompanyMemberships, type ListMembership } from "../issuer-membership";
|
|
84
87
|
import { isNotATickerMessage } from "../not-a-ticker";
|
|
85
88
|
import { sharesOutstandingInReceipts } from "../../utils/depositary-receipt";
|
|
@@ -305,6 +308,34 @@ function normalizeComparable(value: string): string {
|
|
|
305
308
|
return value.toUpperCase().replace(/\bFORM\b/g, "").replace(/[^A-Z0-9]+/g, "");
|
|
306
309
|
}
|
|
307
310
|
|
|
311
|
+
/** A recent filing with what an insider form's own XML says: who filed it and what it reports (`rowSummary`). */
|
|
312
|
+
type RecentFiling = SecFilingItem & { summary?: string };
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The recent filings, each insider form (3, 4, 5, 144) with its filer and what it reports, read from the
|
|
316
|
+
* filing as fn SEC's rows read it: "Fred Thiel: sale of 50K shares". A filing that cannot be read keeps its
|
|
317
|
+
* form's meaning alone.
|
|
318
|
+
*/
|
|
319
|
+
async function withFilingSummaries(
|
|
320
|
+
filings: readonly SecFilingItem[],
|
|
321
|
+
dataProvider: { getSecFilingContent?(filing: SecFilingItem): Promise<string | null> },
|
|
322
|
+
): Promise<RecentFiling[]> {
|
|
323
|
+
if (!dataProvider.getSecFilingContent) return [...filings];
|
|
324
|
+
return Promise.all(filings.map(async (filing) => {
|
|
325
|
+
const target = rowSummaryTarget(filing);
|
|
326
|
+
if (!target) return filing;
|
|
327
|
+
const content = await Promise.resolve().then(() => dataProvider.getSecFilingContent!(target))
|
|
328
|
+
.catch(logFailure("cli.commands-ticker", "Read an insider filing failed", null));
|
|
329
|
+
const summary = rowSummary(filing.form, content)?.title;
|
|
330
|
+
return summary ? { ...filing, summary } : filing;
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** What the form is: the filing's own description, else the form's meaning ("Insider Transaction"); none beside an 8-K's items. */
|
|
335
|
+
function filingMeaning(filing: SecFilingItem): string {
|
|
336
|
+
return getFilingDescription(filing) ?? (currentReportItemsLabel(filing.items) ? "" : getFormDescription(filing.form));
|
|
337
|
+
}
|
|
338
|
+
|
|
308
339
|
function getFilingDescription(filing: SecFilingItem): string | undefined {
|
|
309
340
|
const description = filing.primaryDocDescription?.trim();
|
|
310
341
|
if (!description) return undefined;
|
|
@@ -1117,7 +1148,7 @@ export async function buildTickerReport({
|
|
|
1117
1148
|
/** "CAR = Avis Budget Group, NASDAQ (different companies on XETRA, ASX)", for a bare symbol several companies list. */
|
|
1118
1149
|
listingNote?: string;
|
|
1119
1150
|
recentNews?: NewsArticle[];
|
|
1120
|
-
recentSecFilings?:
|
|
1151
|
+
recentSecFilings?: RecentFiling[];
|
|
1121
1152
|
/** What `recentSecFilings` left out. */
|
|
1122
1153
|
hiddenSecFilings?: HiddenFilings;
|
|
1123
1154
|
}): Promise<string> {
|
|
@@ -1226,6 +1257,8 @@ export async function buildTickerReport({
|
|
|
1226
1257
|
)
|
|
1227
1258
|
: "—";
|
|
1228
1259
|
const volume = reportedQuoteVolume(quote);
|
|
1260
|
+
// A coin's volume is traded value in its quote currency (BTC-USD: US dollars), not a count of coins.
|
|
1261
|
+
const volumeCurrency = quote.currency && isRoundTheClockCoin(quote.symbol ?? symbol, quote.exchangeName) ? ` ${quote.currency}` : "";
|
|
1229
1262
|
// A futures price is its latest trade; the exchange settlement is its own row, dated by its session.
|
|
1230
1263
|
appendMetricSection(lines, "Quote", [
|
|
1231
1264
|
// A price in cents says what it is per: 314.95¢/lb.
|
|
@@ -1247,7 +1280,7 @@ export async function buildTickerReport({
|
|
|
1247
1280
|
: "—"],
|
|
1248
1281
|
["Bid / Ask", bidAskText(quote, quoteOptions)],
|
|
1249
1282
|
// An index's and a perpetual's 0 means none is counted, not no trades: no Volume row.
|
|
1250
|
-
["Volume", volume != null ? formatNumber(volume, 0) : "—"],
|
|
1283
|
+
["Volume", volume != null ? `${formatNumber(volume, 0)}${volumeCurrency}` : "—"],
|
|
1251
1284
|
["Updated", formatTimestamp(quoteHeadlineTime(quote))],
|
|
1252
1285
|
]);
|
|
1253
1286
|
|
|
@@ -1338,9 +1371,10 @@ export async function buildTickerReport({
|
|
|
1338
1371
|
meta: [
|
|
1339
1372
|
// The items by number and name, so a results 8-K reads apart from another 8-K filed the same day.
|
|
1340
1373
|
filingItemsMeta(filing),
|
|
1341
|
-
|
|
1342
|
-
filing
|
|
1374
|
+
// What the form is, and below it who filed an insider form and what it reports: the file name said neither.
|
|
1375
|
+
filingMeaning(filing),
|
|
1343
1376
|
],
|
|
1377
|
+
body: filing.summary,
|
|
1344
1378
|
link: filing.filingUrl,
|
|
1345
1379
|
})), hiddenFilingsNote(hiddenSecFilings));
|
|
1346
1380
|
|
|
@@ -1384,7 +1418,7 @@ function buildTickerStructuredData({
|
|
|
1384
1418
|
config: AppConfig;
|
|
1385
1419
|
notes: string;
|
|
1386
1420
|
recentNews: NewsArticle[];
|
|
1387
|
-
recentSecFilings:
|
|
1421
|
+
recentSecFilings: RecentFiling[];
|
|
1388
1422
|
hiddenSecFilings: HiddenFilings;
|
|
1389
1423
|
}) {
|
|
1390
1424
|
const quote = financials.quote;
|
|
@@ -1472,6 +1506,8 @@ function buildTickerStructuredData({
|
|
|
1472
1506
|
filingUrl: filing.filingUrl,
|
|
1473
1507
|
primaryDocumentUrl: filing.primaryDocumentUrl ?? "",
|
|
1474
1508
|
description: getFilingDescription(filing) ?? "",
|
|
1509
|
+
// An insider form's filer and what it reports, as the text report shows it.
|
|
1510
|
+
summary: filing.summary ?? "",
|
|
1475
1511
|
items: secFilingItemCodes(filing.items) ?? "",
|
|
1476
1512
|
// The items by number and name, as the text report shows them.
|
|
1477
1513
|
itemsLabel: currentReportItemsLabel(filing.items) ?? "",
|
|
@@ -1524,7 +1560,7 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
|
|
|
1524
1560
|
// So does what became of the symbol, read from the research's quote as `history` reads it: a
|
|
1525
1561
|
// company that moved (HFC became DINO), with nothing new listed there, has no profile of its own.
|
|
1526
1562
|
const researchQuote = Promise.withResolvers<Quote | null>();
|
|
1527
|
-
const fate = listingFateLoader(listing, { dataProvider }, researchQuote.promise);
|
|
1563
|
+
const fate = listingFateLoader(listing, { dataProvider }, researchQuote.promise, { successor: true });
|
|
1528
1564
|
const request: ListingDataRequest = { command: "ticker", noun: "research", fate };
|
|
1529
1565
|
/** Nothing to show: a company that moved is under its new symbol. */
|
|
1530
1566
|
const failIfMovedAway = async () => {
|
|
@@ -1568,8 +1604,8 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
|
|
|
1568
1604
|
const quote = resolvedFinancials.quote;
|
|
1569
1605
|
// The quote is the old line's (stopped, or named for the company that left): the profile and figures are another security's.
|
|
1570
1606
|
researchQuote.resolve(quote ?? null);
|
|
1571
|
-
const
|
|
1572
|
-
if (moved) failMovedSymbol(moved, request);
|
|
1607
|
+
const researchFate = await fate();
|
|
1608
|
+
if (researchFate.moved) failMovedSymbol(researchFate.moved, request);
|
|
1573
1609
|
|
|
1574
1610
|
const notesFiles = new NotesFiles(dataDir);
|
|
1575
1611
|
const listingName = listingIdentity(listing, quote).name;
|
|
@@ -1630,14 +1666,18 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
|
|
|
1630
1666
|
(currency) => dataProvider.getExchangeRate(currency));
|
|
1631
1667
|
const notes = notesResult.status === "fulfilled" ? notesResult.value : "";
|
|
1632
1668
|
const recentNews = newsResult.status === "fulfilled" ? newsResult.value : [];
|
|
1633
|
-
const { filings:
|
|
1669
|
+
const { filings: secFilings, hidden: hiddenSecFilings } = secFilingsResult.status === "fulfilled"
|
|
1634
1670
|
? secFilingsResult.value : { filings: [], hidden: NOTHING_HIDDEN };
|
|
1671
|
+
const recentSecFilings = await withFilingSummaries(secFilings, dataProvider);
|
|
1635
1672
|
|
|
1636
1673
|
// The quote is the feed in this report; without one, the fundamentals date it.
|
|
1637
1674
|
const freshness = quote ? quotesFreshness([quote]) : fundamentalsFreshness(resolvedFinancials);
|
|
1638
1675
|
const quoteNote = quote ? undefined : await quoteUnavailableNote(dataProvider, requestSymbol, exchange);
|
|
1639
1676
|
// A bare coin ticker read as another listing says where the coin is, as `quote` does: from the quote, else from the saved listing.
|
|
1677
|
+
const successor = listingChangeLine(researchFate);
|
|
1640
1678
|
const choiceNotes = [
|
|
1679
|
+
// What became of the company that had the symbol (GOGL), as `quote` says it, then the listing it was read as.
|
|
1680
|
+
...successor ? [successor] : [],
|
|
1641
1681
|
...await listingChoiceNotes(listing, { store, dataProvider }, quote, venues),
|
|
1642
1682
|
...quote || !tickerFile ? [] : coinListingNotes(listing, { name: tickerFile.metadata.name, instrumentType: tickerFile.metadata.assetCategory }),
|
|
1643
1683
|
];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { VERSION } from "../../version";
|
|
2
|
+
import { logFailure } from "../../utils/log-failure";
|
|
3
|
+
import type { CliCommandContext, CliCommandDef } from "../../types/plugin";
|
|
4
|
+
import { resolveCliDataDir } from "../data-dir";
|
|
5
|
+
import { CLI_COMMAND_GROUPS } from "../help";
|
|
6
|
+
|
|
7
|
+
/** Shared by the early entry point and the command registry. */
|
|
8
|
+
export async function printVersion(printResult: CliCommandContext["printResult"]): Promise<void> {
|
|
9
|
+
// Reading the version never creates the data folder.
|
|
10
|
+
let dataDir: string | null = null;
|
|
11
|
+
try {
|
|
12
|
+
dataDir = await resolveCliDataDir();
|
|
13
|
+
} catch (error) {
|
|
14
|
+
logFailure("cli-system", "Resolve data directory failed")(error);
|
|
15
|
+
}
|
|
16
|
+
printResult({
|
|
17
|
+
data: [{
|
|
18
|
+
version: VERSION,
|
|
19
|
+
bun: typeof Bun === "undefined" ? null : Bun.version,
|
|
20
|
+
platform: process.platform,
|
|
21
|
+
arch: process.arch,
|
|
22
|
+
dataDir,
|
|
23
|
+
}],
|
|
24
|
+
}, { layout: "record" });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const versionCommand: CliCommandDef = {
|
|
28
|
+
name: "version",
|
|
29
|
+
aliases: ["--version", "-v"],
|
|
30
|
+
description: "Print the version, runtime, and data folder",
|
|
31
|
+
help: { group: CLI_COMMAND_GROUPS.app, usage: ["version"] },
|
|
32
|
+
execute: (_args, ctx) => printVersion(ctx.printResult),
|
|
33
|
+
};
|
package/src/cli/context.ts
CHANGED
|
@@ -9,8 +9,7 @@ import { createAppServices } from "../core/app-services";
|
|
|
9
9
|
import type { PluginCapability } from "../capabilities";
|
|
10
10
|
import type { AppConfig } from "../types/config";
|
|
11
11
|
import type { GloomPlugin } from "../types/plugin";
|
|
12
|
-
import {
|
|
13
|
-
import { standaloneSeriesCatalog, standaloneSeriesInvoker } from "../series/standalone";
|
|
12
|
+
import { standaloneSeriesCatalog, standaloneSeriesInvoker, standaloneSeriesNamed } from "../series/standalone";
|
|
14
13
|
import type { PluginRegistry } from "../plugins/registry";
|
|
15
14
|
import type { LoadedExternalPlugin } from "../plugins/loader";
|
|
16
15
|
import { apiClient } from "../api-client";
|
|
@@ -73,11 +72,6 @@ function isUnreadableConfig(configFile: string): boolean {
|
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/** The data folder this profile uses, whether or not anything has created it yet. Never writes. */
|
|
77
|
-
export async function resolveCliDataDir(): Promise<string> {
|
|
78
|
-
return await getDataDir() ?? getGloomberbDirs().data;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
75
|
/**
|
|
82
76
|
* The data folder and config a command works with. The first command on a new
|
|
83
77
|
* profile creates them with the same `initDataDir` a first launch of the
|
|
@@ -120,7 +114,7 @@ function applyPersistedCloudSession(context: Pick<MarketContext, "persistence">)
|
|
|
120
114
|
export async function initMarketData(options: CliContextOptions = {}): Promise<MarketContext> {
|
|
121
115
|
const context = await initConfigData();
|
|
122
116
|
applyPersistedCloudSession(context);
|
|
123
|
-
const plugins = options.plugins ?? getLoadablePlugins();
|
|
117
|
+
const plugins = options.plugins ?? (require("../plugins/catalog") as typeof import("../plugins/catalog")).getLoadablePlugins();
|
|
124
118
|
const capabilities = resolveCliCapabilities(context.config, plugins);
|
|
125
119
|
const dataProvider = new AssetDataRouter(null, [], context.persistence.resources);
|
|
126
120
|
const registryAdapter: Pick<PluginRegistry, "brokers" | "getEnabledCapabilities"> = {
|
|
@@ -132,15 +126,18 @@ export async function initMarketData(options: CliContextOptions = {}): Promise<M
|
|
|
132
126
|
dataProvider.setBackgroundRevalidation(false);
|
|
133
127
|
const seriesCapabilities = standaloneSeriesInvoker(plugins, context.dataDir, context.config.disabledPlugins);
|
|
134
128
|
const findCatalogSeries = standaloneSeriesCatalog(plugins, context.config.disabledPlugins);
|
|
135
|
-
|
|
129
|
+
const findCatalogSeriesNamed = standaloneSeriesNamed(plugins, context.config.disabledPlugins);
|
|
130
|
+
return { ...context, dataProvider, seriesCapabilities, findCatalogSeries, findCatalogSeriesNamed, statementSources: cloudStatementReportSources };
|
|
136
131
|
}
|
|
137
132
|
|
|
138
133
|
export async function initCliServices(options: CliServicesOptions = {}) {
|
|
134
|
+
const { getLoadablePlugins } = require("../plugins/catalog") as typeof import("../plugins/catalog");
|
|
139
135
|
const { dataDir, config, created } = await openCliDataDir();
|
|
140
136
|
const services = createAppServices({
|
|
141
137
|
config,
|
|
142
138
|
plugins: getLoadablePlugins(options.externalPlugins ?? []),
|
|
143
139
|
externalPlugins: options.externalPlugins ?? [],
|
|
140
|
+
interactive: false,
|
|
144
141
|
});
|
|
145
142
|
services.providerRouter.setConfigAccessor(() => config);
|
|
146
143
|
services.providerRouter.setBackgroundRevalidation(false);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { getDataDir } from "../data/config/store";
|
|
2
|
+
import { getGloomberbDirs } from "../data/config/home";
|
|
3
|
+
|
|
4
|
+
/** The data folder this profile uses, whether or not anything has created it yet. Never writes. */
|
|
5
|
+
export async function resolveCliDataDir(): Promise<string> {
|
|
6
|
+
return await getDataDir() ?? getGloomberbDirs().data;
|
|
7
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which Gloom Cloud requests the pane screenshot page may send through its
|
|
3
|
+
* local proxy, which adds the user's session (desktop-pane-shot.ts). The page
|
|
4
|
+
* only draws panes, so it gets the reads panes make and nothing that acts on
|
|
5
|
+
* the account: no sign-in or sign-out, billing, keys, chat, sync or sharing,
|
|
6
|
+
* and no write of any kind.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** The API families panes read their data from. */
|
|
10
|
+
const READ_FAMILIES = /^\/(?:cloud|market|news|public|research)(?:\/|$)/;
|
|
11
|
+
/** Auth routes inside a read family. */
|
|
12
|
+
const AUTH_IN_FAMILY = /^\/cloud\/auth(?:\/|$)/;
|
|
13
|
+
/**
|
|
14
|
+
* Reads outside those families that panes make, none of whose answers holds a
|
|
15
|
+
* credential, token or key: the session and plan an access gate reads (the
|
|
16
|
+
* session cut to its user, see `isShotSessionPath`), plan prices, the service
|
|
17
|
+
* probe, the roadmap, the layout gallery (marketplace and team layouts),
|
|
18
|
+
* theses, and a signed-in broker's catalog, connection status, holdings,
|
|
19
|
+
* orders and fills.
|
|
20
|
+
*/
|
|
21
|
+
const READ_PATHS = [
|
|
22
|
+
/^\/auth\/get-session$/,
|
|
23
|
+
/^\/account\/cloud$/,
|
|
24
|
+
/^\/pricing$/,
|
|
25
|
+
/^\/health$/,
|
|
26
|
+
/^\/roadmap$/,
|
|
27
|
+
/^\/layouts(?:\/[^/]+)?$/,
|
|
28
|
+
/^\/teams\/[^/]+\/layouts$/,
|
|
29
|
+
/^\/theses(?:\/[^/]+)?$/,
|
|
30
|
+
/^\/brokers\/connectors$/,
|
|
31
|
+
/^\/brokers\/[a-z][a-z0-9-]{0,31}(?:\/(?:snapshot|orders|executions))?$/,
|
|
32
|
+
];
|
|
33
|
+
/** Reads sent as a POST because their query is a list: batches, scans and analyses. */
|
|
34
|
+
const READ_POSTS = new Set([
|
|
35
|
+
"/cloud/exposure/analyze",
|
|
36
|
+
"/cloud/perps/equities",
|
|
37
|
+
"/market/financials/batch",
|
|
38
|
+
"/market/history/batch",
|
|
39
|
+
"/market/organization-tickers",
|
|
40
|
+
"/market/quotes/batch",
|
|
41
|
+
"/market/scanner/live",
|
|
42
|
+
"/market/search/batch",
|
|
43
|
+
"/market/ticker-changes/batch",
|
|
44
|
+
"/research/equity-diagnostic",
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The path as a server could read it: decoded and lower-cased, so an escaped
|
|
49
|
+
* letter or another case cannot step past a check. Null for one that steps up
|
|
50
|
+
* a folder or holds a backslash once decoded, which is refused rather than
|
|
51
|
+
* trusted to reach the server as the path it was checked as. An encoded slash
|
|
52
|
+
* inside a segment (a symbol such as `EUR/USD`) is fine.
|
|
53
|
+
*/
|
|
54
|
+
function readablePath(path: string): string | null {
|
|
55
|
+
if (!path.startsWith("/") || path.includes("//")) return null;
|
|
56
|
+
let decoded: string;
|
|
57
|
+
try {
|
|
58
|
+
decoded = decodeURIComponent(path);
|
|
59
|
+
} catch {
|
|
60
|
+
// Intentional ignore: a path that does not decode is refused outright, never sent on.
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (decoded.includes("\\") || decoded.split("/").some((segment) => segment === "." || segment === "..")) return null;
|
|
64
|
+
return decoded.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether the page may send `method` to the API path `path` (the part after
|
|
69
|
+
* the API's base, query excluded). A batch read sent as POST must be named
|
|
70
|
+
* exactly, with nothing escaped.
|
|
71
|
+
*/
|
|
72
|
+
export function isShotApiRequestAllowed(method: string, path: string): boolean {
|
|
73
|
+
const readable = readablePath(path);
|
|
74
|
+
if (readable === null) return false;
|
|
75
|
+
const verb = method.toUpperCase();
|
|
76
|
+
if (verb === "POST") return READ_POSTS.has(path);
|
|
77
|
+
if (verb !== "GET" && verb !== "HEAD") return false;
|
|
78
|
+
if (READ_PATHS.some((pattern) => pattern.test(readable))) return true;
|
|
79
|
+
return READ_FAMILIES.test(readable) && !AUTH_IN_FAMILY.test(readable);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The session read, whose answer also carries the session's token: the proxy hands the page its user alone. */
|
|
83
|
+
export function isShotSessionPath(path: string): boolean {
|
|
84
|
+
return readablePath(path) === "/auth/get-session";
|
|
85
|
+
}
|