gloomberb 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -1
- package/src/api-client/auth.ts +1 -0
- package/src/api-client/index.test.ts +359 -108
- package/src/api-client/index.ts +24 -13
- package/src/api-client/socket.ts +70 -35
- package/src/api-client/types.ts +1 -1
- package/src/app/language-observer.test.tsx +82 -0
- package/src/app/language-observer.tsx +13 -0
- package/src/app/pane-runtime/index.tsx +2 -2
- package/src/app/pane-runtime/pane-settings-runtime.tsx +2 -2
- package/src/app/pane-runtime/pane-template-runtime.ts +2 -2
- package/src/app/pane-runtime/plugin-bindings.ts +2 -2
- package/src/app/pane-runtime/ticker-open-runtime.ts +2 -2
- package/src/app/runtime/broker-import.ts +2 -2
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +2 -2
- package/src/app.tsx +16 -7
- package/src/brokers/account-cache.ts +6 -6
- package/src/brokers/broker-ticker-sync.ts +3 -3
- package/src/brokers/remote-broker-adapter.ts +2 -2
- package/src/brokers/sync-broker-instance.ts +5 -6
- package/src/cli/commands/api.ts +3 -6
- package/src/cli/commands/automation.ts +7 -15
- package/src/cli/commands/market.ts +26 -61
- package/src/cli/commands/overview.ts +34 -45
- package/src/cli/commands/search.ts +25 -27
- package/src/cli/commands/system.ts +27 -57
- package/src/cli/commands/ticker.ts +62 -63
- package/src/cli/context.test.ts +63 -0
- package/src/cli/context.ts +15 -1
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/data.ts +2 -19
- package/src/cli/pane-functions/index.ts +26 -25
- package/src/cli/pane-functions/report.ts +1 -1
- package/src/cli/pane-functions/screenshot.ts +3 -3
- package/src/cli/scoped-context.ts +41 -0
- package/src/components/chart/composite/composite-chart.test.tsx +615 -18
- package/src/components/chart/composite/composite-chart.tsx +179 -79
- package/src/components/chart/composite/format.test.ts +20 -7
- package/src/components/chart/composite/index.ts +0 -21
- package/src/components/chart/composite/interactions.test.ts +90 -3
- package/src/components/chart/composite/interactions.ts +189 -24
- package/src/components/chart/composite/price-series.ts +0 -2
- package/src/components/chart/composite/rasterizer.ts +10 -9
- package/src/components/chart/composite/renderers.test.ts +108 -2
- package/src/components/chart/composite/scene.test.ts +36 -0
- package/src/components/chart/composite/scene.ts +5 -2
- package/src/components/chart/composite/text-renderer.ts +37 -25
- package/src/components/chart/composite/time-axis.test.ts +243 -0
- package/src/components/chart/composite/time-axis.ts +608 -0
- package/src/components/chart/composite/types.ts +12 -1
- package/src/components/chart/core/data.ts +2 -159
- package/src/components/chart/core/date-window.ts +6 -225
- package/src/components/chart/core/palette.ts +1 -12
- package/src/components/chart/core/pointer.ts +0 -142
- package/src/components/chart/core/types.ts +1 -58
- package/src/components/chart/cursor-motion.ts +2 -4
- package/src/components/chart/indicators/types.ts +0 -9
- package/src/components/chart/market-session.ts +0 -11
- package/src/components/chart/native/kitty/protocol.ts +1 -1
- package/src/components/chart/native/raster/layout.ts +3 -10
- package/src/components/chart/static/chart/axis-overlays.test.tsx +64 -0
- package/src/components/chart/static/chart/axis-overlays.tsx +59 -14
- package/src/components/chart/static/chart/surface.tsx +1 -2
- package/src/components/command-bar/action-runtime.ts +2 -2
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +2 -2
- package/src/components/command-bar/panel/index.tsx +11 -9
- package/src/components/command-bar/routes/ticker-search/actions.ts +2 -2
- package/src/components/command-bar/surface/index.tsx +2 -2
- package/src/components/command-bar/workflow/body.tsx +3 -3
- package/src/components/command-bar/workflow/collection-actions.ts +2 -2
- package/src/components/command-bar/workflow/coordinator.ts +2 -2
- package/src/components/command-bar/workflow/field-row.tsx +25 -11
- package/src/components/command-bar/workflow/tickers.ts +2 -2
- package/src/components/feed-data-table/stack-view.test.tsx +28 -36
- package/src/components/feed-data-table/stack-view.tsx +3 -1
- package/src/components/layout/detached-pane-shell.tsx +6 -6
- package/src/components/layout/header.tsx +13 -9
- package/src/components/layout/pane/content.test.tsx +42 -3
- package/src/components/layout/pane/content.tsx +3 -1
- package/src/components/layout/pane/footer/index.test.tsx +45 -2
- package/src/components/layout/pane/footer/registration.tsx +3 -1
- package/src/components/layout/shell/index.tsx +1 -2
- package/src/components/layout/shell/native/surfaces.ts +1 -1
- package/src/components/layout/shell/native/window-state.ts +1 -1
- package/src/components/layout/status-bar.tsx +12 -8
- package/src/components/layout/window-controls.tsx +8 -6
- package/src/components/onboarding/onboarding-wizard.tsx +3 -1
- package/src/components/pane-settings-dialog/desktop.tsx +0 -2
- package/src/components/pane-settings-dialog.tsx +0 -2
- package/src/components/pane-template-wizard.tsx +10 -9
- package/src/components/price-performance.tsx +2 -113
- package/src/components/price-sparkline/model.ts +15 -17
- package/src/components/price-sparkline/view.tsx +8 -7
- package/src/components/speedometer-gauge/desktop.tsx +2 -1
- package/src/components/speedometer-gauge/index.tsx +0 -2
- package/src/components/speedometer-gauge/terminal.tsx +24 -5
- package/src/components/ticker/badge/index.tsx +1 -18
- package/src/components/ui/data-table/opentui/index.tsx +3 -3
- package/src/components/ui/frame.tsx +1 -2
- package/src/components/ui/page-stack-view.test.tsx +126 -0
- package/src/components/ui/page-stack-view.tsx +24 -12
- package/src/components/ui/tabs.tsx +7 -3
- package/src/components/ui/ui.test.tsx +23 -2
- package/src/components/use-external-link-footer.ts +4 -2
- package/src/core/app-service-ports.ts +46 -0
- package/src/core/app-services.test.ts +66 -0
- package/src/core/app-services.ts +17 -15
- package/src/data/config/chart-settings.ts +3 -10
- package/src/data/config/store/index.test.ts +99 -2
- package/src/data/config/store/migrations.ts +278 -0
- package/src/data/config/store/normalize.ts +44 -186
- package/src/data/resource-store.ts +73 -12
- package/src/data/sqlite/cache.test.ts +104 -15
- package/src/i18n/index.test.ts +17 -0
- package/src/i18n/index.ts +23 -6
- package/src/i18n/ja.ts +13 -0
- package/src/i18n/ko.ts +13 -0
- package/src/i18n/react.ts +6 -0
- package/src/i18n/zh-cn.ts +13 -0
- package/src/i18n/zh-tw.ts +13 -0
- package/src/market-data/coordinator/auxiliary.ts +7 -3
- package/src/market-data/coordinator/chart.ts +1 -1
- package/src/market-data/coordinator/index.test.ts +26 -0
- package/src/market-data/coordinator/index.ts +11 -5
- package/src/market-data/coordinator/quotes.ts +4 -40
- package/src/market-data/coordinator/subscriptions.test.ts +89 -1
- package/src/market-data/hooks.tsx +47 -1
- package/src/market-data/market/status.ts +6 -6
- package/src/market-data/performance.ts +1 -1
- package/src/market-data/quote-subscription-target.test.ts +37 -0
- package/src/market-data/quote-subscription-target.ts +60 -0
- package/src/market-data/quotes/freshness.ts +1 -0
- package/src/market-data/quotes/resolution.ts +4 -0
- package/src/market-data/request-types.ts +3 -3
- package/src/news/aggregator.test.ts +97 -1
- package/src/news/aggregator.ts +100 -57
- package/src/news/hooks.test.tsx +64 -0
- package/src/news/hooks.ts +18 -25
- package/src/plugins/builtin/account-management/footer.ts +4 -2
- package/src/plugins/builtin/account-management/pane.tsx +9 -4
- package/src/plugins/builtin/ai/agent-history.ts +11 -7
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +0 -5
- package/src/plugins/builtin/ai/ask-ai.test.tsx +3 -4
- package/src/plugins/builtin/ai/index.test.ts +2 -2
- package/src/plugins/builtin/ai/index.tsx +10 -11
- package/src/plugins/builtin/ai/pane-settings.ts +1 -1
- package/src/plugins/builtin/ai/pi/runtime.ts +6 -5
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +0 -6
- package/src/plugins/builtin/ai/screener/footer.ts +4 -1
- package/src/plugins/builtin/ai/screener/pane.test.tsx +9 -9
- package/src/plugins/builtin/ai/workspace/model.ts +2 -27
- package/src/plugins/builtin/ai/workspace/pane.tsx +2 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +3 -2
- package/src/plugins/builtin/broker-manager/index.tsx +4 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +643 -0
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +134 -0
- package/src/plugins/builtin/chart-composer/editor/model.test.ts +85 -0
- package/src/plugins/builtin/chart-composer/editor/model.ts +292 -0
- package/src/plugins/builtin/chart-composer/editor/opentui-view.tsx +108 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +65 -947
- package/src/plugins/builtin/chart-composer/index.tsx +0 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +69 -36
- package/src/plugins/builtin/chart-composer/presets.ts +1 -19
- package/src/plugins/builtin/chart-composer/settings.ts +1 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +1 -1
- package/src/plugins/builtin/chat/content/shortcuts.ts +0 -1
- package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
- package/src/plugins/builtin/chat/controller/persistence.ts +2 -0
- package/src/plugins/builtin/chat/controller/session.test.ts +25 -0
- package/src/plugins/builtin/chat/controller/session.ts +2 -0
- package/src/plugins/builtin/chat/controller/state.ts +7 -0
- package/src/plugins/builtin/chat/controller.test.ts +73 -20
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
- package/src/plugins/builtin/cloud-tweets/footer.ts +9 -28
- package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -5
- package/src/plugins/builtin/cloud-tweets/table.tsx +12 -17
- package/src/plugins/builtin/correlation/compute.ts +4 -10
- package/src/plugins/builtin/econ/index.tsx +17 -19
- package/src/plugins/builtin/holders/types.ts +0 -1
- package/src/plugins/builtin/insider/index.tsx +6 -31
- package/src/plugins/builtin/news/index.tsx +5 -7
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +6 -8
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -11
- package/src/plugins/builtin/news/wire/read-state.test.ts +7 -0
- package/src/plugins/builtin/news/wire/read-state.ts +18 -28
- package/src/plugins/builtin/news/wire/rss/parser.ts +1 -11
- package/src/plugins/builtin/options/footer.test.ts +115 -0
- package/src/plugins/builtin/options/footer.ts +110 -0
- package/src/plugins/builtin/options/live-quotes.test.ts +238 -0
- package/src/plugins/builtin/options/live-quotes.ts +147 -0
- package/src/plugins/builtin/options/view.test.tsx +71 -1
- package/src/plugins/builtin/options/view.tsx +39 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +103 -117
- package/src/plugins/builtin/portfolio-list/cli/render.ts +11 -5
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -135
- package/src/plugins/builtin/portfolio-list/header.tsx +9 -4
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +4 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +24 -52
- package/src/plugins/builtin/sec/filing-content.test.ts +41 -0
- package/src/plugins/builtin/sec/filing-content.ts +94 -0
- package/src/plugins/builtin/sec/index.tsx +21 -104
- package/src/plugins/builtin/sectors/sector-model.ts +1 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -0
- package/src/plugins/builtin/shared/pane-footer.ts +82 -0
- package/src/plugins/builtin/shared/read-state.ts +64 -3
- package/src/plugins/builtin/substack/read-state.ts +16 -41
- package/src/plugins/builtin/thirteenf/pane.tsx +24 -73
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +10 -10
- package/src/plugins/builtin/yield-curve/index.tsx +11 -9
- package/src/plugins/ibkr/dialogs.tsx +3 -3
- package/src/plugins/ibkr/gateway/fundamentals.ts +1 -1
- package/src/plugins/ibkr/gateway/history-requests.ts +2 -2
- package/src/plugins/ibkr/gateway/history.ts +2 -2
- package/src/plugins/ibkr/gateway/selection.ts +2 -2
- package/src/plugins/ibkr/gateway/service/index.ts +2 -2
- package/src/plugins/ibkr/gateway/service/native.ts +2 -2
- package/src/plugins/plugin-persistence.ts +3 -4
- package/src/plugins/prediction-markets/controller/keyboard.ts +1 -1
- package/src/plugins/prediction-markets/controller/live-updates.ts +6 -5
- package/src/plugins/prediction-markets/detail/book.tsx +2 -2
- package/src/plugins/prediction-markets/detail/outcomes.tsx +3 -1
- package/src/plugins/prediction-markets/detail/pane.tsx +3 -1
- package/src/plugins/prediction-markets/detail/trades.tsx +3 -3
- package/src/plugins/registry/context.ts +3 -4
- package/src/plugins/registry/index.ts +5 -6
- package/src/react/input.test.ts +126 -3
- package/src/react/input.ts +120 -1
- package/src/remote/app-host.tsx +2 -13
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +44 -60
- package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +15 -13
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +26 -22
- package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +11 -6
- package/src/renderers/electrobun/bun/desktop/media.ts +5 -3
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +21 -22
- package/src/renderers/electrobun/bun/desktop/window-controls.ts +3 -1
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +18 -18
- package/src/renderers/electrobun/bun/index.ts +123 -83
- package/src/renderers/electrobun/shared/protocol.ts +146 -7
- package/src/renderers/electrobun/view/app-services.ts +5 -8
- package/src/renderers/electrobun/view/backend-rpc.ts +18 -8
- package/src/renderers/electrobun/view/broker-remote-client.ts +2 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +5 -22
- package/src/renderers/electrobun/view/config-host.ts +1 -1
- package/src/renderers/electrobun/view/context-menu.ts +1 -17
- package/src/renderers/electrobun/view/data-table/index.tsx +0 -1
- package/src/renderers/electrobun/view/data-table/row.tsx +0 -4
- package/src/renderers/electrobun/view/desktop/control-styles.ts +37 -34
- package/src/renderers/electrobun/view/desktop/controls.tsx +36 -33
- package/src/renderers/electrobun/view/desktop/list-view.tsx +15 -12
- package/src/renderers/electrobun/view/desktop/popover.tsx +2 -3
- package/src/renderers/electrobun/view/dialog-host.tsx +2 -3
- package/src/renderers/electrobun/view/fatal-screen.tsx +0 -1
- package/src/renderers/electrobun/view/host/box.tsx +0 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +0 -1
- package/src/renderers/electrobun/view/host/input.tsx +0 -1
- package/src/renderers/electrobun/view/host/media-surface.tsx +0 -1
- package/src/renderers/electrobun/view/host/native.ts +0 -1
- package/src/renderers/electrobun/view/host/scroll-box.tsx +0 -1
- package/src/renderers/electrobun/view/host/tabs.tsx +0 -1
- package/src/renderers/electrobun/view/host/text.tsx +0 -1
- package/src/renderers/electrobun/view/http-fetch.ts +3 -11
- package/src/renderers/electrobun/view/input-host.tsx +10 -53
- package/src/renderers/electrobun/view/key-event.ts +0 -1
- package/src/renderers/electrobun/view/main.tsx +3 -3
- package/src/renderers/electrobun/view/native-renderer.ts +0 -1
- package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +13 -1
- package/src/renderers/electrobun/view/native-stubs/chart/surface-manager.ts +11 -0
- package/src/renderers/electrobun/view/native-stubs/native-stubs.test.ts +47 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +6 -44
- package/src/renderers/electrobun/view/remote/persistence.ts +6 -2
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.test.ts +86 -0
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +72 -0
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +2 -2
- package/src/renderers/electrobun/view/toast-host.tsx +0 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -2
- package/src/renderers/electrobun/view/update-host.ts +1 -1
- package/src/renderers/opentui/chart-surface.tsx +2 -1
- package/src/renderers/opentui/dialog-host.tsx +20 -4
- package/src/renderers/opentui/image/loader.ts +12 -2
- package/src/renderers/opentui/input-host.tsx +7 -49
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/sources/gloomberb-cloud/history-quality.ts +44 -0
- package/src/sources/gloomberb-cloud/index.test.ts +173 -0
- package/src/sources/gloomberb-cloud/index.ts +42 -20
- package/src/sources/gloomberb-cloud/normalizers.ts +9 -3
- package/src/sources/provider-router/cache.ts +1 -1
- package/src/sources/provider-router/document-routes.ts +10 -5
- package/src/sources/provider-router/financials.test.ts +14 -0
- package/src/sources/provider-router/financials.ts +6 -1
- package/src/sources/provider-router/history.test.ts +163 -1
- package/src/sources/provider-router/history.ts +278 -347
- package/src/sources/provider-router/index.ts +2 -2
- package/src/sources/provider-router/routing.test.ts +39 -0
- package/src/sources/provider-router/routing.ts +24 -11
- package/src/sources/provider-router/supplemental.ts +37 -17
- package/src/sources/sec-edgar/content.ts +2 -12
- package/src/sources/sec-edgar.ts +2 -13
- package/src/sources/yahoo-finance/history.ts +2 -2
- package/src/sources/yahoo-finance/options.ts +5 -0
- package/src/sources/yahoo-finance.ts +2 -2
- package/src/state/app/bootstrap.ts +2 -2
- package/src/state/app/context/index.tsx +2 -2
- package/src/state/hooks/quote-streaming.ts +66 -1
- package/src/state/hooks/session-snapshot.ts +2 -2
- package/src/state/selectors.ts +0 -9
- package/src/sync/controller.test.ts +98 -0
- package/src/sync/controller.ts +29 -5
- package/src/sync/react.ts +2 -2
- package/src/sync/types.ts +2 -2
- package/src/theme/colors.test.ts +10 -0
- package/src/theme/colors.ts +75 -78
- package/src/theme/theme-context.test.tsx +67 -0
- package/src/theme/theme-context.tsx +9 -5
- package/src/tickers/open-target.ts +2 -2
- package/src/tickers/search/upsert.ts +2 -2
- package/src/time-series/alignment.ts +0 -7
- package/src/time-series/chart-data.ts +145 -0
- package/src/time-series/date-window.ts +44 -0
- package/src/time-series/history-window.ts +21 -0
- package/src/time-series/hooks.test.ts +59 -0
- package/src/time-series/hooks.ts +33 -12
- package/src/time-series/market-resolution.ts +2 -2
- package/src/time-series/market.ts +1 -1
- package/src/time-series/range.ts +9 -0
- package/src/{components/chart/core → time-series}/resolution.ts +3 -102
- package/src/time-series/resolve.test.ts +203 -1
- package/src/time-series/resolve.ts +96 -18
- package/src/time-series/spec-studies.test.ts +15 -0
- package/src/time-series/spec.ts +2 -2
- package/src/time-series/studies.ts +5 -2
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.test.tsx +358 -0
- package/src/time-series/use-chart-resolution.ts +46 -4
- package/src/types/broker.ts +2 -2
- package/src/types/data-provider.ts +3 -3
- package/src/types/financials.ts +12 -0
- package/src/types/plugin.ts +2 -2
- package/src/ui/context-menu.tsx +3 -2
- package/src/ui/dialog.tsx +2 -2
- package/src/utils/html-entities.ts +3 -5
- package/src/utils/main-thread-monitor.ts +1 -1
- package/src/utils/price-history.ts +5 -5
- package/src/version.ts +3 -1
- package/src/components/chart/core/viewport.test.ts +0 -8
- package/src/components/chart/core/viewport.ts +0 -24
- package/src/time-series/index.ts +0 -14
- /package/src/{components/chart/core → time-series}/resolution.test.ts +0 -0
|
@@ -7,6 +7,7 @@ import { createAlert, deserializeAlerts, serializeAlerts } from "../../plugins/b
|
|
|
7
7
|
import type { AlertCondition } from "../../plugins/builtin/alerts/types";
|
|
8
8
|
import type { CliCommandDef } from "../../types/plugin";
|
|
9
9
|
import { debugLog, type LogLevel } from "../../utils/debug-log";
|
|
10
|
+
import { withCliServices, withConfigData } from "../context";
|
|
10
11
|
import { parsePositiveInt, requireArg, takeOption } from "./command-utils";
|
|
11
12
|
|
|
12
13
|
const ALERTS_PLUGIN_ID = "alerts";
|
|
@@ -51,9 +52,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
51
52
|
execute: async (_args, ctx) => {
|
|
52
53
|
let dataDir: string | null = null;
|
|
53
54
|
try {
|
|
54
|
-
|
|
55
|
-
dataDir = config.dataDir;
|
|
56
|
-
config.persistence.close();
|
|
55
|
+
dataDir = await withConfigData(ctx, ({ dataDir: configuredDataDir }) => configuredDataDir);
|
|
57
56
|
} catch {
|
|
58
57
|
dataDir = null;
|
|
59
58
|
}
|
|
@@ -74,17 +73,15 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
74
73
|
description: "Check local CLI runtime, config, plugins, and capability health",
|
|
75
74
|
execute: async (_args, ctx) => {
|
|
76
75
|
const checks: Array<{ check: string; status: string; detail: string }> = [];
|
|
77
|
-
let services: Awaited<ReturnType<typeof ctx.initServices>> | null = null;
|
|
78
76
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
await withCliServices(ctx, async (services) => {
|
|
78
|
+
checks.push({ check: "config", status: "ok", detail: services.dataDir });
|
|
79
|
+
checks.push({ check: "database", status: "ok", detail: join(services.dataDir, ".gloomberb-cache.db") });
|
|
80
|
+
checks.push({ check: "plugins", status: "ok", detail: String(services.services.pluginRegistry.allPlugins.size) });
|
|
81
|
+
checks.push({ check: "capabilities", status: "ok", detail: String(services.services.pluginRegistry.capabilities.manifests().length) });
|
|
82
|
+
});
|
|
84
83
|
} catch (error) {
|
|
85
84
|
checks.push({ check: "runtime", status: "error", detail: error instanceof Error ? error.message : String(error) });
|
|
86
|
-
} finally {
|
|
87
|
-
services?.destroy();
|
|
88
85
|
}
|
|
89
86
|
ctx.printResult({ data: checks }, {
|
|
90
87
|
columns: [
|
|
@@ -102,8 +99,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
102
99
|
help: { usage: ["config list", "config get <key>", "config set <key> <value>"] },
|
|
103
100
|
execute: async (args, ctx) => {
|
|
104
101
|
const action = args[0] ?? "list";
|
|
105
|
-
|
|
106
|
-
try {
|
|
102
|
+
await withConfigData(ctx, async (context) => {
|
|
107
103
|
const safeConfig: Record<string, unknown> = {
|
|
108
104
|
dataDir: context.config.dataDir,
|
|
109
105
|
baseCurrency: context.config.baseCurrency,
|
|
@@ -146,9 +142,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
146
142
|
return;
|
|
147
143
|
}
|
|
148
144
|
ctx.fail("Usage: gloomberb config list|get|set");
|
|
149
|
-
}
|
|
150
|
-
context.persistence.close();
|
|
151
|
-
}
|
|
145
|
+
});
|
|
152
146
|
},
|
|
153
147
|
};
|
|
154
148
|
|
|
@@ -158,8 +152,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
158
152
|
help: { usage: ["cache status", "cache clear [namespace]"] },
|
|
159
153
|
execute: async (args, ctx) => {
|
|
160
154
|
const action = args[0] ?? "status";
|
|
161
|
-
|
|
162
|
-
try {
|
|
155
|
+
await withCliServices(ctx, async (services) => {
|
|
163
156
|
if (action === "status") {
|
|
164
157
|
ctx.printResult({ data: [resourceCacheStats(services)] });
|
|
165
158
|
return;
|
|
@@ -173,9 +166,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
173
166
|
return;
|
|
174
167
|
}
|
|
175
168
|
ctx.fail("Usage: gloomberb cache status|clear");
|
|
176
|
-
}
|
|
177
|
-
services.destroy();
|
|
178
|
-
}
|
|
169
|
+
});
|
|
179
170
|
},
|
|
180
171
|
};
|
|
181
172
|
|
|
@@ -185,8 +176,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
185
176
|
description: "Inspect provider/source capability status",
|
|
186
177
|
help: { usage: ["provider status"] },
|
|
187
178
|
execute: async (_args, ctx) => {
|
|
188
|
-
|
|
189
|
-
try {
|
|
179
|
+
await withCliServices(ctx, async (services) => {
|
|
190
180
|
const disabledSources = new Set(services.config.disabledSources ?? []);
|
|
191
181
|
const rows = services.services.pluginRegistry.capabilities.manifests().map((manifest) => ({
|
|
192
182
|
id: manifest.sourceId ?? manifest.id,
|
|
@@ -203,9 +193,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
203
193
|
{ key: "operations", header: "Operations" },
|
|
204
194
|
],
|
|
205
195
|
});
|
|
206
|
-
}
|
|
207
|
-
services.destroy();
|
|
208
|
-
}
|
|
196
|
+
});
|
|
209
197
|
},
|
|
210
198
|
};
|
|
211
199
|
|
|
@@ -215,8 +203,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
215
203
|
help: { usage: ["plugin list", "plugin info <id>", "plugin enable <id>", "plugin disable <id>", "plugin doctor"] },
|
|
216
204
|
execute: async (args, ctx) => {
|
|
217
205
|
const action = args[0] ?? "list";
|
|
218
|
-
|
|
219
|
-
try {
|
|
206
|
+
await withCliServices(ctx, async (services) => {
|
|
220
207
|
const pluginRows = () => [...services.services.pluginRegistry.allPlugins.values()].map((plugin) => ({
|
|
221
208
|
id: plugin.id,
|
|
222
209
|
name: plugin.name,
|
|
@@ -255,9 +242,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
255
242
|
return;
|
|
256
243
|
}
|
|
257
244
|
ctx.fail("Usage: gloomberb plugin list|info|enable|disable|doctor");
|
|
258
|
-
}
|
|
259
|
-
services.destroy();
|
|
260
|
-
}
|
|
245
|
+
});
|
|
261
246
|
},
|
|
262
247
|
};
|
|
263
248
|
|
|
@@ -265,8 +250,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
265
250
|
name: "layout",
|
|
266
251
|
description: "Inspect saved layouts",
|
|
267
252
|
execute: async (_args, ctx) => {
|
|
268
|
-
|
|
269
|
-
try {
|
|
253
|
+
await withConfigData(ctx, async (config) => {
|
|
270
254
|
const rows = config.config.layouts.map((layout, index) => ({
|
|
271
255
|
index,
|
|
272
256
|
active: index === config.config.activeLayoutIndex,
|
|
@@ -276,9 +260,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
276
260
|
detached: layout.layout.detached.length,
|
|
277
261
|
}));
|
|
278
262
|
ctx.printResult({ data: rows });
|
|
279
|
-
}
|
|
280
|
-
config.persistence.close();
|
|
281
|
-
}
|
|
263
|
+
});
|
|
282
264
|
},
|
|
283
265
|
};
|
|
284
266
|
|
|
@@ -287,8 +269,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
287
269
|
description: "Inspect pane and pane-template inventory",
|
|
288
270
|
help: { usage: ["pane list"] },
|
|
289
271
|
execute: async (_args, ctx) => {
|
|
290
|
-
|
|
291
|
-
try {
|
|
272
|
+
await withCliServices(ctx, async (services) => {
|
|
292
273
|
const rows = [
|
|
293
274
|
...[...services.services.pluginRegistry.panes.entries()].map(([id, pane]) => ({
|
|
294
275
|
kind: "pane",
|
|
@@ -311,9 +292,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
311
292
|
{ key: "owner", header: "Owner" },
|
|
312
293
|
],
|
|
313
294
|
});
|
|
314
|
-
}
|
|
315
|
-
services.destroy();
|
|
316
|
-
}
|
|
295
|
+
});
|
|
317
296
|
},
|
|
318
297
|
};
|
|
319
298
|
|
|
@@ -322,9 +301,8 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
322
301
|
description: "Read and mutate ticker or quick notes",
|
|
323
302
|
help: { usage: ["notes show <symbol>", "notes set <symbol> TEXT", "notes delete <symbol>", "notes quick list"] },
|
|
324
303
|
execute: async (args, ctx) => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
try {
|
|
304
|
+
await withConfigData(ctx, async (config) => {
|
|
305
|
+
const notes = new NotesFiles(config.dataDir);
|
|
328
306
|
const action = args[0] ?? "list";
|
|
329
307
|
if (action === "quick") {
|
|
330
308
|
const subaction = args[1] ?? "list";
|
|
@@ -352,9 +330,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
352
330
|
return;
|
|
353
331
|
}
|
|
354
332
|
ctx.fail("Usage: gloomberb notes show|set|delete|quick");
|
|
355
|
-
}
|
|
356
|
-
config.persistence.close();
|
|
357
|
-
}
|
|
333
|
+
});
|
|
358
334
|
},
|
|
359
335
|
};
|
|
360
336
|
|
|
@@ -365,8 +341,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
365
341
|
help: { usage: ["alerts list", "alerts add <symbol> <above|below|crosses> <price>", "alerts delete <id>", "alerts rearm <id>"] },
|
|
366
342
|
execute: async (args, ctx) => {
|
|
367
343
|
const action = args[0] ?? "list";
|
|
368
|
-
|
|
369
|
-
try {
|
|
344
|
+
await withConfigData(ctx, async (config) => {
|
|
370
345
|
const raw = config.config.pluginConfig[ALERTS_PLUGIN_ID]?.[ALERTS_KEY];
|
|
371
346
|
const alerts = deserializeAlerts(typeof raw === "string" ? raw : "[]");
|
|
372
347
|
const saveAlerts = async (nextAlerts: typeof alerts) => {
|
|
@@ -413,9 +388,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
413
388
|
return;
|
|
414
389
|
}
|
|
415
390
|
ctx.fail("Usage: gloomberb alerts list|add|delete|rearm");
|
|
416
|
-
}
|
|
417
|
-
config.persistence.close();
|
|
418
|
-
}
|
|
391
|
+
});
|
|
419
392
|
},
|
|
420
393
|
};
|
|
421
394
|
|
|
@@ -485,8 +458,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
485
458
|
description: "Show CLI coverage for panes, templates, capabilities, and deferred exceptions",
|
|
486
459
|
execute: async (_args, ctx) => {
|
|
487
460
|
const commandNames = new Set(allCommands().map((command) => command.name));
|
|
488
|
-
|
|
489
|
-
try {
|
|
461
|
+
await withCliServices(ctx, async (services) => {
|
|
490
462
|
const rows = [
|
|
491
463
|
...[...services.services.pluginRegistry.panes.entries()].map(([id, pane]) => ({
|
|
492
464
|
surface: "pane",
|
|
@@ -531,9 +503,7 @@ export function createSystemCliCommands(allCommands: () => CliCommandDef[]): Cli
|
|
|
531
503
|
{ key: "label", header: "Label" },
|
|
532
504
|
],
|
|
533
505
|
});
|
|
534
|
-
}
|
|
535
|
-
services.destroy();
|
|
536
|
-
}
|
|
506
|
+
});
|
|
537
507
|
},
|
|
538
508
|
};
|
|
539
509
|
|
|
@@ -19,8 +19,8 @@ import type { NewsArticle } from "../../news/types";
|
|
|
19
19
|
import type { TickerRecord } from "../../types/ticker";
|
|
20
20
|
import type { CliCommandContext } from "../../types/plugin";
|
|
21
21
|
import { createBaseConverter } from "../base-converter";
|
|
22
|
-
import { initMarketData } from "../context";
|
|
23
|
-
import {
|
|
22
|
+
import { initMarketData, withMarketData } from "../context";
|
|
23
|
+
import { fail } from "../errors";
|
|
24
24
|
import type { MarketContext } from "../types";
|
|
25
25
|
import {
|
|
26
26
|
formatBidAsk,
|
|
@@ -40,7 +40,7 @@ const SEC_FILING_LIMIT = 5;
|
|
|
40
40
|
|
|
41
41
|
interface TickerCommandDependencies {
|
|
42
42
|
initMarketData?: () => Promise<MarketContext>;
|
|
43
|
-
|
|
43
|
+
fail?: (message: string, details?: string) => never;
|
|
44
44
|
printResult?: CliCommandContext["printResult"];
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -443,74 +443,73 @@ function buildTickerStructuredData({
|
|
|
443
443
|
|
|
444
444
|
export async function ticker(symbol: string, dependencies: TickerCommandDependencies = {}) {
|
|
445
445
|
const initMarketDataFn = dependencies.initMarketData ?? initMarketData;
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (!financials?.quote) {
|
|
465
|
-
closeAndFailCommand(persistence, `No quote data available for ${normalized}.`);
|
|
466
|
-
}
|
|
467
|
-
const resolvedFinancials = financials as TickerFinancials;
|
|
468
|
-
const quote = resolvedFinancials.quote!;
|
|
469
|
-
|
|
470
|
-
const notesFiles = new NotesFiles(dataDir);
|
|
471
|
-
const [notesResult, newsResult, secFilingsResult] = await Promise.allSettled([
|
|
472
|
-
notesFiles.load(normalized),
|
|
473
|
-
dataProvider.getNews({
|
|
474
|
-
feed: "ticker",
|
|
475
|
-
scope: "ticker",
|
|
476
|
-
ticker: normalized,
|
|
477
|
-
exchange: exchange || quote.exchangeName || "",
|
|
478
|
-
tickerTier: "primary",
|
|
479
|
-
limit: NEWS_ITEM_LIMIT,
|
|
480
|
-
}),
|
|
481
|
-
shouldFetchSecFilings(tickerFile, resolvedFinancials) && dataProvider.getSecFilings
|
|
482
|
-
? dataProvider.getSecFilings(normalized, SEC_FILING_LIMIT, exchange || quote.exchangeName || "")
|
|
483
|
-
: Promise.resolve([]),
|
|
484
|
-
]);
|
|
446
|
+
const failCommand = dependencies.fail ?? fail;
|
|
447
|
+
await withMarketData(initMarketDataFn, async ({ config, store, dataProvider, dataDir }) => {
|
|
448
|
+
const normalized = symbol.trim().toUpperCase();
|
|
449
|
+
const tickerFile = await store.loadTicker(normalized);
|
|
450
|
+
const exchange = tickerFile?.metadata.exchange ?? "";
|
|
451
|
+
const toBase = createBaseConverter(dataProvider, config.baseCurrency);
|
|
452
|
+
|
|
453
|
+
let financials: TickerFinancials | null = null;
|
|
454
|
+
try {
|
|
455
|
+
financials = await dataProvider.getTickerFinancials(normalized, exchange);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
failCommand(
|
|
458
|
+
`Failed to fetch data for ${normalized}.`,
|
|
459
|
+
error instanceof Error ? error.message : String(error),
|
|
460
|
+
);
|
|
461
|
+
}
|
|
485
462
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
463
|
+
if (!financials?.quote) {
|
|
464
|
+
failCommand(`No quote data available for ${normalized}.`);
|
|
465
|
+
}
|
|
466
|
+
const resolvedFinancials = financials as TickerFinancials;
|
|
467
|
+
const quote = resolvedFinancials.quote!;
|
|
468
|
+
|
|
469
|
+
const notesFiles = new NotesFiles(dataDir);
|
|
470
|
+
const [notesResult, newsResult, secFilingsResult] = await Promise.allSettled([
|
|
471
|
+
notesFiles.load(normalized),
|
|
472
|
+
dataProvider.getNews({
|
|
473
|
+
feed: "ticker",
|
|
474
|
+
scope: "ticker",
|
|
475
|
+
ticker: normalized,
|
|
476
|
+
exchange: exchange || quote.exchangeName || "",
|
|
477
|
+
tickerTier: "primary",
|
|
478
|
+
limit: NEWS_ITEM_LIMIT,
|
|
479
|
+
}),
|
|
480
|
+
shouldFetchSecFilings(tickerFile, resolvedFinancials) && dataProvider.getSecFilings
|
|
481
|
+
? dataProvider.getSecFilings(normalized, SEC_FILING_LIMIT, exchange || quote.exchangeName || "")
|
|
482
|
+
: Promise.resolve([]),
|
|
483
|
+
]);
|
|
484
|
+
|
|
485
|
+
const notes = notesResult.status === "fulfilled" ? notesResult.value : "";
|
|
486
|
+
const recentNews = newsResult.status === "fulfilled" ? newsResult.value : [];
|
|
487
|
+
const recentSecFilings = secFilingsResult.status === "fulfilled" ? secFilingsResult.value : [];
|
|
488
|
+
|
|
489
|
+
if (dependencies.printResult) {
|
|
490
|
+
dependencies.printResult({
|
|
491
|
+
data: buildTickerStructuredData({
|
|
492
|
+
symbol: normalized,
|
|
493
|
+
tickerFile,
|
|
494
|
+
financials: resolvedFinancials,
|
|
495
|
+
config,
|
|
496
|
+
notes,
|
|
497
|
+
recentNews,
|
|
498
|
+
recentSecFilings,
|
|
499
|
+
}),
|
|
500
|
+
});
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
489
503
|
|
|
490
|
-
|
|
491
|
-
dependencies.printResult({
|
|
492
|
-
data: buildTickerStructuredData({
|
|
504
|
+
console.log(await buildTickerReport({
|
|
493
505
|
symbol: normalized,
|
|
494
506
|
tickerFile,
|
|
495
507
|
financials: resolvedFinancials,
|
|
496
508
|
config,
|
|
509
|
+
toBase,
|
|
497
510
|
notes,
|
|
498
511
|
recentNews,
|
|
499
512
|
recentSecFilings,
|
|
500
|
-
}),
|
|
501
|
-
});
|
|
502
|
-
} else {
|
|
503
|
-
console.log(await buildTickerReport({
|
|
504
|
-
symbol: normalized,
|
|
505
|
-
tickerFile,
|
|
506
|
-
financials: resolvedFinancials,
|
|
507
|
-
config,
|
|
508
|
-
toBase,
|
|
509
|
-
notes,
|
|
510
|
-
recentNews,
|
|
511
|
-
recentSecFilings,
|
|
512
513
|
}));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
persistence.close();
|
|
514
|
+
});
|
|
516
515
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { CliCommandContext } from "../types/plugin";
|
|
3
|
+
import type { CliServicesContext, ConfigContext, MarketContext } from "./types";
|
|
4
|
+
import {
|
|
5
|
+
ensureCliServicesReady,
|
|
6
|
+
withCliServices,
|
|
7
|
+
withConfigData,
|
|
8
|
+
withMarketData,
|
|
9
|
+
} from "./context";
|
|
10
|
+
|
|
11
|
+
function initializer<K extends "initConfigData" | "initMarketData" | "initServices", T>(
|
|
12
|
+
key: K,
|
|
13
|
+
value: T,
|
|
14
|
+
): Pick<CliCommandContext, K> {
|
|
15
|
+
return { [key]: async () => value } as Pick<CliCommandContext, K>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("CLI scoped contexts", () => {
|
|
19
|
+
test("closes config and market persistence after successful operations", async () => {
|
|
20
|
+
let configCloses = 0;
|
|
21
|
+
let marketCloses = 0;
|
|
22
|
+
const config = { persistence: { close: () => configCloses++ } } as unknown as ConfigContext;
|
|
23
|
+
const market = { persistence: { close: () => marketCloses++ } } as unknown as MarketContext;
|
|
24
|
+
|
|
25
|
+
expect(await withConfigData(initializer("initConfigData", config), async () => "config"))
|
|
26
|
+
.toBe("config");
|
|
27
|
+
expect(await withMarketData(initializer("initMarketData", market), async () => "market"))
|
|
28
|
+
.toBe("market");
|
|
29
|
+
expect(configCloses).toBe(1);
|
|
30
|
+
expect(marketCloses).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("destroys services when an operation throws", async () => {
|
|
34
|
+
let destroys = 0;
|
|
35
|
+
const services = { destroy: () => destroys++ } as unknown as CliServicesContext;
|
|
36
|
+
|
|
37
|
+
await expect(withCliServices(initializer("initServices", services), async () => {
|
|
38
|
+
throw new Error("operation failed");
|
|
39
|
+
})).rejects.toThrow("operation failed");
|
|
40
|
+
expect(destroys).toBe(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("accepts an injected market initializer and closes it when an operation throws", async () => {
|
|
44
|
+
let closes = 0;
|
|
45
|
+
const market = { persistence: { close: () => closes++ } } as unknown as MarketContext;
|
|
46
|
+
|
|
47
|
+
await expect(withMarketData(async () => market, async () => {
|
|
48
|
+
throw new Error("market operation failed");
|
|
49
|
+
})).rejects.toThrow("market operation failed");
|
|
50
|
+
expect(closes).toBe(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("destroys partially initialized services when plugin setup rejects", async () => {
|
|
54
|
+
let destroys = 0;
|
|
55
|
+
const services = {
|
|
56
|
+
ready: Promise.reject(new Error("plugin setup failed")),
|
|
57
|
+
destroy: () => destroys++,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
await expect(ensureCliServicesReady(services)).rejects.toThrow("plugin setup failed");
|
|
61
|
+
expect(destroys).toBe(1);
|
|
62
|
+
});
|
|
63
|
+
});
|
package/src/cli/context.ts
CHANGED
|
@@ -14,6 +14,8 @@ import type { LoadedExternalPlugin } from "../plugins/loader";
|
|
|
14
14
|
import { fail } from "./errors";
|
|
15
15
|
import type { ConfigContext, MarketContext } from "./types";
|
|
16
16
|
|
|
17
|
+
export { withCliServices, withConfigData, withMarketData } from "./scoped-context";
|
|
18
|
+
|
|
17
19
|
interface CliContextOptions {
|
|
18
20
|
plugins?: GloomPlugin[];
|
|
19
21
|
}
|
|
@@ -22,6 +24,18 @@ interface CliServicesOptions {
|
|
|
22
24
|
externalPlugins?: LoadedExternalPlugin[];
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
export async function ensureCliServicesReady<T extends { ready: Promise<unknown>; destroy(): void }>(
|
|
28
|
+
services: T,
|
|
29
|
+
): Promise<T> {
|
|
30
|
+
try {
|
|
31
|
+
await services.ready;
|
|
32
|
+
return services;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
services.destroy();
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
function resolveCliCapabilities(config: AppConfig, plugins: GloomPlugin[]): PluginCapability[] {
|
|
26
40
|
const disabledPlugins = new Set(config.disabledPlugins ?? []);
|
|
27
41
|
const disabledSources = new Set(config.disabledSources ?? []);
|
|
@@ -83,7 +97,7 @@ export async function initCliServices(options: CliServicesOptions = {}) {
|
|
|
83
97
|
plugins: getLoadablePlugins(options.externalPlugins ?? []),
|
|
84
98
|
});
|
|
85
99
|
services.providerRouter.setConfigAccessor(() => config);
|
|
86
|
-
await services
|
|
100
|
+
await ensureCliServicesReady(services);
|
|
87
101
|
return {
|
|
88
102
|
config,
|
|
89
103
|
dataDir,
|
package/src/cli/index.ts
CHANGED
|
@@ -98,7 +98,7 @@ function createCoreCliCommands(renderHelp: () => string): CliCommandDef[] {
|
|
|
98
98
|
}
|
|
99
99
|
await ticker(symbol!, {
|
|
100
100
|
initMarketData: ctx.initMarketData,
|
|
101
|
-
|
|
101
|
+
fail: ctx.fail,
|
|
102
102
|
...(ctx.cliOptions.format === "text" ? {} : { printResult: ctx.printResult }),
|
|
103
103
|
});
|
|
104
104
|
},
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TickerFinancials } from "../../types/financials";
|
|
2
2
|
import type { TickerRecord } from "../../types/ticker";
|
|
3
|
-
|
|
4
|
-
import { subtractTimeRange } from "../../components/chart/core/date-window";
|
|
3
|
+
export { clipPriceHistoryToRange } from "../../time-series/history-window";
|
|
5
4
|
import { CHART_COMPOSER_PANE_ID, LEGACY_TICKER_DETAIL_PANE_ID } from "../../types/config";
|
|
6
5
|
import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
|
|
7
6
|
import { normalizeTickerInput } from "../../tickers/search";
|
|
@@ -102,19 +101,3 @@ export function collectShotSymbols(resolved: ResolvedPaneFunction, rawArg: strin
|
|
|
102
101
|
}
|
|
103
102
|
return [...new Set(symbols.map(cleanTickerInput).filter(Boolean))];
|
|
104
103
|
}
|
|
105
|
-
|
|
106
|
-
export function clipPriceHistoryToRange(points: PricePoint[], range: TimeRange): PricePoint[] {
|
|
107
|
-
if (range === "ALL" || points.length === 0) return points;
|
|
108
|
-
const dated = points.flatMap((point) => {
|
|
109
|
-
const date = point.date instanceof Date ? point.date : new Date(point.date);
|
|
110
|
-
return Number.isFinite(date.getTime()) ? [{ point, date }] : [];
|
|
111
|
-
});
|
|
112
|
-
const end = dated.reduce<Date | null>((latest, entry) => (
|
|
113
|
-
!latest || entry.date.getTime() > latest.getTime() ? entry.date : latest
|
|
114
|
-
), null);
|
|
115
|
-
if (!end) return [];
|
|
116
|
-
const start = subtractTimeRange(end, range);
|
|
117
|
-
return dated
|
|
118
|
-
.filter(({ date }) => date.getTime() >= start.getTime() && date.getTime() <= end.getTime())
|
|
119
|
-
.map(({ point }) => point);
|
|
120
|
-
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { extname, resolve } from "path";
|
|
2
2
|
import type { CliCommandContext } from "../../types/plugin";
|
|
3
3
|
import type { MarketContext } from "../types";
|
|
4
|
+
import { withMarketData } from "../context";
|
|
4
5
|
import {
|
|
5
6
|
filterPaneCatalogEntries,
|
|
6
7
|
renderPaneCatalogReport,
|
|
@@ -38,15 +39,15 @@ async function withPaneRuntime<T>(
|
|
|
38
39
|
}) => Promise<T>,
|
|
39
40
|
): Promise<T> {
|
|
40
41
|
const parsed = parsePaneFunctionArgs(args);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
42
|
+
return withMarketData(ctx, async (context) => {
|
|
43
|
+
const registry = await createPaneCatalog(context, ctx.plugins);
|
|
44
|
+
try {
|
|
45
|
+
const resolved = await resolvePaneFunction(registry, context, parsed);
|
|
46
|
+
return await run({ parsed, context, registry, resolved });
|
|
47
|
+
} finally {
|
|
48
|
+
registry.destroy();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
|
|
@@ -133,23 +134,23 @@ export async function runPaneCatalog(args: string[], ctx: CliCommandContext) {
|
|
|
133
134
|
...parsed,
|
|
134
135
|
limit: ctx.cliOptions.limit ?? parsed.limit,
|
|
135
136
|
};
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
await withMarketData(ctx, async (context) => {
|
|
138
|
+
const registry = await createPaneCatalog(context, ctx.plugins);
|
|
139
|
+
try {
|
|
140
|
+
const entries = await buildPaneCatalogEntries(registry, context);
|
|
141
|
+
const botSafeEntries = effectiveParsed.botSafeOnly
|
|
142
|
+
? entries.filter((entry) => entry.capability.botSafe)
|
|
143
|
+
: entries;
|
|
144
|
+
const filtered = filterPaneCatalogEntries(botSafeEntries, parsed.query);
|
|
145
|
+
if (ctx.cliOptions.format === "text") {
|
|
146
|
+
console.log(renderPaneCatalogReport(filtered, effectiveParsed));
|
|
147
|
+
} else {
|
|
148
|
+
ctx.printResult({ data: filtered.slice(0, effectiveParsed.limit) });
|
|
149
|
+
}
|
|
150
|
+
} finally {
|
|
151
|
+
registry.destroy();
|
|
148
152
|
}
|
|
149
|
-
}
|
|
150
|
-
registry.destroy();
|
|
151
|
-
context.persistence.close();
|
|
152
|
-
}
|
|
153
|
+
});
|
|
153
154
|
});
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -29,12 +29,12 @@ import type {
|
|
|
29
29
|
GraphKind,
|
|
30
30
|
GraphMetricKey,
|
|
31
31
|
} from "../../time-series/reporting";
|
|
32
|
-
import type { TimeRange } from "../../
|
|
33
|
-
import { appendLiveQuotePoint } from "../../
|
|
32
|
+
import type { TimeRange } from "../../time-series/range";
|
|
33
|
+
import { appendLiveQuotePoint } from "../../time-series/chart-data";
|
|
34
|
+
import { subtractTimeRange } from "../../time-series/date-window";
|
|
34
35
|
import {
|
|
35
36
|
buildPresetDateWindow,
|
|
36
37
|
getVisibleWindowForDateRange,
|
|
37
|
-
subtractTimeRange,
|
|
38
38
|
} from "../../components/chart/core/date-window";
|
|
39
39
|
import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
|
|
40
40
|
import { publicTickerKey } from "../../utils/exchanges";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CliCommandContext } from "../types/plugin";
|
|
2
|
+
import type { CliServicesContext, ConfigContext, MarketContext } from "./types";
|
|
3
|
+
|
|
4
|
+
type AsyncInitializer<T> = () => Promise<T>;
|
|
5
|
+
|
|
6
|
+
async function withCleanup<TContext, TResult>(
|
|
7
|
+
initialize: AsyncInitializer<TContext>,
|
|
8
|
+
cleanup: (context: TContext) => void,
|
|
9
|
+
operation: (context: TContext) => TResult | Promise<TResult>,
|
|
10
|
+
): Promise<TResult> {
|
|
11
|
+
const context = await initialize();
|
|
12
|
+
try {
|
|
13
|
+
return await operation(context);
|
|
14
|
+
} finally {
|
|
15
|
+
cleanup(context);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function withConfigData<TResult>(
|
|
20
|
+
source: Pick<CliCommandContext, "initConfigData"> | AsyncInitializer<ConfigContext>,
|
|
21
|
+
operation: (context: ConfigContext) => TResult | Promise<TResult>,
|
|
22
|
+
): Promise<TResult> {
|
|
23
|
+
const initialize = typeof source === "function" ? source : () => source.initConfigData();
|
|
24
|
+
return withCleanup(initialize, (context) => context.persistence.close(), operation);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function withMarketData<TResult>(
|
|
28
|
+
source: Pick<CliCommandContext, "initMarketData"> | AsyncInitializer<MarketContext>,
|
|
29
|
+
operation: (context: MarketContext) => TResult | Promise<TResult>,
|
|
30
|
+
): Promise<TResult> {
|
|
31
|
+
const initialize = typeof source === "function" ? source : () => source.initMarketData();
|
|
32
|
+
return withCleanup(initialize, (context) => context.persistence.close(), operation);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function withCliServices<TResult>(
|
|
36
|
+
source: Pick<CliCommandContext, "initServices"> | AsyncInitializer<CliServicesContext>,
|
|
37
|
+
operation: (context: CliServicesContext) => TResult | Promise<TResult>,
|
|
38
|
+
): Promise<TResult> {
|
|
39
|
+
const initialize = typeof source === "function" ? source : () => source.initServices();
|
|
40
|
+
return withCleanup(initialize, (context) => context.destroy(), operation);
|
|
41
|
+
}
|