@gloom-sh/terminal 0.18.5 → 0.19.1
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -1,20 +1,32 @@
|
|
|
1
|
-
import { metricDescription } from "./metric-model";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { closeBasis, isDailyMetric, isRelativeMetric, metricDescription, metricName, metricReadingText, validMetricRule } from "./metric-model";
|
|
2
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
3
|
+
import { benchmarkKey, parseMetricAlert, type MetricAlertInput } from "./metric-command";
|
|
4
|
+
import { firstDailyCheck, metricAvailabilityError, metricReadings } from "./metric-data";
|
|
5
|
+
import { ALERT_ADD_FORMS, ALERT_KINDS_TEXT, parseRuleAddArgs, RULE_US_DATA, ruleDelivery, type RuleAddRequest } from "./cli-forms";
|
|
6
|
+
import { alertChangeEntry, appendAlertLog, type AlertLogAction } from "./fired-log";
|
|
7
|
+
import { alertLogRule } from "./alert-log";
|
|
8
|
+
import { alertListing, alertReadingAt, alertReadingText, isoUtc, listingWithCurrency, NOT_CHECKED_YET } from "./format";
|
|
9
|
+
import { isQuoteStaleForCurrentSession } from "../../../market-data/quotes/freshness";
|
|
10
|
+
import { quoteAlertFields } from "./quotes";
|
|
11
|
+
import { createEventAlert } from "./events";
|
|
12
|
+
import { isResearchAlertKind, readResearchRule } from "./research-rules";
|
|
4
13
|
import { withConfigData, withMarketData } from "../../../cli/scoped-context";
|
|
14
|
+
import { resolvePersistedCloudSessionToken } from "../../../cli/pane-functions/cloud-session";
|
|
15
|
+
import type { ConfigContext } from "../../../cli/types";
|
|
16
|
+
import { checkedWhenSignedIn, NEEDS_SIGN_IN, needsSignInNote } from "./event-rows";
|
|
5
17
|
import { CLI_COMMAND_GROUPS } from "../../../cli/help";
|
|
6
18
|
import { dryRunNote } from "../../../cli/helpers";
|
|
7
19
|
import { requireArg, takeOption } from "../../../cli/commands/command-utils";
|
|
8
20
|
import { requireQuotedListing } from "../../../cli/quote-answer";
|
|
9
|
-
import { EXCHANGE_OPTION, listingReadNotes, requireListingArg, usListingsOnlyMessage } from "../../../cli/listing-arg";
|
|
10
|
-
import { notTheCoinNote } from "../../../cli/commands/crypto-hints";
|
|
21
|
+
import { coinListingNotes, EXCHANGE_OPTION, listingReadNotes, requireListingArg, usListingsOnlyMessage } from "../../../cli/listing-arg";
|
|
11
22
|
import { saveConfig } from "../../../data/config/store";
|
|
12
23
|
import { resumeStateKey } from "../../registry/plugin-state";
|
|
13
24
|
import type { CliCommandDef } from "../../../types/plugin";
|
|
14
25
|
import type { Quote } from "../../../types/financials";
|
|
15
26
|
import { getActiveQuoteDisplay } from "../../../market-data/market/status";
|
|
16
27
|
import { cliStyles } from "../../../utils/cli-output";
|
|
17
|
-
import { canonicalExchange, isUsListingExchange } from "../../../utils/exchanges";
|
|
28
|
+
import { canonicalExchange, isUsListingExchange, parsePublicTickerKey } from "../../../utils/exchanges";
|
|
29
|
+
import { notATickerSymbol } from "../../../sources/provider-errors";
|
|
18
30
|
import { meantThousandsDot, parsePastedNumber, thousandsDotReadings, type ThousandsDotReadings } from "../../../utils/decimal-comma";
|
|
19
31
|
import { newYorkToday } from "../earnings/board-model";
|
|
20
32
|
import { fetchEconCalendarFrom } from "../econ/calendar-source";
|
|
@@ -32,14 +44,15 @@ import {
|
|
|
32
44
|
type CalendarEventChoice,
|
|
33
45
|
type ReleaseRename,
|
|
34
46
|
} from "./calendar-events";
|
|
35
|
-
import { createAlert, rearmAlert } from "./alert-engine";
|
|
47
|
+
import { createAlert, rearmAlert, sameLevelRule } from "./alert-engine";
|
|
36
48
|
import { ALERTS_PLUGIN_ID } from "./constants";
|
|
37
49
|
import { MAX_EVENT_ALERTS, eventAlertTarget, toggleEventAlert, writeEventAlertStores, type EventAlertRule } from "./events";
|
|
38
|
-
import { alertKindLabel } from "./history";
|
|
39
|
-
import {
|
|
50
|
+
import { alertKindLabel, fetchAlertHistory, ruleStateText, type AlertRuleState } from "./history";
|
|
51
|
+
import { apiClient } from "../../../api-client";
|
|
52
|
+
import { IV_LEVEL_CHECKS_KEY, ivLevelCheckText, readIvLevelChecks, rearmIvLevelAlert, type IvLevelCheck } from "./iv-level";
|
|
40
53
|
import { parseAlertPrice, readAlertPrice } from "./price";
|
|
41
54
|
import { farTargetMessage } from "./quotes";
|
|
42
|
-
import { MACRO_EVENT_NAMES, createResearchAlert, formatPremium, ivLevelUnitError, parseAmount, parseIvLevel
|
|
55
|
+
import { MACRO_EVENT_NAMES, createResearchAlert, formatPremium, ivLevelUnitError, parseAmount, parseIvLevel } from "./research-builder";
|
|
43
56
|
import { IV_LEVEL_RANGES, MAX_LEAD_DAYS, type IvLevelMetric, type MacroAlertEvent } from "./research-rules";
|
|
44
57
|
import {
|
|
45
58
|
createScheduledAlert,
|
|
@@ -75,11 +88,19 @@ import type { AlertCondition, AlertRule } from "./types";
|
|
|
75
88
|
*/
|
|
76
89
|
const PRICE_DELIVERY = "the app alerts while it is running; sign in with Cloud sync for email or phone alerts when it is closed";
|
|
77
90
|
|
|
78
|
-
const ADD_USAGE =
|
|
91
|
+
const ADD_USAGE = `Usage: gloom alerts add <symbol> <above|below|crosses> <price>, or another kind. ${ALERT_KINDS_TEXT}`;
|
|
79
92
|
const DAYS_RULE = `--days takes a whole number of days from 1 to ${MAX_LEAD_DAYS}; without it the alert goes off 1 day before.`;
|
|
80
|
-
/**
|
|
81
|
-
const
|
|
93
|
+
/** How a release in the economic calendar is named, CPI and FOMC among them. */
|
|
94
|
+
const CALENDAR_EXAMPLES = `alerts add "NBS Manufacturing PMI" --country CN, alerts add CN PMI, alerts add CPI --days 1`;
|
|
82
95
|
const COUNTRY_RULE = "--country is for an alert on a calendar event, such as alerts add PMI --country CN.";
|
|
96
|
+
const BENCHMARK_RULE = "--benchmark takes the listing an RRG alert is measured against (SPY by default, as in RRG),";
|
|
97
|
+
/** `XLK/SPY`, `XLK:NYSEARCA/SPY`: two listings, or one written with a slash (BRK/B, EUR/USD). */
|
|
98
|
+
const PAIR_SHAPE = /^[^/\s]+\/[^/\s]+$/;
|
|
99
|
+
/** A listing check that hands back its refusal instead of ending the command, so a pair can be read as a ratio. */
|
|
100
|
+
class ListingRefusal extends Error {
|
|
101
|
+
constructor(message: string, readonly details?: string) { super(message); }
|
|
102
|
+
}
|
|
103
|
+
const PROBE = { fail: (message: string, details?: string): never => { throw new ListingRefusal(message, details); } };
|
|
83
104
|
|
|
84
105
|
/** What `alerts add` was asked for, before any listing or date is looked up. */
|
|
85
106
|
export type AlertAddRequest =
|
|
@@ -100,7 +121,9 @@ export type AlertAddRequest =
|
|
|
100
121
|
/** The symbol's 30-day ATM IV (`atm`, in %) or 25-delta risk reversal (`rr`, in vol points) above or below a level, checked by the running app. */
|
|
101
122
|
| { kind: "iv_level"; symbol: string; metric: IvLevelMetric; direction: "above" | "below"; level: number }
|
|
102
123
|
/** One OCC contract's IV rising by `points`. */
|
|
103
|
-
| { kind: "iv_spike"; contract: string; points: number }
|
|
124
|
+
| { kind: "iv_spike"; contract: string; points: number }
|
|
125
|
+
/** The rest of what Add Event Alert offers: 52-week range, unusual volume, filings, insiders, short interest and more. */
|
|
126
|
+
| RuleAddRequest;
|
|
104
127
|
|
|
105
128
|
/** The `alerts add` forms Gloom Cloud's research rules take: they follow US listings. */
|
|
106
129
|
type ResearchAddRequest = Extract<AlertAddRequest, { kind: "news" | "flow" | "iv_level" | "iv_spike" }>;
|
|
@@ -153,18 +176,30 @@ export function parseAlertAddArgs(
|
|
|
153
176
|
days: string | null | undefined,
|
|
154
177
|
exchange?: string | null,
|
|
155
178
|
country?: string | null,
|
|
179
|
+
benchmark?: string | null,
|
|
156
180
|
): AlertAddRequest {
|
|
157
181
|
const [first, second, third, ...rest] = args;
|
|
158
182
|
if (!first) throw new Error(ADD_USAGE);
|
|
159
183
|
const countryFilter = readCountryOption(country);
|
|
160
|
-
|
|
184
|
+
if (benchmark === null) throw new Error(`${BENCHMARK_RULE} such as alerts add XLE rrg leading --benchmark QQQ.`);
|
|
185
|
+
const rrgFlag = benchmark !== undefined && second?.toLowerCase() === "rrg";
|
|
186
|
+
const typedVs = /^(vs\.?|versus|against)$/i.test(args[3] ?? "") ? args[4] : undefined;
|
|
187
|
+
if (rrgFlag && typedVs !== undefined) {
|
|
188
|
+
// `vs QQQ --benchmark SMH` names two benchmarks; the same one twice (SPY and SPY:NYSEARCA) is one.
|
|
189
|
+
const [typed, flag] = [typedVs, benchmark].map((text) => parsePublicTickerKey(text.toUpperCase()));
|
|
190
|
+
if (typed!.symbol !== flag!.symbol || (typed!.exchange && flag!.exchange && typed!.exchange !== flag!.exchange)) {
|
|
191
|
+
throw new Error(`An RRG alert names its benchmark once: vs ${typedVs.toUpperCase()} or --benchmark ${benchmark.toUpperCase()}, not both.`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const metric = parseMetricAlert(rrgFlag && typedVs === undefined ? [...args, "vs", benchmark].join(" ") : args.join(" "));
|
|
195
|
+
if (benchmark !== undefined && metric?.metric.kind !== "rrg") throw new Error(`${BENCHMARK_RULE} such as alerts add XLE rrg leading --benchmark QQQ; a ratio names both listings, as in alerts add XLK/SPY above 0.25.`);
|
|
161
196
|
if (metric) {
|
|
162
|
-
if (days !== undefined || country !== undefined) throw new Error("Valuation and daily technical alerts take neither --days nor --country
|
|
197
|
+
if (days !== undefined || country !== undefined) throw new Error(`${isRelativeMetric(metric.metric) ? "Relative strength" : "Valuation and daily technical"} alerts take neither --days nor --country.`);
|
|
163
198
|
return { kind: "metric", ...metric };
|
|
164
199
|
}
|
|
165
|
-
const research = parseResearchAddArgs(args);
|
|
200
|
+
const research = parseResearchAddArgs(args) ?? parseRuleAddArgs(args);
|
|
166
201
|
if (research) {
|
|
167
|
-
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not
|
|
202
|
+
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not this kind of alert.");
|
|
168
203
|
if (country !== undefined) throw new Error(COUNTRY_RULE);
|
|
169
204
|
return research;
|
|
170
205
|
}
|
|
@@ -192,7 +227,7 @@ export function parseAlertAddArgs(
|
|
|
192
227
|
if (!CONDITIONS.includes(condition)) {
|
|
193
228
|
// Words with no price after them name a release (CN PMI, Industrial Profits); a price ends a mistyped price alert.
|
|
194
229
|
if (parseAlertPrice(args.at(-1)!).kind === "invalid") return releaseRequest(args, countryFilter, leadDays());
|
|
195
|
-
throw new Error(`Unknown alert "${second}".
|
|
230
|
+
throw new Error(`Unknown alert "${second}". ${ALERT_KINDS_TEXT}`);
|
|
196
231
|
}
|
|
197
232
|
if (country !== undefined) throw new Error(COUNTRY_RULE);
|
|
198
233
|
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not price alerts.");
|
|
@@ -312,6 +347,25 @@ function researchDelivery(kind: ResearchAddRequest["kind"]): string {
|
|
|
312
347
|
return `Gloom Cloud checks it for a signed-in account with Cloud sync${plan} and sends it to your phone`;
|
|
313
348
|
}
|
|
314
349
|
|
|
350
|
+
/**
|
|
351
|
+
* The rule Add Event Alert saves for a form `alerts add` named by its words,
|
|
352
|
+
* on the listing `quote` read the symbol as: a US research rule keeps its
|
|
353
|
+
* symbol and venue, hiring and a catalyst's company their listing as typed
|
|
354
|
+
* (ADYEN:AMS).
|
|
355
|
+
*/
|
|
356
|
+
function createFormRule(request: RuleAddRequest, listing: { symbol: string; key: string; exchange: string } | null): EventAlertRule {
|
|
357
|
+
const { event } = request;
|
|
358
|
+
if (!isResearchAlertKind(event)) return createEventAlert(event, listing?.key ?? request.values.value ?? "");
|
|
359
|
+
const values = Object.fromEntries(Object.entries(request.values).map(([key, value]) => [`${event}:${key}`, value]));
|
|
360
|
+
if (listing && request.listing === "any") values[`${event}:symbol`] = listing.key;
|
|
361
|
+
else if (listing) {
|
|
362
|
+
const typed = listing.exchange ? canonicalExchange(listing.exchange) : "";
|
|
363
|
+
values[`${event}:symbol`] = listing.symbol;
|
|
364
|
+
values[`${event}:exchange`] = RESEARCH_EXCHANGES.has(typed) ? typed : "US";
|
|
365
|
+
}
|
|
366
|
+
return createResearchAlert(event, values);
|
|
367
|
+
}
|
|
368
|
+
|
|
315
369
|
const isResearchRequest = (request: AlertAddRequest): request is ResearchAddRequest =>
|
|
316
370
|
request.kind === "news" || request.kind === "flow" || request.kind === "iv_level" || request.kind === "iv_spike";
|
|
317
371
|
|
|
@@ -349,6 +403,33 @@ export function thousandsDotMessage(
|
|
|
349
403
|
return `${dot.text} could be ${dot.thousands} (thousands) or ${dot.decimal} (decimal point), and ${listing.toUpperCase()} last traded at ${Number(last!.toFixed(2))}; type ${retype(dot.thousands)} or ${retype(dot.decimal)}`;
|
|
350
404
|
}
|
|
351
405
|
|
|
406
|
+
/**
|
|
407
|
+
* What a valuation or daily technical alert read when it was added:
|
|
408
|
+
* "RSI(14) was 61.23 on the 2026-10-26 close", "50-day MA was 186.32 against
|
|
409
|
+
* the 200-day MA at 180.11 on the 2026-10-26 close", "P/E (TTM) is 24.10".
|
|
410
|
+
*/
|
|
411
|
+
function metricReadingNote(alert: AlertRule): string {
|
|
412
|
+
const metric = alert.metric!;
|
|
413
|
+
// "Ratio to SPY was 0.2434 on the 2026-10-26 close", "RRG vs SPY was Leading (strength 110.06, momentum 108.88) on the 2026-10-23 weekly close".
|
|
414
|
+
if (isRelativeMetric(metric)) {
|
|
415
|
+
const reading = metricReadingText(metric, { value: alert.lastCheckedPrice ?? Number.NaN, target: alert.lastCheckedTarget ?? Number.NaN });
|
|
416
|
+
return `${metricName(metric)} was ${reading} on the ${alert.lastDailyClose} ${closeBasis(metric) === "weekly close" ? "weekly close" : "close"}`;
|
|
417
|
+
}
|
|
418
|
+
const value = (alert.lastCheckedPrice ?? Number.NaN).toFixed(2);
|
|
419
|
+
if (!isDailyMetric(metric)) return `${metricName(metric)} is ${value}`;
|
|
420
|
+
const against = metric.kind === "ma_cross" ? metric.slowPeriod : metric.kind === "close_ma" ? metric.period : null;
|
|
421
|
+
const target = against != null && alert.lastCheckedTarget != null ? ` against the ${against}-day MA at ${alert.lastCheckedTarget.toFixed(2)}` : "";
|
|
422
|
+
return `${metricName(metric)} was ${value}${target} on the ${alert.lastDailyClose} close`;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** An event alert still waiting to go off, as `alerts list` says `active`: not paused, and not alerted for its coming event or level. */
|
|
426
|
+
function eventRuleArmed(rule: EventAlertRule, now: number): boolean {
|
|
427
|
+
if (rule.status !== "active") return false;
|
|
428
|
+
const scheduled = readScheduledRule(rule);
|
|
429
|
+
if (!scheduled) return !rule.triggeredAt;
|
|
430
|
+
return scheduledAlertStatus(rule, scheduled.leadDays, scheduledToday(scheduled.target, now), scheduled.trigger) === "active";
|
|
431
|
+
}
|
|
432
|
+
|
|
352
433
|
/** One row of `alerts list`: a price alert as stored, or an event alert with its next date. */
|
|
353
434
|
type AlertListRow = Record<string, unknown> & {
|
|
354
435
|
id: string;
|
|
@@ -357,14 +438,34 @@ type AlertListRow = Record<string, unknown> & {
|
|
|
357
438
|
};
|
|
358
439
|
|
|
359
440
|
function priceRow(alert: AlertRule): AlertListRow {
|
|
360
|
-
return {
|
|
441
|
+
return {
|
|
442
|
+
...alert, kind: alert.metric?.kind ?? "price", rule: alert.metric ? metricDescription(alert) : undefined,
|
|
443
|
+
target: alert.exchange ? `${alert.symbol}:${alert.exchange}` : alert.symbol,
|
|
444
|
+
// The listing it is checked on, in its currency, and its last reading in words with when it is from.
|
|
445
|
+
listing: alertListing(alert), currency: alert.lastQuoteCurrency ?? null,
|
|
446
|
+
lastReading: alertReadingText(alert), lastReadingAt: isoUtc(alertReadingAt(alert)) || null,
|
|
447
|
+
};
|
|
361
448
|
}
|
|
362
449
|
|
|
363
|
-
function eventRow(
|
|
450
|
+
function eventRow(
|
|
451
|
+
rule: EventAlertRule, now: number, eventDate: string | null | undefined, check?: IvLevelCheck, cloud?: CloudRuleStates, signedIn = true,
|
|
452
|
+
): AlertListRow {
|
|
364
453
|
const scheduled = readScheduledRule(rule);
|
|
365
454
|
if (!scheduled) {
|
|
366
|
-
const
|
|
455
|
+
const triggered = rule.status === "active" && rule.triggeredAt ? "triggered" : rule.status;
|
|
456
|
+
// Signed out, nothing checks a rule only a signed-in account gets checked: it waits on a sign-in, not on its event.
|
|
457
|
+
const status = triggered === "active" && !signedIn && checkedWhenSignedIn(rule) ? NEEDS_SIGN_IN : triggered;
|
|
458
|
+
// An IV level is read by this app, so its last reading is this device's; Gloom Cloud reads the rest.
|
|
459
|
+
const state = cloud?.states.get(rule.id);
|
|
460
|
+
const reading = rule.kind === "iv_level" ? {
|
|
461
|
+
lastReading: ivLevelCheckText(rule, check),
|
|
462
|
+
lastReadingAt: check?.value != null ? isoUtc(check.quoteAt ? Date.parse(check.quoteAt) : check.checkedAt) || null : null,
|
|
463
|
+
} : state ? {
|
|
464
|
+
lastReading: ruleStateText(state),
|
|
465
|
+
lastReadingAt: state.asOf ? isoUtc(Date.parse(state.asOf)) || null : null,
|
|
466
|
+
} : cloud?.failed ? {} : { lastReading: NOT_CHECKED_YET };
|
|
367
467
|
return { id: rule.id, kind: rule.kind, target: eventAlertTarget(rule), status, createdAt: rule.createdAt,
|
|
468
|
+
listing: eventRuleListing(rule), ...reading,
|
|
368
469
|
...check ? { lastCheck: check } : {}, ...rule.triggeredAt ? { triggeredAt: rule.triggeredAt } : {} };
|
|
369
470
|
}
|
|
370
471
|
const date = eventDate ?? null;
|
|
@@ -380,11 +481,57 @@ function eventRow(rule: EventAlertRule, now: number, eventDate: string | null |
|
|
|
380
481
|
eventDate: date,
|
|
381
482
|
status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays, scheduledToday(scheduled.target, now), scheduled.trigger),
|
|
382
483
|
createdAt: rule.createdAt,
|
|
484
|
+
listing: scheduled.target.kind === "earnings" ? scheduled.target.exchange ? `${scheduled.target.symbol}:${scheduled.target.exchange}` : scheduled.target.symbol : "",
|
|
383
485
|
};
|
|
384
486
|
}
|
|
385
487
|
|
|
488
|
+
/** Gloom Cloud's last reading of each rule it checks, as ALRT's Events tab shows them; `failed` when it could not be asked. */
|
|
489
|
+
interface CloudRuleStates {
|
|
490
|
+
states: Map<string, AlertRuleState>;
|
|
491
|
+
failed: boolean;
|
|
492
|
+
warning?: string;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Gloom Cloud checks the event rules this app does not, for a signed-in account. With no session nothing has
|
|
497
|
+
* read them, so they are not checked yet; with one (`session`, the app's saved one), their readings are Cloud's.
|
|
498
|
+
* The command runs with that session from here on, as market commands do (`initMarketData`): it is set once and
|
|
499
|
+
* never put back, so the earnings-date lookup running beside this never loses it midway.
|
|
500
|
+
*/
|
|
501
|
+
async function cloudRuleStates(rules: readonly EventAlertRule[], session: string | null): Promise<CloudRuleStates> {
|
|
502
|
+
const empty = { states: new Map<string, AlertRuleState>(), failed: false };
|
|
503
|
+
if (!session || !rules.some(checkedWhenSignedIn)) return empty;
|
|
504
|
+
apiClient.setSessionToken(session);
|
|
505
|
+
try {
|
|
506
|
+
const history = await fetchAlertHistory(0);
|
|
507
|
+
return { states: new Map(history.states.map((state) => [state.ruleId, state])), failed: false };
|
|
508
|
+
} catch (error) {
|
|
509
|
+
return { ...empty, failed: true, warning: `Gloom Cloud's readings of event alerts could not be loaded: ${error instanceof Error ? error.message : String(error)}` };
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** The Gloom Cloud session the app saved after sign-in, as `fn` and market commands read with; null signed out. */
|
|
514
|
+
function savedSession(config: Partial<Pick<ConfigContext, "persistence">>): string | null {
|
|
515
|
+
const persistence = config.persistence;
|
|
516
|
+
return apiClient.getSessionToken() ?? (persistence?.pluginState ? resolvePersistedCloudSessionToken({ persistence }) : null);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** The listing a research rule follows (AAPL, ADYEN:AMS for a hiring rule); empty for one on no symbol. */
|
|
520
|
+
function eventRuleListing(rule: EventAlertRule): string {
|
|
521
|
+
if (rule.target === "ticker") return rule.value;
|
|
522
|
+
if (!isResearchAlertKind(rule.kind)) return "";
|
|
523
|
+
const config = readResearchRule(rule.kind, rule.value);
|
|
524
|
+
if (!config?.symbol) return "";
|
|
525
|
+
return config.symbol.includes(":") || !config.exchange || config.exchange === "US" ? config.symbol : `${config.symbol}:${config.exchange}`;
|
|
526
|
+
}
|
|
527
|
+
|
|
386
528
|
const plural = (count: number, word: string) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
387
529
|
|
|
530
|
+
/**
|
|
531
|
+
* One row as text and CSV give it. CSV keeps the columns it has always had in
|
|
532
|
+
* their order, then Created (ISO UTC), the listing, its currency and the last
|
|
533
|
+
* reading, in words and dated.
|
|
534
|
+
*/
|
|
388
535
|
function textRow(row: AlertListRow): Record<string, unknown> {
|
|
389
536
|
const price = row.kind === "price";
|
|
390
537
|
return {
|
|
@@ -400,6 +547,11 @@ function textRow(row: AlertListRow): Record<string, unknown> {
|
|
|
400
547
|
status: row.status,
|
|
401
548
|
lastCheckedPrice: row.lastCheckedPrice,
|
|
402
549
|
triggeredAt: row.triggeredAt,
|
|
550
|
+
createdAt: isoUtc(typeof row.createdAt === "number" ? row.createdAt : null),
|
|
551
|
+
listing: row.listing ?? "",
|
|
552
|
+
currency: row.currency ?? "",
|
|
553
|
+
lastReading: row.lastReading ?? "",
|
|
554
|
+
lastReadingAt: row.lastReadingAt ?? "",
|
|
403
555
|
};
|
|
404
556
|
}
|
|
405
557
|
|
|
@@ -485,8 +637,8 @@ function releaseReadNotes(
|
|
|
485
637
|
return notes;
|
|
486
638
|
}
|
|
487
639
|
|
|
488
|
-
/** Every
|
|
489
|
-
const ADD_FORMS =
|
|
640
|
+
/** Every kind `alerts add` takes, as a refusal of words none of them reads ends. */
|
|
641
|
+
const ADD_FORMS = ALERT_KINDS_TEXT;
|
|
490
642
|
|
|
491
643
|
/**
|
|
492
644
|
* Why a name could not be looked up. A single word (alerts add AAPL) may be
|
|
@@ -506,9 +658,14 @@ function noReleaseMessage(
|
|
|
506
658
|
renamed?: ReleaseRename,
|
|
507
659
|
): string {
|
|
508
660
|
const near = suggestions.length > 0 ? ` Did you mean ${choiceList(suggestions).join(", ")}?` : "";
|
|
509
|
-
|
|
661
|
+
// Words with a country are a release named; anything else may as well be a kind of alert mistyped.
|
|
662
|
+
if (country) {
|
|
663
|
+
return `No release in the economic calendar matches "${query}" in ${country.split(",").join(", ")}.`
|
|
664
|
+
+ `${renamed ? ` ${releaseRenameNote(renamed)}` : ""}${near} gloom econ --country <code> lists the calendar.`;
|
|
665
|
+
}
|
|
666
|
+
return `No kind of alert and no release in the economic calendar matches "${query}".`
|
|
510
667
|
+ `${renamed ? ` ${releaseRenameNote(renamed)}` : ""}${near} `
|
|
511
|
-
+ `${ADD_FORMS} gloom econ --country <code> lists the calendar.`;
|
|
668
|
+
+ `${ADD_FORMS} Calendar releases go by name (${CALENDAR_EXAMPLES}); gloom econ --country <code> lists the calendar.`;
|
|
512
669
|
}
|
|
513
670
|
|
|
514
671
|
/** The refusal of a name listed in several countries, with the best of each. */
|
|
@@ -534,21 +691,13 @@ async function requireEventDate(
|
|
|
534
691
|
export const alertsCliCommand: CliCommandDef = {
|
|
535
692
|
name: "alerts",
|
|
536
693
|
aliases: ["alert"],
|
|
537
|
-
description: "List, add, and remove price, valuation
|
|
694
|
+
description: "List, add, re-arm and remove price, valuation, daily technical and relative strength alerts, alerts before earnings and any economic calendar release (CPI, FOMC, NBS PMI), and every event alert ALRT offers: news, options flow, IV, 52-week range, unusual volume, filings, insiders, short interest, analysts, hiring, perpetuals, catalysts, Congress and 13F",
|
|
538
695
|
help: {
|
|
539
696
|
group: CLI_COMMAND_GROUPS.portfolios,
|
|
697
|
+
// Every form `alerts add` takes, as the refusal of an unknown phrase names them and the CLI docs page lists them.
|
|
540
698
|
usage: [
|
|
541
699
|
"alerts list",
|
|
542
|
-
|
|
543
|
-
"alerts add <symbol> <pe|ps|rsi(14)> <above|below> <level>",
|
|
544
|
-
"alerts add <symbol> ma(50) crosses <above|below> ma(200)",
|
|
545
|
-
"alerts add <symbol> closes <above|below> ma(200)",
|
|
546
|
-
"alerts add <symbol> earnings [--days <n> | results]",
|
|
547
|
-
"alerts add <calendar event> [--country <code>] [--days <n>]",
|
|
548
|
-
"alerts add [symbol] news <keyword>",
|
|
549
|
-
"alerts add [symbol] flow [premium] [calls|puts] [sweeps|blocks]",
|
|
550
|
-
"alerts add <symbol> <iv|rr> <above|below> <level>",
|
|
551
|
-
"alerts add <OCC contract> iv spike <points>",
|
|
700
|
+
...ALERT_ADD_FORMS.map((form) => `alerts add ${form.usage}`),
|
|
552
701
|
"alerts delete <id>",
|
|
553
702
|
"alerts rearm <id>",
|
|
554
703
|
],
|
|
@@ -556,6 +705,7 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
556
705
|
EXCHANGE_OPTION,
|
|
557
706
|
{ flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings and calendar event alerts` },
|
|
558
707
|
{ flags: "--country <code>", description: "The country of a calendar event, as econ --country takes it (CN, US, DE); a code before the name does the same (alerts add CN PMI)" },
|
|
708
|
+
{ flags: "--benchmark <symbol>", description: "The listing an RRG alert is measured against; SPY by default, as in RRG" },
|
|
559
709
|
],
|
|
560
710
|
examples: [
|
|
561
711
|
"alerts",
|
|
@@ -565,6 +715,9 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
565
715
|
'alerts add NVDA "rsi(14)" under 30',
|
|
566
716
|
'alerts add SPY "ma(50)" crosses above "ma(200)"',
|
|
567
717
|
'alerts add TSLA closes below "ma(200)"',
|
|
718
|
+
"alerts add XLK/SPY above 0.25",
|
|
719
|
+
"alerts add XLE rrg leading",
|
|
720
|
+
"alerts add SMH rrg weakening --benchmark QQQ",
|
|
568
721
|
"alerts add SAN:EPA below 70",
|
|
569
722
|
"alerts add CRWV earnings --days 3",
|
|
570
723
|
"alerts add CRWV earnings results",
|
|
@@ -575,6 +728,17 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
575
728
|
"alerts add NVDA flow 5m calls sweeps",
|
|
576
729
|
"alerts add IBIT iv above 50",
|
|
577
730
|
"alerts add SPY rr below -6",
|
|
731
|
+
"alerts add XLE 52-week high",
|
|
732
|
+
"alerts add NVDA unusual volume 3x",
|
|
733
|
+
"alerts add AAPL filing 8-K",
|
|
734
|
+
"alerts add TSLA insider sell",
|
|
735
|
+
"alerts add GME short interest increase 20%",
|
|
736
|
+
"alerts add AAPL analyst downgrade",
|
|
737
|
+
"alerts add RIVN hiring slowing",
|
|
738
|
+
"alerts add perps hyperliquid:default:BTC funding above 0.05%",
|
|
739
|
+
"alerts add catalyst type clinical",
|
|
740
|
+
"alerts add congress \"Nancy Pelosi\"",
|
|
741
|
+
"alerts add 13f 1067983",
|
|
578
742
|
],
|
|
579
743
|
},
|
|
580
744
|
execute: async (rawArgs, ctx) => {
|
|
@@ -586,31 +750,55 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
586
750
|
const countryFlag = rawArgs.some((arg) => arg === "--country" || arg.startsWith("--country="));
|
|
587
751
|
const countryValue = takeOption(args, "--country");
|
|
588
752
|
const country = countryFlag && countryValue === undefined ? null : countryValue;
|
|
753
|
+
const benchmarkFlag = rawArgs.some((arg) => arg === "--benchmark" || arg.startsWith("--benchmark="));
|
|
754
|
+
const benchmarkValue = takeOption(args, "--benchmark");
|
|
755
|
+
const benchmark = benchmarkFlag && benchmarkValue === undefined ? null : benchmarkValue;
|
|
589
756
|
const action = args[0] ?? "list";
|
|
590
757
|
const now = Date.now();
|
|
591
758
|
const today = newYorkToday(new Date(now));
|
|
759
|
+
const failWith = (error: unknown): never => ctx.fail(error instanceof Error ? error.message : String(error));
|
|
592
760
|
|
|
593
761
|
const parseAdd = (): AlertAddRequest => {
|
|
594
762
|
try {
|
|
595
|
-
return parseAlertAddArgs(args.slice(1), days, exchangeOption, country);
|
|
763
|
+
return parseAlertAddArgs(args.slice(1), days, exchangeOption, country, benchmark);
|
|
596
764
|
} catch (error) {
|
|
597
|
-
return
|
|
765
|
+
return failWith(error);
|
|
598
766
|
}
|
|
599
767
|
};
|
|
600
|
-
const
|
|
768
|
+
const typedRequest = action === "add" ? parseAdd() : null;
|
|
769
|
+
// XLK/SPY is first a listing written with a slash (BRK/B, EUR/USD); one that quotes nothing is the ratio of the two.
|
|
770
|
+
const pairListing = typedRequest?.kind === "price" && PAIR_SHAPE.test(typedRequest.symbol)
|
|
771
|
+
? await withMarketData(ctx, (market) => requireQuotedListing(typedRequest.symbol, exchangeOption, market, PROBE, "alerts add")).catch((error: unknown) => {
|
|
772
|
+
if (!(error instanceof ListingRefusal)) throw error;
|
|
773
|
+
if (!notATickerSymbol(error.message)) return ctx.fail(error.message, error.details);
|
|
774
|
+
return null;
|
|
775
|
+
})
|
|
776
|
+
: undefined;
|
|
777
|
+
let request: AlertAddRequest | null = typedRequest;
|
|
778
|
+
if (typedRequest?.kind === "price" && pairListing === null) {
|
|
779
|
+
if (exchangeOption) ctx.fail("A ratio alert names each listing, such as alerts add XLK:NYSEARCA/SPY above 0.25, and takes no --exchange.");
|
|
780
|
+
try {
|
|
781
|
+
request = { kind: "metric", ...parseMetricAlert(`${typedRequest.symbol} ratio ${typedRequest.condition} ${typedRequest.price}`)! };
|
|
782
|
+
} catch (error) {
|
|
783
|
+
failWith(error);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
601
786
|
const research = request && isResearchRequest(request) ? request : null;
|
|
787
|
+
const formRule = request?.kind === "rule" ? request : null;
|
|
602
788
|
const listedSymbol = (request?.kind === "price" || request?.kind === "metric") ? request.symbol
|
|
603
|
-
: research && research.kind !== "iv_spike" ? research.symbol : null;
|
|
789
|
+
: research && research.kind !== "iv_spike" ? research.symbol : formRule?.symbol ?? null;
|
|
790
|
+
// Gloom Cloud's research rules follow US listings; hiring and a catalyst's company may list anywhere.
|
|
791
|
+
const usOnly = research ? research.kind === "news" ? "News alerting" : research.kind === "flow" ? "Options flow" : "Options data"
|
|
792
|
+
: formRule?.listing === "us" ? RULE_US_DATA[formRule.event] ?? "This alert" : null;
|
|
604
793
|
// A price alert is kept for the listing `quote` prints for the symbol, and for no symbol that quotes nothing;
|
|
605
794
|
// a news, flow or IV rule on a symbol follows that listing too, and only in the US, where their data is.
|
|
606
|
-
const priced = listedSymbol
|
|
795
|
+
const priced = pairListing ? pairListing : listedSymbol
|
|
607
796
|
? await withMarketData(ctx, async (market) => {
|
|
608
797
|
const quoted = await requireQuotedListing(listedSymbol, exchangeOption, market, ctx, "alerts add");
|
|
609
|
-
if (
|
|
798
|
+
if (usOnly) {
|
|
610
799
|
const venue = quoted.listing.exchange || canonicalExchange(quoted.quote.listingExchangeName || quoted.quote.exchangeName);
|
|
611
800
|
if (!isUsListingExchange(venue)) {
|
|
612
|
-
const
|
|
613
|
-
const { message, details } = await usListingsOnlyMessage(what, quoted.listing, market,
|
|
801
|
+
const { message, details } = await usListingsOnlyMessage(usOnly, quoted.listing, market,
|
|
614
802
|
(key) => `gloom alerts add ${key} ${args.slice(2).join(" ")}`);
|
|
615
803
|
ctx.fail(message, details);
|
|
616
804
|
}
|
|
@@ -619,18 +807,56 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
619
807
|
})
|
|
620
808
|
: null;
|
|
621
809
|
const priceListing = priced?.listing ?? null;
|
|
810
|
+
// A bare coin ticker read as another listing (SOL, a stale company; BTC, the Grayscale trust) gets `quote`'s note
|
|
811
|
+
// naming the coin's pair, before a metric alert names the venue it was read on.
|
|
812
|
+
const coinNote = priced ? coinListingNotes(priced.listing, priced.quote)[0] ?? null : null;
|
|
813
|
+
// What the alert reads now, which it is saved with, so lists and ALRT show it before the app has run.
|
|
814
|
+
let reading: Partial<AlertRule> = {};
|
|
815
|
+
// A daily rule whose close could not be read yet: why, which the add says beside when it is first checked.
|
|
816
|
+
let unread: string | null = null;
|
|
817
|
+
// A ratio or RRG alert's benchmark, as its listing was read.
|
|
818
|
+
const benchmarkNotes: string[] = [];
|
|
622
819
|
if (request?.kind === "metric" && priced) {
|
|
623
820
|
const exchange = canonicalExchange(priced.listing.exchange || priced.quote.listingExchangeName || priced.quote.exchangeName);
|
|
821
|
+
const typedMetric = request.metric;
|
|
822
|
+
if (isRelativeMetric(typedMetric)) {
|
|
823
|
+
// The benchmark is held to a listing that quotes, as the symbol is.
|
|
824
|
+
const reference = await withMarketData(ctx, (market) => requireQuotedListing(benchmarkKey(typedMetric), null, market, ctx, "alerts add"));
|
|
825
|
+
const benchmarkExchange = canonicalExchange(reference.listing.exchange || reference.quote.listingExchangeName || reference.quote.exchangeName);
|
|
826
|
+
if (reference.listing.symbol === priced.listing.symbol && benchmarkExchange === exchange) {
|
|
827
|
+
ctx.fail(`${typedMetric.kind === "rrg" ? "An RRG alert" : "A ratio alert"} compares two listings; ${priced.listing.symbol} is on both sides.`);
|
|
828
|
+
}
|
|
829
|
+
request.metric = { ...typedMetric, benchmark: reference.listing.symbol, ...(benchmarkExchange ? { benchmarkExchange } : {}) };
|
|
830
|
+
// Never save a rule its own list would refuse to read back.
|
|
831
|
+
if (!validMetricRule({ ...request, targetPrice: request.price })) ctx.fail(`${reference.listing.key} cannot be a benchmark: relative strength alerts compare two US listings.`);
|
|
832
|
+
benchmarkNotes.push(...reference.notes);
|
|
833
|
+
}
|
|
624
834
|
const preview = { ...createAlert(priced.listing.symbol, request.condition, request.price, exchange), metric: request.metric };
|
|
625
|
-
|
|
835
|
+
// A listing without a published close calendar can never be checked; anything else is saved and checked as it can be.
|
|
836
|
+
const unavailable = metricAvailabilityError(preview);
|
|
837
|
+
if (unavailable) ctx.fail(unavailable);
|
|
838
|
+
try {
|
|
839
|
+
const value = await withMarketData(ctx, market => metricReadings(market.dataProvider, now)(preview));
|
|
840
|
+
reading = { lastCheckedPrice: value.value, lastCheckedTarget: value.target, lastQuoteUpdatedAt: value.at, lastCheckedAt: now,
|
|
841
|
+
...value.day ? { lastDailyClose: value.day } : {} };
|
|
842
|
+
} catch (error) {
|
|
843
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
844
|
+
// A valuation the inputs cannot give is refused; a daily close not yet final, or history not there yet, is read once it is.
|
|
845
|
+
if (!isDailyMetric(request.metric)) ctx.fail(message);
|
|
846
|
+
unread = message;
|
|
847
|
+
}
|
|
848
|
+
if (priced.quote.currency) reading.lastQuoteCurrency = priced.quote.currency;
|
|
626
849
|
priced.listing.exchange = exchange;
|
|
627
850
|
}
|
|
851
|
+
if (request?.kind === "price" && priced && !isQuoteStaleForCurrentSession(priced.quote)) {
|
|
852
|
+
// The price it is added at is its first reading and the baseline a crossing is measured from, as Add Alert keeps it.
|
|
853
|
+
const price = lastPrice(priced.quote);
|
|
854
|
+
if (price != null) reading = quoteAlertFields({ ...priced.quote, price }, now);
|
|
855
|
+
}
|
|
628
856
|
if (request?.kind === "price" && request.thousandsDot && priced) {
|
|
629
857
|
const question = thousandsDotMessage(request, priced.listing.key, lastPrice(priced.quote), exchangeOption);
|
|
630
858
|
if (question) ctx.fail(question);
|
|
631
859
|
}
|
|
632
|
-
// A bare coin ticker read as a fund (BTC, the Grayscale trust) gets `quote`'s note naming the coin's pair.
|
|
633
|
-
const coinNote = priced && !priced.listing.exchange ? notTheCoinNote(priced.listing.symbol, priced.quote) : null;
|
|
634
860
|
if (request?.kind === "price" && priced) {
|
|
635
861
|
// A target ten times away is a slip or another instrument's price, such as bitcoin's on the BTC fund.
|
|
636
862
|
// Against the price the alert is checked on, as Add Alert measures it.
|
|
@@ -638,7 +864,7 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
638
864
|
if (far) ctx.fail(far);
|
|
639
865
|
}
|
|
640
866
|
// What a typed listing was read as (7203:TSE as 7203:JPX, MXN as MXN:FWB2), said beside what was added.
|
|
641
|
-
let read = [...priced?.notes ?? [], ...coinNote ? [coinNote] : []];
|
|
867
|
+
let read = [...priced?.notes ?? [], ...coinNote ? [coinNote] : [], ...benchmarkNotes];
|
|
642
868
|
let newEventRule: EventAlertRule | null = null;
|
|
643
869
|
let newResearchRule: EventAlertRule | null = null;
|
|
644
870
|
if (research) {
|
|
@@ -653,6 +879,13 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
653
879
|
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
654
880
|
}
|
|
655
881
|
}
|
|
882
|
+
if (formRule) {
|
|
883
|
+
try {
|
|
884
|
+
newResearchRule = createFormRule(formRule, priceListing);
|
|
885
|
+
} catch (error) {
|
|
886
|
+
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
887
|
+
}
|
|
888
|
+
}
|
|
656
889
|
// A release named from the calendar, with what the words were read as and what else they name.
|
|
657
890
|
let newRelease: CalendarEventChoice | null = null;
|
|
658
891
|
// CPI and FOMC, typed so or named by one of their calendar rows (CPI y/y, Fed Interest Rate Decision).
|
|
@@ -719,30 +952,36 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
719
952
|
|
|
720
953
|
if (action === "list") {
|
|
721
954
|
// An IV level's last reading is this device's, beside the rules.
|
|
722
|
-
const { alerts, alertsError, events, checks } = await withConfigData(ctx, (config) => ({
|
|
955
|
+
const { alerts, alertsError, events, checks, session } = await withConfigData(ctx, (config) => ({
|
|
723
956
|
...readAlertStores(config.config),
|
|
724
957
|
checks: readIvLevelChecks(config.persistence?.pluginState?.get(ALERTS_PLUGIN_ID, resumeStateKey(IV_LEVEL_CHECKS_KEY))?.value),
|
|
958
|
+
session: savedSession(config),
|
|
725
959
|
}));
|
|
726
960
|
// Earnings dates come through market data; CPI, FOMC and calendar dates do not need it.
|
|
727
961
|
const lookup = (targets: ScheduledEventTarget[]) => targets.some((target) => target.kind === "earnings")
|
|
728
962
|
? withMarketData(ctx, (market) => lookupScheduledDates(targets, scheduledEventSources(market.dataProvider), today, now))
|
|
729
963
|
: lookupScheduledDates(targets, scheduledEventSources(null), today, now);
|
|
730
|
-
const { dates, warnings } = await listEventDates(events.rules, now, lookup);
|
|
964
|
+
const [{ dates, warnings }, cloud] = await Promise.all([listEventDates(events.rules, now, lookup), cloudRuleStates(events.rules, session)]);
|
|
731
965
|
const rows = [
|
|
732
966
|
...alerts.map(priceRow),
|
|
733
|
-
...events.rules.map((rule) => eventRow(rule, now, dates.has(rule.id) ? dates.get(rule.id) : rule.eventDate, checks[rule.id])),
|
|
967
|
+
...events.rules.map((rule) => eventRow(rule, now, dates.has(rule.id) ? dates.get(rule.id) : rule.eventDate, checks[rule.id], cloud, !!session)),
|
|
734
968
|
];
|
|
735
|
-
const
|
|
969
|
+
const waiting = rows.filter((row) => row.status === NEEDS_SIGN_IN).length;
|
|
970
|
+
const allWarnings = [...(alertsError ? [alertsError] : []), ...(events.error ? [events.error] : []), ...warnings, ...cloud.warning ? [cloud.warning] : [],
|
|
971
|
+
...waiting ? [needsSignInNote(waiting)] : []];
|
|
736
972
|
ctx.printResult({ data: rows, ...(allWarnings.length ? { warnings: allWarnings } : {}) }, {
|
|
737
973
|
rows: (data) => data.map(textRow),
|
|
738
974
|
textColumns: [
|
|
739
975
|
{ key: "id", header: "ID", shrink: false },
|
|
740
976
|
{ key: "kind", header: "Kind" },
|
|
741
|
-
|
|
977
|
+
// A price or technical alert names the listing it is checked on, in its currency.
|
|
978
|
+
{ key: "target", header: "Target", format: (value, row) => row.currency ? listingWithCurrency(String(row.listing), String(row.currency)) : String(value ?? "") },
|
|
742
979
|
{ key: "trigger", header: "Trigger" },
|
|
743
980
|
{ key: "eventDate", header: "Event Date" },
|
|
744
981
|
{ key: "status", header: "Status" },
|
|
745
|
-
{ key: "
|
|
982
|
+
{ key: "lastReading", header: "Last Reading" },
|
|
983
|
+
// ISO 8601 in UTC, as CSV writes it.
|
|
984
|
+
{ key: "createdAt", header: "Created", format: (value) => String(value ?? "") },
|
|
746
985
|
{ key: "triggeredAt", header: "Triggered", optional: true },
|
|
747
986
|
],
|
|
748
987
|
empty: "No alerts. Add one with gloom alerts add AAPL above 250, alerts add CRWV earnings --days 3, alerts add CPI --days 1, or alerts add CN PMI.",
|
|
@@ -775,15 +1014,45 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
775
1014
|
if (events.error) ctx.fail(events.error);
|
|
776
1015
|
return events.rules;
|
|
777
1016
|
};
|
|
1017
|
+
// Each rule created, re-armed or deleted here goes in this device's alert log, beside the alerts that fired.
|
|
1018
|
+
const record = (action: AlertLogAction, rule: AlertRule | EventAlertRule) => {
|
|
1019
|
+
const state = config.persistence?.pluginState;
|
|
1020
|
+
if (ctx.cliOptions.dryRun || !state) return;
|
|
1021
|
+
try {
|
|
1022
|
+
appendAlertLog({
|
|
1023
|
+
get: (key) => state.get(ALERTS_PLUGIN_ID, resumeStateKey(key))?.value,
|
|
1024
|
+
set: (key, value) => state.set(ALERTS_PLUGIN_ID, resumeStateKey(key), value),
|
|
1025
|
+
}, [alertChangeEntry(action, alertLogRule(rule), Date.now())]);
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
logFailure("alerts.cli", "Record alert change failed")(error);
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
778
1030
|
|
|
779
1031
|
if (request?.kind === "price" || request?.kind === "metric") {
|
|
780
1032
|
const { condition, price } = request;
|
|
781
1033
|
const symbol = requireArg(priceListing?.key, ADD_USAGE, ctx);
|
|
782
|
-
const alert = { ...createAlert(symbol, condition, price, priceListing?.exchange), ...(request.kind === "metric" ? { metric: request.metric } : {}) };
|
|
1034
|
+
const alert: AlertRule = { ...createAlert(symbol, condition, price, priceListing?.exchange), ...(request.kind === "metric" ? { metric: request.metric } : {}), ...reading };
|
|
1035
|
+
// The same rule on the same listing is one alert: a second add names it and adds nothing.
|
|
1036
|
+
const existing = alerts.find((entry) => sameLevelRule(entry, alert));
|
|
1037
|
+
if (existing) {
|
|
1038
|
+
const rearm = existing.status === "triggered" ? `; it went off, so re-arm it with gloom alerts rearm ${existing.id}` : "";
|
|
1039
|
+
ctx.fail(`${alertLogRule(alert).rule} already exists as alert ${existing.id}${rearm}. Nothing was added.`);
|
|
1040
|
+
}
|
|
783
1041
|
await save({ alerts: [...alerts, alert] });
|
|
784
|
-
|
|
1042
|
+
record("created", alert);
|
|
1043
|
+
// A daily rule is first judged on the first close after it was set, once that close has settled.
|
|
1044
|
+
// An RRG quadrant, on the last session of that week.
|
|
1045
|
+
const weekly = alert.metric?.kind === "rrg";
|
|
1046
|
+
const firstCheck = alert.metric && isDailyMetric(alert.metric) ? firstDailyCheck(alert.exchange, now, weekly) : null;
|
|
1047
|
+
const notes = [
|
|
1048
|
+
alert.metric ? unread ? `Not read yet: ${unread.replace(/\.$/, "")}.` : `${metricReadingNote(alert)}.` : null,
|
|
1049
|
+
firstCheck ? `First checked on the ${firstCheck.day} ${weekly ? "weekly close" : "close"}, from ${isoUtc(firstCheck.at).slice(11, 16)} UTC.` : null,
|
|
1050
|
+
].filter(Boolean).join(" ");
|
|
1051
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert,
|
|
1052
|
+
...firstCheck ? { firstCheck: { close: firstCheck.day, at: isoUtc(firstCheck.at) } } : {},
|
|
1053
|
+
...unread ? { unread } : {} }, ...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
785
1054
|
// The delivery, as docs/usage.md states it, before any full stop, which after the price would read as part of it (below 81.).
|
|
786
|
-
text: (data) => `Added alert ${data.alert.id}: ${data.alert.metric ? metricDescription(data.alert) : `${symbol} ${condition} ${price}`} (${PRICE_DELIVERY}).${dryRunNote(data.dryRun)}`,
|
|
1055
|
+
text: (data) => `Added alert ${data.alert.id}: ${data.alert.metric ? metricDescription(data.alert) : `${symbol} ${condition} ${price}`} (${PRICE_DELIVERY}).${notes ? ` ${notes}` : ""}${dryRunNote(data.dryRun)}`,
|
|
787
1056
|
});
|
|
788
1057
|
return;
|
|
789
1058
|
}
|
|
@@ -791,12 +1060,19 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
791
1060
|
const rule = newResearchRule;
|
|
792
1061
|
const rules = requireEvents();
|
|
793
1062
|
const existing = rules.find((entry) => entry.kind === rule.kind && entry.target === rule.target && entry.value.toLowerCase() === rule.value.toLowerCase());
|
|
794
|
-
if (existing) ctx.fail(`This alert already exists (ID ${existing.id}).`);
|
|
1063
|
+
if (existing) ctx.fail(`This alert already exists (ID ${existing.id}). Nothing was added.`);
|
|
795
1064
|
if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
|
|
796
1065
|
await save({ events: [...rules, rule] });
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1066
|
+
record("created", rule);
|
|
1067
|
+
const delivery = research ? researchDelivery(research.kind) : ruleDelivery(formRule!.event);
|
|
1068
|
+
// Saved signed out, it waits on a sign-in, and `alerts list` and ALRT say so until there is one.
|
|
1069
|
+
const needsSignIn = checkedWhenSignedIn(rule) && !savedSession(config);
|
|
1070
|
+
const signIn = needsSignIn
|
|
1071
|
+
? " It needs sign-in: nothing checks it while you are signed out. Open the app (gloom) and log in or sign up free from the command bar (Ctrl+P, Log In or Sign Up)."
|
|
1072
|
+
: "";
|
|
1073
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule, ...needsSignIn ? { status: NEEDS_SIGN_IN } : {} },
|
|
1074
|
+
...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
1075
|
+
text: (data) => `Added alert ${data.alert.id}: ${alertKindLabel(rule.kind)} ${eventAlertTarget(rule)} (${delivery}).${signIn}${dryRunNote(data.dryRun)}`,
|
|
800
1076
|
});
|
|
801
1077
|
return;
|
|
802
1078
|
}
|
|
@@ -807,10 +1083,11 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
807
1083
|
const name = scheduledEventName(scheduled.target);
|
|
808
1084
|
const existing = rules.find((entry) => entry.kind === rule.kind && entry.value.toLowerCase() === rule.value.toLowerCase());
|
|
809
1085
|
if (existing) ctx.fail(scheduled.trigger === "results"
|
|
810
|
-
? `An alert for ${name} results already exists (ID ${existing.id}).`
|
|
811
|
-
: `An alert ${plural(scheduled.leadDays, "day")} before ${name} already exists (ID ${existing.id}).`);
|
|
1086
|
+
? `An alert for ${name} results already exists (ID ${existing.id}). Nothing was added.`
|
|
1087
|
+
: `An alert ${plural(scheduled.leadDays, "day")} before ${name} already exists (ID ${existing.id}). Nothing was added.`);
|
|
812
1088
|
if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
|
|
813
1089
|
await save({ events: [...rules, rule] });
|
|
1090
|
+
record("created", rule);
|
|
814
1091
|
const eventDate = rule.eventDate ?? null;
|
|
815
1092
|
const due = !!eventDate && scheduledPhase(eventDate, scheduled.leadDays, undefined, scheduledToday(scheduled.target, now)) === "due";
|
|
816
1093
|
const release = followsRelease(scheduled.target);
|
|
@@ -835,12 +1112,14 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
835
1112
|
}
|
|
836
1113
|
if (action === "delete" || action === "rm") {
|
|
837
1114
|
const id = requireArg(args[1], "Usage: gloom alerts delete <id>", ctx);
|
|
838
|
-
const
|
|
839
|
-
const
|
|
840
|
-
if (!
|
|
841
|
-
if (
|
|
842
|
-
if (
|
|
843
|
-
const
|
|
1115
|
+
const level = alerts.find((alert) => alert.id === id) ?? null;
|
|
1116
|
+
const rule = level ? null : events.rules.find((entry) => entry.id === id) ?? null;
|
|
1117
|
+
if (!level && !rule && alertsError) ctx.fail(alertsError);
|
|
1118
|
+
if (level) await save({ alerts: alerts.filter((alert) => alert.id !== id) });
|
|
1119
|
+
if (rule) await save({ events: requireEvents().filter((entry) => entry.id !== id) });
|
|
1120
|
+
const gone = level ?? rule;
|
|
1121
|
+
if (gone) record("deleted", gone);
|
|
1122
|
+
const changed = !!gone;
|
|
844
1123
|
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun && changed, dryRun: ctx.cliOptions.dryRun, id } }, {
|
|
845
1124
|
text: (data) => changed
|
|
846
1125
|
? `Deleted alert ${id}.${dryRunNote(data.dryRun)}`
|
|
@@ -850,22 +1129,25 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
850
1129
|
}
|
|
851
1130
|
if (action === "rearm") {
|
|
852
1131
|
const id = requireArg(args[1], "Usage: gloom alerts rearm <id>", ctx);
|
|
853
|
-
const
|
|
854
|
-
const rule =
|
|
855
|
-
if (!
|
|
856
|
-
|
|
857
|
-
|
|
1132
|
+
const level = alerts.find((alert) => alert.id === id) ?? null;
|
|
1133
|
+
const rule = level ? null : events.rules.find((entry) => entry.id === id) ?? null;
|
|
1134
|
+
if (!level && !rule && alertsError) ctx.fail(alertsError);
|
|
1135
|
+
// An alert still waiting to go off has nothing to re-arm, and is left as it is.
|
|
1136
|
+
const active = level ? level.status === "active" : rule ? eventRuleArmed(rule, now) : false;
|
|
1137
|
+
if (level && !active) await save({ alerts: alerts.map((alert) => alert.id === id ? rearmAlert(alert) : alert) });
|
|
1138
|
+
if (rule && !active) {
|
|
858
1139
|
// A scheduled alert alerts again for its coming event; any other event alert resumes.
|
|
859
1140
|
const next = readScheduledRule(rule) ? rearmScheduledAlert(rule)
|
|
860
1141
|
: rule.kind === "iv_level" && rule.triggeredAt ? rearmIvLevelAlert(rule)
|
|
861
1142
|
: rule.status === "paused" ? toggleEventAlert(rule) : rule;
|
|
862
1143
|
await save({ events: requireEvents().map((entry) => entry.id === id ? next : entry) });
|
|
863
1144
|
}
|
|
864
|
-
const found =
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
: cliStyles.muted(`
|
|
1145
|
+
const found = level ?? rule;
|
|
1146
|
+
if (found && !active) record("rearmed", found);
|
|
1147
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun && !!found && !active, dryRun: ctx.cliOptions.dryRun, id, ...active ? { active: true } : {} } }, {
|
|
1148
|
+
text: (data) => !found ? cliStyles.muted(`No alert with ID ${id}.`)
|
|
1149
|
+
: active ? cliStyles.muted(`Alert ${id} is already active; nothing to re-arm.`)
|
|
1150
|
+
: `Re-armed alert ${id}.${dryRunNote(data.dryRun)}`,
|
|
869
1151
|
});
|
|
870
1152
|
return;
|
|
871
1153
|
}
|