aria-code 0.167.0__py3-none-win_amd64.whl

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