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
aria_code/doctor.py
ADDED
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
"""Reusable health checks for Aria Code installations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
import time
|
|
13
|
+
import tempfile
|
|
14
|
+
import urllib.request
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Dict, Iterable, List, Optional
|
|
18
|
+
from aria_code.packages.aria_core.paths import aria_home
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class DoctorCheck:
|
|
23
|
+
name: str
|
|
24
|
+
status: str
|
|
25
|
+
detail: str = ""
|
|
26
|
+
suggestion: str = ""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class DoctorReport:
|
|
31
|
+
checks: List[DoctorCheck] = field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def passed(self) -> int:
|
|
35
|
+
return sum(1 for check in self.checks if check.status == "ok")
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def warnings(self) -> int:
|
|
39
|
+
return sum(1 for check in self.checks if check.status == "warn")
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def errors(self) -> int:
|
|
43
|
+
return sum(1 for check in self.checks if check.status == "err")
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def status(self) -> str:
|
|
47
|
+
if self.errors:
|
|
48
|
+
return "err"
|
|
49
|
+
if self.warnings:
|
|
50
|
+
return "warn"
|
|
51
|
+
return "ok"
|
|
52
|
+
|
|
53
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
54
|
+
return {
|
|
55
|
+
"status": self.status,
|
|
56
|
+
"passed": self.passed,
|
|
57
|
+
"warnings": self.warnings,
|
|
58
|
+
"errors": self.errors,
|
|
59
|
+
"checks": [check.__dict__ for check in self.checks],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# `npm run repair` used to be here as the npm-install branch of this hint. The
|
|
64
|
+
# npm package no longer has that script, or any script but `test`: it was
|
|
65
|
+
# rewritten from a postinstall that built a venv into a dispatcher that runs a
|
|
66
|
+
# prebuilt binary. An npm install therefore has no venv to rebuild, and this
|
|
67
|
+
# hint only applies to a repo checkout.
|
|
68
|
+
VENV_REBUILD_HINT = (
|
|
69
|
+
"Rebuild the venv with your current Python: bash install.sh --rebuild "
|
|
70
|
+
"(repo checkout), or reinstall with pip install -U \"aria-code<4\"."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# What the npm package is today: a zero-dependency launcher that execs a
|
|
74
|
+
# prebuilt binary delivered as an optionalDependency, one package per platform.
|
|
75
|
+
# Nothing to repair in place — the fix for every broken state is a reinstall.
|
|
76
|
+
NPM_REINSTALL_HINT = "Reinstall with: npm install -g @artheras/aria-code@latest"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def analyze_python_drift(
|
|
80
|
+
recorded_version: str,
|
|
81
|
+
running_version: str,
|
|
82
|
+
home_exists: bool,
|
|
83
|
+
) -> DoctorCheck:
|
|
84
|
+
"""Pure drift analysis for a venv's pyvenv.cfg vs the running interpreter.
|
|
85
|
+
|
|
86
|
+
Two real-world failure signals:
|
|
87
|
+
• the venv's base interpreter (``home =``) was removed — e.g. Homebrew
|
|
88
|
+
upgraded python@3.13 → 3.14 and deleted the old keg; the venv breaks
|
|
89
|
+
on the next symlink resolution even though it still "exists";
|
|
90
|
+
• the recorded creation version differs from the interpreter actually
|
|
91
|
+
running — stale metadata after a relink, or the entrypoint resolved a
|
|
92
|
+
different Python than the one the venv was built with.
|
|
93
|
+
"""
|
|
94
|
+
if not home_exists:
|
|
95
|
+
return _check(
|
|
96
|
+
"python_venv",
|
|
97
|
+
"err",
|
|
98
|
+
f"venv base interpreter is gone (built with {recorded_version or 'unknown'})",
|
|
99
|
+
VENV_REBUILD_HINT,
|
|
100
|
+
)
|
|
101
|
+
rec = tuple((recorded_version or "").split(".")[:2])
|
|
102
|
+
run = tuple((running_version or "").split(".")[:2])
|
|
103
|
+
if rec and run and rec != run:
|
|
104
|
+
return _check(
|
|
105
|
+
"python_venv",
|
|
106
|
+
"warn",
|
|
107
|
+
f"venv was created with Python {recorded_version} but {running_version} is running",
|
|
108
|
+
VENV_REBUILD_HINT,
|
|
109
|
+
)
|
|
110
|
+
return _check("python_venv", "ok", f"venv matches running Python {running_version}")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _check_python_drift() -> Optional[DoctorCheck]:
|
|
114
|
+
"""Read the active venv's pyvenv.cfg; None when not running inside a venv."""
|
|
115
|
+
if sys.prefix == getattr(sys, "base_prefix", sys.prefix):
|
|
116
|
+
return None
|
|
117
|
+
cfg = Path(sys.prefix) / "pyvenv.cfg"
|
|
118
|
+
if not cfg.exists():
|
|
119
|
+
return None
|
|
120
|
+
recorded, home = "", ""
|
|
121
|
+
try:
|
|
122
|
+
for line in cfg.read_text(encoding="utf-8").splitlines():
|
|
123
|
+
key, _, value = line.partition("=")
|
|
124
|
+
key = key.strip().lower()
|
|
125
|
+
if key == "version":
|
|
126
|
+
recorded = value.strip()
|
|
127
|
+
elif key == "home":
|
|
128
|
+
home = value.strip()
|
|
129
|
+
except Exception:
|
|
130
|
+
return None
|
|
131
|
+
running = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
132
|
+
return analyze_python_drift(recorded, running, home_exists=bool(home) and Path(home).exists())
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _check(name: str, status: str, detail: str = "", suggestion: str = "") -> DoctorCheck:
|
|
136
|
+
return DoctorCheck(name=name, status=status, detail=detail, suggestion=suggestion)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _format_age(seconds: float | int | None) -> str:
|
|
140
|
+
if not seconds or seconds <= 0:
|
|
141
|
+
return ""
|
|
142
|
+
seconds = int(seconds)
|
|
143
|
+
if seconds < 60:
|
|
144
|
+
return f"{seconds}s"
|
|
145
|
+
minutes, sec = divmod(seconds, 60)
|
|
146
|
+
if minutes < 60:
|
|
147
|
+
return f"{minutes}m{sec:02d}s"
|
|
148
|
+
hours, rem = divmod(minutes, 60)
|
|
149
|
+
return f"{hours}h{rem:02d}m"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _has_module(name: str) -> bool:
|
|
153
|
+
# find_spec on a dotted name imports the parent package first, and raises
|
|
154
|
+
# rather than returning None when that parent is absent ("google.genai"
|
|
155
|
+
# with no "google" installed). Absent is the answer either way.
|
|
156
|
+
try:
|
|
157
|
+
return importlib.util.find_spec(name) is not None
|
|
158
|
+
except (ImportError, ValueError):
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _is_writable(path: Path) -> tuple[bool, str]:
|
|
163
|
+
try:
|
|
164
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
with tempfile.NamedTemporaryFile(prefix=".aria-doctor-", dir=path, delete=True) as handle:
|
|
166
|
+
handle.write(b"ok")
|
|
167
|
+
return True, str(path)
|
|
168
|
+
except Exception as exc:
|
|
169
|
+
return False, str(exc)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _is_path_ready(path: Path, *, expect_file: bool = False) -> tuple[str, str]:
|
|
173
|
+
"""Read-only path readiness check for runtime/config/cache diagnostics."""
|
|
174
|
+
try:
|
|
175
|
+
path = path.expanduser()
|
|
176
|
+
if path.exists():
|
|
177
|
+
if expect_file and not path.is_file():
|
|
178
|
+
return "err", f"{path} exists but is not a file"
|
|
179
|
+
if not expect_file and not path.is_dir():
|
|
180
|
+
return "err", f"{path} exists but is not a directory"
|
|
181
|
+
writable_target = path.parent if expect_file else path
|
|
182
|
+
if os.access(writable_target, os.W_OK):
|
|
183
|
+
return "ok", str(path)
|
|
184
|
+
return "warn", f"{path} exists but may not be writable"
|
|
185
|
+
parent = path.parent
|
|
186
|
+
if parent.exists() and os.access(parent, os.W_OK):
|
|
187
|
+
return "warn", f"{path} not created yet; parent writable"
|
|
188
|
+
return "warn", f"{path} not found"
|
|
189
|
+
except Exception as exc:
|
|
190
|
+
return "err", str(exc)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _capture_cmd(cmd: list[str], timeout: float = 2.0) -> tuple[int, str]:
|
|
194
|
+
try:
|
|
195
|
+
result = subprocess.run(
|
|
196
|
+
cmd,
|
|
197
|
+
check=False,
|
|
198
|
+
stdout=subprocess.PIPE,
|
|
199
|
+
stderr=subprocess.PIPE,
|
|
200
|
+
text=True,
|
|
201
|
+
timeout=timeout,
|
|
202
|
+
)
|
|
203
|
+
return result.returncode, (result.stdout or result.stderr or "").strip()
|
|
204
|
+
except Exception as exc:
|
|
205
|
+
return 1, str(exc)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _expand_home(raw: str | None) -> str:
|
|
209
|
+
value = str(raw or "").strip()
|
|
210
|
+
if not value:
|
|
211
|
+
return ""
|
|
212
|
+
if value == "~":
|
|
213
|
+
return str(Path.home())
|
|
214
|
+
if value.startswith("~/") or value.startswith("~\\"):
|
|
215
|
+
return str(Path.home() / value[2:])
|
|
216
|
+
return value
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _npm_config_home() -> tuple[str, str]:
|
|
220
|
+
for key in ("npm_config_aria_code_home", "npm_config_aria_home", "npm_config_ariacode_home"):
|
|
221
|
+
value = _expand_home(os.getenv(key))
|
|
222
|
+
if value:
|
|
223
|
+
return str(Path(value).expanduser().resolve()), f"env:{key}"
|
|
224
|
+
npm = shutil.which("npm")
|
|
225
|
+
if not npm:
|
|
226
|
+
return "", ""
|
|
227
|
+
code, out = _capture_cmd([npm, "config", "get", "aria-code:home"], timeout=1.5)
|
|
228
|
+
value = _expand_home(out)
|
|
229
|
+
if code == 0 and value and value.lower() not in ("undefined", "null"):
|
|
230
|
+
return str(Path(value).expanduser().resolve()), "npm-config:aria-code:home"
|
|
231
|
+
return "", ""
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _platform_data_dir() -> Path:
|
|
235
|
+
system = platform.system().lower()
|
|
236
|
+
if system == "darwin":
|
|
237
|
+
return Path.home() / "Library" / "Application Support" / "Aria Code"
|
|
238
|
+
if system == "windows":
|
|
239
|
+
return Path(os.getenv("LOCALAPPDATA") or (Path.home() / "AppData" / "Local")) / "AriaCode"
|
|
240
|
+
return Path(os.getenv("XDG_DATA_HOME") or (Path.home() / ".local" / "share")) / "aria-code"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _platform_config_dir() -> Path:
|
|
244
|
+
system = platform.system().lower()
|
|
245
|
+
if system == "darwin":
|
|
246
|
+
return Path.home() / "Library" / "Application Support" / "Aria Code"
|
|
247
|
+
if system == "windows":
|
|
248
|
+
return Path(os.getenv("APPDATA") or (Path.home() / "AppData" / "Roaming")) / "AriaCode"
|
|
249
|
+
return Path(os.getenv("XDG_CONFIG_HOME") or (Path.home() / ".config")) / "aria-code"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _platform_cache_dir() -> Path:
|
|
253
|
+
system = platform.system().lower()
|
|
254
|
+
if system == "darwin":
|
|
255
|
+
return Path.home() / "Library" / "Caches" / "Aria Code"
|
|
256
|
+
if system == "windows":
|
|
257
|
+
return Path(os.getenv("LOCALAPPDATA") or (Path.home() / "AppData" / "Local")) / "AriaCode" / "Cache"
|
|
258
|
+
return Path(os.getenv("XDG_CACHE_HOME") or (Path.home() / ".cache")) / "aria-code"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _resolve_runtime_paths() -> dict[str, Any]:
|
|
262
|
+
legacy = Path.home() / ".aria-code"
|
|
263
|
+
source = ""
|
|
264
|
+
install_raw = os.getenv("ARIA_HOME") or os.getenv("ARIA_CODE_HOME") or ""
|
|
265
|
+
if install_raw:
|
|
266
|
+
install_dir = Path(_expand_home(install_raw)).expanduser().resolve()
|
|
267
|
+
source = "env:ARIA_HOME" if os.getenv("ARIA_HOME") else "env:ARIA_CODE_HOME"
|
|
268
|
+
else:
|
|
269
|
+
npm_home, npm_source = _npm_config_home()
|
|
270
|
+
if npm_home:
|
|
271
|
+
install_dir = Path(npm_home)
|
|
272
|
+
source = npm_source
|
|
273
|
+
elif legacy.exists():
|
|
274
|
+
install_dir = legacy
|
|
275
|
+
source = "legacy-existing"
|
|
276
|
+
else:
|
|
277
|
+
install_dir = _platform_data_dir()
|
|
278
|
+
source = "platform-default"
|
|
279
|
+
|
|
280
|
+
config_dir = Path(_expand_home(os.getenv("ARIA_CONFIG_DIR"))).expanduser().resolve() if os.getenv("ARIA_CONFIG_DIR") else _platform_config_dir()
|
|
281
|
+
cache_dir = Path(_expand_home(os.getenv("ARIA_CACHE_DIR"))).expanduser().resolve() if os.getenv("ARIA_CACHE_DIR") else _platform_cache_dir()
|
|
282
|
+
info_file = install_dir / ".npm-install-info.json"
|
|
283
|
+
config_info_file = config_dir / "install.json"
|
|
284
|
+
legacy_info_file = legacy / ".npm-install-info.json"
|
|
285
|
+
info_candidates = []
|
|
286
|
+
for candidate in (info_file, config_info_file, legacy_info_file):
|
|
287
|
+
if candidate not in info_candidates:
|
|
288
|
+
info_candidates.append(candidate)
|
|
289
|
+
return {
|
|
290
|
+
"install_dir": install_dir,
|
|
291
|
+
"install_dir_source": source,
|
|
292
|
+
"legacy_install_dir": legacy,
|
|
293
|
+
"venv_dir": install_dir / ".venv",
|
|
294
|
+
"venv_py": install_dir / ".venv" / ("Scripts/python.exe" if platform.system().lower() == "windows" else "bin/python"),
|
|
295
|
+
"aria_cli": install_dir / "aria_cli.py",
|
|
296
|
+
"config_dir": config_dir,
|
|
297
|
+
"cache_dir": cache_dir,
|
|
298
|
+
"info_file": info_file,
|
|
299
|
+
"config_info_file": config_info_file,
|
|
300
|
+
"legacy_info_file": legacy_info_file,
|
|
301
|
+
"info_candidates": info_candidates,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _provider_key_present(module_name: str, key_fn: str) -> bool:
|
|
306
|
+
"""Call a client module's own key-lookup function (e.g.
|
|
307
|
+
openai_image_client._api_key) rather than re-implementing env-var/
|
|
308
|
+
providers.json lookup here — avoids doctor.py silently drifting out of
|
|
309
|
+
sync with each module's actual lookup logic."""
|
|
310
|
+
try:
|
|
311
|
+
import importlib
|
|
312
|
+
mod = importlib.import_module(module_name)
|
|
313
|
+
result = getattr(mod, key_fn)()
|
|
314
|
+
if isinstance(result, tuple):
|
|
315
|
+
return all(bool(v) for v in result)
|
|
316
|
+
return bool(result)
|
|
317
|
+
except Exception:
|
|
318
|
+
return False
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def integration_checks() -> List[DoctorCheck]:
|
|
322
|
+
"""Health checks for the report/media generation integrations added
|
|
323
|
+
across the MCP-exposure work: ffmpeg (video editing), faster-whisper +
|
|
324
|
+
opencv (video analysis), self-hosted image generation, and the
|
|
325
|
+
API-key-gated cloud providers (OpenAI images, Kling/Runway video, Canva,
|
|
326
|
+
Figma). None of these are required for core aria-code usage — missing
|
|
327
|
+
ones are "warn", not "err", and each points at how to fix it."""
|
|
328
|
+
checks: List[DoctorCheck] = []
|
|
329
|
+
|
|
330
|
+
if shutil.which("ffmpeg"):
|
|
331
|
+
checks.append(_check("integration:ffmpeg", "ok", "found on PATH — local video editing (aria.video.*) available"))
|
|
332
|
+
else:
|
|
333
|
+
checks.append(_check(
|
|
334
|
+
"integration:ffmpeg", "warn", "not found on PATH",
|
|
335
|
+
"Install ffmpeg (e.g. `brew install ffmpeg`) to use aria.video.trim/concat/overlay_*/convert/change_speed.",
|
|
336
|
+
))
|
|
337
|
+
|
|
338
|
+
if _has_module("faster_whisper"):
|
|
339
|
+
checks.append(_check("integration:faster_whisper", "ok", "available — aria.video.transcribe ready"))
|
|
340
|
+
else:
|
|
341
|
+
checks.append(_check(
|
|
342
|
+
"integration:faster_whisper", "warn", "optional 'video_analysis' extra not installed",
|
|
343
|
+
"pip install 'aria-code[video_analysis]' to use aria.video.transcribe.",
|
|
344
|
+
))
|
|
345
|
+
|
|
346
|
+
if _has_module("cv2"):
|
|
347
|
+
checks.append(_check("integration:opencv", "ok", "available — aria.video.detect_scenes ready"))
|
|
348
|
+
else:
|
|
349
|
+
checks.append(_check(
|
|
350
|
+
"integration:opencv", "warn", "optional 'video' extra not installed",
|
|
351
|
+
"pip install 'aria-code[video]' to use aria.video.detect_scenes.",
|
|
352
|
+
))
|
|
353
|
+
|
|
354
|
+
if _has_module("diffusers") and _has_module("torch"):
|
|
355
|
+
checks.append(_check("integration:local_image_gen", "ok", "available — aria.report.generate_image_local/edit_image_local ready (no API key needed)"))
|
|
356
|
+
else:
|
|
357
|
+
checks.append(_check(
|
|
358
|
+
"integration:local_image_gen", "warn", "optional 'image_gen' extra not installed",
|
|
359
|
+
"pip install 'aria-code[image_gen]' for free local image generation, or use aria.report.generate_image (OpenAI, needs an API key).",
|
|
360
|
+
))
|
|
361
|
+
|
|
362
|
+
key_checks = [
|
|
363
|
+
("integration:openai_images", "openai_image_client", "_api_key", "/apikey set openai sk-...", "aria.report.generate_image/edit_image"),
|
|
364
|
+
("integration:kling", "kling_video_client", "_keys", "/apikey set kling <access_key>:<secret_key>", "aria.video.generate_submit (provider=kling)"),
|
|
365
|
+
("integration:runway", "runway_video_client", "_api_key", "/apikey set runway <key>", "aria.video.generate_submit (provider=runway)"),
|
|
366
|
+
("integration:figma", "figma_client", "_token", "/apikey set figma <personal_access_token>", "aria.figma.read_file/comments"),
|
|
367
|
+
]
|
|
368
|
+
for name, module_name, key_fn, setup_hint, gates in key_checks:
|
|
369
|
+
if _provider_key_present(module_name, key_fn):
|
|
370
|
+
checks.append(_check(name, "ok", f"key configured — {gates} usable"))
|
|
371
|
+
else:
|
|
372
|
+
checks.append(_check(name, "warn", "no API key configured", f"{setup_hint} to use {gates}."))
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
# Read stored config directly rather than calling _access_token(),
|
|
376
|
+
# which refreshes an expired token over the network — this function
|
|
377
|
+
# is a local-first, no-mutation health check, not a live probe.
|
|
378
|
+
from canva_client import _load_canva_config
|
|
379
|
+
|
|
380
|
+
connected = bool(_load_canva_config().get("access_token"))
|
|
381
|
+
checks.append(_check(
|
|
382
|
+
"integration:canva", "ok" if connected else "warn",
|
|
383
|
+
"connected — aria.report.canva_design/canva_upload_asset usable" if connected else "not connected",
|
|
384
|
+
"" if connected else "Run /canva connect <client_id> <client_secret> (needs a Canva Connect app + brand template).",
|
|
385
|
+
))
|
|
386
|
+
except Exception as exc:
|
|
387
|
+
checks.append(_check("integration:canva", "warn", f"could not read config: {exc}", "Run /canva connect <client_id> <client_secret>."))
|
|
388
|
+
|
|
389
|
+
return checks
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def npm_platform_key(system: str = "", machine: str = "") -> str:
|
|
393
|
+
"""This machine's platform key, matching npm/lib/platform.js PLATFORM_KEYS.
|
|
394
|
+
|
|
395
|
+
Returns "" for a platform with no prebuilt binary, which is a real answer:
|
|
396
|
+
those users install from pip.
|
|
397
|
+
"""
|
|
398
|
+
system = (system or platform.system()).lower()
|
|
399
|
+
machine = (machine or platform.machine()).lower()
|
|
400
|
+
os_part = {"darwin": "darwin", "linux": "linux", "windows": "win32"}.get(system, "")
|
|
401
|
+
arch_part = {
|
|
402
|
+
"x86_64": "x64", "amd64": "x64",
|
|
403
|
+
"arm64": "arm64", "aarch64": "arm64",
|
|
404
|
+
}.get(machine, "")
|
|
405
|
+
if not os_part or not arch_part:
|
|
406
|
+
return ""
|
|
407
|
+
key = f"{os_part}-{arch_part}"
|
|
408
|
+
# win32-arm64 and linux-arm64-on-win are not built; see PLATFORM_KEYS.
|
|
409
|
+
return key if key in (
|
|
410
|
+
"darwin-arm64", "darwin-x64", "linux-x64", "linux-arm64", "win32-x64"
|
|
411
|
+
) else ""
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _platform_package_check(npm: Optional[str]) -> Optional[DoctorCheck]:
|
|
415
|
+
"""Did this machine's prebuilt-binary package actually get installed?
|
|
416
|
+
|
|
417
|
+
The launcher lists one package per platform as an optionalDependency, and
|
|
418
|
+
npm skips an optionalDependency it cannot resolve *silently* — by design,
|
|
419
|
+
that is what "optional" means. So the failure mode this catches is a
|
|
420
|
+
launcher with no binary behind it, which reports only when the user tries
|
|
421
|
+
to run something.
|
|
422
|
+
|
|
423
|
+
It is not hypothetical: @artheras/aria-code-darwin-arm64 and its four
|
|
424
|
+
siblings have never been published, so every npm install to date has been
|
|
425
|
+
in exactly this state.
|
|
426
|
+
"""
|
|
427
|
+
key = npm_platform_key()
|
|
428
|
+
if not key:
|
|
429
|
+
return _check(
|
|
430
|
+
"npm_runtime:binary",
|
|
431
|
+
"skip",
|
|
432
|
+
f"no prebuilt binary for {platform.system()}/{platform.machine()}",
|
|
433
|
+
"Install with pip instead: pip install -U \"aria-code<4\"",
|
|
434
|
+
)
|
|
435
|
+
pkg = f"@artheras/aria-code-{key}"
|
|
436
|
+
if not npm:
|
|
437
|
+
return None
|
|
438
|
+
code, root = _capture_cmd([npm, "root", "-g"], timeout=1.5)
|
|
439
|
+
if code != 0 or not root:
|
|
440
|
+
return None
|
|
441
|
+
installed = Path(root) / "@artheras" / f"aria-code-{key}"
|
|
442
|
+
if installed.is_dir():
|
|
443
|
+
return _check("npm_runtime:binary", "ok", f"{pkg} at {installed}")
|
|
444
|
+
return _check(
|
|
445
|
+
"npm_runtime:binary",
|
|
446
|
+
"err",
|
|
447
|
+
f"{pkg} is not installed — the launcher has no binary to run",
|
|
448
|
+
"npm skips an optional dependency it cannot resolve without saying so. "
|
|
449
|
+
+ NPM_REINSTALL_HINT,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _npm_install_detected(paths: dict) -> bool:
|
|
454
|
+
"""Is there an npm-launcher installation for these checks to be about?
|
|
455
|
+
|
|
456
|
+
Any one of these is proof: the launcher's own aria_cli.py, the install
|
|
457
|
+
metadata it writes, or an install dir that came from npm config rather
|
|
458
|
+
than a platform default.
|
|
459
|
+
"""
|
|
460
|
+
if paths["aria_cli"].is_file():
|
|
461
|
+
return True
|
|
462
|
+
if any(candidate.is_file() for candidate in paths["info_candidates"]):
|
|
463
|
+
return True
|
|
464
|
+
return str(paths.get("install_dir_source", "")).startswith("npm")
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def npm_runtime_checks(*, cwd: Optional[Path] = None) -> List[DoctorCheck]:
|
|
468
|
+
"""Return npm launcher/runtime path diagnostics."""
|
|
469
|
+
checks: List[DoctorCheck] = []
|
|
470
|
+
paths = _resolve_runtime_paths()
|
|
471
|
+
cwd = (cwd or Path.cwd()).expanduser().resolve()
|
|
472
|
+
source_cli = cwd / "aria_cli.py"
|
|
473
|
+
source_venv_py = cwd / ".venv" / ("Scripts/python.exe" if platform.system().lower() == "windows" else "bin/python")
|
|
474
|
+
using_source_checkout = source_cli.is_file() and not paths["aria_cli"].is_file()
|
|
475
|
+
|
|
476
|
+
# A pip install has no npm launcher, so every check below is about
|
|
477
|
+
# something that was never supposed to exist. Reporting them made a clean
|
|
478
|
+
# `pip install aria-code` end in "1 errors", and the repair suggestion —
|
|
479
|
+
# `node $(npm root -g)/aria-code/scripts/postinstall.js` — is not just
|
|
480
|
+
# wrong for that user, it is unrunnable on a machine with no node.
|
|
481
|
+
if not using_source_checkout and not _npm_install_detected(paths):
|
|
482
|
+
checks.append(_check(
|
|
483
|
+
"npm_runtime",
|
|
484
|
+
"skip",
|
|
485
|
+
"not an npm install — nothing to check",
|
|
486
|
+
"These checks apply to `npm install -g @artheras/aria-code`.",
|
|
487
|
+
))
|
|
488
|
+
return checks
|
|
489
|
+
|
|
490
|
+
node = shutil.which("node")
|
|
491
|
+
if node:
|
|
492
|
+
code, version = _capture_cmd([node, "--version"], timeout=1.5)
|
|
493
|
+
checks.append(_check("npm_runtime:node", "ok" if code == 0 else "warn", f"{node} {version}".strip()))
|
|
494
|
+
else:
|
|
495
|
+
checks.append(_check("npm_runtime:node", "warn", "node not found", "Install Node.js if you use the npm launcher."))
|
|
496
|
+
|
|
497
|
+
npm = shutil.which("npm")
|
|
498
|
+
if npm:
|
|
499
|
+
_code, version = _capture_cmd([npm, "--version"], timeout=1.5)
|
|
500
|
+
prefix_code, prefix = _capture_cmd([npm, "config", "get", "prefix"], timeout=1.5)
|
|
501
|
+
root_code, root = _capture_cmd([npm, "root", "-g"], timeout=1.5)
|
|
502
|
+
detail = f"{npm} v{version or '?'}"
|
|
503
|
+
if prefix_code == 0 and prefix:
|
|
504
|
+
detail += f"; prefix={prefix}"
|
|
505
|
+
if root_code == 0 and root:
|
|
506
|
+
detail += f"; root={root}"
|
|
507
|
+
checks.append(_check("npm_runtime:npm", "ok", detail))
|
|
508
|
+
else:
|
|
509
|
+
checks.append(_check("npm_runtime:npm", "warn", "npm not found", "Install Node.js/npm if you use npm install -g aria-code."))
|
|
510
|
+
|
|
511
|
+
binary_check = _platform_package_check(npm)
|
|
512
|
+
if binary_check is not None:
|
|
513
|
+
checks.append(binary_check)
|
|
514
|
+
|
|
515
|
+
install_status, install_detail = _is_path_ready(paths["install_dir"])
|
|
516
|
+
install_suggestion = ""
|
|
517
|
+
if using_source_checkout:
|
|
518
|
+
install_suggestion = f"Set ARIA_HOME={cwd} when launching through npm, or run npm install -g aria-code."
|
|
519
|
+
elif not paths["aria_cli"].exists():
|
|
520
|
+
install_suggestion = "Run npm install -g aria-code or set ARIA_HOME to the cloned repo."
|
|
521
|
+
checks.append(_check(
|
|
522
|
+
"npm_runtime:install_dir",
|
|
523
|
+
"warn" if using_source_checkout else ("ok" if paths["aria_cli"].exists() else install_status),
|
|
524
|
+
(
|
|
525
|
+
f"{install_detail}; source={paths['install_dir_source']}"
|
|
526
|
+
+ (f"; source_checkout={cwd}" if using_source_checkout else "")
|
|
527
|
+
),
|
|
528
|
+
install_suggestion,
|
|
529
|
+
))
|
|
530
|
+
|
|
531
|
+
actual_cli = paths["aria_cli"] if paths["aria_cli"].is_file() else (source_cli if source_cli.is_file() else paths["aria_cli"])
|
|
532
|
+
checks.append(_check(
|
|
533
|
+
"npm_runtime:aria_cli",
|
|
534
|
+
"ok" if actual_cli.is_file() else "err",
|
|
535
|
+
str(actual_cli),
|
|
536
|
+
# Was "node $(npm root -g)/aria-code/scripts/postinstall.js". That file
|
|
537
|
+
# was deleted with the launcher rewrite, so the suggestion sent anyone
|
|
538
|
+
# who hit it to a path that does not exist.
|
|
539
|
+
NPM_REINSTALL_HINT if not actual_cli.is_file() else "",
|
|
540
|
+
))
|
|
541
|
+
|
|
542
|
+
actual_venv_py = paths["venv_py"] if paths["venv_py"].is_file() else (source_venv_py if source_venv_py.is_file() else paths["venv_py"])
|
|
543
|
+
checks.append(_check(
|
|
544
|
+
"npm_runtime:venv",
|
|
545
|
+
"ok" if actual_venv_py.is_file() else "warn",
|
|
546
|
+
str(actual_venv_py),
|
|
547
|
+
# No `repair` or `update-engine` script exists any more. A missing venv
|
|
548
|
+
# is also no longer a fault on an npm install, which ships a binary and
|
|
549
|
+
# builds no venv at all — so this is only meaningful for the legacy
|
|
550
|
+
# layout or a source checkout.
|
|
551
|
+
(NPM_REINSTALL_HINT if not actual_venv_py.is_file() else ""),
|
|
552
|
+
))
|
|
553
|
+
|
|
554
|
+
info_hit = next((p for p in paths["info_candidates"] if p.is_file()), None)
|
|
555
|
+
checks.append(_check(
|
|
556
|
+
"npm_runtime:install_info",
|
|
557
|
+
"ok" if info_hit else "warn",
|
|
558
|
+
str(info_hit) if info_hit else "none found; checked " + ", ".join(str(p) for p in paths["info_candidates"]),
|
|
559
|
+
NPM_REINSTALL_HINT if not info_hit else "",
|
|
560
|
+
))
|
|
561
|
+
|
|
562
|
+
config_status, config_detail = _is_path_ready(paths["config_dir"])
|
|
563
|
+
cache_status, cache_detail = _is_path_ready(paths["cache_dir"])
|
|
564
|
+
checks.append(_check("npm_runtime:config_dir", config_status, config_detail))
|
|
565
|
+
checks.append(_check("npm_runtime:cache_dir", cache_status, cache_detail))
|
|
566
|
+
return checks
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _check_ollama(url: str, timeout: float = 1.5, *, required: bool = True) -> DoctorCheck:
|
|
570
|
+
"""Report on the local Ollama server.
|
|
571
|
+
|
|
572
|
+
``required`` says whether this install actually depends on it. Ollama is one
|
|
573
|
+
of several ways to get a model, and reporting its absence as a warning meant
|
|
574
|
+
a perfectly healthy cloud-only install could never show green — so the
|
|
575
|
+
signal people were meant to read stopped meaning anything.
|
|
576
|
+
"""
|
|
577
|
+
try:
|
|
578
|
+
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
|
579
|
+
with opener.open(f"{url.rstrip('/')}/api/tags", timeout=timeout) as response:
|
|
580
|
+
data = json.loads(response.read().decode("utf-8"))
|
|
581
|
+
models = [str(model.get("name", "")) for model in data.get("models", []) if model.get("name")]
|
|
582
|
+
if models:
|
|
583
|
+
return _check("ollama", "ok", f"{len(models)} models: {', '.join(models[:4])}")
|
|
584
|
+
if required:
|
|
585
|
+
return _check("ollama", "warn", "running but no models installed", "ollama pull qwen2.5-coder:7b")
|
|
586
|
+
return _check("ollama", "skip", "running, no models installed (not this install's provider)")
|
|
587
|
+
except Exception as exc:
|
|
588
|
+
if required:
|
|
589
|
+
return _check("ollama", "warn", f"not reachable at {url}: {exc}",
|
|
590
|
+
"Start Ollama, or switch provider with /model.")
|
|
591
|
+
return _check("ollama", "skip", "not running (offline mode unused — this install uses another provider)")
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def provider_health_checks(snapshot: Optional[List[Dict[str, Any]]] = None) -> List[DoctorCheck]:
|
|
595
|
+
"""Convert data provider health state into doctor checks."""
|
|
596
|
+
if snapshot is None:
|
|
597
|
+
try:
|
|
598
|
+
from packages.aria_services.provider_health import GLOBAL_PROVIDER_HEALTH
|
|
599
|
+
|
|
600
|
+
snapshot = GLOBAL_PROVIDER_HEALTH.snapshot()
|
|
601
|
+
except Exception:
|
|
602
|
+
snapshot = []
|
|
603
|
+
|
|
604
|
+
if not snapshot:
|
|
605
|
+
return [
|
|
606
|
+
_check(
|
|
607
|
+
"data_provider_health",
|
|
608
|
+
"warn",
|
|
609
|
+
"no provider calls recorded in this session",
|
|
610
|
+
"Run /quote, /ta, /analyze, or /report to populate provider health.",
|
|
611
|
+
)
|
|
612
|
+
]
|
|
613
|
+
|
|
614
|
+
checks: List[DoctorCheck] = []
|
|
615
|
+
for row in snapshot:
|
|
616
|
+
provider = str(row.get("provider") or "provider")
|
|
617
|
+
status = str(row.get("status") or "unknown")
|
|
618
|
+
failures = int(row.get("failures") or 0)
|
|
619
|
+
cooldown = bool(row.get("cooldown_active"))
|
|
620
|
+
remaining = int(row.get("cooldown_remaining_seconds") or 0)
|
|
621
|
+
detail = status
|
|
622
|
+
if failures:
|
|
623
|
+
detail += f", failures={failures}"
|
|
624
|
+
if cooldown:
|
|
625
|
+
detail += f", cooldown={remaining}s"
|
|
626
|
+
if row.get("last_error"):
|
|
627
|
+
detail += f", last={row.get('last_error')}"
|
|
628
|
+
last_success = _format_age((time.time() - float(row.get("last_success_at"))) if row.get("last_success_at") else None)
|
|
629
|
+
if last_success:
|
|
630
|
+
detail += f", last_ok={last_success}"
|
|
631
|
+
check_status = "ok" if status == "ok" else "warn" if row.get("last_error_category") != "auth" else "err"
|
|
632
|
+
suggestion = "Wait for cooldown or switch provider/API key." if cooldown else ""
|
|
633
|
+
if row.get("last_error_category") == "auth":
|
|
634
|
+
suggestion = "Check provider API key with /apikey list or /apikey set."
|
|
635
|
+
checks.append(_check(f"data_provider:{provider}", check_status, detail, suggestion))
|
|
636
|
+
return checks
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def provider_health_summary(snapshot: Optional[List[Dict[str, Any]]] = None) -> DoctorCheck:
|
|
640
|
+
"""Summarise provider health into one dashboard-style check."""
|
|
641
|
+
if snapshot is None:
|
|
642
|
+
try:
|
|
643
|
+
from packages.aria_services.provider_health import GLOBAL_PROVIDER_HEALTH
|
|
644
|
+
|
|
645
|
+
snapshot = GLOBAL_PROVIDER_HEALTH.snapshot()
|
|
646
|
+
except Exception:
|
|
647
|
+
snapshot = []
|
|
648
|
+
try:
|
|
649
|
+
from packages.aria_services.provider_health import summarize_provider_health
|
|
650
|
+
|
|
651
|
+
summary = summarize_provider_health(snapshot)
|
|
652
|
+
return _check("provider_health_summary", summary.status, summary.detail, summary.suggestion)
|
|
653
|
+
except Exception:
|
|
654
|
+
if not snapshot:
|
|
655
|
+
return _check(
|
|
656
|
+
"provider_health_summary",
|
|
657
|
+
"warn",
|
|
658
|
+
"no provider calls recorded in this session",
|
|
659
|
+
"Run /quote, /ta, /analyze, or /report to populate provider health.",
|
|
660
|
+
)
|
|
661
|
+
total = len(snapshot)
|
|
662
|
+
return _check(
|
|
663
|
+
"provider_health_summary",
|
|
664
|
+
"warn",
|
|
665
|
+
f"{total} providers",
|
|
666
|
+
"Run /doctor --network or inspect /apikey.",
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def _iter_required_modules() -> Iterable[tuple[str, str]]:
|
|
671
|
+
"""Core dependencies: a plain `pip install aria-code` must provide these.
|
|
672
|
+
|
|
673
|
+
Kept in step with [project].dependencies in pyproject.toml;
|
|
674
|
+
tests/test_first_run_works.py fails if one is listed here but not there.
|
|
675
|
+
"""
|
|
676
|
+
yield "aiohttp", "async HTTP"
|
|
677
|
+
yield "rich", "terminal UI"
|
|
678
|
+
yield "prompt_toolkit", "interactive input"
|
|
679
|
+
yield "requests", "HTTP client"
|
|
680
|
+
# The default model is google/gemini-3.8-flash, so without this a fresh
|
|
681
|
+
# install cannot answer a single prompt.
|
|
682
|
+
yield "google.genai", "Gemini / Vertex AI (the default model)"
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def _iter_optional_modules() -> Iterable[tuple[str, str, str]]:
|
|
686
|
+
"""Optional extras: (module, purpose, extra that installs it).
|
|
687
|
+
|
|
688
|
+
These used to sit in the required list, so a clean install of aria-code
|
|
689
|
+
always ended doctor with "1 errors" — for akshare, which pyproject has
|
|
690
|
+
always declared as the optional `cn` extra. Missing an optional extra is a
|
|
691
|
+
choice the user made, not a fault.
|
|
692
|
+
"""
|
|
693
|
+
yield "pandas", "dataframes", "finance"
|
|
694
|
+
yield "numpy", "numeric processing", "finance"
|
|
695
|
+
yield "yfinance", "US/HK/global market data", "finance"
|
|
696
|
+
yield "akshare", "China A-share market data", "cn"
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def sandbox_check(config: Dict[str, Any]) -> DoctorCheck:
|
|
700
|
+
"""Report the actual command confinement available on this machine."""
|
|
701
|
+
from aria_code.safety.sandbox import capability
|
|
702
|
+
mode = str(config.get("permission_mode", "workspace-write"))
|
|
703
|
+
kind = "disabled" if mode == "full-access" else capability(config.get("os_sandbox"))
|
|
704
|
+
if kind == "policy-only":
|
|
705
|
+
return _check("command_sandbox", "warn", f"{mode}: command policy only; no OS filesystem/network confinement",
|
|
706
|
+
"Install bubblewrap on Linux; Windows currently uses command policy only.")
|
|
707
|
+
if kind == "disabled":
|
|
708
|
+
return _check("command_sandbox", "skip", "OS command sandbox disabled by your settings")
|
|
709
|
+
return _check("command_sandbox", "ok", f"{mode}: {kind}; violations fail the command without an unconfined retry")
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
def run_doctor(
|
|
713
|
+
config: Optional[Dict[str, Any]] = None,
|
|
714
|
+
*,
|
|
715
|
+
cwd: Optional[Path] = None,
|
|
716
|
+
check_network: bool = False,
|
|
717
|
+
context_stats: Optional[Dict[str, Any]] = None,
|
|
718
|
+
) -> DoctorReport:
|
|
719
|
+
"""Run local-first diagnostics without mutating user configuration."""
|
|
720
|
+
|
|
721
|
+
config = config or {}
|
|
722
|
+
cwd = (cwd or Path.cwd()).expanduser().resolve()
|
|
723
|
+
checks: List[DoctorCheck] = []
|
|
724
|
+
|
|
725
|
+
pyver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
726
|
+
if sys.version_info >= (3, 10):
|
|
727
|
+
checks.append(_check("python", "ok", f"{pyver} on {platform.system()}"))
|
|
728
|
+
else:
|
|
729
|
+
checks.append(_check("python", "err", f"{pyver} is unsupported", "Install Python 3.10 or newer."))
|
|
730
|
+
|
|
731
|
+
drift = _check_python_drift()
|
|
732
|
+
if drift is not None:
|
|
733
|
+
checks.append(drift)
|
|
734
|
+
|
|
735
|
+
checks.append(sandbox_check(config))
|
|
736
|
+
|
|
737
|
+
if context_stats:
|
|
738
|
+
_fill = float(context_stats.get("fill_ratio") or 0.0)
|
|
739
|
+
_thr = float(context_stats.get("threshold") or 0.78)
|
|
740
|
+
_ctx_status = "ok" if _fill < _thr else ("warn" if _fill < 0.95 else "err")
|
|
741
|
+
checks.append(_check(
|
|
742
|
+
"context",
|
|
743
|
+
_ctx_status,
|
|
744
|
+
f"{context_stats.get('estimated_tokens', 0)}/{context_stats.get('max_tokens', 0)} tokens "
|
|
745
|
+
f"({context_stats.get('fill_pct', 0)}%) across {context_stats.get('message_count', 0)} messages",
|
|
746
|
+
"" if _ctx_status == "ok" else "/compact to shrink the conversation before the window overflows.",
|
|
747
|
+
))
|
|
748
|
+
|
|
749
|
+
for module, purpose in _iter_required_modules():
|
|
750
|
+
if _has_module(module):
|
|
751
|
+
checks.append(_check(f"package:{module}", "ok", purpose))
|
|
752
|
+
else:
|
|
753
|
+
checks.append(_check(f"package:{module}", "err", f"{purpose} missing",
|
|
754
|
+
"Reinstall: pip install -U \"aria-code<4\""))
|
|
755
|
+
|
|
756
|
+
for module, purpose, extra in _iter_optional_modules():
|
|
757
|
+
if _has_module(module):
|
|
758
|
+
checks.append(_check(f"package:{module}", "ok", purpose))
|
|
759
|
+
else:
|
|
760
|
+
checks.append(_check(f"package:{module}", "skip", f"{purpose} not installed (optional)",
|
|
761
|
+
f"pip install 'aria-code[{extra}]<4'"))
|
|
762
|
+
|
|
763
|
+
for module in ("data_service", "artifacts", "report_generator", "backtest_report"):
|
|
764
|
+
checks.append(
|
|
765
|
+
_check(f"module:{module}", "ok" if _has_module(module) else "err", "importable" if _has_module(module) else "missing from install")
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
checks.extend(npm_runtime_checks(cwd=cwd))
|
|
769
|
+
checks.extend(integration_checks())
|
|
770
|
+
|
|
771
|
+
try:
|
|
772
|
+
from artifacts import artifact_root, artifact_summary
|
|
773
|
+
|
|
774
|
+
root = artifact_root()
|
|
775
|
+
writable, detail = _is_writable(root)
|
|
776
|
+
checks.append(_check("artifact_root", "ok" if writable else "err", detail, "Set ARIA_ARTIFACT_ROOT to a writable folder."))
|
|
777
|
+
summary = artifact_summary(root)
|
|
778
|
+
total = int(summary.get("total") or 0)
|
|
779
|
+
total_size = int(summary.get("total_size_bytes") or 0)
|
|
780
|
+
by_kind = summary.get("by_kind") or {}
|
|
781
|
+
detail_bits = [f"{total} artifacts", f"{total_size} bytes"]
|
|
782
|
+
if by_kind:
|
|
783
|
+
detail_bits.extend(f"{kind}={count}" for kind, count in list(by_kind.items())[:4])
|
|
784
|
+
checks.append(
|
|
785
|
+
_check(
|
|
786
|
+
"artifact_inventory",
|
|
787
|
+
"ok" if total else "warn",
|
|
788
|
+
", ".join(detail_bits),
|
|
789
|
+
"Run /artifacts stats or generate a report to populate local outputs.",
|
|
790
|
+
)
|
|
791
|
+
)
|
|
792
|
+
except Exception as exc:
|
|
793
|
+
checks.append(_check("artifact_root", "err", str(exc)))
|
|
794
|
+
|
|
795
|
+
config_dir = aria_home()
|
|
796
|
+
config_file = config_dir / "config.json"
|
|
797
|
+
if config_file.exists():
|
|
798
|
+
checks.append(_check("config", "ok", str(config_file)))
|
|
799
|
+
else:
|
|
800
|
+
checks.append(_check("config", "warn", f"{config_file} not found", "Run aria-code once or use /config set key=value."))
|
|
801
|
+
|
|
802
|
+
data_sharing = bool(config.get("data_sharing", False))
|
|
803
|
+
feedback_upload = bool(config.get("feedback_upload", False))
|
|
804
|
+
privacy_detail = f"data_sharing={data_sharing}, feedback_upload={feedback_upload}"
|
|
805
|
+
checks.append(_check("privacy", "ok", privacy_detail))
|
|
806
|
+
|
|
807
|
+
try:
|
|
808
|
+
from datasources.router import DataRouter
|
|
809
|
+
|
|
810
|
+
sources = DataRouter().list_sources()
|
|
811
|
+
configured = [src["name"] for src in sources if src.get("configured")]
|
|
812
|
+
missing = [src["name"] for src in sources if src.get("needs_key") and not src.get("configured")]
|
|
813
|
+
status = "ok" if configured else "warn"
|
|
814
|
+
detail = f"configured: {', '.join(configured) or 'none'}"
|
|
815
|
+
suggestion = f"optional keys missing: {', '.join(missing)}" if missing else ""
|
|
816
|
+
checks.append(_check("datasources", status, detail, suggestion))
|
|
817
|
+
except Exception as exc:
|
|
818
|
+
checks.append(_check("datasources", "warn", str(exc)))
|
|
819
|
+
|
|
820
|
+
checks.append(provider_health_summary())
|
|
821
|
+
checks.extend(provider_health_checks())
|
|
822
|
+
|
|
823
|
+
# Ollama only matters when this install routes through it. A user on the
|
|
824
|
+
# gateway or a cloud key never wants it, and flagging that as a warning is
|
|
825
|
+
# how a green report becomes unreachable.
|
|
826
|
+
_uses_ollama = (
|
|
827
|
+
str(config.get("local_provider") or "").lower() == "ollama"
|
|
828
|
+
or not str(config.get("model") or "").strip()
|
|
829
|
+
or ":" in str(config.get("model") or "") # bare Ollama-style tag, e.g. qwen2.5-coder:1.5b
|
|
830
|
+
)
|
|
831
|
+
if check_network:
|
|
832
|
+
checks.append(_check_ollama(
|
|
833
|
+
str(config.get("ollama_url") or "http://localhost:11434"),
|
|
834
|
+
required=_uses_ollama,
|
|
835
|
+
))
|
|
836
|
+
else:
|
|
837
|
+
# A check that did not run is not a warning about the thing it checks.
|
|
838
|
+
checks.append(_check("ollama", "skip", "network check not requested",
|
|
839
|
+
"Run /doctor --network to probe the local server."))
|
|
840
|
+
|
|
841
|
+
if (cwd / ".ariarc").exists():
|
|
842
|
+
checks.append(_check("project_config", "ok", str(cwd / ".ariarc")))
|
|
843
|
+
else:
|
|
844
|
+
checks.append(_check("project_config", "warn", ".ariarc not found", "Optional: add .ariarc for project-local settings."))
|
|
845
|
+
|
|
846
|
+
return DoctorReport(checks=checks)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def format_doctor_plain(report: DoctorReport) -> str:
|
|
850
|
+
marks = {"ok": "OK", "warn": "WARN", "err": "ERR", "skip": "--"}
|
|
851
|
+
lines = ["Aria Code doctor"]
|
|
852
|
+
for check in report.checks:
|
|
853
|
+
suffix = f" — {check.detail}" if check.detail else ""
|
|
854
|
+
if check.suggestion:
|
|
855
|
+
suffix += f" ({check.suggestion})"
|
|
856
|
+
lines.append(f"{marks.get(check.status, check.status.upper()):<4} {check.name}{suffix}")
|
|
857
|
+
skipped = sum(1 for check in report.checks if check.status == "skip")
|
|
858
|
+
tail = f" · {skipped} skipped" if skipped else ""
|
|
859
|
+
lines.append(f"{report.passed} passed · {report.warnings} warnings · {report.errors} errors{tail}")
|
|
860
|
+
return "\n".join(lines)
|