opencandle 0.5.0 → 0.7.0
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 +170 -186
- package/dist/analysts/contracts.d.ts +1 -3
- package/dist/analysts/contracts.js +1 -11
- package/dist/analysts/contracts.js.map +1 -1
- package/dist/analysts/orchestrator.d.ts +1 -3
- package/dist/analysts/orchestrator.js +1 -26
- package/dist/analysts/orchestrator.js.map +1 -1
- package/dist/cli.js +66 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +13 -3
- package/dist/config.js +25 -5
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/cache.d.ts +8 -11
- package/dist/infra/cache.js +17 -15
- package/dist/infra/cache.js.map +1 -1
- package/dist/infra/http-client.d.ts +4 -1
- package/dist/infra/http-client.js +59 -6
- package/dist/infra/http-client.js.map +1 -1
- package/dist/infra/index.d.ts +2 -3
- package/dist/infra/index.js +2 -3
- package/dist/infra/index.js.map +1 -1
- package/dist/infra/native-dependencies.js +2 -2
- package/dist/infra/native-dependencies.js.map +1 -1
- package/dist/infra/node-version.js.map +1 -1
- package/dist/infra/opencandle-paths.d.ts +0 -3
- package/dist/infra/opencandle-paths.js +4 -11
- package/dist/infra/opencandle-paths.js.map +1 -1
- package/dist/infra/rate-limiter.js +12 -9
- package/dist/infra/rate-limiter.js.map +1 -1
- package/dist/market-state/alert-conditions.d.ts +34 -0
- package/dist/market-state/alert-conditions.js +23 -0
- package/dist/market-state/alert-conditions.js.map +1 -0
- package/dist/market-state/alert-runner.d.ts +55 -0
- package/dist/market-state/alert-runner.js +634 -0
- package/dist/market-state/alert-runner.js.map +1 -0
- package/dist/market-state/daily-report.d.ts +26 -0
- package/dist/market-state/daily-report.js +179 -0
- package/dist/market-state/daily-report.js.map +1 -0
- package/dist/market-state/local-automation-service.d.ts +25 -0
- package/dist/market-state/local-automation-service.js +119 -0
- package/dist/market-state/local-automation-service.js.map +1 -0
- package/dist/market-state/notification-delivery.d.ts +14 -0
- package/dist/market-state/notification-delivery.js +139 -0
- package/dist/market-state/notification-delivery.js.map +1 -0
- package/dist/market-state/resolve-for-mutation.d.ts +10 -0
- package/dist/market-state/resolve-for-mutation.js +15 -0
- package/dist/market-state/resolve-for-mutation.js.map +1 -0
- package/dist/market-state/resolve.d.ts +14 -0
- package/dist/market-state/resolve.js +89 -0
- package/dist/market-state/resolve.js.map +1 -0
- package/dist/market-state/service.d.ts +527 -0
- package/dist/market-state/service.js +1099 -0
- package/dist/market-state/service.js.map +1 -0
- package/dist/memory/index.d.ts +7 -7
- package/dist/memory/index.js +6 -6
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/manager.js +11 -11
- package/dist/memory/manager.js.map +1 -1
- package/dist/memory/retrieval.js +7 -4
- package/dist/memory/retrieval.js.map +1 -1
- package/dist/memory/sqlite.js +385 -3
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/memory/storage.js +1 -2
- package/dist/memory/storage.js.map +1 -1
- package/dist/memory/tool-defaults.js +64 -28
- package/dist/memory/tool-defaults.js.map +1 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/monitor.d.ts +2 -0
- package/dist/monitor.js +104 -0
- package/dist/monitor.js.map +1 -0
- package/dist/onboarding/connect.d.ts +2 -2
- package/dist/onboarding/connect.js +13 -8
- package/dist/onboarding/connect.js.map +1 -1
- package/dist/onboarding/credential-interceptor.js +1 -1
- package/dist/onboarding/credential-interceptor.js.map +1 -1
- package/dist/onboarding/degradation-accumulator.js +1 -3
- package/dist/onboarding/degradation-accumulator.js.map +1 -1
- package/dist/onboarding/provider-status.d.ts +48 -0
- package/dist/onboarding/provider-status.js +285 -0
- package/dist/onboarding/provider-status.js.map +1 -0
- package/dist/onboarding/providers.d.ts +85 -8
- package/dist/onboarding/providers.js +83 -18
- package/dist/onboarding/providers.js.map +1 -1
- package/dist/onboarding/state.d.ts +1 -0
- package/dist/onboarding/state.js +5 -0
- package/dist/onboarding/state.js.map +1 -1
- package/dist/onboarding/tool-helpers.js +1 -1
- package/dist/onboarding/tool-helpers.js.map +1 -1
- package/dist/onboarding/tool-tags.d.ts +12 -1
- package/dist/onboarding/tool-tags.js +37 -5
- package/dist/onboarding/tool-tags.js.map +1 -1
- package/dist/onboarding/validation.d.ts +2 -2
- package/dist/onboarding/validation.js +1 -1
- package/dist/onboarding/validation.js.map +1 -1
- package/dist/pi/opencandle-extension.d.ts +8 -0
- package/dist/pi/opencandle-extension.js +502 -42
- package/dist/pi/opencandle-extension.js.map +1 -1
- package/dist/pi/session.d.ts +1 -1
- package/dist/pi/session.js +3 -1
- package/dist/pi/session.js.map +1 -1
- package/dist/pi/setup.js +8 -3
- package/dist/pi/setup.js.map +1 -1
- package/dist/pi/tool-adapter.d.ts +4 -1
- package/dist/pi/tool-adapter.js +10 -6
- package/dist/pi/tool-adapter.js.map +1 -1
- package/dist/prompts/context-builder.d.ts +1 -1
- package/dist/prompts/context-builder.js +20 -7
- package/dist/prompts/context-builder.js.map +1 -1
- package/dist/prompts/policy-cards.d.ts +1 -1
- package/dist/prompts/policy-cards.js +2 -2
- package/dist/prompts/policy-cards.js.map +1 -1
- package/dist/prompts/sections.d.ts +1 -1
- package/dist/prompts/symbol-preflight.d.ts +20 -0
- package/dist/prompts/symbol-preflight.js +49 -0
- package/dist/prompts/symbol-preflight.js.map +1 -0
- package/dist/prompts/workflow-prompts.d.ts +1 -1
- package/dist/prompts/workflow-prompts.js +54 -16
- package/dist/prompts/workflow-prompts.js.map +1 -1
- package/dist/providers/alpha-vantage.d.ts +1 -1
- package/dist/providers/alpha-vantage.js +26 -7
- package/dist/providers/alpha-vantage.js.map +1 -1
- package/dist/providers/coingecko.js +1 -1
- package/dist/providers/coingecko.js.map +1 -1
- package/dist/providers/errors.d.ts +5 -0
- package/dist/providers/errors.js +11 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/exa-search.d.ts +2 -2
- package/dist/providers/exa-search.js +19 -11
- package/dist/providers/exa-search.js.map +1 -1
- package/dist/providers/external-tool-error.d.ts +10 -0
- package/dist/providers/external-tool-error.js +21 -0
- package/dist/providers/external-tool-error.js.map +1 -0
- package/dist/providers/fear-greed.js +1 -1
- package/dist/providers/fear-greed.js.map +1 -1
- package/dist/providers/finnhub.js +3 -5
- package/dist/providers/finnhub.js.map +1 -1
- package/dist/providers/fred.js +2 -2
- package/dist/providers/fred.js.map +1 -1
- package/dist/providers/index.d.ts +7 -6
- package/dist/providers/index.js +6 -5
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/reddit-cli.d.ts +36 -0
- package/dist/providers/reddit-cli.js +201 -0
- package/dist/providers/reddit-cli.js.map +1 -0
- package/dist/providers/reddit.d.ts +1 -1
- package/dist/providers/reddit.js +9 -37
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/sec-edgar.d.ts +1 -0
- package/dist/providers/sec-edgar.js +12 -4
- package/dist/providers/sec-edgar.js.map +1 -1
- package/dist/providers/tradingview.d.ts +47 -0
- package/dist/providers/tradingview.js +275 -0
- package/dist/providers/tradingview.js.map +1 -0
- package/dist/providers/twitter-cli.d.ts +40 -0
- package/dist/providers/twitter-cli.js +153 -0
- package/dist/providers/twitter-cli.js.map +1 -0
- package/dist/providers/twitter.d.ts +0 -8
- package/dist/providers/twitter.js +8 -60
- package/dist/providers/twitter.js.map +1 -1
- package/dist/providers/web-search.js +26 -12
- package/dist/providers/web-search.js.map +1 -1
- package/dist/providers/with-fallback.js +4 -2
- package/dist/providers/with-fallback.js.map +1 -1
- package/dist/providers/wrap-provider.d.ts +2 -3
- package/dist/providers/wrap-provider.js +44 -8
- package/dist/providers/wrap-provider.js.map +1 -1
- package/dist/providers/yahoo-finance.d.ts +1 -1
- package/dist/providers/yahoo-finance.js +153 -48
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/classify-intent.d.ts +6 -0
- package/dist/routing/classify-intent.js +78 -7
- package/dist/routing/classify-intent.js.map +1 -1
- package/dist/routing/defaults.d.ts +1 -1
- package/dist/routing/entity-extractor.d.ts +1 -0
- package/dist/routing/entity-extractor.js +234 -29
- package/dist/routing/entity-extractor.js.map +1 -1
- package/dist/routing/fund-symbols.d.ts +2 -0
- package/dist/routing/fund-symbols.js +55 -0
- package/dist/routing/fund-symbols.js.map +1 -0
- package/dist/routing/horizon.d.ts +1 -0
- package/dist/routing/horizon.js +10 -0
- package/dist/routing/horizon.js.map +1 -0
- package/dist/routing/index.d.ts +10 -10
- package/dist/routing/index.js +6 -6
- package/dist/routing/index.js.map +1 -1
- package/dist/routing/planning.d.ts +2 -2
- package/dist/routing/planning.js +65 -34
- package/dist/routing/planning.js.map +1 -1
- package/dist/routing/route-manifest.d.ts +2 -2
- package/dist/routing/route-manifest.js +25 -4
- package/dist/routing/route-manifest.js.map +1 -1
- package/dist/routing/router-llm-client.js.map +1 -1
- package/dist/routing/router-prompt.js +7 -9
- package/dist/routing/router-prompt.js.map +1 -1
- package/dist/routing/router-types.d.ts +1 -0
- package/dist/routing/router.js +137 -22
- package/dist/routing/router.js.map +1 -1
- package/dist/routing/slot-resolver.d.ts +1 -1
- package/dist/routing/slot-resolver.js +2 -4
- package/dist/routing/slot-resolver.js.map +1 -1
- package/dist/routing/symbol-disambiguator.d.ts +11 -0
- package/dist/routing/symbol-disambiguator.js +52 -0
- package/dist/routing/symbol-disambiguator.js.map +1 -0
- package/dist/routing/turn-context.d.ts +1 -1
- package/dist/routing/turn-context.js +1 -1
- package/dist/routing/turn-context.js.map +1 -1
- package/dist/routing/types.d.ts +2 -0
- package/dist/runtime/answer-contracts.d.ts +1 -1
- package/dist/runtime/answer-contracts.js +48 -9
- package/dist/runtime/answer-contracts.js.map +1 -1
- package/dist/runtime/artifact-contracts.js.map +1 -1
- package/dist/runtime/planning-evidence.js +47 -26
- package/dist/runtime/planning-evidence.js.map +1 -1
- package/dist/runtime/prompt-step.d.ts +1 -9
- package/dist/runtime/prompt-step.js +0 -10
- package/dist/runtime/prompt-step.js.map +1 -1
- package/dist/runtime/run-context.d.ts +5 -2
- package/dist/runtime/run-context.js +8 -1
- package/dist/runtime/run-context.js.map +1 -1
- package/dist/runtime/session-coordinator.d.ts +13 -5
- package/dist/runtime/session-coordinator.js +160 -20
- package/dist/runtime/session-coordinator.js.map +1 -1
- package/dist/runtime/session-title.d.ts +14 -0
- package/dist/runtime/session-title.js +50 -0
- package/dist/runtime/session-title.js.map +1 -0
- package/dist/runtime/tool-defaults-wrapper.js +7 -5
- package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
- package/dist/runtime/validation.js.map +1 -1
- package/dist/runtime/workflow-events.js.map +1 -1
- package/dist/runtime/workflow-runner.d.ts +3 -3
- package/dist/runtime/workflow-runner.js +1 -1
- package/dist/runtime/workflow-runner.js.map +1 -1
- package/dist/sentiment/adapters/finnhub.d.ts +1 -1
- package/dist/sentiment/adapters/finnhub.js +6 -1
- package/dist/sentiment/adapters/finnhub.js.map +1 -1
- package/dist/sentiment/adapters/reddit.d.ts +2 -2
- package/dist/sentiment/adapters/twitter.d.ts +1 -1
- package/dist/sentiment/adapters/web.d.ts +1 -1
- package/dist/sentiment/index.d.ts +10 -11
- package/dist/sentiment/index.js +10 -20
- package/dist/sentiment/index.js.map +1 -1
- package/dist/sentiment/insights.d.ts +17 -0
- package/dist/sentiment/insights.js +206 -0
- package/dist/sentiment/insights.js.map +1 -0
- package/dist/sentiment/keywords.js +26 -4
- package/dist/sentiment/keywords.js.map +1 -1
- package/dist/sentiment/pipeline.d.ts +2 -2
- package/dist/sentiment/pipeline.js +14 -2
- package/dist/sentiment/pipeline.js.map +1 -1
- package/dist/sentiment/scorer.d.ts +2 -0
- package/dist/sentiment/scorer.js +11 -2
- package/dist/sentiment/scorer.js.map +1 -1
- package/dist/sentiment/store.d.ts +1 -1
- package/dist/sentiment/store.js +1 -1
- package/dist/sentiment/store.js.map +1 -1
- package/dist/sentiment/trends.d.ts +1 -1
- package/dist/sentiment/trends.js.map +1 -1
- package/dist/sentiment/types.d.ts +2 -0
- package/dist/sentiment/types.js.map +1 -1
- package/dist/system-prompt.js +6 -9
- package/dist/system-prompt.js.map +1 -1
- package/dist/tool-kit.d.ts +7 -7
- package/dist/tool-kit.js +4 -4
- package/dist/tool-kit.js.map +1 -1
- package/dist/tools/fundamentals/company-overview.js +11 -6
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +18 -9
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +23 -11
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.js +8 -3
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.js +8 -3
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.js +21 -6
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.d.ts +27 -20
- package/dist/tools/index.js +55 -43
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interaction/ask-user.js +15 -3
- package/dist/tools/interaction/ask-user.js.map +1 -1
- package/dist/tools/macro/fear-greed.js.map +1 -1
- package/dist/tools/macro/fred-data.d.ts +1 -1
- package/dist/tools/macro/fred-data.js +17 -6
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +3 -1
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +3 -1
- package/dist/tools/market/crypto-price.js.map +1 -1
- package/dist/tools/market/screen-stocks.d.ts +18 -0
- package/dist/tools/market/screen-stocks.js +252 -0
- package/dist/tools/market/screen-stocks.js.map +1 -0
- package/dist/tools/market/search-ticker.js +160 -8
- package/dist/tools/market/search-ticker.js.map +1 -1
- package/dist/tools/market/stock-history.d.ts +2 -2
- package/dist/tools/market/stock-history.js +26 -7
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +5 -3
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/greeks.js +1 -1
- package/dist/tools/options/greeks.js.map +1 -1
- package/dist/tools/options/option-chain.js +19 -6
- package/dist/tools/options/option-chain.js.map +1 -1
- package/dist/tools/portfolio/alerts.d.ts +15 -0
- package/dist/tools/portfolio/alerts.js +357 -0
- package/dist/tools/portfolio/alerts.js.map +1 -0
- package/dist/tools/portfolio/correlation.d.ts +1 -1
- package/dist/tools/portfolio/correlation.js +33 -13
- package/dist/tools/portfolio/correlation.js.map +1 -1
- package/dist/tools/portfolio/daily-report.d.ts +8 -0
- package/dist/tools/portfolio/daily-report.js +83 -0
- package/dist/tools/portfolio/daily-report.js.map +1 -0
- package/dist/tools/portfolio/holdings-overlap.js +10 -3
- package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
- package/dist/tools/portfolio/notifications.d.ts +7 -0
- package/dist/tools/portfolio/notifications.js +43 -0
- package/dist/tools/portfolio/notifications.js.map +1 -0
- package/dist/tools/portfolio/predictions.d.ts +12 -6
- package/dist/tools/portfolio/predictions.js +337 -87
- package/dist/tools/portfolio/predictions.js.map +1 -1
- package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
- package/dist/tools/portfolio/risk-analysis.js +45 -6
- package/dist/tools/portfolio/risk-analysis.js.map +1 -1
- package/dist/tools/portfolio/tracker.d.ts +4 -3
- package/dist/tools/portfolio/tracker.js +246 -101
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.d.ts +6 -4
- package/dist/tools/portfolio/watchlist.js +208 -108
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/insight-format.d.ts +2 -0
- package/dist/tools/sentiment/insight-format.js +36 -0
- package/dist/tools/sentiment/insight-format.js.map +1 -0
- package/dist/tools/sentiment/query-match.d.ts +3 -0
- package/dist/tools/sentiment/query-match.js +113 -0
- package/dist/tools/sentiment/query-match.js.map +1 -0
- package/dist/tools/sentiment/reddit-sentiment.d.ts +12 -1
- package/dist/tools/sentiment/reddit-sentiment.js +266 -107
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.d.ts +9 -1
- package/dist/tools/sentiment/sentiment-summary.js +223 -205
- package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
- package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
- package/dist/tools/sentiment/sentiment-trend.js +12 -2
- package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
- package/dist/tools/sentiment/twitter-sentiment.d.ts +11 -1
- package/dist/tools/sentiment/twitter-sentiment.js +188 -58
- package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
- package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
- package/dist/tools/sentiment/untrusted-text.js +17 -0
- package/dist/tools/sentiment/untrusted-text.js.map +1 -0
- package/dist/tools/sentiment/web-search.js +9 -13
- package/dist/tools/sentiment/web-search.js.map +1 -1
- package/dist/tools/sentiment/web-sentiment.js +19 -3
- package/dist/tools/sentiment/web-sentiment.js.map +1 -1
- package/dist/tools/technical/backtest.d.ts +1 -1
- package/dist/tools/technical/backtest.js +27 -20
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +23 -5
- package/dist/tools/technical/indicators.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/market.d.ts +1 -0
- package/dist/types/portfolio.d.ts +14 -4
- package/dist/types/sentiment.d.ts +52 -0
- package/dist/workflows/compare-assets.d.ts +0 -3
- package/dist/workflows/compare-assets.js +20 -11
- package/dist/workflows/compare-assets.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -4
- package/dist/workflows/index.js +3 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/options-screener.d.ts +0 -3
- package/dist/workflows/options-screener.js +4 -11
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +0 -3
- package/dist/workflows/portfolio-builder.js +0 -8
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/gui/server/ask-user-bridge.ts +1 -1
- package/gui/server/automation-heartbeat.ts +97 -0
- package/gui/server/background-quotes.ts +97 -1
- package/gui/server/chat-event-adapter.ts +32 -10
- package/gui/server/chat-run-session.ts +16 -0
- package/gui/server/invoke-tool.ts +160 -3
- package/gui/server/live-chat-event-adapter.ts +21 -6
- package/gui/server/market-state-api.ts +315 -0
- package/gui/server/model-setup.ts +156 -2
- package/gui/server/private-api-access.ts +62 -0
- package/gui/server/projector.ts +18 -9
- package/gui/server/prompt-observation.ts +4 -7
- package/gui/server/quote-snapshot-store.ts +50 -0
- package/gui/server/server.ts +218 -451
- package/gui/server/session-actions.ts +186 -1
- package/gui/server/shutdown.ts +47 -0
- package/gui/server/tool-invoke-ack.ts +49 -0
- package/gui/server/tool-metadata.ts +101 -24
- package/gui/server/websocket.ts +13 -3
- package/gui/server/writer-lock.ts +6 -2
- package/gui/server/ws-hub.ts +311 -0
- package/gui/shared/chat-events.ts +16 -1
- package/gui/shared/event-reducer.ts +24 -6
- package/gui/web/dist/assets/CatalogOverlay-CgeY5Pkp.js +1 -0
- package/gui/web/dist/assets/index-C6W_2eAn.js +69 -0
- package/gui/web/dist/assets/index-hwbx24a5.css +1 -0
- package/gui/web/dist/index.html +2 -2
- package/package.json +9 -6
- package/src/analysts/contracts.ts +10 -23
- package/src/analysts/orchestrator.ts +8 -43
- package/src/cli.ts +76 -12
- package/src/config.ts +44 -9
- package/src/index.ts +1 -1
- package/src/infra/cache.ts +41 -30
- package/src/infra/http-client.ts +72 -6
- package/src/infra/index.ts +6 -10
- package/src/infra/native-dependencies.ts +8 -3
- package/src/infra/node-version.ts +3 -1
- package/src/infra/opencandle-paths.ts +3 -14
- package/src/infra/rate-limiter.ts +22 -19
- package/src/market-state/alert-conditions.ts +82 -0
- package/src/market-state/alert-runner.ts +863 -0
- package/src/market-state/daily-report.ts +247 -0
- package/src/market-state/local-automation-service.ts +162 -0
- package/src/market-state/notification-delivery.ts +158 -0
- package/src/market-state/resolve-for-mutation.ts +24 -0
- package/src/market-state/resolve.ts +112 -0
- package/src/market-state/service.ts +2344 -0
- package/src/memory/index.ts +7 -7
- package/src/memory/manager.ts +14 -16
- package/src/memory/retrieval.ts +8 -7
- package/src/memory/sqlite.ts +407 -6
- package/src/memory/storage.ts +5 -15
- package/src/memory/tool-defaults.ts +60 -39
- package/src/memory/types.ts +3 -3
- package/src/monitor.ts +121 -0
- package/src/onboarding/connect.ts +24 -31
- package/src/onboarding/credential-interceptor.ts +3 -15
- package/src/onboarding/degradation-accumulator.ts +1 -3
- package/src/onboarding/provider-status.ts +410 -0
- package/src/onboarding/providers.ts +144 -45
- package/src/onboarding/state.ts +13 -15
- package/src/onboarding/tool-helpers.ts +2 -9
- package/src/onboarding/tool-tags.ts +51 -8
- package/src/onboarding/validation.ts +16 -22
- package/src/pi/opencandle-extension.ts +643 -101
- package/src/pi/session.ts +7 -5
- package/src/pi/setup.ts +61 -43
- package/src/pi/tool-adapter.ts +19 -6
- package/src/prompts/context-builder.ts +24 -13
- package/src/prompts/policy-cards.ts +3 -3
- package/src/prompts/sections.ts +1 -1
- package/src/prompts/symbol-preflight.ts +80 -0
- package/src/prompts/workflow-prompts.ts +77 -28
- package/src/providers/alpha-vantage.ts +58 -39
- package/src/providers/coingecko.ts +2 -5
- package/src/providers/errors.ts +9 -0
- package/src/providers/exa-search.ts +24 -22
- package/src/providers/external-tool-error.ts +20 -0
- package/src/providers/fear-greed.ts +1 -1
- package/src/providers/finnhub.ts +7 -6
- package/src/providers/fred.ts +3 -3
- package/src/providers/index.ts +14 -6
- package/src/providers/reddit-cli.ts +317 -0
- package/src/providers/reddit.ts +14 -59
- package/src/providers/sec-edgar.ts +20 -6
- package/src/providers/tradingview.ts +399 -0
- package/src/providers/twitter-cli.ts +233 -0
- package/src/providers/twitter.ts +8 -79
- package/src/providers/web-search.ts +30 -20
- package/src/providers/with-fallback.ts +8 -7
- package/src/providers/wrap-provider.ts +49 -10
- package/src/providers/yahoo-finance.ts +204 -66
- package/src/routing/classify-intent.ts +101 -10
- package/src/routing/defaults.ts +1 -1
- package/src/routing/entity-extractor.ts +287 -38
- package/src/routing/fund-symbols.ts +58 -0
- package/src/routing/horizon.ts +7 -0
- package/src/routing/index.ts +48 -48
- package/src/routing/planning.ts +145 -53
- package/src/routing/route-manifest.ts +37 -15
- package/src/routing/router-llm-client.ts +4 -4
- package/src/routing/router-prompt.ts +15 -19
- package/src/routing/router-types.ts +2 -5
- package/src/routing/router.ts +251 -53
- package/src/routing/slot-resolver.ts +34 -11
- package/src/routing/symbol-disambiguator.ts +72 -0
- package/src/routing/turn-context.ts +6 -9
- package/src/routing/types.ts +2 -0
- package/src/runtime/answer-contracts.ts +105 -45
- package/src/runtime/artifact-contracts.ts +2 -1
- package/src/runtime/planning-evidence.ts +157 -66
- package/src/runtime/prompt-step.ts +1 -16
- package/src/runtime/run-context.ts +12 -2
- package/src/runtime/session-coordinator.ts +238 -63
- package/src/runtime/session-title.ts +60 -0
- package/src/runtime/tool-defaults-wrapper.ts +13 -5
- package/src/runtime/validation.ts +1 -4
- package/src/runtime/workflow-events.ts +7 -7
- package/src/runtime/workflow-runner.ts +5 -11
- package/src/sentiment/adapters/finnhub.ts +7 -2
- package/src/sentiment/adapters/reddit.ts +2 -2
- package/src/sentiment/adapters/twitter.ts +1 -1
- package/src/sentiment/adapters/web.ts +1 -1
- package/src/sentiment/index.ts +17 -26
- package/src/sentiment/insights.ts +269 -0
- package/src/sentiment/keywords.ts +26 -4
- package/src/sentiment/pipeline.ts +28 -5
- package/src/sentiment/scorer.ts +13 -2
- package/src/sentiment/store.ts +2 -2
- package/src/sentiment/trends.ts +9 -3
- package/src/sentiment/types.ts +8 -4
- package/src/system-prompt.ts +6 -9
- package/src/tool-kit.ts +10 -9
- package/src/tools/fundamentals/company-overview.ts +19 -9
- package/src/tools/fundamentals/comps.ts +68 -55
- package/src/tools/fundamentals/dcf.ts +145 -95
- package/src/tools/fundamentals/earnings.ts +16 -6
- package/src/tools/fundamentals/financials.ts +16 -7
- package/src/tools/fundamentals/sec-filings.ts +37 -16
- package/src/tools/index.ts +56 -43
- package/src/tools/interaction/ask-user.ts +22 -10
- package/src/tools/macro/fear-greed.ts +1 -1
- package/src/tools/macro/fred-data.ts +58 -46
- package/src/tools/market/crypto-history.ts +8 -3
- package/src/tools/market/crypto-price.ts +6 -6
- package/src/tools/market/screen-stocks.ts +279 -0
- package/src/tools/market/search-ticker.ts +218 -17
- package/src/tools/market/stock-history.ts +37 -12
- package/src/tools/market/stock-quote.ts +10 -7
- package/src/tools/options/greeks.ts +5 -5
- package/src/tools/options/option-chain.ts +41 -17
- package/src/tools/portfolio/alerts.ts +457 -0
- package/src/tools/portfolio/correlation.ts +47 -20
- package/src/tools/portfolio/daily-report.ts +101 -0
- package/src/tools/portfolio/holdings-overlap.ts +31 -15
- package/src/tools/portfolio/notifications.ts +45 -0
- package/src/tools/portfolio/predictions.ts +406 -106
- package/src/tools/portfolio/risk-analysis.ts +46 -7
- package/src/tools/portfolio/tracker.ts +270 -109
- package/src/tools/portfolio/watchlist.ts +250 -121
- package/src/tools/sentiment/insight-format.ts +50 -0
- package/src/tools/sentiment/query-match.ts +117 -0
- package/src/tools/sentiment/reddit-sentiment.ts +360 -121
- package/src/tools/sentiment/sentiment-summary.ts +302 -235
- package/src/tools/sentiment/sentiment-trend.ts +24 -7
- package/src/tools/sentiment/twitter-sentiment.ts +264 -73
- package/src/tools/sentiment/untrusted-text.ts +21 -0
- package/src/tools/sentiment/web-search.ts +21 -18
- package/src/tools/sentiment/web-sentiment.ts +30 -10
- package/src/tools/technical/backtest.ts +32 -22
- package/src/tools/technical/indicators.ts +39 -14
- package/src/types/index.ts +8 -3
- package/src/types/market.ts +1 -0
- package/src/types/portfolio.ts +14 -4
- package/src/types/sentiment.ts +61 -2
- package/src/workflows/compare-assets.ts +33 -21
- package/src/workflows/index.ts +3 -4
- package/src/workflows/options-screener.ts +27 -29
- package/src/workflows/portfolio-builder.ts +34 -27
- package/dist/infra/browser.d.ts +0 -35
- package/dist/infra/browser.js +0 -103
- package/dist/infra/browser.js.map +0 -1
- package/dist/tools/interaction/twitter-login.d.ts +0 -8
- package/dist/tools/interaction/twitter-login.js +0 -77
- package/dist/tools/interaction/twitter-login.js.map +0 -1
- package/dist/workflows/types.d.ts +0 -4
- package/dist/workflows/types.js +0 -2
- package/dist/workflows/types.js.map +0 -1
- package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
- package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
- package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
- package/src/infra/browser.ts +0 -111
- package/src/tools/interaction/twitter-login.ts +0 -93
- package/src/workflows/types.ts +0 -4
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { ExternalToolError, ExternalToolNotInstalled } from "./external-tool-error.js";
|
|
3
|
+
import type { RedditComment } from "./reddit.js";
|
|
4
|
+
|
|
5
|
+
const RDT_BINARY = "rdt";
|
|
6
|
+
const RDT_INSTALL_CMD = "uv tool install rdt-cli";
|
|
7
|
+
const COMMAND_TIMEOUT_MS = 20_000;
|
|
8
|
+
const MAX_OUTPUT_CHARS = 2_000_000;
|
|
9
|
+
|
|
10
|
+
interface RdtEnvelope<T> {
|
|
11
|
+
readonly ok: boolean;
|
|
12
|
+
readonly schema_version: string;
|
|
13
|
+
readonly data?: T;
|
|
14
|
+
readonly error?: {
|
|
15
|
+
readonly code?: string;
|
|
16
|
+
readonly message?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RdtPost {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly title: string;
|
|
23
|
+
readonly selftext: string;
|
|
24
|
+
readonly author: string;
|
|
25
|
+
readonly score: number;
|
|
26
|
+
readonly comments: number;
|
|
27
|
+
readonly subreddit?: string;
|
|
28
|
+
readonly url: string;
|
|
29
|
+
readonly permalink?: string;
|
|
30
|
+
readonly created: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface RawRdtPost {
|
|
34
|
+
readonly id?: unknown;
|
|
35
|
+
readonly title?: unknown;
|
|
36
|
+
readonly selftext?: unknown;
|
|
37
|
+
readonly author?: unknown;
|
|
38
|
+
readonly score?: unknown;
|
|
39
|
+
readonly num_comments?: unknown;
|
|
40
|
+
readonly subreddit?: unknown;
|
|
41
|
+
readonly url?: unknown;
|
|
42
|
+
readonly permalink?: unknown;
|
|
43
|
+
readonly created_utc?: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface RawRdtComment {
|
|
47
|
+
readonly id?: unknown;
|
|
48
|
+
readonly body?: unknown;
|
|
49
|
+
readonly author?: unknown;
|
|
50
|
+
readonly score?: unknown;
|
|
51
|
+
readonly permalink?: unknown;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface RdtListingChild<T> {
|
|
55
|
+
readonly kind?: string;
|
|
56
|
+
readonly data?: T;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface RdtListing<T> {
|
|
60
|
+
readonly data?: {
|
|
61
|
+
readonly children?: Array<RdtListingChild<T>>;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface CommandResult {
|
|
66
|
+
readonly code: number | null;
|
|
67
|
+
readonly stdout: string;
|
|
68
|
+
readonly stderr: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type RdtCommandRunner = (command: string, args: readonly string[]) => Promise<CommandResult>;
|
|
72
|
+
|
|
73
|
+
let commandRunner: RdtCommandRunner = runCommand;
|
|
74
|
+
|
|
75
|
+
export function setRdtCommandRunnerForTests(runner: RdtCommandRunner): void {
|
|
76
|
+
commandRunner = runner;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function resetRdtCommandRunnerForTests(): void {
|
|
80
|
+
commandRunner = runCommand;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function searchRedditPosts(
|
|
84
|
+
query: string,
|
|
85
|
+
opts: { subreddit?: string; limit: number },
|
|
86
|
+
): Promise<RdtPost[]> {
|
|
87
|
+
const args = ["search", query];
|
|
88
|
+
if (opts.subreddit) {
|
|
89
|
+
args.push("--subreddit", opts.subreddit);
|
|
90
|
+
}
|
|
91
|
+
args.push("--json", "--compact", "-n", String(opts.limit));
|
|
92
|
+
const data = await runRdt<RawRdtPost[]>(args);
|
|
93
|
+
if (!Array.isArray(data)) {
|
|
94
|
+
throw new ExternalToolError(RDT_BINARY, "rdt-cli returned invalid search data");
|
|
95
|
+
}
|
|
96
|
+
return data.map(adaptPost).filter((post) => post.id.length > 0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function listSubredditPosts(
|
|
100
|
+
subreddit: string,
|
|
101
|
+
opts: { limit: number },
|
|
102
|
+
): Promise<RdtPost[]> {
|
|
103
|
+
const data = await runRdt<RawRdtPost[]>([
|
|
104
|
+
"sub",
|
|
105
|
+
subreddit,
|
|
106
|
+
"--json",
|
|
107
|
+
"--compact",
|
|
108
|
+
"-n",
|
|
109
|
+
String(opts.limit),
|
|
110
|
+
]);
|
|
111
|
+
if (!Array.isArray(data)) {
|
|
112
|
+
throw new ExternalToolError(RDT_BINARY, "rdt-cli returned invalid subreddit data");
|
|
113
|
+
}
|
|
114
|
+
return data.map((raw) => adaptPost({ ...raw, subreddit })).filter((post) => post.id.length > 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function readRedditPost(
|
|
118
|
+
postId: string,
|
|
119
|
+
opts: { limit: number },
|
|
120
|
+
): Promise<{ post: RdtPost; comments: RedditComment[] }> {
|
|
121
|
+
const data = await runRdt<Array<RdtListing<RawRdtPost | RawRdtComment>>>([
|
|
122
|
+
"read",
|
|
123
|
+
postId,
|
|
124
|
+
"--json",
|
|
125
|
+
"-n",
|
|
126
|
+
String(opts.limit),
|
|
127
|
+
]);
|
|
128
|
+
if (!Array.isArray(data)) {
|
|
129
|
+
throw new ExternalToolError(RDT_BINARY, "rdt-cli returned invalid read data");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const rawPost = data[0]?.data?.children?.find((child) => child.kind === "t3")?.data;
|
|
133
|
+
if (!rawPost) {
|
|
134
|
+
throw new ExternalToolError(RDT_BINARY, "rdt-cli read output did not include a post");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const post = adaptPost(rawPost as RawRdtPost);
|
|
138
|
+
const commentListing = data[1]?.data?.children ?? [];
|
|
139
|
+
const comments = commentListing
|
|
140
|
+
.filter((child): child is RdtListingChild<RawRdtComment> => child.kind === "t1")
|
|
141
|
+
.map((child) => adaptComment(child.data, post.permalink))
|
|
142
|
+
.filter((comment): comment is RedditComment => comment !== null)
|
|
143
|
+
.sort((a, b) => b.score - a.score)
|
|
144
|
+
.slice(0, opts.limit);
|
|
145
|
+
|
|
146
|
+
return { post, comments };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function runRdt<T>(args: readonly string[]): Promise<T> {
|
|
150
|
+
let result: CommandResult;
|
|
151
|
+
try {
|
|
152
|
+
result = await commandRunner(RDT_BINARY, args);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
const nodeError = err as NodeJS.ErrnoException;
|
|
155
|
+
if (nodeError.code === "ENOENT") {
|
|
156
|
+
throw new ExternalToolNotInstalled(RDT_BINARY, RDT_INSTALL_CMD);
|
|
157
|
+
}
|
|
158
|
+
throw new ExternalToolError(
|
|
159
|
+
RDT_BINARY,
|
|
160
|
+
redactSensitiveOutput(err instanceof Error ? err.message : String(err)),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (result.code !== 0) {
|
|
165
|
+
const envelopeError = parseCliErrorEnvelope(result.stdout);
|
|
166
|
+
if (envelopeError) {
|
|
167
|
+
throw new ExternalToolError(
|
|
168
|
+
RDT_BINARY,
|
|
169
|
+
redactSensitiveOutput(envelopeError.message),
|
|
170
|
+
envelopeError.code,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
throw new ExternalToolError(
|
|
174
|
+
RDT_BINARY,
|
|
175
|
+
redactSensitiveOutput(result.stderr.trim() || `rdt-cli exited with code ${result.code}`),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let envelope: RdtEnvelope<T>;
|
|
180
|
+
try {
|
|
181
|
+
envelope = JSON.parse(result.stdout) as RdtEnvelope<T>;
|
|
182
|
+
} catch {
|
|
183
|
+
throw new ExternalToolError(
|
|
184
|
+
RDT_BINARY,
|
|
185
|
+
`rdt-cli returned non-JSON output: ${redactSensitiveOutput(result.stdout.slice(0, 200))}`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!envelope.ok) {
|
|
190
|
+
throw new ExternalToolError(
|
|
191
|
+
RDT_BINARY,
|
|
192
|
+
redactSensitiveOutput(envelope.error?.message ?? "rdt-cli returned an error"),
|
|
193
|
+
envelope.error?.code,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (envelope.data === undefined) {
|
|
197
|
+
throw new ExternalToolError(RDT_BINARY, "rdt-cli returned no data");
|
|
198
|
+
}
|
|
199
|
+
return envelope.data;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseCliErrorEnvelope(stdout: string): { code?: string; message: string } | null {
|
|
203
|
+
try {
|
|
204
|
+
const parsed = JSON.parse(stdout) as {
|
|
205
|
+
ok?: unknown;
|
|
206
|
+
error?: { code?: unknown; message?: unknown };
|
|
207
|
+
};
|
|
208
|
+
if (parsed.ok !== false || typeof parsed.error?.message !== "string") return null;
|
|
209
|
+
return {
|
|
210
|
+
code: typeof parsed.error.code === "string" ? parsed.error.code : undefined,
|
|
211
|
+
message: parsed.error.message,
|
|
212
|
+
};
|
|
213
|
+
} catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function adaptPost(raw: RawRdtPost): RdtPost {
|
|
219
|
+
const permalink = stringValue(raw.permalink);
|
|
220
|
+
const url = stringValue(raw.url) || (permalink ? `https://reddit.com${permalink}` : "");
|
|
221
|
+
return {
|
|
222
|
+
id: stringValue(raw.id),
|
|
223
|
+
title: stringValue(raw.title),
|
|
224
|
+
selftext: stringValue(raw.selftext),
|
|
225
|
+
author: stringValue(raw.author) || "unknown",
|
|
226
|
+
score: numberValue(raw.score),
|
|
227
|
+
comments: numberValue(raw.num_comments),
|
|
228
|
+
subreddit: stringValue(raw.subreddit) || undefined,
|
|
229
|
+
url,
|
|
230
|
+
permalink: permalink || undefined,
|
|
231
|
+
created: normalizeCreatedAt(raw.created_utc),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function adaptComment(
|
|
236
|
+
raw: RawRdtComment | undefined,
|
|
237
|
+
postPermalink?: string,
|
|
238
|
+
): RedditComment | null {
|
|
239
|
+
if (!raw || typeof raw.body !== "string" || raw.body.length === 0) return null;
|
|
240
|
+
const permalink = stringValue(raw.permalink) || postPermalink || "";
|
|
241
|
+
return {
|
|
242
|
+
id: stringValue(raw.id),
|
|
243
|
+
body: raw.body,
|
|
244
|
+
author: stringValue(raw.author) || "unknown",
|
|
245
|
+
score: numberValue(raw.score),
|
|
246
|
+
permalink: permalink ? `https://reddit.com${permalink}` : "",
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function normalizeCreatedAt(value: unknown): string {
|
|
251
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
252
|
+
const millis = value > 1_000_000_000_000 ? value : value * 1000;
|
|
253
|
+
return new Date(millis).toISOString();
|
|
254
|
+
}
|
|
255
|
+
if (typeof value === "string" && value.length > 0) {
|
|
256
|
+
const millis = Date.parse(value);
|
|
257
|
+
if (!Number.isNaN(millis)) return new Date(millis).toISOString();
|
|
258
|
+
}
|
|
259
|
+
return new Date(0).toISOString();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function stringValue(value: unknown): string {
|
|
263
|
+
return typeof value === "string" ? value : "";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function numberValue(value: unknown): number {
|
|
267
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function redactSensitiveOutput(input: string): string {
|
|
271
|
+
return input
|
|
272
|
+
.slice(0, MAX_OUTPUT_CHARS)
|
|
273
|
+
.replace(
|
|
274
|
+
/\b([a-z0-9_]*(?:cookie|session|token)[a-z0-9_]*)\b\s*[:=]\s*[^;\s,)]+/gi,
|
|
275
|
+
"$1=[redacted]",
|
|
276
|
+
)
|
|
277
|
+
.replace(/\b([a-z0-9_]*(?:cookie|session|token)[a-z0-9_]*)=([^;\s,)]+)/gi, "$1=[redacted]")
|
|
278
|
+
.replace(
|
|
279
|
+
/(?:~|\/Users\/[^/\s]+|\/home\/[^/\s]+)?\/\.config\/rdt-cli\/credential\.json/g,
|
|
280
|
+
"[redacted-credential-path]",
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function runCommand(command: string, args: readonly string[]): Promise<CommandResult> {
|
|
285
|
+
return new Promise((resolve, reject) => {
|
|
286
|
+
const child = spawn(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
|
|
287
|
+
let stdout = "";
|
|
288
|
+
let stderr = "";
|
|
289
|
+
let settled = false;
|
|
290
|
+
|
|
291
|
+
const timeout = setTimeout(() => {
|
|
292
|
+
if (settled) return;
|
|
293
|
+
settled = true;
|
|
294
|
+
child.kill("SIGTERM");
|
|
295
|
+
reject(new Error(`${command} timed out after ${COMMAND_TIMEOUT_MS}ms`));
|
|
296
|
+
}, COMMAND_TIMEOUT_MS);
|
|
297
|
+
|
|
298
|
+
child.stdout.on("data", (chunk: Buffer) => {
|
|
299
|
+
stdout = (stdout + chunk.toString("utf8")).slice(0, MAX_OUTPUT_CHARS);
|
|
300
|
+
});
|
|
301
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
302
|
+
stderr = (stderr + chunk.toString("utf8")).slice(0, MAX_OUTPUT_CHARS);
|
|
303
|
+
});
|
|
304
|
+
child.on("error", (err) => {
|
|
305
|
+
if (settled) return;
|
|
306
|
+
settled = true;
|
|
307
|
+
clearTimeout(timeout);
|
|
308
|
+
reject(err);
|
|
309
|
+
});
|
|
310
|
+
child.on("close", (code) => {
|
|
311
|
+
if (settled) return;
|
|
312
|
+
settled = true;
|
|
313
|
+
clearTimeout(timeout);
|
|
314
|
+
resolve({ code, stdout, stderr });
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
}
|
package/src/providers/reddit.ts
CHANGED
|
@@ -1,53 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
|
|
1
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
3
2
|
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
3
|
+
import { BEARISH_TERMS, BULLISH_TERMS } from "../sentiment/keywords.js";
|
|
4
4
|
import type { RedditSentimentResult } from "../types/sentiment.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
interface RedditListingResponse {
|
|
8
|
-
data: {
|
|
9
|
-
children: Array<{
|
|
10
|
-
data: {
|
|
11
|
-
id: string;
|
|
12
|
-
title: string;
|
|
13
|
-
selftext: string;
|
|
14
|
-
author: string;
|
|
15
|
-
score: number;
|
|
16
|
-
num_comments: number;
|
|
17
|
-
permalink: string;
|
|
18
|
-
created_utc: number;
|
|
19
|
-
};
|
|
20
|
-
}>;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const REDDIT_HEADERS = { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" };
|
|
5
|
+
import { listSubredditPosts, readRedditPost, searchRedditPosts } from "./reddit-cli.js";
|
|
25
6
|
|
|
26
7
|
export async function getSubredditPosts(
|
|
27
8
|
subreddit: string,
|
|
28
9
|
limit: number = 25,
|
|
10
|
+
query?: string,
|
|
29
11
|
): Promise<RedditSentimentResult> {
|
|
30
|
-
const cacheKey = `reddit:${subreddit}:${limit}`;
|
|
12
|
+
const cacheKey = `reddit:${subreddit}:${query ?? "hot"}:${limit}`;
|
|
31
13
|
const cached = cache.get<RedditSentimentResult>(cacheKey);
|
|
32
14
|
if (cached) return cached;
|
|
33
15
|
|
|
34
16
|
try {
|
|
35
17
|
await rateLimiter.acquire("reddit");
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const posts = data.data.children.map((child) => ({
|
|
42
|
-
id: child.data.id,
|
|
43
|
-
title: child.data.title,
|
|
44
|
-
selftext: child.data.selftext ?? "",
|
|
45
|
-
author: child.data.author ?? "unknown",
|
|
46
|
-
score: child.data.score,
|
|
47
|
-
comments: child.data.num_comments,
|
|
48
|
-
url: `https://reddit.com${child.data.permalink}`,
|
|
49
|
-
created: new Date(child.data.created_utc * 1000).toISOString(),
|
|
50
|
-
}));
|
|
18
|
+
const posts = query
|
|
19
|
+
? await searchRedditPosts(query, { subreddit, limit })
|
|
20
|
+
: await listSubredditPosts(subreddit, { limit });
|
|
51
21
|
|
|
52
22
|
// Extract ticker-like mentions ($AAPL, $TSLA, etc.)
|
|
53
23
|
const tickerRegex = /\$([A-Z]{1,5})\b/g;
|
|
@@ -107,24 +77,7 @@ export async function getPostComments(
|
|
|
107
77
|
if (cached) return cached;
|
|
108
78
|
|
|
109
79
|
await rateLimiter.acquire("reddit_comments");
|
|
110
|
-
const
|
|
111
|
-
const data = await httpGet<Array<{ data: { children: Array<{ kind: string; data: { id: string; body?: string; author?: string; score?: number; permalink?: string } }> } }>>(url, {
|
|
112
|
-
headers: REDDIT_HEADERS,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Comments are in the second listing element
|
|
116
|
-
const commentListing = data[1]?.data?.children ?? [];
|
|
117
|
-
const comments: RedditComment[] = commentListing
|
|
118
|
-
.filter((c) => c.kind === "t1" && c.data.body)
|
|
119
|
-
.sort((a, b) => (b.data.score ?? 0) - (a.data.score ?? 0))
|
|
120
|
-
.slice(0, limit)
|
|
121
|
-
.map((c) => ({
|
|
122
|
-
id: c.data.id,
|
|
123
|
-
body: c.data.body!,
|
|
124
|
-
author: c.data.author ?? "unknown",
|
|
125
|
-
score: c.data.score ?? 0,
|
|
126
|
-
permalink: `https://reddit.com${c.data.permalink ?? ""}`,
|
|
127
|
-
}));
|
|
80
|
+
const { comments } = await readRedditPost(postId, { limit });
|
|
128
81
|
|
|
129
82
|
cache.set(cacheKey, comments, COMMENT_TTL);
|
|
130
83
|
return comments;
|
|
@@ -132,9 +85,11 @@ export async function getPostComments(
|
|
|
132
85
|
|
|
133
86
|
// ── Sentiment scoring ───────────────────────────────────
|
|
134
87
|
|
|
135
|
-
export function scoreSentiment(
|
|
136
|
-
|
|
137
|
-
|
|
88
|
+
export function scoreSentiment(posts: Array<{ title: string }>): {
|
|
89
|
+
score: number;
|
|
90
|
+
bullish: number;
|
|
91
|
+
bearish: number;
|
|
92
|
+
} {
|
|
138
93
|
let bullish = 0;
|
|
139
94
|
let bearish = 0;
|
|
140
95
|
for (const post of posts) {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { httpGet } from "../infra/http-client.js";
|
|
2
1
|
import { cache, TTL } from "../infra/cache.js";
|
|
2
|
+
import { httpGet } from "../infra/http-client.js";
|
|
3
|
+
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
3
4
|
|
|
4
5
|
const EFTS_BASE = "https://efts.sec.gov/LATEST/search-index";
|
|
5
6
|
const COMPANY_TICKERS_URL = "https://www.sec.gov/files/company_tickers.json";
|
|
6
7
|
const SUBMISSIONS_BASE = "https://data.sec.gov/submissions";
|
|
8
|
+
const SEC_DOCUMENT_FETCH_TIMEOUT_MS = 10_000;
|
|
7
9
|
|
|
8
10
|
export interface SECFiling {
|
|
9
11
|
formType: string;
|
|
@@ -15,6 +17,7 @@ export interface SECFiling {
|
|
|
15
17
|
primaryDocumentUrl?: string;
|
|
16
18
|
items?: string[];
|
|
17
19
|
evidenceSnippets?: string[];
|
|
20
|
+
evidenceWarning?: string;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export interface SearchFilingsOptions {
|
|
@@ -71,7 +74,9 @@ export async function searchFilings(
|
|
|
71
74
|
if (cached) return cached;
|
|
72
75
|
|
|
73
76
|
if (options.includeSnippets) {
|
|
74
|
-
const submissions = await searchFilingsFromCompanySubmissions(ticker, formTypes, limit).catch(
|
|
77
|
+
const submissions = await searchFilingsFromCompanySubmissions(ticker, formTypes, limit).catch(
|
|
78
|
+
() => [],
|
|
79
|
+
);
|
|
75
80
|
if (submissions.length > 0) {
|
|
76
81
|
await enrichWithEvidenceSnippets(submissions, options.snippetLimitPerFiling ?? 3);
|
|
77
82
|
cache.set(cacheKey, submissions, TTL.FUNDAMENTALS);
|
|
@@ -186,7 +191,10 @@ async function resolveCompanyTicker(ticker: string): Promise<CompanyTickerEntry
|
|
|
186
191
|
|
|
187
192
|
function splitFilingItems(raw: string | undefined): string[] {
|
|
188
193
|
return raw
|
|
189
|
-
? raw
|
|
194
|
+
? raw
|
|
195
|
+
.split(",")
|
|
196
|
+
.map((item) => item.trim())
|
|
197
|
+
.filter(Boolean)
|
|
190
198
|
: [];
|
|
191
199
|
}
|
|
192
200
|
|
|
@@ -222,23 +230,29 @@ function getDateYearsAgo(years: number): string {
|
|
|
222
230
|
return d.toISOString().split("T")[0];
|
|
223
231
|
}
|
|
224
232
|
|
|
225
|
-
async function enrichWithEvidenceSnippets(
|
|
233
|
+
async function enrichWithEvidenceSnippets(
|
|
234
|
+
filings: SECFiling[],
|
|
235
|
+
limitPerFiling: number,
|
|
236
|
+
): Promise<void> {
|
|
226
237
|
await Promise.all(
|
|
227
238
|
filings.map(async (filing) => {
|
|
228
239
|
if (!filing.primaryDocumentUrl) return;
|
|
229
240
|
try {
|
|
230
241
|
const raw = await fetchText(filing.primaryDocumentUrl);
|
|
231
242
|
filing.evidenceSnippets = extractEvidenceSnippets(raw, limitPerFiling);
|
|
232
|
-
} catch {
|
|
243
|
+
} catch (error) {
|
|
233
244
|
filing.evidenceSnippets = [];
|
|
245
|
+
filing.evidenceWarning = `Evidence fetch failed for primary document: ${error instanceof Error ? error.message : String(error)}`;
|
|
234
246
|
}
|
|
235
247
|
}),
|
|
236
248
|
);
|
|
237
249
|
}
|
|
238
250
|
|
|
239
251
|
async function fetchText(url: string): Promise<string> {
|
|
252
|
+
await rateLimiter.acquire("sec_edgar");
|
|
240
253
|
const response = await fetch(url, {
|
|
241
254
|
headers: { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" },
|
|
255
|
+
signal: AbortSignal.timeout(SEC_DOCUMENT_FETCH_TIMEOUT_MS),
|
|
242
256
|
});
|
|
243
257
|
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
244
258
|
return response.text();
|
|
@@ -300,7 +314,7 @@ function stripFilingMarkup(raw: string): string {
|
|
|
300
314
|
.replace(/&/gi, "&")
|
|
301
315
|
.replace(/ /g, " ")
|
|
302
316
|
.replace(/’/g, "'")
|
|
303
|
-
.replace(/“|”/g, "
|
|
317
|
+
.replace(/“|”/g, '"')
|
|
304
318
|
.replace(/\s+/g, " ")
|
|
305
319
|
.trim();
|
|
306
320
|
}
|