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,7 @@
1
+ #!/usr/bin/env python3
2
+ """Entry point for the aria-code MCP server. See packages/aria_mcp/server.py."""
3
+
4
+ from aria_code.packages.aria_mcp.server import main
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -0,0 +1,4 @@
1
+ """Compatibility import for :mod:`clients.aria_relay_client`."""
2
+ from importlib import import_module as _import_module
3
+ import sys as _sys
4
+ _sys.modules[__name__] = _import_module("clients.aria_relay_client")
@@ -0,0 +1,490 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ aria_relay_server.py — Aria 中继服务器
4
+ =======================================
5
+ 你(产品方)部署一次,所有用户共用。
6
+
7
+ 架构:
8
+ 用户飞书消息
9
+ → 飞书云 → POST /feishu/event
10
+ → relay_server 查 feishu_user_id → 对应 WebSocket
11
+ → relay_server 转发 payload 到用户本机
12
+ → aria_relay_client.dispatch_event()
13
+ → Aria LLM 回复
14
+ → relay_server 收到 response
15
+ → relay_server 调飞书 reply_message API
16
+ → 飞书消息卡片展示给用户
17
+
18
+ WebSocket 注册流程:
19
+ 1. 用户本机 aria_relay_client 连接 wss://relay.yourdomain.com
20
+ 2. 发送: {"type": "register", "client_id": "aria-xxxxxxxxxxxx"}
21
+ 3. 用户在飞书向 Aria Bot 发: /bind ARIA-BIND-ARIA-XXXXXXXXXXXX
22
+ 4. 服务器记录: feishu_user_id → client_id
23
+ 5. 后续消息经 WebSocket 透传
24
+
25
+ 依赖:
26
+ pip install fastapi uvicorn websockets httpx
27
+
28
+ 启动:
29
+ python3 aria_relay_server.py
30
+ # 或
31
+ uvicorn aria_relay_server:app --host 0.0.0.0 --port 8765
32
+
33
+ 所需环境变量:
34
+ FEISHU_APP_ID 飞书应用 App ID
35
+ FEISHU_APP_SECRET 飞书应用 App Secret
36
+ FEISHU_ENCRYPT_KEY 飞书事件订阅的 Encrypt Key(推荐)——启用请求头签名校验
37
+ FEISHU_VERIFICATION_TOKEN
38
+ 飞书 Verification Token(次选)——校验事件体里的 token
39
+ ⚠️ 二者至少配一个,否则 /feishu/event 一律拒绝。
40
+ 仅本地联调可设 RELAY_ALLOW_UNVERIFIED_EVENTS=1 绕过。
41
+ RELAY_SECRET WebSocket 注册鉴权(可选,留空则不鉴权)
42
+ DB_PATH SQLite 路径(默认 ./relay.db)
43
+ MESSAGE_TIMEOUT 等待用户本机回复的超时秒数(默认 90)
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ import asyncio
49
+ import hashlib
50
+ import hmac
51
+ import json
52
+ import logging
53
+ import os
54
+ import sqlite3
55
+ import time
56
+ import uuid
57
+ from contextlib import asynccontextmanager
58
+ from typing import Any, Optional
59
+
60
+ import httpx
61
+ from fastapi import FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect
62
+ from fastapi.responses import JSONResponse
63
+
64
+ logger = logging.getLogger("aria.relay_server")
65
+
66
+ # ── Config ────────────────────────────────────────────────────────────────────
67
+
68
+ _FEISHU_APP_ID = os.environ.get("FEISHU_APP_ID", "")
69
+ _FEISHU_APP_SECRET = os.environ.get("FEISHU_APP_SECRET", "")
70
+ _RELAY_SECRET = os.environ.get("RELAY_SECRET", "")
71
+ _FEISHU_VERIFICATION_TOKEN = os.environ.get("FEISHU_VERIFICATION_TOKEN", "")
72
+ _FEISHU_ENCRYPT_KEY = os.environ.get("FEISHU_ENCRYPT_KEY", "")
73
+ _ALLOW_UNVERIFIED_EVENTS = os.environ.get(
74
+ "RELAY_ALLOW_UNVERIFIED_EVENTS", "").strip().lower() in {"1", "true", "yes", "on"}
75
+ _DB_PATH = os.environ.get("DB_PATH", "./relay.db")
76
+ _MSG_TIMEOUT = int(os.environ.get("MESSAGE_TIMEOUT", "90"))
77
+ _FEISHU_API = "https://open.feishu.cn/open-apis"
78
+
79
+
80
+ # ── SQLite store ──────────────────────────────────────────────────────────────
81
+
82
+ def _db() -> sqlite3.Connection:
83
+ conn = sqlite3.connect(_DB_PATH, check_same_thread=False)
84
+ conn.row_factory = sqlite3.Row
85
+ conn.execute("""
86
+ CREATE TABLE IF NOT EXISTS bindings (
87
+ feishu_user_id TEXT PRIMARY KEY,
88
+ client_id TEXT NOT NULL,
89
+ bound_at REAL NOT NULL
90
+ )
91
+ """)
92
+ conn.execute("""
93
+ CREATE TABLE IF NOT EXISTS pending_binds (
94
+ client_id TEXT PRIMARY KEY,
95
+ created_at REAL NOT NULL
96
+ )
97
+ """)
98
+ conn.commit()
99
+ return conn
100
+
101
+
102
+ _db_conn: Optional[sqlite3.Connection] = None
103
+
104
+
105
+ def get_db() -> sqlite3.Connection:
106
+ global _db_conn
107
+ if _db_conn is None:
108
+ _db_conn = _db()
109
+ return _db_conn
110
+
111
+
112
+ def _lookup_client(feishu_user_id: str) -> Optional[str]:
113
+ row = get_db().execute(
114
+ "SELECT client_id FROM bindings WHERE feishu_user_id = ?",
115
+ (feishu_user_id,),
116
+ ).fetchone()
117
+ return row["client_id"] if row else None
118
+
119
+
120
+ def _bind(feishu_user_id: str, client_id: str) -> None:
121
+ get_db().execute(
122
+ "INSERT OR REPLACE INTO bindings VALUES (?, ?, ?)",
123
+ (feishu_user_id, client_id, time.time()),
124
+ )
125
+ get_db().execute(
126
+ "DELETE FROM pending_binds WHERE client_id = ?", (client_id,)
127
+ )
128
+ get_db().commit()
129
+
130
+
131
+ def _register_pending(client_id: str) -> None:
132
+ get_db().execute(
133
+ "INSERT OR REPLACE INTO pending_binds VALUES (?, ?)",
134
+ (client_id, time.time()),
135
+ )
136
+ get_db().commit()
137
+
138
+
139
+ def _is_valid_client_id(client_id: str) -> bool:
140
+ row = get_db().execute(
141
+ "SELECT client_id FROM pending_binds WHERE client_id = ?", (client_id,)
142
+ ).fetchone()
143
+ return row is not None
144
+
145
+
146
+ # ── WebSocket connection registry ─────────────────────────────────────────────
147
+
148
+ _connections: dict[str, WebSocket] = {} # client_id → WebSocket
149
+ _pending_responses: dict[str, asyncio.Future] = {} # request_id → Future
150
+
151
+
152
+ # ── Feishu API helpers ────────────────────────────────────────────────────────
153
+
154
+ _feishu_token_cache: dict[str, Any] = {}
155
+
156
+
157
+ async def _get_tenant_token() -> str:
158
+ now = time.time()
159
+ if _feishu_token_cache.get("expires_at", 0) > now + 60:
160
+ return _feishu_token_cache["token"]
161
+
162
+ async with httpx.AsyncClient() as client:
163
+ resp = await client.post(
164
+ f"{_FEISHU_API}/auth/v3/tenant_access_token/internal",
165
+ json={"app_id": _FEISHU_APP_ID, "app_secret": _FEISHU_APP_SECRET},
166
+ timeout=10,
167
+ )
168
+ data = resp.json()
169
+
170
+ token = data.get("tenant_access_token", "")
171
+ expire = int(data.get("expire", 7200))
172
+ _feishu_token_cache.update({"token": token, "expires_at": now + expire})
173
+ return token
174
+
175
+
176
+ async def _reply_feishu(message_id: str, content: str, color: str = "blue") -> None:
177
+ token = await _get_tenant_token()
178
+ card = {
179
+ "msg_type": "interactive",
180
+ "card": {
181
+ "header": {
182
+ "title": {"tag": "plain_text", "content": "Aria"},
183
+ "template": color,
184
+ },
185
+ "elements": [{"tag": "div", "text": {"tag": "lark_md", "content": content}}],
186
+ },
187
+ }
188
+ async with httpx.AsyncClient() as client:
189
+ await client.post(
190
+ f"{_FEISHU_API}/im/v1/messages/{message_id}/reply",
191
+ headers={"Authorization": f"Bearer {token}"},
192
+ json=card,
193
+ timeout=15,
194
+ )
195
+
196
+
197
+ async def _send_feishu_text(open_id: str, text: str) -> None:
198
+ token = await _get_tenant_token()
199
+ async with httpx.AsyncClient() as client:
200
+ await client.post(
201
+ f"{_FEISHU_API}/im/v1/messages",
202
+ headers={"Authorization": f"Bearer {token}"},
203
+ params={"receive_id_type": "open_id"},
204
+ json={
205
+ "receive_id": open_id,
206
+ "msg_type": "text",
207
+ "content": json.dumps({"text": text}),
208
+ },
209
+ timeout=15,
210
+ )
211
+
212
+
213
+ # ── Route message to local aria instance ─────────────────────────────────────
214
+
215
+ async def _route_to_local(feishu_user_id: str, payload: dict) -> Optional[Any]:
216
+ """Forward Feishu event to the user's connected local aria instance."""
217
+ client_id = _lookup_client(feishu_user_id)
218
+ if not client_id:
219
+ return None
220
+
221
+ ws = _connections.get(client_id)
222
+ if not ws:
223
+ return None
224
+
225
+ req_id = f"req_{uuid.uuid4().hex[:10]}"
226
+ future: asyncio.Future = asyncio.get_event_loop().create_future()
227
+ _pending_responses[req_id] = future
228
+
229
+ try:
230
+ await ws.send_text(json.dumps({
231
+ "type": "message",
232
+ "id": req_id,
233
+ "payload": payload,
234
+ }))
235
+ result = await asyncio.wait_for(future, timeout=_MSG_TIMEOUT)
236
+ return result
237
+ except asyncio.TimeoutError:
238
+ logger.warning("Timeout waiting for response from client_id=%s", client_id)
239
+ return {"error": "Aria 本机响应超时,请检查 aria_relay_client 是否在线"}
240
+ finally:
241
+ _pending_responses.pop(req_id, None)
242
+
243
+
244
+ # ── FastAPI app ───────────────────────────────────────────────────────────────
245
+
246
+ @asynccontextmanager
247
+ async def lifespan(app: FastAPI):
248
+ logging.basicConfig(
249
+ level=logging.INFO,
250
+ format="%(asctime)s [relay] %(message)s",
251
+ datefmt="%H:%M:%S",
252
+ )
253
+ logger.info("Aria Relay Server started db=%s", _DB_PATH)
254
+ get_db() # init tables
255
+ yield
256
+ logger.info("Relay Server shutting down")
257
+
258
+
259
+ app = FastAPI(title="Aria Relay Server", lifespan=lifespan)
260
+
261
+
262
+ # ── WebSocket endpoint (users' local machines) ────────────────────────────────
263
+
264
+ @app.websocket("/ws")
265
+ async def ws_endpoint(websocket: WebSocket):
266
+ await websocket.accept()
267
+ client_id: Optional[str] = None
268
+
269
+ try:
270
+ # First message must be register
271
+ raw = await asyncio.wait_for(websocket.receive_text(), timeout=15)
272
+ msg = json.loads(raw)
273
+
274
+ if msg.get("type") != "register":
275
+ await websocket.send_text(json.dumps({"ok": False, "reason": "first message must be register"}))
276
+ return
277
+
278
+ client_id = msg.get("client_id", "")
279
+ if not client_id:
280
+ await websocket.send_text(json.dumps({"ok": False, "reason": "client_id required"}))
281
+ return
282
+
283
+ # Validate RELAY_SECRET if configured
284
+ if _RELAY_SECRET and msg.get("secret") != _RELAY_SECRET:
285
+ await websocket.send_text(json.dumps({"ok": False, "reason": "invalid secret"}))
286
+ return
287
+
288
+ # Register in-memory + mark as pending bind (if first time)
289
+ _connections[client_id] = websocket
290
+ if _lookup_client.__module__: # always true; used as noop to be explicit
291
+ _register_pending(client_id)
292
+
293
+ await websocket.send_text(json.dumps({"ok": True, "client_id": client_id}))
294
+ logger.info("Client connected: %s", client_id)
295
+
296
+ # Message loop
297
+ async for raw in websocket.iter_text():
298
+ try:
299
+ response_msg = json.loads(raw)
300
+ except json.JSONDecodeError:
301
+ continue
302
+
303
+ if response_msg.get("type") == "pong":
304
+ continue
305
+
306
+ if response_msg.get("type") == "response":
307
+ req_id = response_msg.get("id", "")
308
+ future = _pending_responses.get(req_id)
309
+ if future and not future.done():
310
+ future.set_result(response_msg.get("result"))
311
+
312
+ except WebSocketDisconnect:
313
+ pass
314
+ except asyncio.TimeoutError:
315
+ logger.warning("Register timeout for new connection")
316
+ except Exception as e:
317
+ logger.exception("WebSocket error: %s", e)
318
+ finally:
319
+ if client_id:
320
+ _connections.pop(client_id, None)
321
+ logger.info("Client disconnected: %s", client_id)
322
+
323
+
324
+ # ── Feishu event endpoint ─────────────────────────────────────────────────────
325
+
326
+
327
+ # ── Feishu event authenticity ─────────────────────────────────────────────────
328
+ #
329
+ # 2026-08-19 修复一条完整的未授权账户劫持链。修复前 /feishu/event 对请求来源
330
+ # 不做任何校验,配合公开的 /status,四步即可劫持任意在线用户:
331
+ #
332
+ # 1. GET /status → 拿到全部在线 client_ids(该端点当时把它们直接列出)
333
+ # 2. POST /feishu/event → 伪造事件,sender.open_id 填攻击者自己的,
334
+ # 正文填 "/bind ARIA-BIND-<受害者 client_id>"
335
+ # 3. 服务端执行 _bind(攻击者, 受害者client_id) → 绑定被改写
336
+ # 4. 攻击者此后发的消息都会被转发到受害者本机执行,结果回传给攻击者
337
+ #
338
+ # RELAY_SECRET 只校验 WebSocket 注册那一侧,HTTP 侧完全没有防护。
339
+ #
340
+ # 飞书提供两种官方校验手段,这里都支持:
341
+ # - Verification Token:事件体里的 token 字段,与控制台配置值比对
342
+ # - Encrypt Key:请求头 X-Lark-Signature = sha256(timestamp+nonce+key+body)
343
+ # 两者只要配置了任意一个就会被强制执行;都没配置时默认**拒绝**事件,
344
+ # 除非显式设置 RELAY_ALLOW_UNVERIFIED_EVENTS=1(仅供本地联调)。
345
+ # 默认拒绝而不是默认放行——这个端点的下游是用户本机,放行的代价太高。
346
+
347
+
348
+ def _verify_feishu_request(headers: dict, body: bytes, payload: dict) -> tuple[bool, str]:
349
+ """返回 (是否可信, 拒绝原因)。"""
350
+ if _FEISHU_ENCRYPT_KEY:
351
+ timestamp = headers.get("x-lark-request-timestamp", "")
352
+ nonce = headers.get("x-lark-request-nonce", "")
353
+ signature = headers.get("x-lark-signature", "")
354
+ if not signature:
355
+ return False, "missing X-Lark-Signature"
356
+ digest = hashlib.sha256(
357
+ (timestamp + nonce + _FEISHU_ENCRYPT_KEY).encode("utf-8") + body
358
+ ).hexdigest()
359
+ # 定长比较,避免按字节短路带来的时序侧信道
360
+ if not hmac.compare_digest(digest, signature):
361
+ return False, "signature mismatch"
362
+ return True, ""
363
+
364
+ if _FEISHU_VERIFICATION_TOKEN:
365
+ token = payload.get("token") or payload.get("header", {}).get("token", "")
366
+ if not hmac.compare_digest(str(token), _FEISHU_VERIFICATION_TOKEN):
367
+ return False, "verification token mismatch"
368
+ return True, ""
369
+
370
+ if _ALLOW_UNVERIFIED_EVENTS:
371
+ return True, ""
372
+
373
+ return False, (
374
+ "relay 未配置 FEISHU_ENCRYPT_KEY 或 FEISHU_VERIFICATION_TOKEN;"
375
+ "未验签的事件一律拒绝(本地联调可设 RELAY_ALLOW_UNVERIFIED_EVENTS=1)"
376
+ )
377
+
378
+
379
+ @app.post("/feishu/event")
380
+ async def feishu_event(request: Request):
381
+ """Feishu Developer Console → Event Subscription → Request URL: /feishu/event"""
382
+ body = await request.body()
383
+ try:
384
+ payload = json.loads(body)
385
+ except Exception:
386
+ raise HTTPException(400, "Invalid JSON")
387
+
388
+ # 先验签再做任何事。URL verification challenge 也要验——飞书在发
389
+ # challenge 时同样带 token/签名,放它过去等于给攻击者一个免验探测点。
390
+ trusted, reason = _verify_feishu_request(
391
+ {k.lower(): v for k, v in request.headers.items()}, body, payload
392
+ )
393
+ if not trusted:
394
+ logger.warning("拒绝未通过校验的 /feishu/event 请求: %s", reason)
395
+ raise HTTPException(401, f"Unverified Feishu event: {reason}")
396
+
397
+ # URL verification challenge
398
+ if "challenge" in payload:
399
+ return {"challenge": payload["challenge"]}
400
+
401
+ # Extract sender + message_id
402
+ event = payload.get("event", {})
403
+ message = event.get("message", {})
404
+ sender = event.get("sender", {})
405
+ feishu_user_id = sender.get("sender_id", {}).get("open_id", "")
406
+ message_id = message.get("message_id", "")
407
+
408
+ if not feishu_user_id:
409
+ return {"code": 0}
410
+
411
+ # Handle /bind command
412
+ msg_type = message.get("message_type", "")
413
+ if msg_type == "text":
414
+ try:
415
+ text_content = json.loads(message.get("content", "{}")).get("text", "").strip()
416
+ except Exception:
417
+ text_content = ""
418
+
419
+ if text_content.upper().startswith("/BIND ") or text_content.upper().startswith("ARIA-BIND-"):
420
+ raw_code = text_content.upper().replace("/BIND ", "").strip()
421
+ # Normalize: "ARIA-BIND-ARIA-XXXX" → "aria-xxxx"
422
+ client_id_upper = raw_code.replace("ARIA-BIND-", "").replace("ARIA-", "aria-").lower()
423
+ _bind(feishu_user_id, client_id_upper)
424
+ await _send_feishu_text(
425
+ feishu_user_id,
426
+ f"✅ 绑定成功!你的 Aria 实例已连接。\n"
427
+ f"Client ID: {client_id_upper}\n"
428
+ f"现在可以直接发消息与你的 Aria 交互了。"
429
+ )
430
+ return {"code": 0}
431
+
432
+ # Route to local aria instance
433
+ result = await _route_to_local(feishu_user_id, payload)
434
+
435
+ if result is None:
436
+ # No binding found or client offline
437
+ if not _lookup_client(feishu_user_id):
438
+ await _send_feishu_text(
439
+ feishu_user_id,
440
+ "👋 你好!要开始使用 Aria,请:\n"
441
+ "1. 在你的电脑上安装 Aria Code\n"
442
+ "2. 运行 `python3 setup_wizard.py` 完成配置\n"
443
+ "3. 发送绑定码绑定你的账户"
444
+ )
445
+ else:
446
+ await _reply_feishu(
447
+ message_id,
448
+ "⚠️ Aria 本机未连接。请确保你的电脑上 `aria_relay_client.py` 正在运行。",
449
+ color="yellow",
450
+ )
451
+
452
+ return {"code": 0}
453
+
454
+
455
+ # ── Status endpoint ───────────────────────────────────────────────────────────
456
+
457
+ @app.get("/status")
458
+ async def status(request: Request):
459
+ """健康检查。**不再无条件列出 client_ids**——它就是 /bind 的绑定凭证,
460
+ 公开等于把劫持所需的最后一块拼图直接送出去(见 _verify_feishu_request
461
+ 上方记录的攻击链)。带正确 RELAY_SECRET 时才返回明细,否则只给计数。
462
+ """
463
+ body = {
464
+ "connected_clients": len(_connections),
465
+ "total_bindings": get_db().execute("SELECT COUNT(*) FROM bindings").fetchone()[0],
466
+ "feishu_app_configured": bool(_FEISHU_APP_ID),
467
+ "event_verification": (
468
+ "encrypt_key" if _FEISHU_ENCRYPT_KEY
469
+ else "verification_token" if _FEISHU_VERIFICATION_TOKEN
470
+ else "DISABLED (unverified events allowed)" if _ALLOW_UNVERIFIED_EVENTS
471
+ else "enforced (events rejected: no key configured)"
472
+ ),
473
+ }
474
+ presented = request.headers.get("x-relay-secret", "")
475
+ if _RELAY_SECRET and hmac.compare_digest(presented, _RELAY_SECRET):
476
+ body["client_ids"] = list(_connections.keys())
477
+ return body
478
+
479
+
480
+ # ── Entry point ───────────────────────────────────────────────────────────────
481
+
482
+ if __name__ == "__main__":
483
+ import uvicorn
484
+ uvicorn.run(
485
+ "aria_relay_server:app",
486
+ host="0.0.0.0",
487
+ port=int(os.environ.get("PORT", "8765")),
488
+ reload=False,
489
+ log_level="info",
490
+ )