aria-code 0.52.0__py3-none-any.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_code/__init__.py +42 -0
- aria_code/_version.py +20 -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 +34 -0
- aria_code/agents/base.py +318 -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 +303 -0
- aria_code/agents/financial/fundamental.py +191 -0
- aria_code/agents/financial/macro.py +99 -0
- aria_code/agents/financial/news.py +207 -0
- aria_code/agents/financial/risk.py +165 -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 +258 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +363 -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 +158 -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 +492 -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/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 +15 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +95 -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/bootstrap.py +145 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +16 -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 +369 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +239 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
- aria_code/apps/cli/commands/broker_cmds.py +1234 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +87 -0
- aria_code/apps/cli/commands/core_cmds.py +2520 -0
- aria_code/apps/cli/commands/data_cmds.py +457 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -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 +434 -0
- aria_code/apps/cli/commands/market_cmds.py +1381 -0
- aria_code/apps/cli/commands/market_context.py +425 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2153 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
- aria_code/apps/cli/commands/report.py +437 -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 +669 -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 +79 -0
- aria_code/apps/cli/commands/workflow_cmds.py +480 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +78 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/deterministic.py +191 -0
- aria_code/apps/cli/direct.py +48 -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 +196 -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 +1309 -0
- aria_code/apps/cli/handlers/market_handlers.py +2513 -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/helpers.py +302 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +297 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +276 -0
- aria_code/apps/cli/intent_signals.py +344 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +536 -0
- aria_code/apps/cli/main.py +29 -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/model_catalog.py +620 -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 +59 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +53 -0
- aria_code/apps/cli/prompts/select.py +142 -0
- aria_code/apps/cli/prompts/system_prompts.py +552 -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 +521 -0
- aria_code/apps/cli/providers/chat_routing.py +128 -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 +1594 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
- aria_code/apps/cli/providers/runtime_bridge.py +353 -0
- aria_code/apps/cli/providers/vertexai_stream.py +316 -0
- aria_code/apps/cli/runtime_consumer.py +657 -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_store.py +112 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +823 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +106 -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 +180 -0
- aria_code/apps/cli/tools/market_tools.py +620 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +726 -0
- aria_code/apps/cli/tools/write_tools.py +845 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +153 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1597 -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 +6743 -0
- aria_code/aria_daemon.py +1398 -0
- aria_code/aria_feishu_bot.py +1423 -0
- aria_code/aria_mcp_server.py +7 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_relay_server.py +490 -0
- aria_code/aria_telegram_bot.py +202 -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 +182 -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 +1976 -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 +181 -0
- aria_code/command_safety.py +19 -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/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/demo_player.py +207 -0
- aria_code/doctor.py +813 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/backtest_engine.py +354 -0
- aria_code/domain/backtest_report.py +472 -0
- aria_code/domain/finance_formulas.py +389 -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/harness.py +598 -0
- aria_code/evals/runner.py +238 -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 +386 -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 +639 -0
- aria_code/memory_manager.py +246 -0
- aria_code/model_capability.py +486 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +23 -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 +52 -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 +201 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +111 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1550 -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 +396 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +79 -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 +268 -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 +57 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +157 -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 +8 -0
- aria_code/packages/aria_tools/financial/_paths.py +130 -0
- aria_code/packages/aria_tools/financial/compliance.py +31 -0
- aria_code/packages/aria_tools/financial/factors.py +33 -0
- aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
- aria_code/packages/aria_tools/financial/strategy.py +31 -0
- aria_code/packages/aria_tools/registry.py +128 -0
- aria_code/packages/quant_engine/__init__.py +34 -0
- aria_code/packages/quant_engine/_namespace.py +62 -0
- aria_code/packages/quant_engine/backtest/__init__.py +14 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +96 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +80 -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/ml_model.py +377 -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 +35 -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/logistics/__init__.py +169 -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/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -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 +129 -0
- aria_code/providers/llm/openai_compat.py +313 -0
- aria_code/providers/llm/registry.py +444 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +589 -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/runtime/__init__.py +169 -0
- aria_code/runtime/acceptance.py +481 -0
- aria_code/runtime/agent_loop.py +1520 -0
- aria_code/runtime/approval.py +57 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +432 -0
- aria_code/runtime/crash_report.py +174 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +150 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/repo_map.py +700 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +450 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/subagent.py +547 -0
- aria_code/runtime/task_ledger.py +86 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/tool_executor.py +217 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +23 -0
- aria_code/safety/permissions.py +306 -0
- aria_code/safety/service.py +102 -0
- aria_code/setup_wizard.py +673 -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 +504 -0
- aria_code/tools/data_analysis_tools.py +809 -0
- aria_code/tools/enterprise_finance_tools.py +156 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +885 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3417 -0
- aria_code/tools/logistics_tools.py +135 -0
- aria_code/tools/macro_tools.py +369 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +659 -0
- aria_code/tools/spreadsheet_tools.py +388 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +508 -0
- aria_code/ui/completer.py +501 -0
- aria_code/ui/console.py +281 -0
- aria_code/ui/image_render.py +243 -0
- aria_code/ui/input_box.py +488 -0
- aria_code/ui/picker.py +201 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/finance.py +1705 -0
- aria_code/ui/render/market.py +225 -0
- aria_code/ui/render/output.py +775 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +354 -0
- aria_code/ui/robot.py +235 -0
- aria_code/ui/startup_dashboard.py +109 -0
- aria_code/utils/vscode_uri.py +37 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +271 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +193 -0
- aria_code/workspace/verify.py +113 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_context.py +233 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.52.0.dist-info/METADATA +237 -0
- aria_code-0.52.0.dist-info/RECORD +478 -0
- aria_code-0.52.0.dist-info/WHEEL +5 -0
- aria_code-0.52.0.dist-info/entry_points.txt +3 -0
- aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.52.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""足球赛事报表渲染 —— 从 aria_cli.py 抽出的第一块。
|
|
2
|
+
|
|
3
|
+
这四个函数原本在 aria_cli.py 末尾(8512-8715 行)。它们几乎完全自包含:
|
|
4
|
+
rich 组件和 football_data_client 都是函数内 import,唯一的外部依赖是模块级
|
|
5
|
+
的 ``console``。
|
|
6
|
+
|
|
7
|
+
⚠️ 调用方式的约束(拆分本文件时最容易踩坏的地方):
|
|
8
|
+
``apps/cli/commands/market_cmds.py`` 是一个 mixin,它用**裸名字**调用
|
|
9
|
+
``_football_standings(...)`` —— 这只有在 aria_cli 通过 ``_rebind_mixin_globals``
|
|
10
|
+
把 mixin 方法的 ``__globals__`` 指向 aria_cli 命名空间时才成立。所以本模块
|
|
11
|
+
**不能**只是被 import,必须由 aria_cli 用 ``_rebind_module_function_globals``
|
|
12
|
+
把这些函数重新绑定回它自己的 globals,``console`` 和裸名调用才都能解析。
|
|
13
|
+
这跟 aria_cli 对 ``apps.cli.tool_executor`` 的处理是同一套既有机制。
|
|
14
|
+
|
|
15
|
+
直接 ``from apps.cli.football_reports import _football_standings`` 再调用会
|
|
16
|
+
在运行期 NameError(找不到 console)——那是预期行为,不是缺陷:状态注入还
|
|
17
|
+
没做到每个调用点,兼容边界仍留在 aria_cli。
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"_football_standings",
|
|
24
|
+
"_football_fixtures",
|
|
25
|
+
"_football_team",
|
|
26
|
+
"_football_h2h",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _football_standings(league: str) -> None:
|
|
31
|
+
from rich.table import Table
|
|
32
|
+
from rich import box as rich_box
|
|
33
|
+
from rich.panel import Panel
|
|
34
|
+
try:
|
|
35
|
+
from football_data_client import get_standings, LEAGUE_NAMES, _resolve_league
|
|
36
|
+
except ImportError:
|
|
37
|
+
console.print("[red]football_data_client.py 未找到[/red]")
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
console.print(f"[dim]获取 {league.upper()} 积分榜…[/dim]")
|
|
41
|
+
data = get_standings(league)
|
|
42
|
+
if not data:
|
|
43
|
+
comp = _resolve_league(league)
|
|
44
|
+
console.print(
|
|
45
|
+
"[yellow]无法获取数据。请设置 FOOTBALL_DATA_API_KEY:[/yellow]\n"
|
|
46
|
+
" 1. 访问 football-data.org 免费注册\n"
|
|
47
|
+
" 2. 在 ~/.aria/.env 中添加:\n"
|
|
48
|
+
" [cyan]FOOTBALL_DATA_API_KEY=your_key_here[/cyan]"
|
|
49
|
+
)
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
t = Table(
|
|
53
|
+
title=f"[bold]{data['league_name']}[/bold] {data['season_start'][:4]}/{data['season_end'][:4]}",
|
|
54
|
+
box=rich_box.SIMPLE,
|
|
55
|
+
show_header=True,
|
|
56
|
+
header_style="bold",
|
|
57
|
+
padding=(0, 1),
|
|
58
|
+
)
|
|
59
|
+
t.add_column("#", width=3, justify="right")
|
|
60
|
+
t.add_column("球队", width=22)
|
|
61
|
+
t.add_column("场", width=4, justify="right")
|
|
62
|
+
t.add_column("胜", width=3, justify="right", style="green")
|
|
63
|
+
t.add_column("平", width=3, justify="right", style="yellow")
|
|
64
|
+
t.add_column("负", width=3, justify="right", style="red")
|
|
65
|
+
t.add_column("进/失", width=7, justify="right")
|
|
66
|
+
t.add_column("净胜", width=5, justify="right")
|
|
67
|
+
t.add_column("积分", width=5, justify="right", style="bold cyan")
|
|
68
|
+
t.add_column("近5场", width=7)
|
|
69
|
+
|
|
70
|
+
for row in data["table"]:
|
|
71
|
+
gd = row["gd"]
|
|
72
|
+
gd_str = f"+{gd}" if gd > 0 else str(gd)
|
|
73
|
+
form_colored = ""
|
|
74
|
+
for c in (row.get("form") or ""):
|
|
75
|
+
if c == "W":
|
|
76
|
+
form_colored += "[green]W[/green]"
|
|
77
|
+
elif c == "L":
|
|
78
|
+
form_colored += "[red]L[/red]"
|
|
79
|
+
elif c == "D":
|
|
80
|
+
form_colored += "[yellow]D[/yellow]"
|
|
81
|
+
else:
|
|
82
|
+
form_colored += c
|
|
83
|
+
t.add_row(
|
|
84
|
+
str(row["pos"]),
|
|
85
|
+
row["team"],
|
|
86
|
+
str(row["played"]),
|
|
87
|
+
str(row["w"]),
|
|
88
|
+
str(row["d"]),
|
|
89
|
+
str(row["l"]),
|
|
90
|
+
f"{row['gf']}/{row['ga']}",
|
|
91
|
+
gd_str,
|
|
92
|
+
str(row["pts"]),
|
|
93
|
+
form_colored,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
console.print(t)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _football_fixtures(league: str, days: int = 7) -> None:
|
|
100
|
+
from rich.table import Table
|
|
101
|
+
from rich import box as rich_box
|
|
102
|
+
try:
|
|
103
|
+
from football_data_client import get_fixtures, LEAGUE_NAMES, _resolve_league
|
|
104
|
+
except ImportError:
|
|
105
|
+
console.print("[red]football_data_client.py 未找到[/red]")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
comp = _resolve_league(league)
|
|
109
|
+
league_label = LEAGUE_NAMES.get(comp, comp)
|
|
110
|
+
console.print(f"[dim]获取 {league_label} 未来 {days} 天赛程…[/dim]")
|
|
111
|
+
|
|
112
|
+
matches = get_fixtures(league, days)
|
|
113
|
+
if matches is None:
|
|
114
|
+
console.print("[yellow]无法获取数据。请检查 FOOTBALL_DATA_API_KEY 设置。[/yellow]")
|
|
115
|
+
return
|
|
116
|
+
if not matches:
|
|
117
|
+
console.print(f"[dim]未来 {days} 天内暂无赛事[/dim]")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
t = Table(
|
|
121
|
+
title=f"[bold]{league_label}[/bold] 未来 {days} 天赛程",
|
|
122
|
+
box=rich_box.SIMPLE,
|
|
123
|
+
show_header=True,
|
|
124
|
+
header_style="bold",
|
|
125
|
+
padding=(0, 1),
|
|
126
|
+
)
|
|
127
|
+
t.add_column("日期(UTC)", width=11)
|
|
128
|
+
t.add_column("主队", width=22)
|
|
129
|
+
t.add_column("", width=3, justify="center")
|
|
130
|
+
t.add_column("客队", width=22)
|
|
131
|
+
t.add_column("轮次", width=5, justify="right")
|
|
132
|
+
|
|
133
|
+
for m in matches:
|
|
134
|
+
t.add_row(
|
|
135
|
+
m["date"],
|
|
136
|
+
m["home"],
|
|
137
|
+
"vs",
|
|
138
|
+
m["away"],
|
|
139
|
+
str(m["matchday"] or m.get("stage", "")),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
console.print(t)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _football_team(team: str, league: str = "pl") -> None:
|
|
146
|
+
from rich.table import Table
|
|
147
|
+
from rich import box as rich_box
|
|
148
|
+
from rich.panel import Panel
|
|
149
|
+
try:
|
|
150
|
+
from football_data_client import get_team_stats
|
|
151
|
+
except ImportError:
|
|
152
|
+
console.print("[red]football_data_client.py 未找到[/red]")
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
console.print(f"[dim]获取 {team} 近期数据 ({league.upper()})…[/dim]")
|
|
156
|
+
stats = get_team_stats(league, team)
|
|
157
|
+
if not stats:
|
|
158
|
+
console.print("[yellow]无法获取球队数据。请检查球队名称和联赛代码。[/yellow]")
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
form_colored = ""
|
|
162
|
+
for c in stats["form"]:
|
|
163
|
+
if c == "W": form_colored += "[bold green]W[/bold green]"
|
|
164
|
+
elif c == "L": form_colored += "[bold red]L[/bold red]"
|
|
165
|
+
elif c == "D": form_colored += "[bold yellow]D[/bold yellow]"
|
|
166
|
+
|
|
167
|
+
summary = (
|
|
168
|
+
f"[bold]{stats['team']}[/bold] | 近 {stats['last_n']} 场\n\n"
|
|
169
|
+
f" 战绩: {stats['w']}胜 {stats['d']}平 {stats['l']}负\n"
|
|
170
|
+
f" 进球: {stats['gf']}球 (场均 {stats['avg_gf']})\n"
|
|
171
|
+
f" 失球: {stats['ga']}球 (场均 {stats['avg_ga']})\n"
|
|
172
|
+
f" 主场进球: 场均 {stats['home_avg_gf']}\n"
|
|
173
|
+
f" 客场进球: 场均 {stats['away_avg_gf']}\n"
|
|
174
|
+
f" 近5场: {form_colored}"
|
|
175
|
+
)
|
|
176
|
+
console.print(Panel(summary, title="[bold green]⚽ 球队状态[/bold green]", border_style="green"))
|
|
177
|
+
|
|
178
|
+
t = Table(box=rich_box.SIMPLE, show_header=True, padding=(0, 1))
|
|
179
|
+
t.add_column("日期", width=10)
|
|
180
|
+
t.add_column("主队", width=20)
|
|
181
|
+
t.add_column("比分", width=7, justify="center", style="bold")
|
|
182
|
+
t.add_column("客队", width=20)
|
|
183
|
+
t.add_column("", width=3)
|
|
184
|
+
|
|
185
|
+
for r in stats["recent"]:
|
|
186
|
+
result_style = {"W": "green", "D": "yellow", "L": "red"}.get(r["result"], "dim")
|
|
187
|
+
t.add_row(
|
|
188
|
+
r["date"],
|
|
189
|
+
r["home"],
|
|
190
|
+
r["score"],
|
|
191
|
+
r["away"],
|
|
192
|
+
f"[{result_style}]{r['result']}[/{result_style}]",
|
|
193
|
+
)
|
|
194
|
+
console.print(t)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _football_h2h(t1: str, t2: str, league: str = "pl") -> None:
|
|
198
|
+
from rich.table import Table
|
|
199
|
+
from rich import box as rich_box
|
|
200
|
+
from rich.panel import Panel
|
|
201
|
+
try:
|
|
202
|
+
from football_data_client import get_head_to_head
|
|
203
|
+
except ImportError:
|
|
204
|
+
console.print("[red]football_data_client.py 未找到[/red]")
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
console.print(f"[dim]获取 {t1} vs {t2} 历史对决 ({league.upper()})…[/dim]")
|
|
208
|
+
data = get_head_to_head(t1, t2, league)
|
|
209
|
+
if not data:
|
|
210
|
+
console.print("[yellow]未找到历史对决记录。[/yellow]")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
summary = (
|
|
214
|
+
f"[bold]{data['team1']}[/bold] vs [bold]{data['team2']}[/bold] | 共 {data['total']} 场\n\n"
|
|
215
|
+
f" {data['team1']} 胜: [green]{data['team1_wins']}[/green]\n"
|
|
216
|
+
f" 平局: [yellow]{data['draws']}[/yellow]\n"
|
|
217
|
+
f" {data['team2']} 胜: [red]{data['team2_wins']}[/red]"
|
|
218
|
+
)
|
|
219
|
+
console.print(Panel(summary, title="[bold]⚽ 历史交锋[/bold]", border_style="dim"))
|
|
220
|
+
|
|
221
|
+
t = Table(box=rich_box.SIMPLE, show_header=True, padding=(0, 1))
|
|
222
|
+
t.add_column("日期", width=10)
|
|
223
|
+
t.add_column("主队", width=22)
|
|
224
|
+
t.add_column("比分", width=7, justify="center", style="bold cyan")
|
|
225
|
+
t.add_column("客队", width=22)
|
|
226
|
+
|
|
227
|
+
for m in data["matches"]:
|
|
228
|
+
t.add_row(m["date"], m["home"], m["score"], m["away"])
|
|
229
|
+
console.print(t)
|
|
230
|
+
|
|
231
|
+
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""GitHub App authentication — generates installation tokens for Aria Code[bot].
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
from apps.cli.github_app_auth import get_installation_token, get_aria_git_url
|
|
5
|
+
|
|
6
|
+
token = get_installation_token() # raises if not configured
|
|
7
|
+
url = get_aria_git_url("artherahq/aria-code", token)
|
|
8
|
+
# git push with url as remote
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import base64
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import time
|
|
17
|
+
import urllib.request
|
|
18
|
+
import urllib.error
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# ── Configuration ──────────────────────────────────────────────────────────────
|
|
22
|
+
ARIA_APP_ID = int(os.getenv("ARIA_GITHUB_APP_ID", "4114189"))
|
|
23
|
+
ARIA_APP_SLUG = os.getenv("ARIA_GITHUB_APP_SLUG", "aria-code")
|
|
24
|
+
|
|
25
|
+
# Search order for private key
|
|
26
|
+
_PEM_SEARCH = [
|
|
27
|
+
os.getenv("ARIA_GITHUB_APP_PEM", ""),
|
|
28
|
+
os.path.expanduser("~/.config/aria-code/github-app.pem"),
|
|
29
|
+
os.path.expanduser("~/.aria-code/github-app.pem"),
|
|
30
|
+
"github-app.pem",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# noreply email GitHub uses for bot commits
|
|
34
|
+
# Format: {app_id}+{app_slug}[bot]@users.noreply.github.com
|
|
35
|
+
ARIA_BOT_EMAIL = f"{ARIA_APP_ID}+{ARIA_APP_SLUG}[bot]@users.noreply.github.com"
|
|
36
|
+
ARIA_BOT_NAME = "Aria Code"
|
|
37
|
+
|
|
38
|
+
# GitHub user account for Aria — shows up in Contributors
|
|
39
|
+
ARIA_GITHUB_LOGIN = os.getenv("ARIA_GITHUB_LOGIN", "ariaaii")
|
|
40
|
+
ARIA_GITHUB_EMAIL = os.getenv("ARIA_GITHUB_EMAIL", "support@arthera.finance")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _find_pem() -> str:
|
|
44
|
+
for path in _PEM_SEARCH:
|
|
45
|
+
if path and os.path.isfile(path):
|
|
46
|
+
return path
|
|
47
|
+
raise FileNotFoundError(
|
|
48
|
+
"GitHub App private key not found. "
|
|
49
|
+
"Set ARIA_GITHUB_APP_PEM=/path/to/key.pem or place it at "
|
|
50
|
+
"~/.config/aria-code/github-app.pem"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _b64url(data: bytes) -> str:
|
|
55
|
+
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _make_jwt(pem_path: str, app_id: int) -> str:
|
|
59
|
+
"""Create a signed JWT using RSA-SHA256 (pure stdlib + cryptography)."""
|
|
60
|
+
try:
|
|
61
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
62
|
+
from cryptography.hazmat.primitives.asymmetric import padding
|
|
63
|
+
from cryptography.hazmat.backends import default_backend
|
|
64
|
+
except ImportError:
|
|
65
|
+
raise ImportError(
|
|
66
|
+
"Install cryptography: pip install cryptography"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
with open(pem_path, "rb") as f:
|
|
70
|
+
private_key = serialization.load_pem_private_key(f.read(), password=None, backend=default_backend())
|
|
71
|
+
|
|
72
|
+
now = int(time.time())
|
|
73
|
+
header = _b64url(json.dumps({"alg": "RS256", "typ": "JWT"}).encode())
|
|
74
|
+
payload = _b64url(json.dumps({"iat": now - 60, "exp": now + 540, "iss": str(app_id)}).encode())
|
|
75
|
+
signing_input = f"{header}.{payload}".encode()
|
|
76
|
+
signature = _b64url(private_key.sign(signing_input, padding.PKCS1v15(), hashes.SHA256()))
|
|
77
|
+
return f"{header}.{payload}.{signature}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _gh_api(path: str, token: str, *, method: str = "GET") -> dict:
|
|
81
|
+
url = f"https://api.github.com{path}"
|
|
82
|
+
req = urllib.request.Request(
|
|
83
|
+
url,
|
|
84
|
+
method=method,
|
|
85
|
+
headers={
|
|
86
|
+
"Authorization": f"Bearer {token}",
|
|
87
|
+
"Accept": "application/vnd.github+json",
|
|
88
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
89
|
+
},
|
|
90
|
+
)
|
|
91
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
92
|
+
return json.loads(resp.read())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_installation_token(owner: str = "artherahq") -> str:
|
|
96
|
+
"""Return a short-lived installation token (valid ~1 hour)."""
|
|
97
|
+
pem = _find_pem()
|
|
98
|
+
jwt = _make_jwt(pem, ARIA_APP_ID)
|
|
99
|
+
|
|
100
|
+
# Find installation for owner
|
|
101
|
+
installations = _gh_api("/app/installations", jwt)
|
|
102
|
+
installation_id = None
|
|
103
|
+
for inst in installations:
|
|
104
|
+
if inst.get("account", {}).get("login", "").lower() == owner.lower():
|
|
105
|
+
installation_id = inst["id"]
|
|
106
|
+
break
|
|
107
|
+
if installation_id is None:
|
|
108
|
+
accts = [i.get("account", {}).get("login") for i in installations]
|
|
109
|
+
raise RuntimeError(
|
|
110
|
+
f"Aria Code GitHub App not installed for '{owner}'. "
|
|
111
|
+
f"Installed accounts: {accts}. "
|
|
112
|
+
f"Go to https://github.com/apps/aria-code and install it."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Exchange for installation token
|
|
116
|
+
result = _gh_api(f"/app/installations/{installation_id}/access_tokens", jwt, method="POST")
|
|
117
|
+
token = result.get("token")
|
|
118
|
+
if not token:
|
|
119
|
+
raise RuntimeError(f"Failed to get installation token: {result}")
|
|
120
|
+
return token
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_aria_git_url(repo: str, token: str) -> str:
|
|
124
|
+
"""Return an authenticated HTTPS remote URL using the installation token."""
|
|
125
|
+
return f"https://x-access-token:{token}@github.com/{repo}.git"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def aria_bot_env() -> dict[str, str]:
|
|
129
|
+
"""Env vars that make git commit as Aria Code[bot]."""
|
|
130
|
+
return {
|
|
131
|
+
"GIT_AUTHOR_NAME": ARIA_BOT_NAME,
|
|
132
|
+
"GIT_AUTHOR_EMAIL": ARIA_BOT_EMAIL,
|
|
133
|
+
"GIT_COMMITTER_NAME": ARIA_BOT_NAME,
|
|
134
|
+
"GIT_COMMITTER_EMAIL": ARIA_BOT_EMAIL,
|
|
135
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Google sign-in for the CLI, following RFC 8252's native-app flow.
|
|
2
|
+
|
|
3
|
+
The desktop app receives its OAuth callback through the arthera:// URL scheme it
|
|
4
|
+
registers with the OS. A CLI cannot own a scheme, so it does what RFC 8252
|
|
5
|
+
prescribes instead: bind an ephemeral port on loopback, hand that port to the
|
|
6
|
+
gateway, and let the browser deliver the callback there.
|
|
7
|
+
|
|
8
|
+
Three properties are worth keeping in mind when editing this:
|
|
9
|
+
|
|
10
|
+
* Only a port number is sent to the gateway, never a redirect URL. The callback
|
|
11
|
+
builds the loopback address itself, so a caller cannot point the credential
|
|
12
|
+
delivery anywhere else.
|
|
13
|
+
* What arrives on loopback is a single-use handoff code, not a token. The code is
|
|
14
|
+
redeemed over POST, so the browser history never holds a credential.
|
|
15
|
+
* The gateway returns a *Google* ID token. The Arthera API authenticates with
|
|
16
|
+
*Firebase* ID tokens, so the credential is exchanged with Identity Toolkit
|
|
17
|
+
before it is stored — the same exchange the desktop app performs through the
|
|
18
|
+
Firebase SDK.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import http.server
|
|
24
|
+
import json
|
|
25
|
+
import secrets
|
|
26
|
+
import socket
|
|
27
|
+
import threading
|
|
28
|
+
import urllib.parse
|
|
29
|
+
import urllib.request
|
|
30
|
+
import webbrowser
|
|
31
|
+
from typing import Any, Dict, Optional
|
|
32
|
+
|
|
33
|
+
# The Firebase Web API key is public by design — it identifies the project and
|
|
34
|
+
# authorises nothing on its own. It is the same value the web and desktop
|
|
35
|
+
# clients ship in their bundles.
|
|
36
|
+
FIREBASE_WEB_API_KEY = "AIzaSyC9iYiroXgxTu2ANUPIjeGbITVzWEUjXAI"
|
|
37
|
+
IDENTITY_TOOLKIT_SIGN_IN_WITH_IDP = (
|
|
38
|
+
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
_LOGIN_TIMEOUT_SECONDS = 300
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class _CallbackResult:
|
|
45
|
+
def __init__(self) -> None:
|
|
46
|
+
self.params: Dict[str, str] = {}
|
|
47
|
+
self.received = threading.Event()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _make_handler(result: _CallbackResult, expected_path: str):
|
|
51
|
+
class Handler(http.server.BaseHTTPRequestHandler):
|
|
52
|
+
def do_GET(self): # noqa: N802 - name fixed by BaseHTTPRequestHandler
|
|
53
|
+
parsed = urllib.parse.urlparse(self.path)
|
|
54
|
+
if parsed.path != expected_path:
|
|
55
|
+
self.send_response(404)
|
|
56
|
+
self.end_headers()
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
result.params = {
|
|
60
|
+
k: v[0] for k, v in urllib.parse.parse_qs(parsed.query).items()
|
|
61
|
+
}
|
|
62
|
+
ok = "handoff_code" in result.params
|
|
63
|
+
body = (
|
|
64
|
+
"<html><body style='font-family:system-ui;padding:3rem'>"
|
|
65
|
+
f"<h2>{'Signed in.' if ok else 'Sign-in failed.'}</h2>"
|
|
66
|
+
"<p>You can close this tab and return to the terminal.</p>"
|
|
67
|
+
"</body></html>"
|
|
68
|
+
).encode("utf-8")
|
|
69
|
+
|
|
70
|
+
self.send_response(200 if ok else 400)
|
|
71
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
72
|
+
self.send_header("Content-Length", str(len(body)))
|
|
73
|
+
self.end_headers()
|
|
74
|
+
self.wfile.write(body)
|
|
75
|
+
result.received.set()
|
|
76
|
+
|
|
77
|
+
def log_message(self, *args): # keep the terminal clean
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
return Handler
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _free_port() -> int:
|
|
84
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
85
|
+
sock.bind(("127.0.0.1", 0))
|
|
86
|
+
return int(sock.getsockname()[1])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _post_json(url: str, payload: Dict[str, Any], timeout: int = 20) -> Dict[str, Any]:
|
|
90
|
+
request = urllib.request.Request(
|
|
91
|
+
url,
|
|
92
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
93
|
+
headers={"Content-Type": "application/json"},
|
|
94
|
+
method="POST",
|
|
95
|
+
)
|
|
96
|
+
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
97
|
+
return json.loads(response.read().decode("utf-8"))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def exchange_google_token_for_firebase(google_id_token: str) -> Dict[str, Any]:
|
|
101
|
+
"""Trade a verified Google ID token for a Firebase session.
|
|
102
|
+
|
|
103
|
+
requestUri is required by the endpoint but only has to be a well-formed URL
|
|
104
|
+
the project would accept; nothing is fetched from it.
|
|
105
|
+
"""
|
|
106
|
+
body = {
|
|
107
|
+
"postBody": urllib.parse.urlencode(
|
|
108
|
+
{"id_token": google_id_token, "providerId": "google.com"}
|
|
109
|
+
),
|
|
110
|
+
"requestUri": "http://127.0.0.1",
|
|
111
|
+
"returnIdpCredential": True,
|
|
112
|
+
"returnSecureToken": True,
|
|
113
|
+
}
|
|
114
|
+
return _post_json(
|
|
115
|
+
f"{IDENTITY_TOOLKIT_SIGN_IN_WITH_IDP}?key={FIREBASE_WEB_API_KEY}", body
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def run_google_login(api_url: str, region: str = "GLOBAL") -> Dict[str, Any]:
|
|
120
|
+
"""Drive the whole flow and return the stored-credential fields.
|
|
121
|
+
|
|
122
|
+
Raises RuntimeError with a message suitable for display on any failure.
|
|
123
|
+
"""
|
|
124
|
+
api_url = api_url.rstrip("/")
|
|
125
|
+
port = _free_port()
|
|
126
|
+
callback_path = "/callback"
|
|
127
|
+
result = _CallbackResult()
|
|
128
|
+
|
|
129
|
+
server = http.server.HTTPServer(
|
|
130
|
+
("127.0.0.1", port), _make_handler(result, callback_path)
|
|
131
|
+
)
|
|
132
|
+
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
133
|
+
thread.start()
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
login_url = (
|
|
137
|
+
f"{api_url}/api/v2/auth/google/login?"
|
|
138
|
+
+ urllib.parse.urlencode(
|
|
139
|
+
{"platform": "cli", "region": region, "port": port}
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
opened = webbrowser.open(login_url)
|
|
143
|
+
if not opened:
|
|
144
|
+
raise RuntimeError(
|
|
145
|
+
"Could not open a browser. Visit this URL manually:\n " + login_url
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if not result.received.wait(timeout=_LOGIN_TIMEOUT_SECONDS):
|
|
149
|
+
raise RuntimeError(
|
|
150
|
+
f"Timed out after {_LOGIN_TIMEOUT_SECONDS}s waiting for the browser."
|
|
151
|
+
)
|
|
152
|
+
finally:
|
|
153
|
+
server.shutdown()
|
|
154
|
+
server.server_close()
|
|
155
|
+
|
|
156
|
+
if "message" in result.params and "handoff_code" not in result.params:
|
|
157
|
+
raise RuntimeError(result.params["message"])
|
|
158
|
+
|
|
159
|
+
handoff_code = result.params.get("handoff_code")
|
|
160
|
+
if not handoff_code:
|
|
161
|
+
raise RuntimeError("The sign-in callback did not carry a handoff code.")
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
exchanged = _post_json(
|
|
165
|
+
f"{api_url}/api/v2/auth/handoff/exchange", {"code": handoff_code}
|
|
166
|
+
)
|
|
167
|
+
except Exception as exc:
|
|
168
|
+
raise RuntimeError(f"Could not redeem the sign-in handoff: {exc}") from exc
|
|
169
|
+
|
|
170
|
+
payload = exchanged.get("data") or {}
|
|
171
|
+
google_id_token = payload.get("token")
|
|
172
|
+
if not google_id_token:
|
|
173
|
+
raise RuntimeError("The sign-in handoff did not contain a credential.")
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
session = exchange_google_token_for_firebase(google_id_token)
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
raise RuntimeError(f"Firebase rejected the Google credential: {exc}") from exc
|
|
179
|
+
|
|
180
|
+
id_token = session.get("idToken")
|
|
181
|
+
if not id_token:
|
|
182
|
+
raise RuntimeError("Firebase did not return a session token.")
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
"auth_token": id_token,
|
|
186
|
+
"refresh_token": session.get("refreshToken"),
|
|
187
|
+
# expiresIn is seconds-as-string; the caller decides how to persist it.
|
|
188
|
+
"expires_in": session.get("expiresIn"),
|
|
189
|
+
"user_id": session.get("localId") or payload.get("uid"),
|
|
190
|
+
"email": session.get("email") or payload.get("email"),
|
|
191
|
+
"display_name": session.get("displayName") or payload.get("display_name"),
|
|
192
|
+
"provider": "google.com",
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
__all__ = ["run_google_login", "exchange_google_token_for_firebase"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""apps/cli/handlers — deterministic pre-LLM response handlers extracted from aria_cli.py."""
|
|
2
|
+
from .broker_handlers import handle_broker_query
|
|
3
|
+
from .realty_handlers import handle_realty_query
|
|
4
|
+
from .chart_handlers import handle_stock_chart_analysis_direct, handle_stock_chart_analysis
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"handle_broker_query",
|
|
8
|
+
"handle_realty_query",
|
|
9
|
+
"handle_stock_chart_analysis_direct",
|
|
10
|
+
"handle_stock_chart_analysis",
|
|
11
|
+
]
|