aria-code 0.167.0__py3-none-win_amd64.whl
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.
- aria_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +49 -0
- aria_code/agents/base.py +329 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +168 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +723 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +239 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +16 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1985 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sec_statements.py +217 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +459 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +58 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +304 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.167.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,813 @@
|
|
|
1
|
+
"""DiagnosticOpsCommandsMixin — bug, accuracy, cost, todo, doctor, datasource commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ._ui import Panel, print_error, rich_box
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import pathlib
|
|
10
|
+
|
|
11
|
+
# Status glyph + colour per architecture-layer status.
|
|
12
|
+
_ARCH_ICON = {
|
|
13
|
+
"done": ("✓", "#3fb950"),
|
|
14
|
+
"partial": ("◐", "#d29922"),
|
|
15
|
+
"planned": ("○", "dim"),
|
|
16
|
+
"blocked": ("✗", "#f85149"),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_architecture_report(layers, counts, *, gaps_only: bool = False,
|
|
21
|
+
rich: bool = True) -> list:
|
|
22
|
+
"""Render the architecture contract as display lines (pure / testable).
|
|
23
|
+
|
|
24
|
+
``layers`` are ArchitectureLayer objects; ``counts`` is the status→n map.
|
|
25
|
+
With ``gaps_only`` the DONE layers are dropped so only work-to-do shows.
|
|
26
|
+
"""
|
|
27
|
+
total = sum(counts.values()) or len(layers)
|
|
28
|
+
done = counts.get("done", 0)
|
|
29
|
+
lines = []
|
|
30
|
+
if rich:
|
|
31
|
+
lines.append(f"[bold]架构契约[/bold] [dim]{done}/{total} 层完成[/dim]")
|
|
32
|
+
lines.append(" " + " ".join(
|
|
33
|
+
f"[{_ARCH_ICON[s][1]}]{_ARCH_ICON[s][0]}[/{_ARCH_ICON[s][1]}] {s} {counts.get(s, 0)}"
|
|
34
|
+
for s in ("done", "partial", "planned", "blocked")
|
|
35
|
+
))
|
|
36
|
+
else:
|
|
37
|
+
lines.append(f"架构契约 {done}/{total} 层完成")
|
|
38
|
+
lines.append("")
|
|
39
|
+
for layer in layers:
|
|
40
|
+
st = getattr(layer.status, "value", str(layer.status))
|
|
41
|
+
if gaps_only and st == "done":
|
|
42
|
+
continue
|
|
43
|
+
icon, color = _ARCH_ICON.get(st, ("•", "dim"))
|
|
44
|
+
if rich:
|
|
45
|
+
lines.append(f"[{color}]{icon}[/{color}] [bold]{layer.name}[/bold] "
|
|
46
|
+
f"[dim]{layer.responsibility}[/dim]")
|
|
47
|
+
else:
|
|
48
|
+
lines.append(f"{icon} {layer.name} {layer.responsibility}")
|
|
49
|
+
if st != "done":
|
|
50
|
+
for ns in (layer.next_steps or [])[:2]:
|
|
51
|
+
lines.append(f" → {ns}")
|
|
52
|
+
for bl in (layer.blockers or [])[:1]:
|
|
53
|
+
lines.append(f" [#f85149]⚠ {bl}[/#f85149]" if rich else f" ⚠ {bl}")
|
|
54
|
+
return lines
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
import json
|
|
58
|
+
import asyncio
|
|
59
|
+
import datetime
|
|
60
|
+
import time
|
|
61
|
+
import shlex
|
|
62
|
+
from typing import Dict, Any, Optional
|
|
63
|
+
|
|
64
|
+
def _test_datasource(*args, **kwargs):
|
|
65
|
+
from aria_cli import _test_datasource as fn
|
|
66
|
+
return fn(*args, **kwargs)
|
|
67
|
+
def _get_ARIA_TOOLS():
|
|
68
|
+
# Owned by apps/cli/tool_registry.py; aria_cli fills it in place.
|
|
69
|
+
from ..tool_registry import ARIA_TOOLS as val
|
|
70
|
+
return val
|
|
71
|
+
def get_model_cfg(*args, **kwargs):
|
|
72
|
+
from ..model_catalog import get_model_cfg as fn
|
|
73
|
+
return fn(*args, **kwargs)
|
|
74
|
+
def _get_LOCAL_TOOLS():
|
|
75
|
+
# Owned by apps/cli/tool_registry.py; aria_cli fills it in place.
|
|
76
|
+
from ..tool_registry import LOCAL_TOOLS as val
|
|
77
|
+
return val
|
|
78
|
+
def _get___version__():
|
|
79
|
+
from aria_code._version import __version__ as val
|
|
80
|
+
return val
|
|
81
|
+
def _get_Panel():
|
|
82
|
+
# Resolved locally now — see ._ui; no aria_cli round-trip.
|
|
83
|
+
return Panel
|
|
84
|
+
def _get_provider_key(*args, **kwargs):
|
|
85
|
+
from aria_code.apps.cli.provider_keys import get_provider_key as fn
|
|
86
|
+
return fn(*args, **kwargs)
|
|
87
|
+
def _get_rich_box():
|
|
88
|
+
# Resolved locally now — see ._ui; no aria_cli round-trip.
|
|
89
|
+
return rich_box
|
|
90
|
+
def _get__HAS_MCP():
|
|
91
|
+
from aria_code.apps.cli._optional import HAS_MCP as val
|
|
92
|
+
return val
|
|
93
|
+
def _get_CONFIG_DIR():
|
|
94
|
+
from aria_cli import CONFIG_DIR as val
|
|
95
|
+
return val
|
|
96
|
+
|
|
97
|
+
import json
|
|
98
|
+
import asyncio
|
|
99
|
+
import datetime
|
|
100
|
+
import time
|
|
101
|
+
import shlex
|
|
102
|
+
import sys
|
|
103
|
+
import os
|
|
104
|
+
from typing import Dict, Any, Optional
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
import json
|
|
108
|
+
import asyncio
|
|
109
|
+
import datetime
|
|
110
|
+
import time
|
|
111
|
+
import shlex
|
|
112
|
+
import sys
|
|
113
|
+
import os
|
|
114
|
+
from typing import Dict, Any, Optional
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class DiagnosticOpsCommandsMixin:
|
|
118
|
+
"""Mixin: diagnostics, feedback, usage, and source inspection commands."""
|
|
119
|
+
|
|
120
|
+
def cmd_architecture(self, args: str):
|
|
121
|
+
"""显示分层架构契约(各层状态 + 每层下一步)。用法: /architecture [--gaps]"""
|
|
122
|
+
try:
|
|
123
|
+
from packages.aria_core import (
|
|
124
|
+
list_architecture_layers, architecture_status_counts)
|
|
125
|
+
except Exception as exc: # pragma: no cover - import guard
|
|
126
|
+
msg = f"架构契约不可用: {exc}"
|
|
127
|
+
self.context.console.print(f"[red]{msg}[/red]") if self.context.has_rich else print(msg)
|
|
128
|
+
return
|
|
129
|
+
gaps_only = "gap" in args.lower()
|
|
130
|
+
lines = format_architecture_report(
|
|
131
|
+
list_architecture_layers(), architecture_status_counts(),
|
|
132
|
+
gaps_only=gaps_only, rich=self.context.has_rich)
|
|
133
|
+
if self.context.has_rich:
|
|
134
|
+
self.context.console.print(_get_Panel()("\n".join(lines), title="[bold]Aria 架构[/bold]",
|
|
135
|
+
border_style="#C08050", box=_get_rich_box().ROUNDED,
|
|
136
|
+
padding=(0, 1)))
|
|
137
|
+
else:
|
|
138
|
+
print("\n".join(lines))
|
|
139
|
+
|
|
140
|
+
def cmd_bug(self, args: str):
|
|
141
|
+
desc = args.strip()
|
|
142
|
+
if not desc:
|
|
143
|
+
self.context.console.print("[dim]用法: /bug <描述你遇到的问题>[/dim]" if self.context.has_rich
|
|
144
|
+
else "Usage: /bug <description>")
|
|
145
|
+
return
|
|
146
|
+
ctx_parts = []
|
|
147
|
+
for m in self.terminal.conversation[-6:]:
|
|
148
|
+
_c = (m.get("content", "") or "")[:300]
|
|
149
|
+
ctx_parts.append(f"{m.get('role','')}: {_c}")
|
|
150
|
+
ctx = "\n".join(ctx_parts)
|
|
151
|
+
import platform as _pf
|
|
152
|
+
env = (f"v{_get___version__()} · {_pf.system()} · py{_pf.python_version()} · "
|
|
153
|
+
f"model={self.terminal.config.get('model','')}")
|
|
154
|
+
self.terminal._record_feedback("bug", ctx, comment=f"{desc}\n\n[env] {env}")
|
|
155
|
+
gh = "https://github.com/artheras/aria-code/issues"
|
|
156
|
+
if self.context.has_rich:
|
|
157
|
+
self.context.console.print(" [#C08050]✓ 已记录问题(本地)[/#C08050]")
|
|
158
|
+
self.context.console.print(f" [dim]上传需 /privacy opt-in · 或直接提 issue: {gh}[/dim]")
|
|
159
|
+
else:
|
|
160
|
+
print(f" ✓ Bug recorded locally. Upload via /privacy opt-in, or file: {gh}")
|
|
161
|
+
|
|
162
|
+
def cmd_accuracy(self, args: str):
|
|
163
|
+
res = self.terminal._verify_predictions(min_age_hours=24.0)
|
|
164
|
+
try:
|
|
165
|
+
from apps.cli.prediction_feedback import PredictionTracker
|
|
166
|
+
acc = PredictionTracker(_get_CONFIG_DIR()).accuracy()
|
|
167
|
+
except Exception:
|
|
168
|
+
acc = {}
|
|
169
|
+
if self.context.has_rich:
|
|
170
|
+
self.context.console.print()
|
|
171
|
+
self.context.console.print(" [bold]预测战绩[/bold] [dim]LLM 方向判断 vs 实际行情[/dim]")
|
|
172
|
+
if res.get("settled"):
|
|
173
|
+
self.context.console.print(f" [dim]本次结算 {res['settled']} 笔:"
|
|
174
|
+
f"命中 [green]{res['correct']}[/green] / "
|
|
175
|
+
f"落空 [red]{res['wrong']}[/red][/dim]")
|
|
176
|
+
_acc = acc.get("accuracy")
|
|
177
|
+
_acc_str = f"{_acc:.0%}" if _acc is not None else "—"
|
|
178
|
+
self.context.console.print(
|
|
179
|
+
f" 累计:已结算 [bold]{acc.get('settled',0)}[/bold] · "
|
|
180
|
+
f"命中率 [#C08050]{_acc_str}[/#C08050] · "
|
|
181
|
+
f"待结算 [dim]{acc.get('pending',0)}[/dim]"
|
|
182
|
+
)
|
|
183
|
+
if not acc.get("total"):
|
|
184
|
+
self.context.console.print(" [dim]暂无记录 — 用 /team 或 /analyze 让 AI 给出方向判断后会自动追踪[/dim]")
|
|
185
|
+
else:
|
|
186
|
+
print(f" 预测战绩: 结算{res.get('settled',0)} 命中率"
|
|
187
|
+
f"{acc.get('accuracy')} 待结算{acc.get('pending',0)}")
|
|
188
|
+
|
|
189
|
+
def cmd_cost(self, args: str):
|
|
190
|
+
import time as _t
|
|
191
|
+
elapsed = _t.time() - self.terminal._session_start
|
|
192
|
+
inp = self.terminal._session_input_tokens
|
|
193
|
+
out = self.terminal._session_output_tokens
|
|
194
|
+
think = self.terminal._session_thinking_tokens
|
|
195
|
+
turns = self.terminal._session_turns
|
|
196
|
+
total = inp + out + think
|
|
197
|
+
|
|
198
|
+
is_local = self.terminal._last_provider in ("ollama", "ollama_cache", "local")
|
|
199
|
+
cost_usd = 0.0
|
|
200
|
+
if not is_local:
|
|
201
|
+
cost_usd = (inp * 0.14 + out * 0.28 + think * 1.10) / 1_000_000
|
|
202
|
+
|
|
203
|
+
hh = int(elapsed // 3600)
|
|
204
|
+
mm = int((elapsed % 3600) // 60)
|
|
205
|
+
ss = int(elapsed % 60)
|
|
206
|
+
duration = f"{hh}h {mm:02d}m {ss:02d}s" if hh else f"{mm}m {ss:02d}s"
|
|
207
|
+
|
|
208
|
+
if self.context.has_rich:
|
|
209
|
+
self.context.console.print()
|
|
210
|
+
self.context.console.print("[bold]Session Usage[/bold]")
|
|
211
|
+
self.context.console.print()
|
|
212
|
+
self.context.console.print(f" [dim]{'Duration':<22}[/dim]{duration}")
|
|
213
|
+
self.context.console.print(f" [dim]{'Turns':<22}[/dim]{turns}")
|
|
214
|
+
self.context.console.print(f" [dim]{'Input tokens':<22}[/dim]{inp:,}")
|
|
215
|
+
self.context.console.print(f" [dim]{'Output tokens':<22}[/dim]{out:,}")
|
|
216
|
+
if think:
|
|
217
|
+
self.context.console.print(f" [dim]{'Thinking tokens':<22}[/dim]{think:,}")
|
|
218
|
+
self.context.console.print(f" [dim]{'Total tokens':<22}[/dim][bold]{total:,}[/bold]")
|
|
219
|
+
if is_local:
|
|
220
|
+
self.context.console.print(f" [dim]{'Est. cost':<22}[/dim][green]$0.00 (local)[/green]")
|
|
221
|
+
elif total > 0:
|
|
222
|
+
self.context.console.print(f" [dim]{'Est. cost':<22}[/dim]${cost_usd:.4f} USD")
|
|
223
|
+
self.context.console.print(f" [dim]{'Provider':<22}[/dim]{self.terminal._last_provider}")
|
|
224
|
+
self.context.console.print()
|
|
225
|
+
else:
|
|
226
|
+
print(f" Session: {duration} Turns: {turns}")
|
|
227
|
+
print(f" Tokens: {inp:,} in / {out:,} out / {total:,} total")
|
|
228
|
+
if not is_local and total > 0:
|
|
229
|
+
print(f" Est. cost: ${cost_usd:.4f}")
|
|
230
|
+
|
|
231
|
+
def cmd_todo(self, args: str):
|
|
232
|
+
import json as _json
|
|
233
|
+
todo_file = _get_CONFIG_DIR() / "todos.json"
|
|
234
|
+
|
|
235
|
+
def _load():
|
|
236
|
+
try:
|
|
237
|
+
if todo_file.exists():
|
|
238
|
+
return _json.loads(todo_file.read_text(encoding="utf-8"))
|
|
239
|
+
except Exception:
|
|
240
|
+
pass
|
|
241
|
+
return []
|
|
242
|
+
|
|
243
|
+
def _save(tasks):
|
|
244
|
+
_get_CONFIG_DIR().mkdir(parents=True, exist_ok=True)
|
|
245
|
+
todo_file.write_text(_json.dumps(tasks, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
246
|
+
|
|
247
|
+
parts = args.strip().split(maxsplit=1)
|
|
248
|
+
sub = parts[0].lower() if parts else "list"
|
|
249
|
+
rest = parts[1].strip() if len(parts) > 1 else ""
|
|
250
|
+
tasks = _load()
|
|
251
|
+
|
|
252
|
+
if sub in ("", "list", "ls"):
|
|
253
|
+
if not tasks:
|
|
254
|
+
self.context.console.print("[dim]No tasks. Add with: /todo add <task>[/dim]" if self.context.has_rich else "No tasks")
|
|
255
|
+
return
|
|
256
|
+
if self.context.has_rich:
|
|
257
|
+
self.context.console.print()
|
|
258
|
+
for i, t in enumerate(tasks):
|
|
259
|
+
status_icon = "[green]✓[/green]" if t.get("done") else "[yellow]○[/yellow]"
|
|
260
|
+
style = "dim" if t.get("done") else ""
|
|
261
|
+
text = t.get("text", "")
|
|
262
|
+
self.context.console.print(f" {status_icon} [dim]{i}[/dim] [{style}]{text}[/{style}]" if style
|
|
263
|
+
else f" {status_icon} [dim]{i}[/dim] {text}")
|
|
264
|
+
pending = sum(1 for t in tasks if not t.get("done"))
|
|
265
|
+
self.context.console.print(f"\n [dim]{pending}/{len(tasks)} pending[/dim]")
|
|
266
|
+
self.context.console.print()
|
|
267
|
+
else:
|
|
268
|
+
for i, t in enumerate(tasks):
|
|
269
|
+
mark = "✓" if t.get("done") else "○"
|
|
270
|
+
print(f" {mark} {i} {t.get('text', '')}")
|
|
271
|
+
elif sub == "add":
|
|
272
|
+
if not rest:
|
|
273
|
+
self.context.console.print("[dim]Usage: /todo add <task text>[/dim]" if self.context.has_rich else "Usage: /todo add <task>")
|
|
274
|
+
return
|
|
275
|
+
task = {"text": rest, "done": False, "id": len(tasks)}
|
|
276
|
+
tasks.append(task)
|
|
277
|
+
_save(tasks)
|
|
278
|
+
self.context.console.print(f" [dim]✓ Added: {rest}[/dim]" if self.context.has_rich else f"Added: {rest}")
|
|
279
|
+
elif sub in ("done", "check", "complete"):
|
|
280
|
+
try:
|
|
281
|
+
idx = int(rest)
|
|
282
|
+
tasks[idx]["done"] = True
|
|
283
|
+
_save(tasks)
|
|
284
|
+
self.context.console.print(f" [dim]✓ Done: {tasks[idx]['text']}[/dim]" if self.context.has_rich
|
|
285
|
+
else f"Done: {tasks[idx]['text']}")
|
|
286
|
+
except (ValueError, IndexError):
|
|
287
|
+
self.context.console.print("[dim]Usage: /todo done <id>[/dim]" if self.context.has_rich else "Usage: /todo done <id>")
|
|
288
|
+
elif sub in ("remove", "rm", "delete", "del"):
|
|
289
|
+
try:
|
|
290
|
+
idx = int(rest)
|
|
291
|
+
removed = tasks.pop(idx)
|
|
292
|
+
_save(tasks)
|
|
293
|
+
self.context.console.print(f" [dim]Removed: {removed['text']}[/dim]" if self.context.has_rich
|
|
294
|
+
else f"Removed: {removed['text']}")
|
|
295
|
+
except (ValueError, IndexError):
|
|
296
|
+
self.context.console.print("[dim]Usage: /todo remove <id>[/dim]" if self.context.has_rich else "bad index")
|
|
297
|
+
elif sub == "clear":
|
|
298
|
+
_save([])
|
|
299
|
+
self.context.console.print("[dim]All tasks cleared[/dim]" if self.context.has_rich else "Cleared")
|
|
300
|
+
else:
|
|
301
|
+
full_text = (sub + " " + rest).strip()
|
|
302
|
+
task = {"text": full_text, "done": False, "id": len(tasks)}
|
|
303
|
+
tasks.append(task)
|
|
304
|
+
_save(tasks)
|
|
305
|
+
self.context.console.print(f" [dim]✓ Added: {full_text}[/dim]" if self.context.has_rich else f"Added: {full_text}")
|
|
306
|
+
|
|
307
|
+
def cmd_doctor(self, args: str):
|
|
308
|
+
try:
|
|
309
|
+
from doctor import run_doctor
|
|
310
|
+
|
|
311
|
+
_ctx_stats = None
|
|
312
|
+
try:
|
|
313
|
+
from packages.aria_services.context import context_health_snapshot
|
|
314
|
+
_mc = get_model_cfg(self.terminal.config.get("model", "qwen2.5:7b"))
|
|
315
|
+
_ctx_stats = context_health_snapshot(
|
|
316
|
+
self.terminal.conversation,
|
|
317
|
+
max_tokens=int(_mc.get("num_ctx", 16384)),
|
|
318
|
+
threshold=float(self.terminal.config.get("auto_compact_threshold", 0.78)),
|
|
319
|
+
)
|
|
320
|
+
except Exception:
|
|
321
|
+
_ctx_stats = None
|
|
322
|
+
report = run_doctor(
|
|
323
|
+
self.terminal.config,
|
|
324
|
+
check_network="--network" in (args or "").split(),
|
|
325
|
+
context_stats=_ctx_stats,
|
|
326
|
+
)
|
|
327
|
+
if self.context.has_rich:
|
|
328
|
+
from rich.table import Table as _DoctorTable
|
|
329
|
+
table = _DoctorTable(title="Aria Code doctor", box=_get_rich_box().ROUNDED)
|
|
330
|
+
table.add_column("Status", width=8)
|
|
331
|
+
table.add_column("Check", style="bold")
|
|
332
|
+
table.add_column("Detail", style="dim")
|
|
333
|
+
table.add_column("Suggestion", style="dim")
|
|
334
|
+
icons = {"ok": "[green]OK[/green]", "warn": "[yellow]WARN[/yellow]", "err": "[red]ERR[/red]"}
|
|
335
|
+
for check in report.checks:
|
|
336
|
+
table.add_row(
|
|
337
|
+
icons.get(check.status, check.status.upper()),
|
|
338
|
+
check.name,
|
|
339
|
+
check.detail,
|
|
340
|
+
check.suggestion,
|
|
341
|
+
)
|
|
342
|
+
self.context.console.print()
|
|
343
|
+
self.context.console.print(table)
|
|
344
|
+
color = "green" if report.errors == 0 and report.warnings == 0 else ("yellow" if report.errors == 0 else "red")
|
|
345
|
+
self.context.console.print(f"[{color}]{report.passed} passed · {report.warnings} warnings · {report.errors} errors[/{color}]")
|
|
346
|
+
self.context.console.print()
|
|
347
|
+
else:
|
|
348
|
+
from doctor import format_doctor_plain
|
|
349
|
+
print(format_doctor_plain(report))
|
|
350
|
+
return
|
|
351
|
+
except Exception as exc:
|
|
352
|
+
self.context.console.print(f"[yellow]doctor module unavailable, using legacy checks: {exc}[/yellow]" if self.context.has_rich else f"doctor module unavailable: {exc}")
|
|
353
|
+
|
|
354
|
+
import importlib as _il
|
|
355
|
+
import subprocess as _sp
|
|
356
|
+
import shutil as _sh
|
|
357
|
+
cfg = self.terminal.config
|
|
358
|
+
ollama_url = cfg.get("ollama_url", "http://localhost:11434")
|
|
359
|
+
api_url = cfg.get("api_url", "http://localhost:8000")
|
|
360
|
+
|
|
361
|
+
checks: list[tuple] = []
|
|
362
|
+
|
|
363
|
+
def _ok(label, detail=""): checks.append(("ok", label, detail))
|
|
364
|
+
def _warn(label, detail=""): checks.append(("warn", label, detail))
|
|
365
|
+
def _err(label, detail=""): checks.append(("err", label, detail))
|
|
366
|
+
|
|
367
|
+
import sys as _sys
|
|
368
|
+
pyver = f"{_sys.version_info.major}.{_sys.version_info.minor}.{_sys.version_info.micro}"
|
|
369
|
+
if _sys.version_info >= (3, 9):
|
|
370
|
+
_ok("Python", pyver)
|
|
371
|
+
else:
|
|
372
|
+
_warn("Python", f"{pyver} (3.9+ recommended)")
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
import urllib.request as _ur
|
|
376
|
+
_opener = _ur.build_opener(_ur.ProxyHandler({}))
|
|
377
|
+
_r = _opener.open(f"{ollama_url}/api/tags", timeout=3)
|
|
378
|
+
_data = json.loads(_r.read())
|
|
379
|
+
models = [m["name"] for m in _data.get("models", [])]
|
|
380
|
+
if models:
|
|
381
|
+
_ok("Ollama", f"{len(models)} models: {', '.join(models[:4])}")
|
|
382
|
+
else:
|
|
383
|
+
_warn("Ollama", "running but no models installed (ollama pull qwen2.5-coder:1.5b)")
|
|
384
|
+
except Exception as e:
|
|
385
|
+
_err("Ollama", f"not reachable at {ollama_url} ({e})")
|
|
386
|
+
|
|
387
|
+
try:
|
|
388
|
+
import urllib.request as _ur
|
|
389
|
+
_opener = _ur.build_opener(_ur.ProxyHandler({}))
|
|
390
|
+
_r = _opener.open(f"{api_url}/health", timeout=3)
|
|
391
|
+
_ok("Backend", f"running at {api_url}")
|
|
392
|
+
except Exception:
|
|
393
|
+
_warn("Backend", f"offline at {api_url} — local Ollama mode will be used")
|
|
394
|
+
|
|
395
|
+
key_checks = [
|
|
396
|
+
("finnhub", "股票行情"),
|
|
397
|
+
("alphavantage", "历史数据"),
|
|
398
|
+
("newsapi", "新闻"),
|
|
399
|
+
("brave", "网络搜索"),
|
|
400
|
+
("coingecko", "加密货币"),
|
|
401
|
+
]
|
|
402
|
+
for svc, desc in key_checks:
|
|
403
|
+
k = _get_provider_key(svc)
|
|
404
|
+
if k:
|
|
405
|
+
_ok(f"API key: {svc}", f"{desc} ({'*'*6}{k[-4:]})")
|
|
406
|
+
else:
|
|
407
|
+
_warn(f"API key: {svc}", f"{desc} 未配置 (/apikey set {svc} <key>)")
|
|
408
|
+
|
|
409
|
+
llm_keys = [("deepseek", "DeepSeek"), ("openai", "OpenAI"),
|
|
410
|
+
("siliconflow", "SiliconFlow"), ("moonshot", "Moonshot")]
|
|
411
|
+
_has_any_llm = False
|
|
412
|
+
for svc, name in llm_keys:
|
|
413
|
+
k = _get_provider_key(svc)
|
|
414
|
+
if k:
|
|
415
|
+
_ok(f"LLM key: {svc}", f"{name} configured")
|
|
416
|
+
_has_any_llm = True
|
|
417
|
+
if not _has_any_llm:
|
|
418
|
+
_warn("LLM keys", "No cloud LLM keys — Ollama must be running for AI responses")
|
|
419
|
+
|
|
420
|
+
_pkgs = [
|
|
421
|
+
("aiohttp", "async HTTP"),
|
|
422
|
+
("rich", "terminal UI"),
|
|
423
|
+
("prompt_toolkit", "autocomplete"),
|
|
424
|
+
("yfinance", "market data"),
|
|
425
|
+
("pandas", "data processing"),
|
|
426
|
+
("requests", "HTTP client"),
|
|
427
|
+
]
|
|
428
|
+
for pkg, desc in _pkgs:
|
|
429
|
+
try:
|
|
430
|
+
m = _il.import_module(pkg)
|
|
431
|
+
ver = getattr(m, "_get___version__()", "?")
|
|
432
|
+
_ok(f"pkg: {pkg}", f"{desc} v{ver}")
|
|
433
|
+
except ImportError:
|
|
434
|
+
_warn(f"pkg: {pkg}", f"{desc} not installed (pip install {pkg})")
|
|
435
|
+
|
|
436
|
+
aria_md = pathlib.Path.cwd() / "ARIA.md"
|
|
437
|
+
if aria_md.exists():
|
|
438
|
+
lines = len(aria_md.read_text(encoding="utf-8").splitlines())
|
|
439
|
+
_ok("ARIA.md", f"{lines} lines of project context")
|
|
440
|
+
else:
|
|
441
|
+
_warn("ARIA.md", f"not found in {pathlib.Path.cwd()} (use /init to create)")
|
|
442
|
+
|
|
443
|
+
if _get__HAS_MCP():
|
|
444
|
+
try:
|
|
445
|
+
reg = self.terminal._mcp_registry
|
|
446
|
+
if reg and hasattr(reg, "list_tools"):
|
|
447
|
+
tools = reg.list_tools()
|
|
448
|
+
_ok("MCP", f"{len(tools)} tools from MCP servers")
|
|
449
|
+
else:
|
|
450
|
+
_warn("MCP", "registry not started yet")
|
|
451
|
+
except Exception:
|
|
452
|
+
_warn("MCP", "loaded but no active servers")
|
|
453
|
+
else:
|
|
454
|
+
_warn("MCP", "mcp_client not found — MCP support disabled")
|
|
455
|
+
|
|
456
|
+
tool_count = len(_get_ARIA_TOOLS()) + len(_get_LOCAL_TOOLS())
|
|
457
|
+
_ok("Aria tools", f"{tool_count} tools loaded")
|
|
458
|
+
|
|
459
|
+
self.context.console.print() if self.context.has_rich else None
|
|
460
|
+
if self.context.has_rich:
|
|
461
|
+
self.context.console.print("[bold]Aria Code — Diagnostics[/bold]")
|
|
462
|
+
self.context.console.print()
|
|
463
|
+
icons = {"ok": "[green]✓[/green]", "warn": "[yellow]⚠[/yellow]", "err": "[red]✗[/red]"}
|
|
464
|
+
for status, label, detail in checks:
|
|
465
|
+
icon = icons[status]
|
|
466
|
+
detail_str = f" [dim]{detail}[/dim]" if detail else ""
|
|
467
|
+
self.context.console.print(f" {icon} {label:<28}{detail_str}")
|
|
468
|
+
self.context.console.print()
|
|
469
|
+
n_ok = sum(1 for s, *_ in checks if s == "ok")
|
|
470
|
+
n_w = sum(1 for s, *_ in checks if s == "warn")
|
|
471
|
+
n_e = sum(1 for s, *_ in checks if s == "err")
|
|
472
|
+
summary_color = "green" if n_e == 0 and n_w == 0 else ("yellow" if n_e == 0 else "red")
|
|
473
|
+
self.context.console.print(f" [{summary_color}]{n_ok} passed · {n_w} warnings · {n_e} errors[/{summary_color}]")
|
|
474
|
+
self.context.console.print()
|
|
475
|
+
|
|
476
|
+
_fh_ok = bool(_get_provider_key("finnhub"))
|
|
477
|
+
_av_ok = bool(_get_provider_key("alphavantage"))
|
|
478
|
+
_na_ok = bool(_get_provider_key("newsapi"))
|
|
479
|
+
_ak_ok = True
|
|
480
|
+
_llm_ok = any(_get_provider_key(p) for p in ("deepseek", "openai", "anthropic", "groq"))
|
|
481
|
+
|
|
482
|
+
_guide_needed = not (_fh_ok and _av_ok and _na_ok and _llm_ok)
|
|
483
|
+
if _guide_needed:
|
|
484
|
+
self.context.console.print("[bold]数据源配置指南[/bold] [dim](完整功能需要以下 key)[/dim]")
|
|
485
|
+
self.context.console.print()
|
|
486
|
+
self.context.console.print(" [dim]Use /doctor --network for network checks[/dim]")
|
|
487
|
+
else:
|
|
488
|
+
print("Diagnostics complete")
|
|
489
|
+
|
|
490
|
+
async def cmd_install(self, args: str):
|
|
491
|
+
"""
|
|
492
|
+
检测并安装缺失的依赖包(环境补全)。
|
|
493
|
+
|
|
494
|
+
/install 扫描全部已知依赖,列出缺失并询问是否安装
|
|
495
|
+
/install <pkg> [pkg] 直接安装指定 Python 包
|
|
496
|
+
/install --auto 根据最近一次提问的意图检测缺失项
|
|
497
|
+
/install --required 仅安装"必需"包,跳过可选项
|
|
498
|
+
/install --plan 只展示安装计划,不安装
|
|
499
|
+
/install --yes 非交互确认,直接安装选中包
|
|
500
|
+
"""
|
|
501
|
+
import shlex as _shlex
|
|
502
|
+
import subprocess as _sp
|
|
503
|
+
import sys as _sys
|
|
504
|
+
from apps.cli.preflight import (
|
|
505
|
+
build_full_dependency_report,
|
|
506
|
+
build_intent_preflight,
|
|
507
|
+
build_install_plan,
|
|
508
|
+
package_to_module,
|
|
509
|
+
select_install_packages,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
raw = (args or "").strip()
|
|
513
|
+
tokens = raw.split()
|
|
514
|
+
flags = {t for t in tokens if t.startswith("--")}
|
|
515
|
+
explicit_pkgs = [t for t in tokens if not t.startswith("--")]
|
|
516
|
+
|
|
517
|
+
# ── Resolve what to install ───────────────────────────────────────────
|
|
518
|
+
report = None
|
|
519
|
+
if explicit_pkgs:
|
|
520
|
+
# Direct package install — no detection needed
|
|
521
|
+
pip_packages = explicit_pkgs
|
|
522
|
+
command_hints: tuple = ()
|
|
523
|
+
env_hints: tuple = ()
|
|
524
|
+
else:
|
|
525
|
+
if "--auto" in flags:
|
|
526
|
+
# Detect from the user's last real question
|
|
527
|
+
last_msg = ""
|
|
528
|
+
for m in reversed(self.terminal.conversation):
|
|
529
|
+
if m.get("role") == "user" and m.get("content"):
|
|
530
|
+
last_msg = m["content"] if isinstance(m["content"], str) else ""
|
|
531
|
+
break
|
|
532
|
+
if not last_msg:
|
|
533
|
+
self.context.console.print("[yellow]没有可分析的历史提问,改用全量扫描[/yellow]" if self.context.has_rich
|
|
534
|
+
else "No history; full scan")
|
|
535
|
+
report = build_full_dependency_report(include_optional="--required" not in flags)
|
|
536
|
+
else:
|
|
537
|
+
report = build_intent_preflight(last_msg)
|
|
538
|
+
else:
|
|
539
|
+
report = build_full_dependency_report(include_optional="--required" not in flags)
|
|
540
|
+
|
|
541
|
+
plan = build_install_plan(report)
|
|
542
|
+
pip_packages = list(plan.pip_packages)
|
|
543
|
+
command_hints = plan.command_hints
|
|
544
|
+
env_hints = plan.env_hints
|
|
545
|
+
|
|
546
|
+
# ── Nothing missing ───────────────────────────────────────────────────
|
|
547
|
+
if not pip_packages and not (report and (report.missing_commands or report.missing_env)):
|
|
548
|
+
self.context.console.print("[green]✓ 环境完整,没有检测到缺失的 Python 包[/green]" if self.context.has_rich
|
|
549
|
+
else "All dependencies satisfied")
|
|
550
|
+
return
|
|
551
|
+
|
|
552
|
+
# ── Show findings ─────────────────────────────────────────────────────
|
|
553
|
+
if self.context.has_rich:
|
|
554
|
+
self.context.console.print()
|
|
555
|
+
self.context.console.print("[bold]环境检测结果[/bold]")
|
|
556
|
+
if pip_packages:
|
|
557
|
+
self.context.console.print(f" [yellow]缺少 {len(pip_packages)} 个 Python 包:[/yellow]")
|
|
558
|
+
for p in pip_packages:
|
|
559
|
+
purpose = ""
|
|
560
|
+
if report:
|
|
561
|
+
for r in report.missing_python:
|
|
562
|
+
if r.package == p:
|
|
563
|
+
purpose = f" [dim]— {r.purpose}{'(可选)' if not r.required else ''}[/dim]"
|
|
564
|
+
break
|
|
565
|
+
self.context.console.print(f" • [cyan]{p}[/cyan]{purpose}")
|
|
566
|
+
if command_hints:
|
|
567
|
+
self.context.console.print(" [yellow]缺少命令行工具:[/yellow]")
|
|
568
|
+
for h in command_hints:
|
|
569
|
+
self.context.console.print(f" • [dim]{h}[/dim]")
|
|
570
|
+
if env_hints:
|
|
571
|
+
self.context.console.print(" [dim]未配置的环境变量(可选,不自动处理):[/dim]")
|
|
572
|
+
for h in env_hints:
|
|
573
|
+
self.context.console.print(f" • [dim]{h}[/dim]")
|
|
574
|
+
self.context.console.print()
|
|
575
|
+
else:
|
|
576
|
+
print(f"Missing packages: {', '.join(pip_packages)}")
|
|
577
|
+
for h in command_hints:
|
|
578
|
+
print(f" tool: {h}")
|
|
579
|
+
|
|
580
|
+
if not pip_packages:
|
|
581
|
+
if command_hints:
|
|
582
|
+
self.context.console.print("[dim]命令行工具需手动安装(见上方提示),Aria 不会自动执行系统级安装[/dim]"
|
|
583
|
+
if self.context.has_rich else "Install CLI tools manually (see hints above)")
|
|
584
|
+
return
|
|
585
|
+
|
|
586
|
+
# ── Select packages ───────────────────────────────────────────────────
|
|
587
|
+
if report:
|
|
588
|
+
if "--plan" in flags:
|
|
589
|
+
selection = select_install_packages(plan, report, mode="plan")
|
|
590
|
+
elif "--yes" in flags:
|
|
591
|
+
_mode = "required" if "--required" in flags else "all"
|
|
592
|
+
selection = select_install_packages(plan, report, mode=_mode)
|
|
593
|
+
else:
|
|
594
|
+
required_pkgs = [
|
|
595
|
+
r.package for r in report.missing_python
|
|
596
|
+
if r.required and r.package in pip_packages
|
|
597
|
+
]
|
|
598
|
+
optional_pkgs = [
|
|
599
|
+
r.package for r in report.missing_python
|
|
600
|
+
if not r.required and r.package in pip_packages
|
|
601
|
+
]
|
|
602
|
+
if self.context.has_rich:
|
|
603
|
+
self.context.console.print("[bold]选择安装范围[/bold]")
|
|
604
|
+
self.context.console.print(" [cyan]all[/cyan] 安装全部缺失 Python 包")
|
|
605
|
+
self.context.console.print(" [cyan]required[/cyan] 只安装必需包")
|
|
606
|
+
self.context.console.print(" [cyan]optional[/cyan] 只安装可选增强包")
|
|
607
|
+
self.context.console.print(" [cyan]custom[/cyan] 手动输入包名或编号")
|
|
608
|
+
self.context.console.print(" [cyan]plan[/cyan] 只显示计划,不安装")
|
|
609
|
+
self.context.console.print(" [cyan]skip[/cyan] 跳过")
|
|
610
|
+
for idx, pkg in enumerate(pip_packages, 1):
|
|
611
|
+
kind = "required" if pkg in required_pkgs else "optional"
|
|
612
|
+
self.context.console.print(f" [dim]{idx}.[/dim] {pkg} [dim]({kind})[/dim]")
|
|
613
|
+
else:
|
|
614
|
+
print("Select install scope: all | required | optional | custom | plan | skip")
|
|
615
|
+
for idx, pkg in enumerate(pip_packages, 1):
|
|
616
|
+
kind = "required" if pkg in required_pkgs else "optional"
|
|
617
|
+
print(f" {idx}. {pkg} ({kind})")
|
|
618
|
+
default_mode = "required" if required_pkgs and optional_pkgs else "all"
|
|
619
|
+
try:
|
|
620
|
+
choice = self.context.console.input(f"安装范围 [{default_mode}]: ") if self.context.has_rich else input(f"Install scope [{default_mode}]: ")
|
|
621
|
+
except (EOFError, KeyboardInterrupt):
|
|
622
|
+
self.context.console.print("\n[dim]已取消[/dim]" if self.context.has_rich else "Cancelled")
|
|
623
|
+
return
|
|
624
|
+
choice = (choice or default_mode).strip().lower()
|
|
625
|
+
custom_items: list[str] = []
|
|
626
|
+
if choice == "custom":
|
|
627
|
+
try:
|
|
628
|
+
raw_custom = self.context.console.input("输入包名或编号(空格/逗号分隔): ") if self.context.has_rich else input("Packages or numbers: ")
|
|
629
|
+
except (EOFError, KeyboardInterrupt):
|
|
630
|
+
self.context.console.print("\n[dim]已取消[/dim]" if self.context.has_rich else "Cancelled")
|
|
631
|
+
return
|
|
632
|
+
for item in raw_custom.replace(",", " ").split():
|
|
633
|
+
if item.isdigit():
|
|
634
|
+
idx = int(item)
|
|
635
|
+
if 1 <= idx <= len(pip_packages):
|
|
636
|
+
custom_items.append(pip_packages[idx - 1])
|
|
637
|
+
else:
|
|
638
|
+
custom_items.append(item)
|
|
639
|
+
selection = select_install_packages(
|
|
640
|
+
plan, report, mode=choice, custom=custom_items
|
|
641
|
+
)
|
|
642
|
+
pip_packages = list(selection.pip_packages)
|
|
643
|
+
if selection.mode in {"plan", "dry-run", "dry_run"}:
|
|
644
|
+
self.context.console.print("[dim]已生成安装计划,未执行安装。[/dim]" if self.context.has_rich else "Install plan only; no changes made.")
|
|
645
|
+
return
|
|
646
|
+
if not pip_packages:
|
|
647
|
+
self.context.console.print("[dim]没有选择任何 Python 包,未安装。[/dim]" if self.context.has_rich else "No packages selected.")
|
|
648
|
+
return
|
|
649
|
+
if selection.skipped_packages and self.context.has_rich:
|
|
650
|
+
self.context.console.print(f"[dim]跳过: {', '.join(selection.skipped_packages)}[/dim]")
|
|
651
|
+
elif "--plan" in flags:
|
|
652
|
+
self.context.console.print("[dim]显式包安装计划已显示,未执行安装。[/dim]" if self.context.has_rich else "Install plan only; no changes made.")
|
|
653
|
+
return
|
|
654
|
+
|
|
655
|
+
# ── Confirm ───────────────────────────────────────────────────────────
|
|
656
|
+
pip_cmd = [_sys.executable, "-m", "pip", "install", *pip_packages]
|
|
657
|
+
pretty = " ".join(_shlex.quote(c) for c in pip_cmd)
|
|
658
|
+
if "--yes" not in flags:
|
|
659
|
+
prompt = f"将运行: {pretty}\n确认安装? [y/N]: "
|
|
660
|
+
try:
|
|
661
|
+
answer = self.context.console.input(prompt) if self.context.has_rich else input(prompt)
|
|
662
|
+
except (EOFError, KeyboardInterrupt):
|
|
663
|
+
self.context.console.print("\n[dim]已取消[/dim]" if self.context.has_rich else "Cancelled")
|
|
664
|
+
return
|
|
665
|
+
if answer.strip().lower() not in {"y", "yes"}:
|
|
666
|
+
self.context.console.print("[dim]已取消,未安装任何包[/dim]" if self.context.has_rich else "Cancelled")
|
|
667
|
+
return
|
|
668
|
+
elif self.context.has_rich:
|
|
669
|
+
self.context.console.print(f"[dim]Auto install: {pretty}[/dim]")
|
|
670
|
+
|
|
671
|
+
# ── Install ───────────────────────────────────────────────────────────
|
|
672
|
+
self.context.console.print(f"\n[dim]⏳ 安装中: {' '.join(pip_packages)}…[/dim]" if self.context.has_rich
|
|
673
|
+
else f"Installing {' '.join(pip_packages)}...")
|
|
674
|
+
try:
|
|
675
|
+
proc = await asyncio.get_event_loop().run_in_executor(
|
|
676
|
+
None,
|
|
677
|
+
lambda: _sp.run(pip_cmd, capture_output=True, text=True, timeout=300),
|
|
678
|
+
)
|
|
679
|
+
except Exception as exc:
|
|
680
|
+
self.context.console.print(f"[red]安装失败: {exc}[/red]" if self.context.has_rich else f"Install failed: {exc}")
|
|
681
|
+
return
|
|
682
|
+
|
|
683
|
+
if proc.returncode == 0:
|
|
684
|
+
# Verify each package now imports
|
|
685
|
+
import importlib as _il
|
|
686
|
+
_il.invalidate_caches()
|
|
687
|
+
ok_list, fail_list = [], []
|
|
688
|
+
for p in pip_packages:
|
|
689
|
+
mod = package_to_module(p)
|
|
690
|
+
try:
|
|
691
|
+
_il.import_module(mod)
|
|
692
|
+
ok_list.append(p)
|
|
693
|
+
except Exception:
|
|
694
|
+
fail_list.append(p)
|
|
695
|
+
# File parser availability is cached for performance. Refresh it
|
|
696
|
+
# now so DOCX/PDF/XLSX tools work in this same Aria session.
|
|
697
|
+
try:
|
|
698
|
+
from file_analysis_tools import refresh_optional_parsers
|
|
699
|
+
|
|
700
|
+
refresh_optional_parsers()
|
|
701
|
+
except Exception:
|
|
702
|
+
pass
|
|
703
|
+
if self.context.has_rich:
|
|
704
|
+
self.context.console.print(f" [green]✓ 安装完成: {', '.join(ok_list) or '—'}[/green]")
|
|
705
|
+
if fail_list:
|
|
706
|
+
self.context.console.print(f" [yellow]⚠ 已安装但当前会话需重启才能加载: {', '.join(fail_list)}[/yellow]")
|
|
707
|
+
elif ok_list:
|
|
708
|
+
self.context.console.print(" [dim]能力已刷新,当前会话可直接使用。[/dim]")
|
|
709
|
+
else:
|
|
710
|
+
print(f"Installed: {', '.join(ok_list)}")
|
|
711
|
+
else:
|
|
712
|
+
err_tail = (proc.stderr or proc.stdout or "")[-400:]
|
|
713
|
+
self.context.console.print(f"[red]pip 安装失败 (code {proc.returncode}):[/red]\n[dim]{err_tail}[/dim]"
|
|
714
|
+
if self.context.has_rich else f"pip failed: {err_tail}")
|
|
715
|
+
|
|
716
|
+
async def cmd_datasource(self, args: str):
|
|
717
|
+
sub = args.strip().lower()
|
|
718
|
+
if sub.startswith("test "):
|
|
719
|
+
src_name = sub[5:].strip()
|
|
720
|
+
await asyncio.get_event_loop().run_in_executor(
|
|
721
|
+
None, lambda: _test_datasource(src_name)
|
|
722
|
+
)
|
|
723
|
+
return
|
|
724
|
+
|
|
725
|
+
if sub == "config":
|
|
726
|
+
paths = [
|
|
727
|
+
"~/.aria/datasources.yaml",
|
|
728
|
+
"~/.aria/.env",
|
|
729
|
+
str(_get_CONFIG_DIR() / "providers.json"),
|
|
730
|
+
]
|
|
731
|
+
if self.context.has_rich:
|
|
732
|
+
self.context.console.print(" [bold]数据源配置文件:[/bold]")
|
|
733
|
+
for p in paths:
|
|
734
|
+
import pathlib
|
|
735
|
+
full = pathlib.Path(p).expanduser()
|
|
736
|
+
exists = "[green]✓[/green]" if full.exists() else "[dim]✗ (未创建)[/dim]"
|
|
737
|
+
self.context.console.print(f" {exists} [dim]{p}[/dim]")
|
|
738
|
+
self.context.console.print("\n [dim]环境变量: TUSHARE_TOKEN FRED_API_KEY ALPHA_VANTAGE_KEY[/dim]")
|
|
739
|
+
return
|
|
740
|
+
|
|
741
|
+
try:
|
|
742
|
+
from datasources.router import _SOURCE_REGISTRY, DataRouter
|
|
743
|
+
router = DataRouter()
|
|
744
|
+
except ImportError:
|
|
745
|
+
print_error(self.context, "datasources 模块未找到")
|
|
746
|
+
return
|
|
747
|
+
|
|
748
|
+
if self.context.has_rich:
|
|
749
|
+
from rich.markup import escape
|
|
750
|
+
from rich.table import Table
|
|
751
|
+
from rich import box
|
|
752
|
+
from ui.render.responsive import StackedRecord, render_stacked_records, structured_layout
|
|
753
|
+
|
|
754
|
+
_DESC = {
|
|
755
|
+
"yfinance": "Yahoo Finance (免费)",
|
|
756
|
+
"akshare": "AkShare A股 (免费)",
|
|
757
|
+
"tushare": "Tushare Pro (需Token)",
|
|
758
|
+
"fred": "美联储经济数据 (免费)",
|
|
759
|
+
"edgar": "SEC EDGAR 财报 (免费)",
|
|
760
|
+
"alpha_vantage": "Alpha Vantage (免费Key)",
|
|
761
|
+
"world_bank": "世界银行 (免费)",
|
|
762
|
+
}
|
|
763
|
+
source_rows = []
|
|
764
|
+
for name, cls in _SOURCE_REGISTRY.items():
|
|
765
|
+
try:
|
|
766
|
+
source = cls()
|
|
767
|
+
configured = source.is_configured()
|
|
768
|
+
status = "[green]✓ 就绪[/green]" if configured else "[dim]✗ 未配置[/dim]"
|
|
769
|
+
needs_key = "是" if cls.requires_key else "否"
|
|
770
|
+
markets = ", ".join(getattr(cls, "markets", []))
|
|
771
|
+
except Exception:
|
|
772
|
+
status, needs_key, markets = "[red]错误[/red]", "?", "?"
|
|
773
|
+
source_rows.append((name, markets, needs_key, status, _DESC.get(name, "")))
|
|
774
|
+
|
|
775
|
+
layout = structured_layout(self.context.console)
|
|
776
|
+
if layout == "stacked":
|
|
777
|
+
records = [
|
|
778
|
+
StackedRecord(
|
|
779
|
+
headline=f"{escape(str(name))} {status}",
|
|
780
|
+
lines=(
|
|
781
|
+
f"[dim]市场[/dim] {escape(str(markets or '—'))} · "
|
|
782
|
+
f"[dim]需要 Key[/dim] {needs_key}",
|
|
783
|
+
f"[dim]说明[/dim] {escape(str(description or '—'))}",
|
|
784
|
+
),
|
|
785
|
+
)
|
|
786
|
+
for name, markets, needs_key, status, description in source_rows
|
|
787
|
+
]
|
|
788
|
+
render_stacked_records(
|
|
789
|
+
self.context.console,
|
|
790
|
+
title="数据源状态",
|
|
791
|
+
records=records,
|
|
792
|
+
footer="/datasource config — 配置文件路径",
|
|
793
|
+
)
|
|
794
|
+
return
|
|
795
|
+
|
|
796
|
+
table = Table(title="数据源状态", box=_get_rich_box().SIMPLE, header_style="bold dim")
|
|
797
|
+
table.add_column("名称", width=16)
|
|
798
|
+
table.add_column("市场", width=20)
|
|
799
|
+
table.add_column("需要Key", width=8)
|
|
800
|
+
table.add_column("状态", width=8)
|
|
801
|
+
if layout == "full":
|
|
802
|
+
table.add_column("说明")
|
|
803
|
+
for name, markets, needs_key, status, description in source_rows:
|
|
804
|
+
row = [name, markets, needs_key, status]
|
|
805
|
+
if layout == "full":
|
|
806
|
+
row.append(description)
|
|
807
|
+
table.add_row(*row)
|
|
808
|
+
self.context.console.print(table)
|
|
809
|
+
self.context.console.print(" [dim]/datasource config — 配置文件路径[/dim]")
|
|
810
|
+
else:
|
|
811
|
+
for name, cls in _SOURCE_REGISTRY.items():
|
|
812
|
+
src = cls()
|
|
813
|
+
print(f" {name}: {'ready' if src.is_configured() else 'not configured'}")
|