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.
Files changed (478) hide show
  1. aria_code/__init__.py +42 -0
  2. aria_code/_version.py +20 -0
  3. aria_code/adk_apps/__init__.py +1 -0
  4. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  5. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  6. aria_code/adk_apps/aria_research/__init__.py +1 -0
  7. aria_code/adk_apps/aria_research/agent.py +31 -0
  8. aria_code/agents/__init__.py +34 -0
  9. aria_code/agents/base.py +318 -0
  10. aria_code/agents/code_review.py +200 -0
  11. aria_code/agents/deep/__init__.py +37 -0
  12. aria_code/agents/deep/calibration_loop.py +144 -0
  13. aria_code/agents/deep/critic.py +125 -0
  14. aria_code/agents/deep/deepen.py +193 -0
  15. aria_code/agents/deep/models.py +149 -0
  16. aria_code/agents/deep/pipeline.py +164 -0
  17. aria_code/agents/deep/quant_fusion.py +192 -0
  18. aria_code/agents/deep/themes.py +95 -0
  19. aria_code/agents/deep/tiers.py +106 -0
  20. aria_code/agents/engineering/__init__.py +14 -0
  21. aria_code/agents/engineering/coder.py +222 -0
  22. aria_code/agents/engineering/tester.py +145 -0
  23. aria_code/agents/financial/__init__.py +21 -0
  24. aria_code/agents/financial/cashflow_burn.py +119 -0
  25. aria_code/agents/financial/catalyst.py +279 -0
  26. aria_code/agents/financial/corporate_finance.py +175 -0
  27. aria_code/agents/financial/debate.py +224 -0
  28. aria_code/agents/financial/earnings.py +303 -0
  29. aria_code/agents/financial/fundamental.py +191 -0
  30. aria_code/agents/financial/macro.py +99 -0
  31. aria_code/agents/financial/news.py +207 -0
  32. aria_code/agents/financial/risk.py +165 -0
  33. aria_code/agents/financial/sector.py +279 -0
  34. aria_code/agents/financial/strategist.py +121 -0
  35. aria_code/agents/financial/stripe_revenue.py +127 -0
  36. aria_code/agents/financial/synthesis.py +274 -0
  37. aria_code/agents/financial/technical.py +258 -0
  38. aria_code/agents/orchestrator.py +44 -0
  39. aria_code/agents/portfolio_agent.py +363 -0
  40. aria_code/agents/realty/__init__.py +62 -0
  41. aria_code/agents/realty/asset_diagnosis.py +152 -0
  42. aria_code/agents/realty/business_match.py +166 -0
  43. aria_code/agents/realty/cashflow_verify.py +209 -0
  44. aria_code/agents/realty/contract_rules.py +210 -0
  45. aria_code/agents/realty/energy_anomaly.py +189 -0
  46. aria_code/agents/realty/exit_settlement.py +208 -0
  47. aria_code/agents/realty/fulfillment_risk.py +206 -0
  48. aria_code/agents/realty/ops_optimize.py +160 -0
  49. aria_code/agents/realty/revenue_share.py +215 -0
  50. aria_code/agents/registry.py +158 -0
  51. aria_code/agents/signal_scheme.py +111 -0
  52. aria_code/agents/sports/__init__.py +0 -0
  53. aria_code/agents/sports/football_agent.py +197 -0
  54. aria_code/agents/supervisor.py +113 -0
  55. aria_code/agents/team.py +492 -0
  56. aria_code/agents/warehouse/__init__.py +25 -0
  57. aria_code/agents/warehouse/contracts.py +26 -0
  58. aria_code/agents/warehouse/dashboard.py +155 -0
  59. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  60. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  61. aria_code/agents/warehouse/inventory_health.py +24 -0
  62. aria_code/agents/warehouse/local_run.py +71 -0
  63. aria_code/agents/warehouse/local_snapshot.py +132 -0
  64. aria_code/agents/warehouse/logistics_cost.py +84 -0
  65. aria_code/agents/warehouse/logistics_sync.py +21 -0
  66. aria_code/agents/warehouse/workflow.py +31 -0
  67. aria_code/aliyun_data_client.py +4 -0
  68. aria_code/apps/README.md +12 -0
  69. aria_code/apps/__init__.py +2 -0
  70. aria_code/apps/channels/README.md +15 -0
  71. aria_code/apps/channels/__init__.py +17 -0
  72. aria_code/apps/channels/intake.py +92 -0
  73. aria_code/apps/channels/registry.py +95 -0
  74. aria_code/apps/channels/tradingview.py +94 -0
  75. aria_code/apps/cli/README.md +13 -0
  76. aria_code/apps/cli/__init__.py +2 -0
  77. aria_code/apps/cli/_optional.py +120 -0
  78. aria_code/apps/cli/bootstrap.py +145 -0
  79. aria_code/apps/cli/broker_render.py +206 -0
  80. aria_code/apps/cli/codegen_paths.py +29 -0
  81. aria_code/apps/cli/commands/__init__.py +16 -0
  82. aria_code/apps/cli/commands/_business_render.py +78 -0
  83. aria_code/apps/cli/commands/_ui.py +71 -0
  84. aria_code/apps/cli/commands/analysis_cmds.py +369 -0
  85. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  86. aria_code/apps/cli/commands/auth_cmds.py +239 -0
  87. aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
  88. aria_code/apps/cli/commands/broker_cmds.py +1234 -0
  89. aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
  90. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  91. aria_code/apps/cli/commands/catalog.py +87 -0
  92. aria_code/apps/cli/commands/core_cmds.py +2520 -0
  93. aria_code/apps/cli/commands/data_cmds.py +457 -0
  94. aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
  95. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  96. aria_code/apps/cli/commands/file_cmds.py +312 -0
  97. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  98. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  99. aria_code/apps/cli/commands/market.py +434 -0
  100. aria_code/apps/cli/commands/market_cmds.py +1381 -0
  101. aria_code/apps/cli/commands/market_context.py +425 -0
  102. aria_code/apps/cli/commands/market_render.py +7 -0
  103. aria_code/apps/cli/commands/model_cmds.py +2153 -0
  104. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  105. aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
  106. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  107. aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
  108. aria_code/apps/cli/commands/report.py +437 -0
  109. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  110. aria_code/apps/cli/commands/session_cmds.py +236 -0
  111. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  112. aria_code/apps/cli/commands/team.py +669 -0
  113. aria_code/apps/cli/commands/team_render.py +8 -0
  114. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  115. aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
  116. aria_code/apps/cli/commands/workflow_cmds.py +480 -0
  117. aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
  118. aria_code/apps/cli/config_paths.py +73 -0
  119. aria_code/apps/cli/config_store.py +78 -0
  120. aria_code/apps/cli/constants.py +88 -0
  121. aria_code/apps/cli/context.py +34 -0
  122. aria_code/apps/cli/deterministic.py +191 -0
  123. aria_code/apps/cli/direct.py +48 -0
  124. aria_code/apps/cli/finance_service_catalog.py +111 -0
  125. aria_code/apps/cli/football_reports.py +231 -0
  126. aria_code/apps/cli/github_app_auth.py +135 -0
  127. aria_code/apps/cli/google_login.py +196 -0
  128. aria_code/apps/cli/handlers/__init__.py +11 -0
  129. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  130. aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
  131. aria_code/apps/cli/handlers/market_handlers.py +2513 -0
  132. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  133. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  134. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  135. aria_code/apps/cli/helpers.py +302 -0
  136. aria_code/apps/cli/hooks.py +180 -0
  137. aria_code/apps/cli/i18n.py +297 -0
  138. aria_code/apps/cli/intent.py +136 -0
  139. aria_code/apps/cli/intent_router.py +276 -0
  140. aria_code/apps/cli/intent_signals.py +344 -0
  141. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  142. aria_code/apps/cli/local_tool_schemas.py +536 -0
  143. aria_code/apps/cli/main.py +29 -0
  144. aria_code/apps/cli/market_metadata.py +135 -0
  145. aria_code/apps/cli/market_universe.py +487 -0
  146. aria_code/apps/cli/message_processing.py +310 -0
  147. aria_code/apps/cli/model_catalog.py +620 -0
  148. aria_code/apps/cli/pdf_report.py +977 -0
  149. aria_code/apps/cli/plan_mode.py +139 -0
  150. aria_code/apps/cli/plotly_html.py +15 -0
  151. aria_code/apps/cli/prediction_feedback.py +202 -0
  152. aria_code/apps/cli/preflight.py +502 -0
  153. aria_code/apps/cli/project_aria.py +60 -0
  154. aria_code/apps/cli/prompt_assembly.py +59 -0
  155. aria_code/apps/cli/prompts/__init__.py +0 -0
  156. aria_code/apps/cli/prompts/coding.py +53 -0
  157. aria_code/apps/cli/prompts/select.py +142 -0
  158. aria_code/apps/cli/prompts/system_prompts.py +552 -0
  159. aria_code/apps/cli/prompts/ui.py +436 -0
  160. aria_code/apps/cli/provider_endpoints.py +237 -0
  161. aria_code/apps/cli/provider_keys.py +73 -0
  162. aria_code/apps/cli/providers/__init__.py +1 -0
  163. aria_code/apps/cli/providers/base.py +521 -0
  164. aria_code/apps/cli/providers/chat_routing.py +128 -0
  165. aria_code/apps/cli/providers/collaboration.py +98 -0
  166. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  167. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  168. aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
  169. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  170. aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
  171. aria_code/apps/cli/providers/runtime_bridge.py +353 -0
  172. aria_code/apps/cli/providers/vertexai_stream.py +316 -0
  173. aria_code/apps/cli/runtime_consumer.py +657 -0
  174. aria_code/apps/cli/sandbox.py +32 -0
  175. aria_code/apps/cli/session_export.py +120 -0
  176. aria_code/apps/cli/session_jsonl.py +208 -0
  177. aria_code/apps/cli/session_store.py +112 -0
  178. aria_code/apps/cli/skills_catalog.py +273 -0
  179. aria_code/apps/cli/todo_tracker.py +190 -0
  180. aria_code/apps/cli/tool_executor.py +823 -0
  181. aria_code/apps/cli/tool_registry.py +34 -0
  182. aria_code/apps/cli/tool_scope.py +106 -0
  183. aria_code/apps/cli/tools/__init__.py +40 -0
  184. aria_code/apps/cli/tools/context.py +46 -0
  185. aria_code/apps/cli/tools/file_tools.py +180 -0
  186. aria_code/apps/cli/tools/market_tools.py +620 -0
  187. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  188. aria_code/apps/cli/tools/system_tools.py +726 -0
  189. aria_code/apps/cli/tools/write_tools.py +845 -0
  190. aria_code/apps/cli/tradingview_bridge.py +529 -0
  191. aria_code/apps/cli/turn_planning.py +137 -0
  192. aria_code/apps/cli/update_check.py +153 -0
  193. aria_code/apps/cli/utils/__init__.py +0 -0
  194. aria_code/apps/cli/utils/market_detect.py +1597 -0
  195. aria_code/apps/daemon/README.md +14 -0
  196. aria_code/apps/vscode/README.md +115 -0
  197. aria_code/apps/vscode/package.json +70 -0
  198. aria_code/aria_cli.py +6743 -0
  199. aria_code/aria_daemon.py +1398 -0
  200. aria_code/aria_feishu_bot.py +1423 -0
  201. aria_code/aria_mcp_server.py +7 -0
  202. aria_code/aria_relay_client.py +4 -0
  203. aria_code/aria_relay_server.py +490 -0
  204. aria_code/aria_telegram_bot.py +202 -0
  205. aria_code/ariarc.py +385 -0
  206. aria_code/artifacts.py +581 -0
  207. aria_code/backtest_engine.py +4 -0
  208. aria_code/backtest_report.py +4 -0
  209. aria_code/brokers/__init__.py +78 -0
  210. aria_code/brokers/automation.py +193 -0
  211. aria_code/brokers/base.py +231 -0
  212. aria_code/brokers/capabilities.py +264 -0
  213. aria_code/brokers/cn/__init__.py +10 -0
  214. aria_code/brokers/cn/easytrader_broker.py +193 -0
  215. aria_code/brokers/cn/futu_broker.py +194 -0
  216. aria_code/brokers/cn/longbridge_broker.py +190 -0
  217. aria_code/brokers/cn/tiger_broker.py +196 -0
  218. aria_code/brokers/cn/xtquant_broker.py +225 -0
  219. aria_code/brokers/config.py +414 -0
  220. aria_code/brokers/intl/__init__.py +5 -0
  221. aria_code/brokers/intl/alpaca_broker.py +183 -0
  222. aria_code/brokers/intl/ibkr_broker.py +215 -0
  223. aria_code/brokers/intl/trading212_broker.py +183 -0
  224. aria_code/brokers/intl/webull_broker.py +156 -0
  225. aria_code/brokers/paper_broker.py +266 -0
  226. aria_code/brokers/planning.py +296 -0
  227. aria_code/brokers/registry.py +213 -0
  228. aria_code/brokers/trading.py +267 -0
  229. aria_code/canva_client.py +4 -0
  230. aria_code/change_store.py +127 -0
  231. aria_code/clients/__init__.py +0 -0
  232. aria_code/clients/aliyun_data_client.py +660 -0
  233. aria_code/clients/aria_relay_client.py +182 -0
  234. aria_code/clients/canva_client.py +284 -0
  235. aria_code/clients/figma_client.py +100 -0
  236. aria_code/clients/football_data_client.py +1671 -0
  237. aria_code/clients/kling_video_client.py +182 -0
  238. aria_code/clients/market_data_client.py +1976 -0
  239. aria_code/clients/openai_image_client.py +242 -0
  240. aria_code/clients/runway_video_client.py +157 -0
  241. aria_code/clients/warehouse_erp_client.py +105 -0
  242. aria_code/cloud_memory.py +181 -0
  243. aria_code/command_safety.py +19 -0
  244. aria_code/computer_use_tools.py +4 -0
  245. aria_code/dashboard_generator.py +579 -0
  246. aria_code/data_analysis_tools.py +4 -0
  247. aria_code/data_cleaner.py +483 -0
  248. aria_code/data_service.py +513 -0
  249. aria_code/datasources/__init__.py +23 -0
  250. aria_code/datasources/base.py +166 -0
  251. aria_code/datasources/router.py +253 -0
  252. aria_code/datasources/sources/__init__.py +15 -0
  253. aria_code/datasources/sources/akshare_source.py +269 -0
  254. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  255. aria_code/datasources/sources/edgar_source.py +219 -0
  256. aria_code/datasources/sources/finnhub_source.py +198 -0
  257. aria_code/datasources/sources/fred_source.py +223 -0
  258. aria_code/datasources/sources/tushare_source.py +143 -0
  259. aria_code/datasources/sources/web_scraper_source.py +278 -0
  260. aria_code/datasources/sources/world_bank_source.py +206 -0
  261. aria_code/datasources/sources/yfinance_source.py +152 -0
  262. aria_code/demo_player.py +207 -0
  263. aria_code/doctor.py +813 -0
  264. aria_code/domain/__init__.py +0 -0
  265. aria_code/domain/backtest_engine.py +354 -0
  266. aria_code/domain/backtest_report.py +472 -0
  267. aria_code/domain/finance_formulas.py +389 -0
  268. aria_code/domain/portfolio_ledger.py +307 -0
  269. aria_code/domain/strategy_vault.py +421 -0
  270. aria_code/evals/__init__.py +37 -0
  271. aria_code/evals/harness.py +598 -0
  272. aria_code/evals/runner.py +238 -0
  273. aria_code/external_agent_runner.py +115 -0
  274. aria_code/figma_client.py +4 -0
  275. aria_code/file_analysis_tools.py +4 -0
  276. aria_code/finance_formulas.py +4 -0
  277. aria_code/football_data_client.py +4 -0
  278. aria_code/image_gen_tools.py +4 -0
  279. aria_code/intent_classifier.py +386 -0
  280. aria_code/kling_video_client.py +4 -0
  281. aria_code/licensing.py +151 -0
  282. aria_code/local_finance_tools.py +4 -0
  283. aria_code/local_image_provider.py +4 -0
  284. aria_code/local_llm_provider.py +4 -0
  285. aria_code/macro_tools.py +4 -0
  286. aria_code/markdown_pdf.py +289 -0
  287. aria_code/market_data_client.py +4 -0
  288. aria_code/mcp_client.py +639 -0
  289. aria_code/memory_manager.py +246 -0
  290. aria_code/model_capability.py +486 -0
  291. aria_code/notification_tools.py +4 -0
  292. aria_code/openai_image_client.py +4 -0
  293. aria_code/packages/__init__.py +23 -0
  294. aria_code/packages/adk_bridge/__init__.py +11 -0
  295. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  296. aria_code/packages/adk_bridge/market_tools.py +125 -0
  297. aria_code/packages/aria_agents/__init__.py +5 -0
  298. aria_code/packages/aria_agents/manifest.py +78 -0
  299. aria_code/packages/aria_core/__init__.py +34 -0
  300. aria_code/packages/aria_core/architecture.py +269 -0
  301. aria_code/packages/aria_core/export.py +147 -0
  302. aria_code/packages/aria_core/manifest.py +65 -0
  303. aria_code/packages/aria_core/paths.py +57 -0
  304. aria_code/packages/aria_core/secure_file.py +68 -0
  305. aria_code/packages/aria_infra/__init__.py +15 -0
  306. aria_code/packages/aria_infra/arthera.py +52 -0
  307. aria_code/packages/aria_infra/doctor.py +246 -0
  308. aria_code/packages/aria_infra/product.py +40 -0
  309. aria_code/packages/aria_mcp/__init__.py +25 -0
  310. aria_code/packages/aria_mcp/bridge.py +201 -0
  311. aria_code/packages/aria_mcp/circuit.py +82 -0
  312. aria_code/packages/aria_mcp/config.py +111 -0
  313. aria_code/packages/aria_mcp/installer.py +95 -0
  314. aria_code/packages/aria_mcp/server.py +1550 -0
  315. aria_code/packages/aria_mcp/tools.py +81 -0
  316. aria_code/packages/aria_sdk/__init__.py +19 -0
  317. aria_code/packages/aria_sdk/client.py +396 -0
  318. aria_code/packages/aria_sdk/providers.py +70 -0
  319. aria_code/packages/aria_sdk/streaming.py +79 -0
  320. aria_code/packages/aria_sdk/types.py +86 -0
  321. aria_code/packages/aria_services/__init__.py +92 -0
  322. aria_code/packages/aria_services/cache.py +60 -0
  323. aria_code/packages/aria_services/context.py +356 -0
  324. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  325. aria_code/packages/aria_services/data.py +11 -0
  326. aria_code/packages/aria_services/provider_health.py +268 -0
  327. aria_code/packages/aria_services/references.py +247 -0
  328. aria_code/packages/aria_services/registry.py +249 -0
  329. aria_code/packages/aria_services/research_protocol.py +57 -0
  330. aria_code/packages/aria_services/research_quality.py +186 -0
  331. aria_code/packages/aria_services/settings.py +157 -0
  332. aria_code/packages/aria_services/usage.py +151 -0
  333. aria_code/packages/aria_skills/__init__.py +46 -0
  334. aria_code/packages/aria_skills/catalog.py +149 -0
  335. aria_code/packages/aria_skills/loader.py +455 -0
  336. aria_code/packages/aria_skills/registry.py +84 -0
  337. aria_code/packages/aria_tools/__init__.py +5 -0
  338. aria_code/packages/aria_tools/financial/__init__.py +8 -0
  339. aria_code/packages/aria_tools/financial/_paths.py +130 -0
  340. aria_code/packages/aria_tools/financial/compliance.py +31 -0
  341. aria_code/packages/aria_tools/financial/factors.py +33 -0
  342. aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
  343. aria_code/packages/aria_tools/financial/strategy.py +31 -0
  344. aria_code/packages/aria_tools/registry.py +128 -0
  345. aria_code/packages/quant_engine/__init__.py +34 -0
  346. aria_code/packages/quant_engine/_namespace.py +62 -0
  347. aria_code/packages/quant_engine/backtest/__init__.py +14 -0
  348. aria_code/packages/quant_engine/backtest/core.py +53 -0
  349. aria_code/packages/quant_engine/backtest/engine.py +96 -0
  350. aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
  351. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  352. aria_code/packages/quant_engine/sports/__init__.py +80 -0
  353. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  354. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  355. aria_code/packages/quant_engine/sports/elo.py +300 -0
  356. aria_code/packages/quant_engine/sports/form.py +188 -0
  357. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  358. aria_code/packages/quant_engine/sports/ml_model.py +377 -0
  359. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  360. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  361. aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
  362. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  363. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  364. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  365. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  366. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  367. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  368. aria_code/packs/__init__.py +85 -0
  369. aria_code/packs/base.py +168 -0
  370. aria_code/packs/finance/__init__.py +250 -0
  371. aria_code/packs/logistics/__init__.py +169 -0
  372. aria_code/packs/payments/__init__.py +139 -0
  373. aria_code/packs/realty/__init__.py +190 -0
  374. aria_code/packs/registry.py +202 -0
  375. aria_code/packs/rules.py +90 -0
  376. aria_code/plan_utils.py +194 -0
  377. aria_code/plugin_loader.py +328 -0
  378. aria_code/portfolio_ledger.py +4 -0
  379. aria_code/preview_server.py +449 -0
  380. aria_code/privacy/__init__.py +5 -0
  381. aria_code/privacy/feedback.py +123 -0
  382. aria_code/project_review.py +278 -0
  383. aria_code/project_review_api.py +235 -0
  384. aria_code/project_review_server.py +18 -0
  385. aria_code/project_tools.py +4 -0
  386. aria_code/providers/__init__.py +30 -0
  387. aria_code/providers/llm/__init__.py +19 -0
  388. aria_code/providers/llm/anthropic.py +184 -0
  389. aria_code/providers/llm/autoconfig.py +213 -0
  390. aria_code/providers/llm/base.py +139 -0
  391. aria_code/providers/llm/custom.py +158 -0
  392. aria_code/providers/llm/ollama.py +129 -0
  393. aria_code/providers/llm/openai_compat.py +313 -0
  394. aria_code/providers/llm/registry.py +444 -0
  395. aria_code/providers/local_image_provider.py +202 -0
  396. aria_code/providers/local_llm_provider.py +589 -0
  397. aria_code/realty_data_tools.py +4 -0
  398. aria_code/report_exporters.py +150 -0
  399. aria_code/report_generator.py +1764 -0
  400. aria_code/runtime/__init__.py +169 -0
  401. aria_code/runtime/acceptance.py +481 -0
  402. aria_code/runtime/agent_loop.py +1520 -0
  403. aria_code/runtime/approval.py +57 -0
  404. aria_code/runtime/budget.py +232 -0
  405. aria_code/runtime/checkpoints.py +432 -0
  406. aria_code/runtime/crash_report.py +174 -0
  407. aria_code/runtime/events.py +113 -0
  408. aria_code/runtime/gateway.py +150 -0
  409. aria_code/runtime/lsp.py +484 -0
  410. aria_code/runtime/orchestrator.py +55 -0
  411. aria_code/runtime/repo_map.py +700 -0
  412. aria_code/runtime/run_state.py +127 -0
  413. aria_code/runtime/run_store.py +450 -0
  414. aria_code/runtime/self_healing.py +422 -0
  415. aria_code/runtime/subagent.py +547 -0
  416. aria_code/runtime/task_ledger.py +86 -0
  417. aria_code/runtime/task_orchestrator.py +90 -0
  418. aria_code/runtime/tool_executor.py +217 -0
  419. aria_code/runtime/tool_policy.py +107 -0
  420. aria_code/runtime/worktrees.py +215 -0
  421. aria_code/runway_video_client.py +4 -0
  422. aria_code/safety/__init__.py +23 -0
  423. aria_code/safety/permissions.py +306 -0
  424. aria_code/safety/service.py +102 -0
  425. aria_code/setup_wizard.py +673 -0
  426. aria_code/spreadsheet_tools.py +4 -0
  427. aria_code/strategy_vault.py +4 -0
  428. aria_code/tools/__init__.py +0 -0
  429. aria_code/tools/artifact_tools.py +252 -0
  430. aria_code/tools/broker_tools.py +92 -0
  431. aria_code/tools/build_quant_engine.py +97 -0
  432. aria_code/tools/code_audit_tools.py +192 -0
  433. aria_code/tools/computer_use_tools.py +504 -0
  434. aria_code/tools/data_analysis_tools.py +809 -0
  435. aria_code/tools/enterprise_finance_tools.py +156 -0
  436. aria_code/tools/extended_tools.py +240 -0
  437. aria_code/tools/file_analysis_tools.py +885 -0
  438. aria_code/tools/image_gen_tools.py +114 -0
  439. aria_code/tools/local_finance_tools.py +3417 -0
  440. aria_code/tools/logistics_tools.py +135 -0
  441. aria_code/tools/macro_tools.py +369 -0
  442. aria_code/tools/notification_tools.py +249 -0
  443. aria_code/tools/project_tools.py +525 -0
  444. aria_code/tools/realty_data_tools.py +659 -0
  445. aria_code/tools/spreadsheet_tools.py +388 -0
  446. aria_code/tools/stripe_tools.py +115 -0
  447. aria_code/ui/__init__.py +105 -0
  448. aria_code/ui/banner.py +508 -0
  449. aria_code/ui/completer.py +501 -0
  450. aria_code/ui/console.py +281 -0
  451. aria_code/ui/image_render.py +243 -0
  452. aria_code/ui/input_box.py +488 -0
  453. aria_code/ui/picker.py +201 -0
  454. aria_code/ui/render/__init__.py +11 -0
  455. aria_code/ui/render/finance.py +1705 -0
  456. aria_code/ui/render/market.py +225 -0
  457. aria_code/ui/render/output.py +775 -0
  458. aria_code/ui/render/responsive.py +75 -0
  459. aria_code/ui/render/team.py +354 -0
  460. aria_code/ui/robot.py +235 -0
  461. aria_code/ui/startup_dashboard.py +109 -0
  462. aria_code/utils/vscode_uri.py +37 -0
  463. aria_code/video_analysis.py +147 -0
  464. aria_code/video_editor.py +271 -0
  465. aria_code/workspace/__init__.py +6 -0
  466. aria_code/workspace/files.py +193 -0
  467. aria_code/workspace/verify.py +113 -0
  468. aria_code/workspace_changes.py +199 -0
  469. aria_code/workspace_context.py +233 -0
  470. aria_code/workspace_service.py +196 -0
  471. aria_code/workspace_tools.py +150 -0
  472. aria_code-0.52.0.dist-info/METADATA +237 -0
  473. aria_code-0.52.0.dist-info/RECORD +478 -0
  474. aria_code-0.52.0.dist-info/WHEEL +5 -0
  475. aria_code-0.52.0.dist-info/entry_points.txt +3 -0
  476. aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
  477. aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
  478. 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
+ ]